summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpjd <pjd@FreeBSD.org>2008-11-17 20:49:29 +0000
committerpjd <pjd@FreeBSD.org>2008-11-17 20:49:29 +0000
commitbbe899b96e388a8b82439f81ed3707e0d9c6070d (patch)
tree81b89fa4ac6467771d5aa291a97f4665981a6108
parentd2f579595c362ce27b4d87e2c40e1c4e09b929e3 (diff)
downloadFreeBSD-src-bbe899b96e388a8b82439f81ed3707e0d9c6070d.zip
FreeBSD-src-bbe899b96e388a8b82439f81ed3707e0d9c6070d.tar.gz
Update ZFS from version 6 to 13 and bring some FreeBSD-specific changes.
This bring huge amount of changes, I'll enumerate only user-visible changes: - Delegated Administration Allows regular users to perform ZFS operations, like file system creation, snapshot creation, etc. - L2ARC Level 2 cache for ZFS - allows to use additional disks for cache. Huge performance improvements mostly for random read of mostly static content. - slog Allow to use additional disks for ZFS Intent Log to speed up operations like fsync(2). - vfs.zfs.super_owner Allows regular users to perform privileged operations on files stored on ZFS file systems owned by him. Very careful with this one. - chflags(2) Not all the flags are supported. This still needs work. - ZFSBoot Support to boot off of ZFS pool. Not finished, AFAIK. Submitted by: dfr - Snapshot properties - New failure modes Before if write requested failed, system paniced. Now one can select from one of three failure modes: - panic - panic on write error - wait - wait for disk to reappear - continue - serve read requests if possible, block write requests - Refquota, refreservation properties Just quota and reservation properties, but don't count space consumed by children file systems, clones and snapshots. - Sparse volumes ZVOLs that don't reserve space in the pool. - External attributes Compatible with extattr(2). - NFSv4-ACLs Not sure about the status, might not be complete yet. Submitted by: trasz - Creation-time properties - Regression tests for zpool(8) command. Obtained from: OpenSolaris
-rw-r--r--cddl/compat/opensolaris/include/libshare.h36
-rw-r--r--cddl/compat/opensolaris/include/mnttab.h2
-rw-r--r--cddl/compat/opensolaris/misc/mnttab.c2
-rw-r--r--cddl/compat/opensolaris/misc/zmount.c5
-rw-r--r--cddl/compat/opensolaris/misc/zone.c4
-rw-r--r--cddl/contrib/opensolaris/cmd/zdb/zdb.86
-rw-r--r--cddl/contrib/opensolaris/cmd/zdb/zdb.c829
-rw-r--r--cddl/contrib/opensolaris/cmd/zdb/zdb_il.c39
-rw-r--r--cddl/contrib/opensolaris/cmd/zfs/zfs.81309
-rw-r--r--cddl/contrib/opensolaris/cmd/zfs/zfs_iter.c71
-rw-r--r--cddl/contrib/opensolaris/cmd/zfs/zfs_iter.h12
-rw-r--r--cddl/contrib/opensolaris/cmd/zfs/zfs_main.c1775
-rw-r--r--cddl/contrib/opensolaris/cmd/zinject/translate.c460
-rw-r--r--cddl/contrib/opensolaris/cmd/zinject/zinject.c770
-rw-r--r--cddl/contrib/opensolaris/cmd/zinject/zinject.h71
-rw-r--r--cddl/contrib/opensolaris/cmd/zpool/zpool.8868
-rw-r--r--cddl/contrib/opensolaris/cmd/zpool/zpool_iter.c22
-rw-r--r--cddl/contrib/opensolaris/cmd/zpool/zpool_main.c1338
-rw-r--r--cddl/contrib/opensolaris/cmd/zpool/zpool_util.c27
-rw-r--r--cddl/contrib/opensolaris/cmd/zpool/zpool_util.h14
-rw-r--r--cddl/contrib/opensolaris/cmd/zpool/zpool_vdev.c408
-rw-r--r--cddl/contrib/opensolaris/cmd/ztest/ztest.c1640
-rw-r--r--cddl/contrib/opensolaris/head/assert.h5
-rw-r--r--cddl/contrib/opensolaris/head/libintl.h11
-rw-r--r--cddl/contrib/opensolaris/head/synch.h18
-rw-r--r--cddl/contrib/opensolaris/head/thread.h2
-rw-r--r--cddl/contrib/opensolaris/lib/libnvpair/libnvpair.c359
-rw-r--r--cddl/contrib/opensolaris/lib/libnvpair/libnvpair.h10
-rw-r--r--cddl/contrib/opensolaris/lib/libuutil/common/libuutil.h10
-rw-r--r--cddl/contrib/opensolaris/lib/libuutil/common/libuutil_common.h17
-rw-r--r--cddl/contrib/opensolaris/lib/libuutil/common/uu_alloc.c25
-rw-r--r--cddl/contrib/opensolaris/lib/libuutil/common/uu_avl.c40
-rw-r--r--cddl/contrib/opensolaris/lib/libuutil/common/uu_dprintf.c2
-rw-r--r--cddl/contrib/opensolaris/lib/libuutil/common/uu_list.c71
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h271
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_changelist.c214
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c3026
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_graph.c154
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h82
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c515
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_mount.c769
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c1691
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c2103
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_status.c76
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c616
-rw-r--r--cddl/contrib/opensolaris/lib/libzpool/common/kernel.c120
-rw-r--r--cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h167
-rw-r--r--cddl/contrib/opensolaris/lib/libzpool/common/taskq.c13
-rw-r--r--cddl/contrib/opensolaris/lib/libzpool/common/util.c88
-rw-r--r--cddl/lib/libzfs/Makefile10
-rw-r--r--cddl/lib/libzpool/Makefile15
-rw-r--r--cddl/sbin/zpool/Makefile6
-rw-r--r--cddl/usr.bin/Makefile2
-rw-r--r--cddl/usr.bin/zinject/Makefile25
-rw-r--r--cddl/usr.bin/ztest/Makefile12
-rw-r--r--cddl/usr.sbin/zdb/Makefile11
-rw-r--r--lib/libc/gen/getvfsbyname.39
-rw-r--r--share/man/man9/VFS_SET.99
-rw-r--r--sys/boot/Makefile4
-rw-r--r--sys/boot/common/bootstrap.h1
-rw-r--r--sys/boot/i386/Makefile4
-rw-r--r--sys/boot/i386/libi386/bootinfo32.c1
-rw-r--r--sys/boot/i386/libi386/devicename.c2
-rw-r--r--sys/boot/i386/loader/Makefile10
-rw-r--r--sys/boot/i386/loader/conf.c14
-rw-r--r--sys/boot/i386/loader/main.c51
-rw-r--r--sys/boot/i386/zfsboot/Makefile108
-rw-r--r--sys/boot/i386/zfsboot/zfsboot.c944
-rw-r--r--sys/boot/i386/zfsboot/zfsldr.S402
-rw-r--r--sys/boot/zfs/Makefile29
-rw-r--r--sys/boot/zfs/zfs.c514
-rw-r--r--sys/boot/zfs/zfsimpl.c1443
-rw-r--r--sys/cddl/boot/zfs/README14
-rw-r--r--sys/cddl/boot/zfs/fletcher.c60
-rw-r--r--sys/cddl/boot/zfs/lzjb.c74
-rw-r--r--sys/cddl/boot/zfs/sha256.c127
-rw-r--r--sys/cddl/boot/zfs/zfsimpl.h1151
-rw-r--r--sys/cddl/boot/zfs/zfssubr.c193
-rw-r--r--sys/cddl/compat/opensolaris/kern/opensolaris_atomic.c9
-rw-r--r--sys/cddl/compat/opensolaris/kern/opensolaris_kmem.c23
-rw-r--r--sys/cddl/compat/opensolaris/kern/opensolaris_lookup.c112
-rw-r--r--sys/cddl/compat/opensolaris/kern/opensolaris_misc.c18
-rw-r--r--sys/cddl/compat/opensolaris/kern/opensolaris_policy.c136
-rw-r--r--sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c69
-rw-r--r--sys/cddl/compat/opensolaris/kern/opensolaris_zone.c104
-rw-r--r--sys/cddl/compat/opensolaris/sys/atomic.h9
-rw-r--r--sys/cddl/compat/opensolaris/sys/callb.h219
-rw-r--r--sys/cddl/compat/opensolaris/sys/cred.h21
-rw-r--r--sys/cddl/compat/opensolaris/sys/dnlc.h2
-rw-r--r--sys/cddl/compat/opensolaris/sys/file.h57
-rw-r--r--sys/cddl/compat/opensolaris/sys/kidmap.h41
-rw-r--r--sys/cddl/compat/opensolaris/sys/kmem.h5
-rw-r--r--sys/cddl/compat/opensolaris/sys/misc.h7
-rw-r--r--sys/cddl/compat/opensolaris/sys/mntent.h2
-rw-r--r--sys/cddl/compat/opensolaris/sys/param.h4
-rw-r--r--sys/cddl/compat/opensolaris/sys/pathname.h54
-rw-r--r--sys/cddl/compat/opensolaris/sys/policy.h34
-rw-r--r--sys/cddl/compat/opensolaris/sys/proc.h9
-rw-r--r--sys/cddl/compat/opensolaris/sys/refstr.h34
-rw-r--r--sys/cddl/compat/opensolaris/sys/sid.h54
-rw-r--r--sys/cddl/compat/opensolaris/sys/sig.h69
-rw-r--r--sys/cddl/compat/opensolaris/sys/sunddi.h2
-rw-r--r--sys/cddl/compat/opensolaris/sys/sysmacros.h4
-rw-r--r--sys/cddl/compat/opensolaris/sys/time.h3
-rw-r--r--sys/cddl/compat/opensolaris/sys/types.h7
-rw-r--r--sys/cddl/compat/opensolaris/sys/uio.h2
-rw-r--r--sys/cddl/compat/opensolaris/sys/vfs.h16
-rw-r--r--sys/cddl/compat/opensolaris/sys/vnode.h125
-rw-r--r--sys/cddl/compat/opensolaris/sys/zone.h6
-rw-r--r--sys/cddl/contrib/opensolaris/common/acl/acl_common.c1598
-rw-r--r--sys/cddl/contrib/opensolaris/common/acl/acl_common.h27
-rw-r--r--sys/cddl/contrib/opensolaris/common/atomic/amd64/atomic.S12
-rw-r--r--sys/cddl/contrib/opensolaris/common/atomic/i386/atomic.S49
-rw-r--r--sys/cddl/contrib/opensolaris/common/avl/avl.c66
-rw-r--r--sys/cddl/contrib/opensolaris/common/nvpair/nvpair.c306
-rw-r--r--sys/cddl/contrib/opensolaris/common/unicode/u8_textprep.c2130
-rw-r--r--sys/cddl/contrib/opensolaris/common/zfs/zfs_comutil.c65
-rw-r--r--sys/cddl/contrib/opensolaris/common/zfs/zfs_comutil.h44
-rw-r--r--sys/cddl/contrib/opensolaris/common/zfs/zfs_deleg.c234
-rw-r--r--sys/cddl/contrib/opensolaris/common/zfs/zfs_deleg.h81
-rw-r--r--sys/cddl/contrib/opensolaris/common/zfs/zfs_namecheck.c84
-rw-r--r--sys/cddl/contrib/opensolaris/common/zfs/zfs_namecheck.h7
-rw-r--r--sys/cddl/contrib/opensolaris/common/zfs/zfs_prop.c800
-rw-r--r--sys/cddl/contrib/opensolaris/common/zfs/zfs_prop.h91
-rw-r--r--sys/cddl/contrib/opensolaris/common/zfs/zpool_prop.c186
-rw-r--r--sys/cddl/contrib/opensolaris/common/zfs/zprop_common.c406
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/Makefile.files15
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/gfs.c397
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/vnode.c74
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/arc.c2295
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/bplist.c53
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dbuf.c405
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu.c285
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_object.c8
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_objset.c597
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c841
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_traverse.c36
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_tx.c248
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_zfetch.c23
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dnode.c403
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dnode_sync.c132
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c2413
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_deleg.c735
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dir.c496
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_pool.c395
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_prop.c313
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_scrub.c929
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_synctask.c35
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/metaslab.c156
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/refcount.c11
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/rrwlock.c249
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sha256.c32
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c3038
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_config.c232
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_errlog.c5
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_history.c121
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_misc.c776
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/space_map.c27
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/arc.h56
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/bplist.h10
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dbuf.h25
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu.h101
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_impl.h14
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_objset.h25
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_traverse.h3
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_tx.h6
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dnode.h26
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dataset.h108
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_deleg.h73
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dir.h41
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_pool.h54
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_prop.h13
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_synctask.h10
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/metaslab.h12
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/refcount.h6
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/rrwlock.h79
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa.h131
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa_boot.h45
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa_impl.h112
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/txg.h20
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/txg_impl.h9
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/uberblock_impl.h4
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/unique.h13
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev.h52
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev_disk.h12
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev_impl.h43
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zap.h76
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zap_impl.h40
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zap_leaf.h22
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_acl.h160
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_context.h24
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ctldir.h10
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_dir.h13
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_fuid.h125
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h72
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_vfsops.h63
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_znode.h162
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zil.h144
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zil_impl.h20
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio.h216
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio_checksum.h6
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio_impl.h172
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h12
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/txg.c148
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/unique.c17
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev.c1206
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_cache.c105
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_disk.c287
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_file.c73
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c445
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_label.c547
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_mirror.c155
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_missing.c14
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_queue.c84
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_raidz.c148
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_root.c22
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zap.c180
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zap_leaf.c178
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zap_micro.c421
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_acl.c2499
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_byteswap.c102
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ctldir.c423
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c326
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_fm.c120
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_fuid.c716
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c2294
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_log.c456
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_replay.c579
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_rlock.c16
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c796
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c1762
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_znode.c1101
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zil.c377
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio.c2470
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio_checksum.c96
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio_inject.c63
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c715
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/os/callb.c31
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/os/list.c84
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/os/taskq.c21
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/rpc/xdr.c58
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/rpc/xdr.h24
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/rpc/xdr_array.c15
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/acl.h (renamed from sys/cddl/compat/opensolaris/sys/acl.h)95
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/acl_impl.h61
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/avl.h25
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/byteorder.h49
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/callb.h11
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/cpuvar.h4
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/cred.h34
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/dkio.h26
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/dklabel.h33
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/extdirent.h77
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/fm/fs/zfs.h13
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/fm/protocol.h37
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/fm/util.h2
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h454
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/gfs.h37
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/idmap.h93
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/isa_defs.h56
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/list.h14
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/nvpair.h31
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/processor.h14
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/synch.h43
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/sysevent/eventdefs.h247
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/sysmacros.h108
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/u8_textprep.h91
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/u8_textprep_data.h35376
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/vnode.h395
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/zmod/zmod.c12
-rw-r--r--sys/conf/files1
-rw-r--r--sys/kern/kern_jail.c262
-rw-r--r--sys/kern/kern_osd.c301
-rw-r--r--sys/kern/kern_proc.c3
-rw-r--r--sys/kern/kern_thread.c3
-rw-r--r--sys/kern/vfs_lookup.c31
-rw-r--r--sys/kern/vfs_subr.c14
-rw-r--r--sys/modules/zfs/Makefile4
-rw-r--r--sys/sys/conf.h2
-rw-r--r--sys/sys/jail.h9
-rw-r--r--sys/sys/mount.h1
-rw-r--r--sys/sys/namei.h14
-rw-r--r--sys/sys/osd.h89
-rw-r--r--sys/sys/priv.h2
-rw-r--r--sys/sys/proc.h2
-rw-r--r--sys/sys/vnode.h1
-rw-r--r--tools/regression/zfs/LICENSE27
-rwxr-xr-xtools/regression/zfs/misc.sh436
-rw-r--r--tools/regression/zfs/zpool/add/cache.t126
-rw-r--r--tools/regression/zfs/zpool/add/disks.t60
-rw-r--r--tools/regression/zfs/zpool/add/doesnt_exist.t20
-rw-r--r--tools/regression/zfs/zpool/add/files.t231
-rw-r--r--tools/regression/zfs/zpool/add/log.t284
-rw-r--r--tools/regression/zfs/zpool/add/mirror.t86
-rw-r--r--tools/regression/zfs/zpool/add/option-f_inuse.t1186
-rw-r--r--tools/regression/zfs/zpool/add/option-f_replication_level_mismatch_0.t161
-rw-r--r--tools/regression/zfs/zpool/add/option-f_replication_level_mismatch_1.t785
-rw-r--r--tools/regression/zfs/zpool/add/option-f_size_mismatch.t434
-rw-r--r--tools/regression/zfs/zpool/add/option-f_type_mismatch.t408
-rw-r--r--tools/regression/zfs/zpool/add/option-n.t34
-rw-r--r--tools/regression/zfs/zpool/add/raidz1.t70
-rw-r--r--tools/regression/zfs/zpool/add/raidz2.t77
-rw-r--r--tools/regression/zfs/zpool/add/spare.t137
-rw-r--r--tools/regression/zfs/zpool/attach/log.t232
-rw-r--r--tools/regression/zfs/zpool/attach/mirror.t208
-rw-r--r--tools/regression/zfs/zpool/attach/option-f_inuse.t670
-rw-r--r--tools/regression/zfs/zpool/create/already_exists.t28
-rw-r--r--tools/regression/zfs/zpool/create/automount.t22
-rw-r--r--tools/regression/zfs/zpool/create/cache.t126
-rw-r--r--tools/regression/zfs/zpool/create/disks.t52
-rw-r--r--tools/regression/zfs/zpool/create/files.t189
-rw-r--r--tools/regression/zfs/zpool/create/log.t202
-rw-r--r--tools/regression/zfs/zpool/create/mirror.t83
-rw-r--r--tools/regression/zfs/zpool/create/option-R.t30
-rw-r--r--tools/regression/zfs/zpool/create/option-f_inuse.t297
-rw-r--r--tools/regression/zfs/zpool/create/option-f_replication_level_mismatch_0.t200
-rw-r--r--tools/regression/zfs/zpool/create/option-f_replication_level_mismatch_1.t540
-rw-r--r--tools/regression/zfs/zpool/create/option-f_size_mismatch.t256
-rw-r--r--tools/regression/zfs/zpool/create/option-f_type_mismatch.t414
-rw-r--r--tools/regression/zfs/zpool/create/option-m.t59
-rw-r--r--tools/regression/zfs/zpool/create/option-n.t23
-rw-r--r--tools/regression/zfs/zpool/create/option-o.t107
-rw-r--r--tools/regression/zfs/zpool/create/raidz1.t129
-rw-r--r--tools/regression/zfs/zpool/create/raidz2.t91
-rw-r--r--tools/regression/zfs/zpool/create/spare.t104
-rw-r--r--tools/regression/zfs/zpool/offline/io.t85
-rw-r--r--tools/regression/zfs/zpool/offline/log.t254
-rw-r--r--tools/regression/zfs/zpool/offline/mirror.t224
-rw-r--r--tools/regression/zfs/zpool/offline/option-t.t1110
-rw-r--r--tools/regression/zfs/zpool/offline/raidz1.t184
-rw-r--r--tools/regression/zfs/zpool/offline/raidz2.t202
-rw-r--r--tools/regression/zfs/zpool/remove/cache.t58
-rw-r--r--tools/regression/zfs/zpool/remove/spare.t106
-rw-r--r--tools/regression/zfs/zpool/replace/cache.t43
-rw-r--r--tools/regression/zfs/zpool/replace/disk.t48
-rw-r--r--tools/regression/zfs/zpool/replace/log.t152
-rw-r--r--tools/regression/zfs/zpool/replace/mirror.t134
-rw-r--r--tools/regression/zfs/zpool/replace/raidz1.t140
-rw-r--r--tools/regression/zfs/zpool/replace/raidz2.t607
-rw-r--r--tools/regression/zfs/zpool/replace/spare.t43
-rw-r--r--usr.bin/lsvfs/lsvfs.c55
341 files changed, 104583 insertions, 15923 deletions
diff --git a/cddl/compat/opensolaris/include/libshare.h b/cddl/compat/opensolaris/include/libshare.h
new file mode 100644
index 0000000..82aacad
--- /dev/null
+++ b/cddl/compat/opensolaris/include/libshare.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2007 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _OPENSOLARIS_LIBSHARE_H_
+#define _OPENSOLARIS_LIBSHARE_H_
+
+#define SA_OK 0
+
+#define SA_INIT_CONTROL_API 0
+
+#endif /* !_OPENSOLARIS_LIBSHARE_H_ */
diff --git a/cddl/compat/opensolaris/include/mnttab.h b/cddl/compat/opensolaris/include/mnttab.h
index 9d0bfc7..abd2f9d 100644
--- a/cddl/compat/opensolaris/include/mnttab.h
+++ b/cddl/compat/opensolaris/include/mnttab.h
@@ -9,6 +9,8 @@
#define MNTTAB _PATH_DEVNULL
#define MNT_LINE_MAX 1024
+#define umount2(p, f) unmount(p, f)
+
struct mnttab {
char *mnt_special;
char *mnt_mountp;
diff --git a/cddl/compat/opensolaris/misc/mnttab.c b/cddl/compat/opensolaris/misc/mnttab.c
index c1971ca..8c1c2d6 100644
--- a/cddl/compat/opensolaris/misc/mnttab.c
+++ b/cddl/compat/opensolaris/misc/mnttab.c
@@ -37,6 +37,8 @@ __FBSDID("$FreeBSD$");
#include <sys/mntent.h>
#include <sys/mnttab.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
static char *
mntopt(char **p)
diff --git a/cddl/compat/opensolaris/misc/zmount.c b/cddl/compat/opensolaris/misc/zmount.c
index d80a3b3..493a4fc 100644
--- a/cddl/compat/opensolaris/misc/zmount.c
+++ b/cddl/compat/opensolaris/misc/zmount.c
@@ -35,9 +35,10 @@ __FBSDID("$FreeBSD$");
#include <sys/mount.h>
#include <sys/uio.h>
#include <sys/mntent.h>
+#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
-#include <assert.h>
+#include <string.h>
static void
build_iovec(struct iovec **iov, int *iovlen, const char *name, void *val,
@@ -86,7 +87,7 @@ zmount(const char *spec, const char *dir, int mflag, char *fstype,
assert(optlen > 0);
optstr = strdup(optptr);
- assert(optptr != NULL);
+ assert(optstr != NULL);
iov = NULL;
iovlen = 0;
diff --git a/cddl/compat/opensolaris/misc/zone.c b/cddl/compat/opensolaris/misc/zone.c
index 1ce77cf..9d48e6d 100644
--- a/cddl/compat/opensolaris/misc/zone.c
+++ b/cddl/compat/opensolaris/misc/zone.c
@@ -32,7 +32,7 @@
#include <sys/sysctl.h>
#include <sys/zone.h>
-int
+zoneid_t
getzoneid(void)
{
size_t size;
@@ -42,5 +42,5 @@ getzoneid(void)
size = sizeof(jailid);
if (sysctlbyname("security.jail.jailed", &jailid, &size, NULL, 0) == -1)
assert(!"No security.jail.jailed sysctl!");
- return (jailid);
+ return ((zoneid_t)jailid);
}
diff --git a/cddl/contrib/opensolaris/cmd/zdb/zdb.8 b/cddl/contrib/opensolaris/cmd/zdb/zdb.8
index 87913f6..c9d5aed 100644
--- a/cddl/contrib/opensolaris/cmd/zdb/zdb.8
+++ b/cddl/contrib/opensolaris/cmd/zdb/zdb.8
@@ -28,13 +28,17 @@ zdb \- ZFS debugger
.fi
.SH DESCRIPTION
+.sp
.LP
The \fBzdb\fR command is used by support engineers to diagnose failures and gather statistics. Since the \fBZFS\fR file system is always consistent on disk and is self-repairing, \fBzdb\fR should only be run under the direction by a support engineer.
+.sp
.LP
If no arguments are specified, \fBzdb\fR, performs basic consistency checks on the pool and associated datasets, and report any problems detected.
+.sp
.LP
Any options supported by this command are internal to Sun and subject to change at any time.
.SH EXIT STATUS
+.sp
.LP
The following exit values are returned:
.sp
@@ -71,6 +75,7 @@ Invalid command line options were specified.
.RE
.SH ATTRIBUTES
+.sp
.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -89,5 +94,6 @@ Interface StabilityUnstable
.TE
.SH SEE ALSO
+.sp
.LP
\fBzfs\fR(1M), \fBzpool\fR(1M), \fBattributes\fR(5)
diff --git a/cddl/contrib/opensolaris/cmd/zdb/zdb.c b/cddl/contrib/opensolaris/cmd/zdb/zdb.c
index 2dc459d..16b2787 100644
--- a/cddl/contrib/opensolaris/cmd/zdb/zdb.c
+++ b/cddl/contrib/opensolaris/cmd/zdb/zdb.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <stdio.h>
#include <stdio_ext.h>
#include <stdlib.h>
@@ -51,6 +49,10 @@
#include <sys/dmu_traverse.h>
#include <sys/zio_checksum.h>
#include <sys/zio_compress.h>
+#include <sys/zfs_fuid.h>
+#undef ZFS_MAXNAMELEN
+#undef verify
+#include <libzfs.h>
const char cmdname[] = "zdb";
uint8_t dump_opt[256];
@@ -62,6 +64,9 @@ uint64_t *zopt_object = NULL;
int zopt_objects = 0;
int zdb_advance = ADVANCE_PRE;
zbookmark_t zdb_noread = { 0, 0, ZB_NO_LEVEL, 0 };
+libzfs_handle_t *g_zfs;
+boolean_t zdb_sig_user_data = B_TRUE;
+int zdb_sig_cksumalg = ZIO_CHECKSUM_SHA256;
/*
* These libumem hooks provide a reasonable set of defaults for the allocator's
@@ -83,12 +88,15 @@ static void
usage(void)
{
(void) fprintf(stderr,
- "Usage: %s [-udibcsvLU] [-O order] [-B os:obj:level:blkid] "
+ "Usage: %s [-udibcsvL] [-U cachefile_path] [-O order] "
+ "[-B os:obj:level:blkid] [-S user:cksumalg] "
"dataset [object...]\n"
" %s -C [pool]\n"
" %s -l dev\n"
- " %s -R vdev:offset:size:flags\n",
- cmdname, cmdname, cmdname, cmdname);
+ " %s -R pool:vdev:offset:size:flags\n"
+ " %s [-p path_to_vdev_dir]\n"
+ " %s -e pool | GUID | devid ...\n",
+ cmdname, cmdname, cmdname, cmdname, cmdname, cmdname);
(void) fprintf(stderr, " -u uberblock\n");
(void) fprintf(stderr, " -d datasets\n");
@@ -97,16 +105,22 @@ usage(void)
(void) fprintf(stderr, " -b block statistics\n");
(void) fprintf(stderr, " -c checksum all data blocks\n");
(void) fprintf(stderr, " -s report stats on zdb's I/O\n");
+ (void) fprintf(stderr, " -S <user|all>:<cksum_alg|all> -- "
+ "dump blkptr signatures\n");
(void) fprintf(stderr, " -v verbose (applies to all others)\n");
(void) fprintf(stderr, " -l dump label contents\n");
(void) fprintf(stderr, " -L live pool (allows some errors)\n");
(void) fprintf(stderr, " -O [!]<pre|post|prune|data|holes> "
"visitation order\n");
- (void) fprintf(stderr, " -U use zpool.cache in /tmp\n");
+ (void) fprintf(stderr, " -U cachefile_path -- use alternate "
+ "cachefile\n");
(void) fprintf(stderr, " -B objset:object:level:blkid -- "
"simulate bad block\n");
- (void) fprintf(stderr, " -R read and display block from a"
+ (void) fprintf(stderr, " -R read and display block from a "
"device\n");
+ (void) fprintf(stderr, " -e Pool is exported/destroyed/"
+ "has altroot\n");
+ (void) fprintf(stderr, " -p <Path to vdev dir> (use with -e)\n");
(void) fprintf(stderr, "Specify an option more than once (e.g. -bb) "
"to make only that option verbose\n");
(void) fprintf(stderr, "Default is to dump everything non-verbosely\n");
@@ -367,6 +381,44 @@ dump_zap(objset_t *os, uint64_t object, void *data, size_t size)
zap_cursor_fini(&zc);
}
+/*ARGSUSED*/
+static void
+dump_zpldir(objset_t *os, uint64_t object, void *data, size_t size)
+{
+ zap_cursor_t zc;
+ zap_attribute_t attr;
+ const char *typenames[] = {
+ /* 0 */ "not specified",
+ /* 1 */ "FIFO",
+ /* 2 */ "Character Device",
+ /* 3 */ "3 (invalid)",
+ /* 4 */ "Directory",
+ /* 5 */ "5 (invalid)",
+ /* 6 */ "Block Device",
+ /* 7 */ "7 (invalid)",
+ /* 8 */ "Regular File",
+ /* 9 */ "9 (invalid)",
+ /* 10 */ "Symbolic Link",
+ /* 11 */ "11 (invalid)",
+ /* 12 */ "Socket",
+ /* 13 */ "Door",
+ /* 14 */ "Event Port",
+ /* 15 */ "15 (invalid)",
+ };
+
+ dump_zap_stats(os, object);
+ (void) printf("\n");
+
+ for (zap_cursor_init(&zc, os, object);
+ zap_cursor_retrieve(&zc, &attr) == 0;
+ zap_cursor_advance(&zc)) {
+ (void) printf("\t\t%s = %lld (type: %s)\n",
+ attr.za_name, ZFS_DIRENT_OBJ(attr.za_first_integer),
+ typenames[ZFS_DIRENT_TYPE(attr.za_first_integer)]);
+ }
+ zap_cursor_fini(&zc);
+}
+
static void
dump_spacemap(objset_t *os, space_map_obj_t *smo, space_map_t *sm)
{
@@ -456,10 +508,7 @@ dump_metaslabs(spa_t *spa)
for (c = 0; c < rvd->vdev_children; c++) {
vd = rvd->vdev_child[c];
- spa_config_enter(spa, RW_READER, FTAG);
- (void) printf("\n vdev %llu = %s\n\n",
- (u_longlong_t)vd->vdev_id, vdev_description(vd));
- spa_config_exit(spa, FTAG);
+ (void) printf("\n vdev %llu\n\n", (u_longlong_t)vd->vdev_id);
if (dump_opt['d'] <= 5) {
(void) printf("\t%10s %10s %5s\n",
@@ -477,7 +526,6 @@ static void
dump_dtl(vdev_t *vd, int indent)
{
avl_tree_t *t = &vd->vdev_dtl_map.sm_root;
- spa_t *spa = vd->vdev_spa;
space_seg_t *ss;
vdev_t *pvd;
int c;
@@ -485,9 +533,10 @@ dump_dtl(vdev_t *vd, int indent)
if (indent == 0)
(void) printf("\nDirty time logs:\n\n");
- spa_config_enter(spa, RW_READER, FTAG);
- (void) printf("\t%*s%s\n", indent, "", vdev_description(vd));
- spa_config_exit(spa, FTAG);
+ (void) printf("\t%*s%s\n", indent, "",
+ vd->vdev_path ? vd->vdev_path :
+ vd->vdev_parent ? vd->vdev_ops->vdev_op_type :
+ spa_name(vd->vdev_spa));
for (ss = avl_first(t); ss; ss = AVL_NEXT(t, ss)) {
/*
@@ -670,36 +719,49 @@ dump_dsl_dir(objset_t *os, uint64_t object, void *data, size_t size)
{
dsl_dir_phys_t *dd = data;
time_t crtime;
- char used[6], compressed[6], uncompressed[6], quota[6], resv[6];
+ char nice[6];
if (dd == NULL)
return;
- ASSERT(size == sizeof (*dd));
+ ASSERT3U(size, >=, sizeof (dsl_dir_phys_t));
crtime = dd->dd_creation_time;
- nicenum(dd->dd_used_bytes, used);
- nicenum(dd->dd_compressed_bytes, compressed);
- nicenum(dd->dd_uncompressed_bytes, uncompressed);
- nicenum(dd->dd_quota, quota);
- nicenum(dd->dd_reserved, resv);
-
(void) printf("\t\tcreation_time = %s", ctime(&crtime));
(void) printf("\t\thead_dataset_obj = %llu\n",
(u_longlong_t)dd->dd_head_dataset_obj);
(void) printf("\t\tparent_dir_obj = %llu\n",
(u_longlong_t)dd->dd_parent_obj);
- (void) printf("\t\tclone_parent_obj = %llu\n",
- (u_longlong_t)dd->dd_clone_parent_obj);
+ (void) printf("\t\torigin_obj = %llu\n",
+ (u_longlong_t)dd->dd_origin_obj);
(void) printf("\t\tchild_dir_zapobj = %llu\n",
(u_longlong_t)dd->dd_child_dir_zapobj);
- (void) printf("\t\tused_bytes = %s\n", used);
- (void) printf("\t\tcompressed_bytes = %s\n", compressed);
- (void) printf("\t\tuncompressed_bytes = %s\n", uncompressed);
- (void) printf("\t\tquota = %s\n", quota);
- (void) printf("\t\treserved = %s\n", resv);
+ nicenum(dd->dd_used_bytes, nice);
+ (void) printf("\t\tused_bytes = %s\n", nice);
+ nicenum(dd->dd_compressed_bytes, nice);
+ (void) printf("\t\tcompressed_bytes = %s\n", nice);
+ nicenum(dd->dd_uncompressed_bytes, nice);
+ (void) printf("\t\tuncompressed_bytes = %s\n", nice);
+ nicenum(dd->dd_quota, nice);
+ (void) printf("\t\tquota = %s\n", nice);
+ nicenum(dd->dd_reserved, nice);
+ (void) printf("\t\treserved = %s\n", nice);
(void) printf("\t\tprops_zapobj = %llu\n",
(u_longlong_t)dd->dd_props_zapobj);
+ (void) printf("\t\tdeleg_zapobj = %llu\n",
+ (u_longlong_t)dd->dd_deleg_zapobj);
+ (void) printf("\t\tflags = %llx\n",
+ (u_longlong_t)dd->dd_flags);
+
+#define DO(which) \
+ nicenum(dd->dd_used_breakdown[DD_USED_ ## which], nice); \
+ (void) printf("\t\tused_breakdown[" #which "] = %s\n", nice)
+ DO(HEAD);
+ DO(SNAP);
+ DO(CHILD);
+ DO(CHILD_RSRV);
+ DO(REFRSRV);
+#undef DO
}
/*ARGSUSED*/
@@ -722,7 +784,7 @@ dump_dsl_dataset(objset_t *os, uint64_t object, void *data, size_t size)
nicenum(ds->ds_unique_bytes, unique);
sprintf_blkptr(blkbuf, BP_SPRINTF_LEN, &ds->ds_bp);
- (void) printf("\t\tdataset_obj = %llu\n",
+ (void) printf("\t\tdir_obj = %llu\n",
(u_longlong_t)ds->ds_dir_obj);
(void) printf("\t\tprev_snap_obj = %llu\n",
(u_longlong_t)ds->ds_prev_snap_obj);
@@ -749,6 +811,10 @@ dump_dsl_dataset(objset_t *os, uint64_t object, void *data, size_t size)
(u_longlong_t)ds->ds_guid);
(void) printf("\t\tflags = %llx\n",
(u_longlong_t)ds->ds_flags);
+ (void) printf("\t\tnext_clones_obj = %llu\n",
+ (u_longlong_t)ds->ds_next_clones_obj);
+ (void) printf("\t\tprops_obj = %llu\n",
+ (u_longlong_t)ds->ds_props_obj);
(void) printf("\t\tbp = %s\n", blkbuf);
}
@@ -765,9 +831,11 @@ dump_bplist(objset_t *mos, uint64_t object, char *name)
if (dump_opt['d'] < 3)
return;
+ mutex_init(&bpl.bpl_lock, NULL, MUTEX_DEFAULT, NULL);
VERIFY(0 == bplist_open(&bpl, mos, object));
if (bplist_empty(&bpl)) {
bplist_close(&bpl);
+ mutex_destroy(&bpl.bpl_lock);
return;
}
@@ -785,6 +853,7 @@ dump_bplist(objset_t *mos, uint64_t object, char *name)
if (dump_opt['d'] < 5) {
bplist_close(&bpl);
+ mutex_destroy(&bpl.bpl_lock);
return;
}
@@ -800,6 +869,65 @@ dump_bplist(objset_t *mos, uint64_t object, char *name)
}
bplist_close(&bpl);
+ mutex_destroy(&bpl.bpl_lock);
+}
+
+static avl_tree_t idx_tree;
+static avl_tree_t domain_tree;
+static boolean_t fuid_table_loaded;
+
+static void
+fuid_table_destroy()
+{
+ if (fuid_table_loaded) {
+ zfs_fuid_table_destroy(&idx_tree, &domain_tree);
+ fuid_table_loaded = B_FALSE;
+ }
+}
+
+/*
+ * print uid or gid information.
+ * For normal POSIX id just the id is printed in decimal format.
+ * For CIFS files with FUID the fuid is printed in hex followed by
+ * the doman-rid string.
+ */
+static void
+print_idstr(uint64_t id, const char *id_type)
+{
+ if (FUID_INDEX(id)) {
+ char *domain;
+
+ domain = zfs_fuid_idx_domain(&idx_tree, FUID_INDEX(id));
+ (void) printf("\t%s %llx [%s-%d]\n", id_type,
+ (u_longlong_t)id, domain, (int)FUID_RID(id));
+ } else {
+ (void) printf("\t%s %llu\n", id_type, (u_longlong_t)id);
+ }
+
+}
+
+static void
+dump_uidgid(objset_t *os, znode_phys_t *zp)
+{
+ uint32_t uid_idx, gid_idx;
+
+ uid_idx = FUID_INDEX(zp->zp_uid);
+ gid_idx = FUID_INDEX(zp->zp_gid);
+
+ /* Load domain table, if not already loaded */
+ if (!fuid_table_loaded && (uid_idx || gid_idx)) {
+ uint64_t fuid_obj;
+
+ /* first find the fuid object. It lives in the master node */
+ VERIFY(zap_lookup(os, MASTER_NODE_OBJ, ZFS_FUID_TABLES,
+ 8, 1, &fuid_obj) == 0);
+ (void) zfs_fuid_table_load(os, fuid_obj,
+ &idx_tree, &domain_tree);
+ fuid_table_loaded = B_TRUE;
+ }
+
+ print_idstr(zp->zp_uid, "uid");
+ print_idstr(zp->zp_gid, "gid");
}
/*ARGSUSED*/
@@ -830,6 +958,7 @@ dump_znode(objset_t *os, uint64_t object, void *data, size_t size)
z_ctime = (time_t)zp->zp_ctime[0];
(void) printf("\tpath %s\n", path);
+ dump_uidgid(os, zp);
(void) printf("\tatime %s", ctime(&z_atime));
(void) printf("\tmtime %s", ctime(&z_mtime));
(void) printf("\tctime %s", ctime(&z_ctime));
@@ -874,9 +1003,9 @@ static object_viewer_t *object_viewer[DMU_OT_NUMTYPES] = {
dump_zap, /* DSL props */
dump_dsl_dataset, /* DSL dataset */
dump_znode, /* ZFS znode */
- dump_acl, /* ZFS ACL */
+ dump_acl, /* ZFS V0 ACL */
dump_uint8, /* ZFS plain file */
- dump_zap, /* ZFS directory */
+ dump_zpldir, /* ZFS directory */
dump_zap, /* ZFS master node */
dump_zap, /* ZFS delete queue */
dump_uint8, /* zvol object */
@@ -888,6 +1017,13 @@ static object_viewer_t *object_viewer[DMU_OT_NUMTYPES] = {
dump_uint8, /* SPA history */
dump_uint64, /* SPA history offsets */
dump_zap, /* Pool properties */
+ dump_zap, /* DSL permissions */
+ dump_acl, /* ZFS ACL */
+ dump_uint8, /* ZFS SYSACL */
+ dump_none, /* FUID nvlist */
+ dump_packed_nvlist, /* FUID nvlist size */
+ dump_zap, /* DSL dataset next clones */
+ dump_zap, /* DSL scrub queue */
};
static void
@@ -930,13 +1066,15 @@ dump_object(objset_t *os, uint64_t object, int verbosity, int *print_header)
aux[0] = '\0';
- if (doi.doi_checksum != ZIO_CHECKSUM_INHERIT || verbosity >= 6)
+ if (doi.doi_checksum != ZIO_CHECKSUM_INHERIT || verbosity >= 6) {
(void) snprintf(aux + strlen(aux), sizeof (aux), " (K=%s)",
- zio_checksum_table[doi.doi_checksum].ci_name);
+ zio_checksum_table[doi.doi_checksum].ci_name);
+ }
- if (doi.doi_compress != ZIO_COMPRESS_INHERIT || verbosity >= 6)
+ if (doi.doi_compress != ZIO_COMPRESS_INHERIT || verbosity >= 6) {
(void) snprintf(aux + strlen(aux), sizeof (aux), " (Z=%s)",
- zio_compress_table[doi.doi_compress].ci_name);
+ zio_compress_table[doi.doi_compress].ci_name);
+ }
(void) printf("%10lld %3u %5s %5s %5s %5s %s%s\n",
(u_longlong_t)object, doi.doi_indirection, iblk, dblk, lsize,
@@ -972,13 +1110,13 @@ dump_object(objset_t *os, uint64_t object, int verbosity, int *print_header)
}
for (;;) {
- error = dnode_next_offset(dn, B_FALSE, &start, minlvl,
- blkfill, 0);
+ error = dnode_next_offset(dn,
+ 0, &start, minlvl, blkfill, 0);
if (error)
break;
end = start;
- error = dnode_next_offset(dn, B_TRUE, &end, minlvl,
- blkfill, 0);
+ error = dnode_next_offset(dn,
+ DNODE_FIND_HOLE, &end, minlvl, blkfill, 0);
nicenum(end - start, segsize);
(void) printf("\t\tsegment [%016llx, %016llx)"
" size %5s\n", (u_longlong_t)start,
@@ -996,7 +1134,6 @@ dump_object(objset_t *os, uint64_t object, int verbosity, int *print_header)
static char *objset_types[DMU_OST_NUMTYPES] = {
"NONE", "META", "ZPL", "ZVOL", "OTHER", "ANY" };
-/*ARGSUSED*/
static void
dump_dir(objset_t *os)
{
@@ -1019,8 +1156,8 @@ dump_dir(objset_t *os)
if (dds.dds_type == DMU_OST_META) {
dds.dds_creation_txg = TXG_INITIAL;
usedobjs = os->os->os_rootbp->blk_fill;
- refdbytes =
- os->os->os_spa->spa_dsl_pool->dp_mos_dir->dd_used_bytes;
+ refdbytes = os->os->os_spa->spa_dsl_pool->
+ dp_mos_dir->dd_phys->dd_used_bytes;
} else {
dmu_objset_space(os, &refdbytes, &scratch, &usedobjs, &scratch);
}
@@ -1054,6 +1191,9 @@ dump_dir(objset_t *os)
if (verbosity < 2)
return;
+ if (os->os->os_rootbp->blk_birth == 0)
+ return;
+
if (zopt_objects != 0) {
for (i = 0; i < zopt_objects; i++)
dump_object(os, zopt_object[i], verbosity,
@@ -1115,6 +1255,52 @@ dump_config(const char *pool)
}
static void
+dump_cachefile(const char *cachefile)
+{
+ int fd;
+ struct stat64 statbuf;
+ char *buf;
+ nvlist_t *config;
+
+ if ((fd = open64(cachefile, O_RDONLY)) < 0) {
+ (void) printf("cannot open '%s': %s\n", cachefile,
+ strerror(errno));
+ exit(1);
+ }
+
+ if (fstat64(fd, &statbuf) != 0) {
+ (void) printf("failed to stat '%s': %s\n", cachefile,
+ strerror(errno));
+ exit(1);
+ }
+
+ if ((buf = malloc(statbuf.st_size)) == NULL) {
+ (void) fprintf(stderr, "failed to allocate %llu bytes\n",
+ (u_longlong_t)statbuf.st_size);
+ exit(1);
+ }
+
+ if (read(fd, buf, statbuf.st_size) != statbuf.st_size) {
+ (void) fprintf(stderr, "failed to read %llu bytes\n",
+ (u_longlong_t)statbuf.st_size);
+ exit(1);
+ }
+
+ (void) close(fd);
+
+ if (nvlist_unpack(buf, statbuf.st_size, &config, 0) != 0) {
+ (void) fprintf(stderr, "failed to unpack nvlist\n");
+ exit(1);
+ }
+
+ free(buf);
+
+ dump_nvlist(config, 0);
+
+ nvlist_free(config);
+}
+
+static void
dump_label(const char *dev)
{
int fd;
@@ -1136,15 +1322,7 @@ dump_label(const char *dev)
exit(1);
}
- if (S_ISCHR(statbuf.st_mode)) {
- if (ioctl(fd, DIOCGMEDIASIZE, &psize) != 0) {
- (void) printf("failed to get size '%s': %s\n", dev,
- strerror(errno));
- exit(1);
- }
- } else
- psize = statbuf.st_size;
-
+ psize = statbuf.st_size;
psize = P2ALIGN(psize, (uint64_t)sizeof (vdev_label_t));
for (l = 0; l < VDEV_LABELS; l++) {
@@ -1178,170 +1356,84 @@ dump_one_dir(char *dsname, void *arg)
objset_t *os;
error = dmu_objset_open(dsname, DMU_OST_ANY,
- DS_MODE_STANDARD | DS_MODE_READONLY, &os);
+ DS_MODE_USER | DS_MODE_READONLY, &os);
if (error) {
(void) printf("Could not open %s\n", dsname);
return (0);
}
dump_dir(os);
dmu_objset_close(os);
+ fuid_table_destroy();
return (0);
}
static void
-zdb_space_map_load(spa_t *spa)
+zdb_leak(space_map_t *sm, uint64_t start, uint64_t size)
{
- vdev_t *rvd = spa->spa_root_vdev;
- vdev_t *vd;
- int c, m, error;
+ vdev_t *vd = sm->sm_ppd;
- for (c = 0; c < rvd->vdev_children; c++) {
- vd = rvd->vdev_child[c];
- for (m = 0; m < vd->vdev_ms_count; m++) {
- metaslab_t *msp = vd->vdev_ms[m];
- mutex_enter(&msp->ms_lock);
- error = space_map_load(&msp->ms_allocmap[0], NULL,
- SM_ALLOC, &msp->ms_smo, spa->spa_meta_objset);
- mutex_exit(&msp->ms_lock);
- if (error)
- fatal("%s bad space map #%d, error %d",
- spa->spa_name, c, error);
- }
- }
+ (void) printf("leaked space: vdev %llu, offset 0x%llx, size %llu\n",
+ (u_longlong_t)vd->vdev_id, (u_longlong_t)start, (u_longlong_t)size);
}
-static int
-zdb_space_map_claim(spa_t *spa, blkptr_t *bp, zbookmark_t *zb)
+/* ARGSUSED */
+static void
+zdb_space_map_load(space_map_t *sm)
{
- dva_t *dva = bp->blk_dva;
- vdev_t *vd;
- metaslab_t *msp;
- space_map_t *allocmap, *freemap;
- int error;
- int d;
- blkptr_t blk = *bp;
-
- for (d = 0; d < BP_GET_NDVAS(bp); d++) {
- uint64_t vdev = DVA_GET_VDEV(&dva[d]);
- uint64_t offset = DVA_GET_OFFSET(&dva[d]);
- uint64_t size = DVA_GET_ASIZE(&dva[d]);
-
- if ((vd = vdev_lookup_top(spa, vdev)) == NULL)
- return (ENXIO);
-
- if ((offset >> vd->vdev_ms_shift) >= vd->vdev_ms_count)
- return (ENXIO);
-
- msp = vd->vdev_ms[offset >> vd->vdev_ms_shift];
- allocmap = &msp->ms_allocmap[0];
- freemap = &msp->ms_freemap[0];
-
- /* Prepare our copy of the bp in case we need to read GBHs */
- if (DVA_GET_GANG(&dva[d])) {
- size = vdev_psize_to_asize(vd, SPA_GANGBLOCKSIZE);
- DVA_SET_ASIZE(&blk.blk_dva[d], size);
- DVA_SET_GANG(&blk.blk_dva[d], 0);
- }
-
- mutex_enter(&msp->ms_lock);
- if (space_map_contains(freemap, offset, size)) {
- mutex_exit(&msp->ms_lock);
- return (EAGAIN); /* allocated more than once */
- }
-
- if (!space_map_contains(allocmap, offset, size)) {
- mutex_exit(&msp->ms_lock);
- return (ESTALE); /* not allocated at all */
- }
-
- space_map_remove(allocmap, offset, size);
- space_map_add(freemap, offset, size);
-
- mutex_exit(&msp->ms_lock);
- }
-
- if (BP_IS_GANG(bp)) {
- zio_gbh_phys_t gbh;
- int g;
-
- /* LINTED - compile time assert */
- ASSERT(sizeof (zio_gbh_phys_t) == SPA_GANGBLOCKSIZE);
-
- BP_SET_CHECKSUM(&blk, ZIO_CHECKSUM_GANG_HEADER);
- BP_SET_PSIZE(&blk, SPA_GANGBLOCKSIZE);
- BP_SET_LSIZE(&blk, SPA_GANGBLOCKSIZE);
- BP_SET_COMPRESS(&blk, ZIO_COMPRESS_OFF);
- error = zio_wait(zio_read(NULL, spa, &blk, &gbh,
- SPA_GANGBLOCKSIZE, NULL, NULL, ZIO_PRIORITY_SYNC_READ,
- ZIO_FLAG_CANFAIL | ZIO_FLAG_CONFIG_HELD, zb));
- if (error)
- return (error);
- if (BP_SHOULD_BYTESWAP(&blk))
- byteswap_uint64_array(&gbh, SPA_GANGBLOCKSIZE);
- for (g = 0; g < SPA_GBH_NBLKPTRS; g++) {
- if (BP_IS_HOLE(&gbh.zg_blkptr[g]))
- break;
- error = zdb_space_map_claim(spa, &gbh.zg_blkptr[g], zb);
- if (error)
- return (error);
- }
- }
-
- return (0);
}
static void
-zdb_leak(space_map_t *sm, uint64_t start, uint64_t size)
+zdb_space_map_unload(space_map_t *sm)
{
- metaslab_t *msp;
-
- /* LINTED */
- msp = (metaslab_t *)((char *)sm - offsetof(metaslab_t, ms_allocmap[0]));
+ space_map_vacate(sm, zdb_leak, sm);
+}
- (void) printf("leaked space: vdev %llu, offset 0x%llx, size %llu\n",
- (u_longlong_t)msp->ms_group->mg_vd->vdev_id,
- (u_longlong_t)start,
- (u_longlong_t)size);
+/* ARGSUSED */
+static void
+zdb_space_map_claim(space_map_t *sm, uint64_t start, uint64_t size)
+{
}
+static space_map_ops_t zdb_space_map_ops = {
+ zdb_space_map_load,
+ zdb_space_map_unload,
+ NULL, /* alloc */
+ zdb_space_map_claim,
+ NULL /* free */
+};
+
static void
-zdb_space_map_unload(spa_t *spa)
+zdb_leak_init(spa_t *spa)
{
vdev_t *rvd = spa->spa_root_vdev;
- vdev_t *vd;
- int c, m;
- for (c = 0; c < rvd->vdev_children; c++) {
- vd = rvd->vdev_child[c];
- for (m = 0; m < vd->vdev_ms_count; m++) {
+ for (int c = 0; c < rvd->vdev_children; c++) {
+ vdev_t *vd = rvd->vdev_child[c];
+ for (int m = 0; m < vd->vdev_ms_count; m++) {
metaslab_t *msp = vd->vdev_ms[m];
mutex_enter(&msp->ms_lock);
- space_map_vacate(&msp->ms_allocmap[0], zdb_leak,
- &msp->ms_allocmap[0]);
- space_map_unload(&msp->ms_allocmap[0]);
- space_map_vacate(&msp->ms_freemap[0], NULL, NULL);
+ VERIFY(space_map_load(&msp->ms_map, &zdb_space_map_ops,
+ SM_ALLOC, &msp->ms_smo, spa->spa_meta_objset) == 0);
+ msp->ms_map.sm_ppd = vd;
mutex_exit(&msp->ms_lock);
}
}
}
static void
-zdb_refresh_ubsync(spa_t *spa)
+zdb_leak_fini(spa_t *spa)
{
- uberblock_t ub = { 0 };
vdev_t *rvd = spa->spa_root_vdev;
- zio_t *zio;
- /*
- * Reload the uberblock.
- */
- zio = zio_root(spa, NULL, NULL,
- ZIO_FLAG_CANFAIL | ZIO_FLAG_SPECULATIVE);
- vdev_uberblock_load(zio, rvd, &ub);
- (void) zio_wait(zio);
-
- if (ub.ub_txg != 0)
- spa->spa_ubsync = ub;
+ for (int c = 0; c < rvd->vdev_children; c++) {
+ vdev_t *vd = rvd->vdev_child[c];
+ for (int m = 0; m < vd->vdev_ms_count; m++) {
+ metaslab_t *msp = vd->vdev_ms[m];
+ mutex_enter(&msp->ms_lock);
+ space_map_unload(&msp->ms_map);
+ mutex_exit(&msp->ms_lock);
+ }
+ }
}
/*
@@ -1371,9 +1463,7 @@ typedef struct zdb_cb {
static void
zdb_count_block(spa_t *spa, zdb_cb_t *zcb, blkptr_t *bp, int type)
{
- int i, error;
-
- for (i = 0; i < 4; i++) {
+ for (int i = 0; i < 4; i++) {
int l = (i < 2) ? BP_GET_LEVEL(bp) : ZB_TOTAL;
int t = (i & 1) ? type : DMU_OT_TOTAL;
zdb_blkstats_t *zb = &zcb->zcb_type[l][t];
@@ -1384,21 +1474,34 @@ zdb_count_block(spa_t *spa, zdb_cb_t *zcb, blkptr_t *bp, int type)
zb->zb_count++;
}
- if (dump_opt['L'])
- return;
-
- error = zdb_space_map_claim(spa, bp, &zcb->zcb_cache->bc_bookmark);
-
- if (error == 0)
- return;
-
- if (error == EAGAIN)
- (void) fatal("double-allocation, bp=%p", bp);
-
- if (error == ESTALE)
- (void) fatal("reference to freed block, bp=%p", bp);
+ if (dump_opt['S']) {
+ boolean_t print_sig;
+
+ print_sig = !zdb_sig_user_data || (BP_GET_LEVEL(bp) == 0 &&
+ BP_GET_TYPE(bp) == DMU_OT_PLAIN_FILE_CONTENTS);
+
+ if (BP_GET_CHECKSUM(bp) < zdb_sig_cksumalg)
+ print_sig = B_FALSE;
+
+ if (print_sig) {
+ (void) printf("%llu\t%lld\t%lld\t%s\t%s\t%s\t"
+ "%llx:%llx:%llx:%llx\n",
+ (u_longlong_t)BP_GET_LEVEL(bp),
+ (longlong_t)BP_GET_PSIZE(bp),
+ (longlong_t)BP_GET_NDVAS(bp),
+ dmu_ot[BP_GET_TYPE(bp)].ot_name,
+ zio_checksum_table[BP_GET_CHECKSUM(bp)].ci_name,
+ zio_compress_table[BP_GET_COMPRESS(bp)].ci_name,
+ (u_longlong_t)bp->blk_cksum.zc_word[0],
+ (u_longlong_t)bp->blk_cksum.zc_word[1],
+ (u_longlong_t)bp->blk_cksum.zc_word[2],
+ (u_longlong_t)bp->blk_cksum.zc_word[3]);
+ }
+ }
- (void) fatal("fatal error %d in bp %p", error, bp);
+ if (!dump_opt['L'])
+ VERIFY(zio_wait(zio_claim(NULL, spa, spa_first_txg(spa), bp,
+ NULL, NULL, ZIO_FLAG_MUSTSUCCEED)) == 0);
}
static int
@@ -1411,9 +1514,16 @@ zdb_blkptr_cb(traverse_blk_cache_t *bc, spa_t *spa, void *arg)
char blkbuf[BP_SPRINTF_LEN];
int error = 0;
+ ASSERT(!BP_IS_HOLE(bp));
+
+ zdb_count_block(spa, zcb, bp, type);
+
if (bc->bc_errno) {
if (zcb->zcb_readfails++ < 10 && dump_opt['L']) {
- zdb_refresh_ubsync(spa);
+ uberblock_t ub;
+ vdev_uberblock_load(NULL, spa->spa_root_vdev, &ub);
+ if (ub.ub_txg != 0)
+ spa->spa_ubsync = ub;
error = EAGAIN;
} else {
zcb->zcb_haderrors = 1;
@@ -1426,35 +1536,32 @@ zdb_blkptr_cb(traverse_blk_cache_t *bc, spa_t *spa, void *arg)
else
blkbuf[0] = '\0';
- (void) printf("zdb_blkptr_cb: Got error %d reading "
- "<%llu, %llu, %lld, %llx> %s -- %s\n",
- bc->bc_errno,
- (u_longlong_t)zb->zb_objset,
- (u_longlong_t)zb->zb_object,
- (u_longlong_t)zb->zb_level,
- (u_longlong_t)zb->zb_blkid,
- blkbuf,
- error == EAGAIN ? "retrying" : "skipping");
+ if (!dump_opt['S']) {
+ (void) printf("zdb_blkptr_cb: Got error %d reading "
+ "<%llu, %llu, %lld, %llx> %s -- %s\n",
+ bc->bc_errno,
+ (u_longlong_t)zb->zb_objset,
+ (u_longlong_t)zb->zb_object,
+ (u_longlong_t)zb->zb_level,
+ (u_longlong_t)zb->zb_blkid,
+ blkbuf,
+ error == EAGAIN ? "retrying" : "skipping");
+ }
return (error);
}
zcb->zcb_readfails = 0;
- ASSERT(!BP_IS_HOLE(bp));
-
if (dump_opt['b'] >= 4) {
sprintf_blkptr(blkbuf, BP_SPRINTF_LEN, bp);
(void) printf("objset %llu object %llu offset 0x%llx %s\n",
(u_longlong_t)zb->zb_objset,
(u_longlong_t)zb->zb_object,
(u_longlong_t)blkid2offset(bc->bc_dnode,
- zb->zb_level, zb->zb_blkid),
- blkbuf);
+ zb->zb_level, zb->zb_blkid), blkbuf);
}
- zdb_count_block(spa, zcb, bp, type);
-
return (0);
}
@@ -1465,32 +1572,35 @@ dump_block_stats(spa_t *spa)
zdb_cb_t zcb = { 0 };
traverse_blk_cache_t dummy_cache = { 0 };
zdb_blkstats_t *zb, *tzb;
- uint64_t alloc, space;
+ uint64_t alloc, space, logalloc;
+ vdev_t *rvd = spa->spa_root_vdev;
int leaks = 0;
int advance = zdb_advance;
- int flags;
- int e;
+ int c, e, flags;
zcb.zcb_cache = &dummy_cache;
- if (dump_opt['c'])
+ if (dump_opt['c'] || dump_opt['S'])
advance |= ADVANCE_DATA;
advance |= ADVANCE_PRUNE | ADVANCE_ZIL;
- (void) printf("\nTraversing all blocks to %sverify"
- " nothing leaked ...\n",
- dump_opt['c'] ? "verify checksums and " : "");
+ if (!dump_opt['S']) {
+ (void) printf("\nTraversing all blocks to %sverify"
+ " nothing leaked ...\n",
+ dump_opt['c'] ? "verify checksums and " : "");
+ }
/*
- * Load all space maps. As we traverse the pool, if we find a block
- * that's not in its space map, that indicates a double-allocation,
- * reference to a freed block, or an unclaimed block. Otherwise we
- * remove the block from the space map. If the space maps are not
- * empty when we're done, that indicates leaked blocks.
+ * Load all space maps as SM_ALLOC maps, then traverse the pool
+ * claiming each block we discover. If the pool is perfectly
+ * consistent, the space maps will be empty when we're done.
+ * Anything left over is a leak; any block we can't claim (because
+ * it's not part of any space map) is a double allocation,
+ * reference to a freed block, or an unclaimed log block.
*/
if (!dump_opt['L'])
- zdb_space_map_load(spa);
+ zdb_leak_init(spa);
/*
* If there's a deferred-free bplist, process that first.
@@ -1533,7 +1643,7 @@ dump_block_stats(spa_t *spa)
traverse_fini(th);
- if (zcb.zcb_haderrors) {
+ if (zcb.zcb_haderrors && !dump_opt['S']) {
(void) printf("\nError counts:\n\n");
(void) printf("\t%5s %s\n", "errno", "count");
for (e = 0; e < 256; e++) {
@@ -1548,7 +1658,15 @@ dump_block_stats(spa_t *spa)
* Report any leaked segments.
*/
if (!dump_opt['L'])
- zdb_space_map_unload(spa);
+ zdb_leak_fini(spa);
+
+ /*
+ * If we're interested in printing out the blkptr signatures,
+ * return now as we don't print out anything else (including
+ * errors and leaks).
+ */
+ if (dump_opt['S'])
+ return (zcb.zcb_haderrors ? 3 : 0);
if (dump_opt['L'])
(void) printf("\n\n *** Live pool traversal; "
@@ -1557,17 +1675,27 @@ dump_block_stats(spa_t *spa)
alloc = spa_get_alloc(spa);
space = spa_get_space(spa);
+ /*
+ * Log blocks allocated from a separate log device don't count
+ * as part of the normal pool space; factor them in here.
+ */
+ logalloc = 0;
+
+ for (c = 0; c < rvd->vdev_children; c++)
+ if (rvd->vdev_child[c]->vdev_islog)
+ logalloc += rvd->vdev_child[c]->vdev_stat.vs_alloc;
+
tzb = &zcb.zcb_type[ZB_TOTAL][DMU_OT_TOTAL];
- if (tzb->zb_asize == alloc) {
+ if (tzb->zb_asize == alloc + logalloc) {
(void) printf("\n\tNo leaks (block sum matches space"
" maps exactly)\n");
} else {
(void) printf("block traversal size %llu != alloc %llu "
"(leaked %lld)\n",
(u_longlong_t)tzb->zb_asize,
- (u_longlong_t)alloc,
- (u_longlong_t)(alloc - tzb->zb_asize));
+ (u_longlong_t)alloc + logalloc,
+ (u_longlong_t)(alloc + logalloc - tzb->zb_asize));
leaks = 1;
}
@@ -1682,11 +1810,11 @@ dump_zpool(spa_t *spa)
dump_dtl(spa->spa_root_vdev, 0);
dump_metaslabs(spa);
}
- (void) dmu_objset_find(spa->spa_name, dump_one_dir, NULL,
+ (void) dmu_objset_find(spa_name(spa), dump_one_dir, NULL,
DS_FIND_SNAPSHOTS | DS_FIND_CHILDREN);
}
- if (dump_opt['b'] || dump_opt['c'])
+ if (dump_opt['b'] || dump_opt['c'] || dump_opt['S'])
rc = dump_block_stats(spa);
if (dump_opt['s'])
@@ -1898,12 +2026,12 @@ zdb_read_block(char *thing, spa_t **spap)
zio_t *zio;
vdev_t *vd;
void *buf;
- char *s, *p, *dup, *spa_name, *vdev, *flagstr;
+ char *s, *p, *dup, *pool, *vdev, *flagstr;
int i, error, zio_flags;
dup = strdup(thing);
s = strtok(dup, ":");
- spa_name = s ? s : "";
+ pool = s ? s : "";
s = strtok(NULL, ":");
vdev = s ? s : "";
s = strtok(NULL, ":");
@@ -1953,14 +2081,13 @@ zdb_read_block(char *thing, spa_t **spap)
}
}
- if (spa == NULL || spa->spa_name == NULL ||
- strcmp(spa->spa_name, spa_name)) {
- if (spa && spa->spa_name)
+ if (spa == NULL || strcmp(spa_name(spa), pool) != 0) {
+ if (spa)
spa_close(spa, (void *)zdb_read_block);
- error = spa_open(spa_name, spap, (void *)zdb_read_block);
+ error = spa_open(pool, spap, (void *)zdb_read_block);
if (error)
fatal("Failed to open pool '%s': %s",
- spa_name, strerror(error));
+ pool, strerror(error));
spa = *spap;
}
@@ -1980,16 +2107,15 @@ zdb_read_block(char *thing, spa_t **spap)
buf = umem_alloc(size, UMEM_NOFAIL);
zio_flags = ZIO_FLAG_DONT_CACHE | ZIO_FLAG_DONT_QUEUE |
- ZIO_FLAG_DONT_PROPAGATE | ZIO_FLAG_DONT_RETRY | ZIO_FLAG_NOBOOKMARK;
-
- if (flags & ZDB_FLAG_PHYS)
- zio_flags |= ZIO_FLAG_PHYSICAL;
+ ZIO_FLAG_DONT_PROPAGATE | ZIO_FLAG_DONT_RETRY;
+ spa_config_enter(spa, SCL_STATE, FTAG, RW_READER);
zio = zio_root(spa, NULL, NULL, 0);
/* XXX todo - cons up a BP so RAID-Z will be happy */
zio_nowait(zio_vdev_child_io(zio, NULL, vd, offset, buf, size,
ZIO_TYPE_READ, ZIO_PRIORITY_SYNC_READ, zio_flags, NULL, NULL));
error = zio_wait(zio);
+ spa_config_exit(spa, SCL_STATE, FTAG);
if (error) {
(void) printf("Read of %s failed, error: %d\n", thing, error);
@@ -2014,6 +2140,132 @@ out:
free(dup);
}
+static boolean_t
+nvlist_string_match(nvlist_t *config, char *name, char *tgt)
+{
+ char *s;
+
+ if (nvlist_lookup_string(config, name, &s) != 0)
+ return (B_FALSE);
+
+ return (strcmp(s, tgt) == 0);
+}
+
+static boolean_t
+nvlist_uint64_match(nvlist_t *config, char *name, uint64_t tgt)
+{
+ uint64_t val;
+
+ if (nvlist_lookup_uint64(config, name, &val) != 0)
+ return (B_FALSE);
+
+ return (val == tgt);
+}
+
+static boolean_t
+vdev_child_guid_match(nvlist_t *vdev, uint64_t guid)
+{
+ nvlist_t **child;
+ uint_t c, children;
+
+ verify(nvlist_lookup_nvlist_array(vdev, ZPOOL_CONFIG_CHILDREN,
+ &child, &children) == 0);
+ for (c = 0; c < children; ++c)
+ if (nvlist_uint64_match(child[c], ZPOOL_CONFIG_GUID, guid))
+ return (B_TRUE);
+ return (B_FALSE);
+}
+
+static boolean_t
+vdev_child_string_match(nvlist_t *vdev, char *tgt)
+{
+ nvlist_t **child;
+ uint_t c, children;
+
+ verify(nvlist_lookup_nvlist_array(vdev, ZPOOL_CONFIG_CHILDREN,
+ &child, &children) == 0);
+ for (c = 0; c < children; ++c) {
+ if (nvlist_string_match(child[c], ZPOOL_CONFIG_PATH, tgt) ||
+ nvlist_string_match(child[c], ZPOOL_CONFIG_DEVID, tgt))
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
+static boolean_t
+vdev_guid_match(nvlist_t *config, uint64_t guid)
+{
+ nvlist_t *nvroot;
+
+ verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
+ &nvroot) == 0);
+
+ return (nvlist_uint64_match(nvroot, ZPOOL_CONFIG_GUID, guid) ||
+ vdev_child_guid_match(nvroot, guid));
+}
+
+static boolean_t
+vdev_string_match(nvlist_t *config, char *tgt)
+{
+ nvlist_t *nvroot;
+
+ verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
+ &nvroot) == 0);
+
+ return (vdev_child_string_match(nvroot, tgt));
+}
+
+static boolean_t
+pool_match(nvlist_t *config, char *tgt)
+{
+ uint64_t guid = strtoull(tgt, NULL, 0);
+
+ if (guid != 0) {
+ return (
+ nvlist_uint64_match(config, ZPOOL_CONFIG_POOL_GUID, guid) ||
+ vdev_guid_match(config, guid));
+ } else {
+ return (
+ nvlist_string_match(config, ZPOOL_CONFIG_POOL_NAME, tgt) ||
+ vdev_string_match(config, tgt));
+ }
+}
+
+static int
+find_exported_zpool(char *pool_id, nvlist_t **configp, char *vdev_dir)
+{
+ nvlist_t *pools;
+ int error = ENOENT;
+ nvlist_t *match = NULL;
+
+ if (vdev_dir != NULL)
+ pools = zpool_find_import_activeok(g_zfs, 1, &vdev_dir);
+ else
+ pools = zpool_find_import_activeok(g_zfs, 0, NULL);
+
+ if (pools != NULL) {
+ nvpair_t *elem = NULL;
+
+ while ((elem = nvlist_next_nvpair(pools, elem)) != NULL) {
+ verify(nvpair_value_nvlist(elem, configp) == 0);
+ if (pool_match(*configp, pool_id)) {
+ if (match != NULL) {
+ (void) fatal(
+ "More than one matching pool - "
+ "specify guid/devid/device path.");
+ } else {
+ match = *configp;
+ error = 0;
+ }
+ }
+ }
+ }
+
+ *configp = error ? NULL : match;
+
+ return (error);
+}
+
int
main(int argc, char **argv)
{
@@ -2026,13 +2278,15 @@ main(int argc, char **argv)
int verbose = 0;
int error;
int flag, set;
+ int exported = 0;
+ char *vdev_dir = NULL;
(void) setrlimit(RLIMIT_NOFILE, &rl);
(void) enable_extended_FILE_stdio(-1, -1);
dprintf_setup(&argc, argv);
- while ((c = getopt(argc, argv, "udibcsvCLO:B:UlR")) != -1) {
+ while ((c = getopt(argc, argv, "udibcsvCLO:B:S:U:lRep:")) != -1) {
switch (c) {
case 'u':
case 'd':
@@ -2093,7 +2347,31 @@ main(int argc, char **argv)
verbose++;
break;
case 'U':
- spa_config_dir = "/tmp";
+ spa_config_path = optarg;
+ break;
+ case 'e':
+ exported = 1;
+ break;
+ case 'p':
+ vdev_dir = optarg;
+ break;
+ case 'S':
+ dump_opt[c]++;
+ dump_all = 0;
+ zdb_sig_user_data = (strncmp(optarg, "user:", 5) == 0);
+ if (!zdb_sig_user_data && strncmp(optarg, "all:", 4))
+ usage();
+ endstr = strchr(optarg, ':') + 1;
+ if (strcmp(endstr, "fletcher2") == 0)
+ zdb_sig_cksumalg = ZIO_CHECKSUM_FLETCHER_2;
+ else if (strcmp(endstr, "fletcher4") == 0)
+ zdb_sig_cksumalg = ZIO_CHECKSUM_FLETCHER_4;
+ else if (strcmp(endstr, "sha256") == 0)
+ zdb_sig_cksumalg = ZIO_CHECKSUM_SHA256;
+ else if (strcmp(endstr, "all") == 0)
+ zdb_sig_cksumalg = ZIO_CHECKSUM_FLETCHER_2;
+ else
+ usage();
break;
default:
usage();
@@ -2101,7 +2379,12 @@ main(int argc, char **argv)
}
}
+ if (vdev_dir != NULL && exported == 0)
+ (void) fatal("-p option requires use of -e\n");
+
kernel_init(FREAD);
+ g_zfs = libzfs_init();
+ ASSERT(g_zfs != NULL);
/*
* Disable vdev caching. If we don't do this, live pool traversal
@@ -2121,7 +2404,7 @@ main(int argc, char **argv)
if (argc < 1) {
if (dump_opt['C']) {
- dump_config(NULL);
+ dump_cachefile(spa_config_path);
return (0);
}
usage();
@@ -2156,11 +2439,48 @@ main(int argc, char **argv)
if (dump_opt['C'])
dump_config(argv[0]);
- if (strchr(argv[0], '/') != NULL) {
- error = dmu_objset_open(argv[0], DMU_OST_ANY,
- DS_MODE_STANDARD | DS_MODE_READONLY, &os);
- } else {
- error = spa_open(argv[0], &spa, FTAG);
+ error = 0;
+ if (exported) {
+ /*
+ * Check to see if the name refers to an exported zpool
+ */
+ char *slash;
+ nvlist_t *exported_conf = NULL;
+
+ if ((slash = strchr(argv[0], '/')) != NULL)
+ *slash = '\0';
+
+ error = find_exported_zpool(argv[0], &exported_conf, vdev_dir);
+ if (error == 0) {
+ nvlist_t *nvl = NULL;
+
+ if (vdev_dir != NULL) {
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
+ error = ENOMEM;
+ else if (nvlist_add_string(nvl,
+ zpool_prop_to_name(ZPOOL_PROP_ALTROOT),
+ vdev_dir) != 0)
+ error = ENOMEM;
+ }
+
+ if (error == 0)
+ error = spa_import_faulted(argv[0],
+ exported_conf, nvl);
+
+ nvlist_free(nvl);
+ }
+
+ if (slash != NULL)
+ *slash = '/';
+ }
+
+ if (error == 0) {
+ if (strchr(argv[0], '/') != NULL) {
+ error = dmu_objset_open(argv[0], DMU_OST_ANY,
+ DS_MODE_USER | DS_MODE_READONLY, &os);
+ } else {
+ error = spa_open(argv[0], &spa, FTAG);
+ }
}
if (error)
@@ -2187,6 +2507,9 @@ main(int argc, char **argv)
spa_close(spa, FTAG);
}
+ fuid_table_destroy();
+
+ libzfs_fini(g_zfs);
kernel_fini();
return (0);
diff --git a/cddl/contrib/opensolaris/cmd/zdb/zdb_il.c b/cddl/contrib/opensolaris/cmd/zdb/zdb_il.c
index 10dfe20..02d35a0 100644
--- a/cddl/contrib/opensolaris/cmd/zdb/zdb_il.c
+++ b/cddl/contrib/opensolaris/cmd/zdb/zdb_il.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -233,19 +233,26 @@ typedef struct zil_rec_info {
} zil_rec_info_t;
static zil_rec_info_t zil_rec_info[TX_MAX_TYPE] = {
- { NULL, "Total " },
- { zil_prt_rec_create, "TX_CREATE " },
- { zil_prt_rec_create, "TX_MKDIR " },
- { zil_prt_rec_create, "TX_MKXATTR " },
- { zil_prt_rec_create, "TX_SYMLINK " },
- { zil_prt_rec_remove, "TX_REMOVE " },
- { zil_prt_rec_remove, "TX_RMDIR " },
- { zil_prt_rec_link, "TX_LINK " },
- { zil_prt_rec_rename, "TX_RENAME " },
- { zil_prt_rec_write, "TX_WRITE " },
- { zil_prt_rec_truncate, "TX_TRUNCATE" },
- { zil_prt_rec_setattr, "TX_SETATTR " },
- { zil_prt_rec_acl, "TX_ACL " },
+ { NULL, "Total " },
+ { zil_prt_rec_create, "TX_CREATE " },
+ { zil_prt_rec_create, "TX_MKDIR " },
+ { zil_prt_rec_create, "TX_MKXATTR " },
+ { zil_prt_rec_create, "TX_SYMLINK " },
+ { zil_prt_rec_remove, "TX_REMOVE " },
+ { zil_prt_rec_remove, "TX_RMDIR " },
+ { zil_prt_rec_link, "TX_LINK " },
+ { zil_prt_rec_rename, "TX_RENAME " },
+ { zil_prt_rec_write, "TX_WRITE " },
+ { zil_prt_rec_truncate, "TX_TRUNCATE " },
+ { zil_prt_rec_setattr, "TX_SETATTR " },
+ { zil_prt_rec_acl, "TX_ACL_V0 " },
+ { zil_prt_rec_acl, "TX_ACL_ACL " },
+ { zil_prt_rec_create, "TX_CREATE_ACL " },
+ { zil_prt_rec_create, "TX_CREATE_ATTR " },
+ { zil_prt_rec_create, "TX_CREATE_ACL_ATTR " },
+ { zil_prt_rec_create, "TX_MKDIR_ACL " },
+ { zil_prt_rec_create, "TX_MKDIR_ATTR " },
+ { zil_prt_rec_create, "TX_MKDIR_ACL_ATTR " },
};
/* ARGSUSED */
@@ -255,12 +262,14 @@ print_log_record(zilog_t *zilog, lr_t *lr, void *arg, uint64_t claim_txg)
int txtype;
int verbose = MAX(dump_opt['d'], dump_opt['i']);
+ /* reduce size of txtype to strip off TX_CI bit */
txtype = lr->lrc_txtype;
ASSERT(txtype != 0 && (uint_t)txtype < TX_MAX_TYPE);
ASSERT(lr->lrc_txg);
- (void) printf("\t\t%s len %6llu, txg %llu, seq %llu\n",
+ (void) printf("\t\t%s%s len %6llu, txg %llu, seq %llu\n",
+ (lr->lrc_txtype & TX_CI) ? "CI-" : "",
zil_rec_info[txtype].zri_name,
(u_longlong_t)lr->lrc_reclen,
(u_longlong_t)lr->lrc_txg,
diff --git a/cddl/contrib/opensolaris/cmd/zfs/zfs.8 b/cddl/contrib/opensolaris/cmd/zfs/zfs.8
index d49cb87..7217fe8 100644
--- a/cddl/contrib/opensolaris/cmd/zfs/zfs.8
+++ b/cddl/contrib/opensolaris/cmd/zfs/zfs.8
@@ -18,7 +18,7 @@
.\"
.\" CDDL HEADER END
.\" Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved.
-.TH zfs 1M "16 Mar 2007" "SunOS 5.11" "System Administration Commands"
+.TH zfs 1M "8 Apr 2008" "SunOS 5.11" "System Administration Commands"
.SH NAME
zfs \- configures ZFS file systems
.SH SYNOPSIS
@@ -29,12 +29,12 @@ zfs \- configures ZFS file systems
.LP
.nf
-\fBzfs\fR \fBcreate\fR [[\fB-o\fR property=\fIvalue\fR]]... \fIfilesystem\fR
+\fBzfs\fR \fBcreate\fR [\fB-p\fR] [\fB-o\fR \fIproperty\fR=\fIvalue\fR] ... \fIfilesystem\fR
.fi
.LP
.nf
-\fBzfs\fR \fBcreate\fR [\fB-s\fR] [\fB-b\fR \fIblocksize\fR] [[\fB-o\fR property=\fIvalue\fR]]... \fB-V\fR \fIsize\fR \fIvolume\fR
+\fBzfs\fR \fBcreate\fR [\fB-ps\fR] [\fB-b\fR \fIblocksize\fR] [\fB-o\fR \fIproperty\fR=\fIvalue\fR] ... \fB-V\fR \fIsize\fR \fIvolume\fR
.fi
.LP
@@ -44,35 +44,44 @@ zfs \- configures ZFS file systems
.LP
.nf
-\fBzfs\fR \fBclone\fR \fIsnapshot\fR \fIfilesystem\fR|\fIvolume\fR
+\fBzfs\fR \fBsnapshot\fR [\fB-r\fR] \fIfilesystem@snapname\fR|\fIvolume@snapname\fR
.fi
.LP
.nf
-\fBzfs\fR \fBpromote\fR \fIfilesystem\fR
+\fBzfs\fR \fBrollback\fR [\fB-rRf\fR] \fIsnapshot\fR
+.fi
+
+.LP
+.nf
+\fBzfs\fR \fBclone\fR [\fB-p\fR] \fIsnapshot\fR \fIfilesystem\fR|\fIvolume\fR
+.fi
+
+.LP
+.nf
+\fBzfs\fR \fBpromote\fR \fIclone-filesystem\fR
.fi
.LP
.nf
\fBzfs\fR \fBrename\fR \fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR
- [\fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR]
+ \fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR
.fi
.LP
.nf
-\fBzfs\fR \fBsnapshot\fR [\fB-r\fR] \fIfilesystem@name\fR|\fIvolume@name\fR
+\fBzfs\fR \fBrename\fR [\fB-p\fR] \fIfilesystem\fR|\fIvolume\fR \fIfilesystem\fR|\fIvolume\fR
.fi
.LP
.nf
-\fBzfs\fR \fBrollback\fR [\fB-rRf\fR] \fIsnapshot\fR
+\fBzfs\fR \fBrename\fR \fB-r\fR \fIsnapshot\fR \fIsnapshot\fR
.fi
.LP
.nf
-\fBzfs\fR \fBlist\fR [\fB-rH\fR] [\fB-o\fR \fIprop\fR[,\fIprop\fR] ]... [ \fB-t\fR \fItype\fR[,\fItype\fR]...]
- [ \fB-s\fR \fIprop\fR [\fB-s\fR \fIprop\fR]... [ \fB-S\fR \fIprop\fR [\fB-S\fR \fIprop\fR]...
- [\fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR|\fI/pathname\fR|.\fI/pathname\fR ...
+\fBzfs\fR \fBlist\fR [\fB-rH\fR] [\fB-o\fR \fIproperty\fR[,...]] [\fB-t\fR \fItype\fR[,...]]
+ [\fB-s\fR \fIproperty\fR] ... [\fB-S\fR \fIproperty\fR ... [\fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR] ...
.fi
.LP
@@ -82,14 +91,23 @@ zfs \- configures ZFS file systems
.LP
.nf
-\fBzfs\fR \fBget\fR [\fB-rHp\fR] [\fB-o\fR \fIfield\fR[,\fIfield\fR]...]
- [\fB-s\fR \fIsource\fR[,\fIsource\fR]...] \fIall\fR | \fIproperty\fR[,\fIproperty\fR]...
+\fBzfs\fR \fBget\fR [\fB-rHp\fR] [\fB-o\fR \fIfield\fR[,...]] [\fB-s\fR \fIsource\fR[,...]] "\fIall\fR" | \fIproperty\fR[,...]
\fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR ...
.fi
.LP
.nf
-\fBzfs\fR \fBinherit\fR [\fB-r\fR] \fIproperty\fR \fIfilesystem\fR|\fIvolume\fR... ...
+\fBzfs\fR \fBinherit\fR [\fB-r\fR] \fIproperty\fR \fIfilesystem\fR|\fIvolume\fR ...
+.fi
+
+.LP
+.nf
+\fBzfs\fR \fBupgrade\fR [\fB-v\fR]
+.fi
+
+.LP
+.nf
+\fBzfs\fR \fBupgrade\fR [\fB-r\fR] [\fB-V\fR \fIversion\fR] \fB-a\fR | \fIfilesystem\fR
.fi
.LP
@@ -99,58 +117,81 @@ zfs \- configures ZFS file systems
.LP
.nf
-\fBzfs\fR \fBmount\fR [\fB-o \fIoptions\fR\fR] [\fB-O\fR] \fB-a\fR
+\fBzfs\fR \fBmount\fR [\fB-vO\fR] [\fB-o \fIoptions\fR\fR] \fB-a\fR | \fIfilesystem\fR
.fi
.LP
.nf
-\fBzfs\fR \fBmount\fR [\fB-o \fIoptions\fR\fR] [\fB-O\fR] \fIfilesystem\fR
+\fBzfs\fR \fBunmount\fR [\fB-f\fR] \fB-a\fR | \fIfilesystem\fR|\fImountpoint\fR
.fi
.LP
.nf
-\fBzfs\fR \fBunmount\fR [\fB-f\fR] \fB-a\fR
+\fBzfs\fR \fBshare\fR \fB-a\fR | \fIfilesystem\fR
.fi
.LP
.nf
-\fBzfs\fR \fBunmount\fR [\fB-f\fR] \fB\fIfilesystem\fR|\fImountpoint\fR\fR
+\fBzfs\fR \fBunshare\fR \fB-a\fR \fIfilesystem\fR|\fImountpoint\fR
.fi
.LP
.nf
-\fBzfs\fR \fBshare\fR \fB-a\fR
+\fBzfs\fR \fBsend\fR [\fB-vR\fR] [\fB-\fR[\fB-iI\fR] \fIsnapshot\fR] \fIsnapshot\fR
.fi
.LP
.nf
-\fBzfs\fR \fBshare\fR \fIfilesystem\fR
+\fBzfs\fR \fBreceive\fR [\fB-vnF\fR] \fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR
.fi
.LP
.nf
-\fBzfs\fR \fBunshare\fR [\fB-f\fR] \fB-a\fR
+\fBzfs\fR \fBreceive\fR [\fB-vnF\fR] \fB-d\fR \fIfilesystem\fR
.fi
.LP
.nf
-\fBzfs\fR \fBunshare\fR [\fB-f\fR] \fB\fIfilesystem\fR|\fImountpoint\fR\fR
+\fBzfs\fR \fBallow\fR [\fB-ldug\fR] "\fIeveryone\fR"|\fIuser\fR|\fIgroup\fR[,...] \fIperm\fR|\fI@setname\fR[,...]
+ \fIfilesystem\fR|\fIvolume\fR
.fi
.LP
.nf
-\fBzfs\fR \fBsend\fR [\fB-i\fR \fIsnapshot1\fR] \fB\fIsnapshot2\fR\fR
+\fBzfs\fR \fBallow\fR [\fB-ld\fR] \fB-e\fR \fIperm\fR|@\fIsetname\fR[,...] \fIfilesystem\fR|\fIvolume\fR
.fi
.LP
.nf
-\fBzfs\fR \fBreceive\fR [\fB-vnF\fR ] \fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR
+\fBzfs\fR \fBallow\fR \fB-c\fR \fIperm\fR|@\fIsetname\fR[,...] \fIfilesystem\fR|\fIvolume\fR
.fi
.LP
.nf
-\fBzfs\fR \fBreceive\fR [\fB-vnF\fR ] \fB-d\fR \fB\fIfilesystem\fR\fR
+\fBzfs\fR \fBallow\fR \fB-s\fR @setname \fIperm\fR|@\fIsetname\fR[,...] \fIfilesystem\fR|\fIvolume\fR
.fi
+
+.LP
+.nf
+\fBzfs\fR \fBunallow\fR [\fB-rldug\fR] "\fIeveryone\fR"|\fIuser\fR|\fIgroup\fR[,...] [\fIperm\fR|@\fIsetname\fR[,... ]]
+ \fIfilesystem\fR|\fIvolume\fR
+.fi
+
+.LP
+.nf
+\fBzfs\fR \fBunallow\fR [\fB-rld\fR] \fB-e\fR [\fIperm\fR|@\fIsetname\fR[,... ]] \fIfilesystem\fR|\fIvolume\fR
+.fi
+
+.LP
+.nf
+\fBzfs\fR \fBunallow\fR [\fB-r\fR] \fB-c\fR [\fIperm\fR|@\fIsetname\fR[ ... ]] \fIfilesystem\fR|\fIvolume\fR
+.fi
+
+.LP
+.nf
+\fBzfs\fR \fBunallow\fR [\fB-r\fR] \fB-s\fR @setname [\fIperm\fR|@\fIsetname\fR[,... ]] \fIfilesystem\fR|\fIvolume\fR
+.fi
+
.LP
.nf
\fBzfs\fR \fBjail\fR \fBjailid\fR \fB\fIfilesystem\fR\fR
@@ -161,6 +202,7 @@ zfs \- configures ZFS file systems
.fi
.SH DESCRIPTION
+.sp
.LP
The \fBzfs\fR command configures \fBZFS\fR datasets within a \fBZFS\fR storage pool, as described in \fBzpool\fR(1M). A
dataset is identified by a unique path within the \fBZFS\fR namespace. For example:
@@ -172,8 +214,10 @@ pool/{filesystem,volume,snapshot}
.in -2
.sp
+.sp
.LP
where the maximum length of a dataset name is \fBMAXNAMELEN\fR (256 bytes).
+.sp
.LP
A dataset can be one of the following:
.sp
@@ -210,65 +254,88 @@ A read-only version of a file system or volume at a given point in time. It is s
.RE
.SS "ZFS File System Hierarchy"
+.sp
.LP
A \fBZFS\fR storage pool is a logical collection of devices that provide space for datasets. A storage pool is also the root of the \fBZFS\fR file system hierarchy.
+.sp
.LP
The root of the pool can be accessed as a file system, such as mounting and unmounting, taking snapshots, and setting properties. The physical storage characteristics, however, are managed by the \fBzpool\fR(1M) command.
+.sp
.LP
See \fBzpool\fR(1M) for more information on creating and administering pools.
.SS "Snapshots"
+.sp
.LP
A snapshot is a read-only copy of a file system or volume. Snapshots can be created extremely quickly, and initially consume no additional space within the pool. As data within the active dataset changes, the snapshot consumes more data than would otherwise be shared with the active dataset.
+.sp
.LP
Snapshots can have arbitrary names. Snapshots of volumes can be cloned or rolled back, but cannot be accessed independently.
+.sp
.LP
File system snapshots can be accessed under the ".zfs/snapshot" directory in the root of the file system. Snapshots are automatically mounted on demand and may be unmounted at regular intervals. The visibility of the ".zfs" directory can be controlled by the "snapdir"
property.
.SS "Clones"
+.sp
.LP
A clone is a writable volume or file system whose initial contents are the same as another dataset. As with snapshots, creating a clone is nearly instantaneous, and initially consumes no additional space.
+.sp
.LP
Clones can only be created from a snapshot. When a snapshot is cloned, it creates an implicit dependency between the parent and child. Even though the clone is created somewhere else in the dataset hierarchy, the original snapshot cannot be destroyed as long as a clone exists. The "origin"
property exposes this dependency, and the \fBdestroy\fR command lists any such dependencies, if they exist.
+.sp
.LP
The clone parent-child dependency relationship can be reversed by using the "\fBpromote\fR" subcommand. This causes the "origin" file system to become a clone of the specified file system, which makes it possible to destroy the file system that the clone
was created from.
.SS "Mount Points"
+.sp
.LP
-Creating a \fBZFS\fR file system is a simple operation, so the number of file systems per system will likely be numerous. To cope with this, \fBZFS\fR automatically manages mounting and unmounting file systems without the need to edit the \fB/etc/vfstab\fR file.
-All automatically managed file systems are mounted by \fBZFS\fR at boot time.
+Creating a \fBZFS\fR file system is a simple operation, so the number of file systems per system is likely to be numerous. To cope with this, \fBZFS\fR automatically manages mounting and unmounting file systems without the need to edit the \fB/etc/vfstab\fR file. All automatically managed file systems are mounted by \fBZFS\fR at boot time.
+.sp
.LP
By default, file systems are mounted under \fB/\fIpath\fR\fR, where \fIpath\fR is the name of the file system in the \fBZFS\fR namespace. Directories are created and destroyed as needed.
+.sp
.LP
A file system can also have a mount point set in the "mountpoint" property. This directory is created as needed, and \fBZFS\fR automatically mounts the file system when the "\fBzfs mount -a\fR" command is invoked (without editing \fB/etc/vfstab\fR). The mountpoint property can be inherited, so if \fBpool/home\fR has a mount point of \fB/export/stuff\fR, then \fBpool/home/user\fR automatically inherits a mount point of \fB/export/stuff/user\fR.
+.sp
.LP
A file system mountpoint property of "none" prevents the file system from being mounted.
+.sp
.LP
If needed, \fBZFS\fR file systems can also be managed with traditional tools (\fBmount\fR, \fBumount\fR, \fB/etc/vfstab\fR). If a file system's mount point is set to "legacy", \fBZFS\fR makes no attempt to manage
the file system, and the administrator is responsible for mounting and unmounting the file system.
.SS "Zones"
+.sp
.LP
A \fBZFS\fR file system can be added to a non-global zone by using zonecfg's "\fBadd fs\fR" subcommand. A \fBZFS\fR file system that is added to a non-global zone must have its mountpoint property set to legacy.
+.sp
.LP
The physical properties of an added file system are controlled by the global administrator. However, the zone administrator can create, modify, or destroy files within the added file system, depending on how the file system is mounted.
+.sp
.LP
A dataset can also be delegated to a non-global zone by using zonecfg's "\fBadd dataset\fR" subcommand. You cannot delegate a dataset to one zone and the children of the same dataset to another zone. The zone administrator can change properties of the dataset or
any of its children. However, the "quota" property is controlled by the global administrator.
+.sp
.LP
A \fBZFS\fR volume can be added as a device to a non-global zone by using zonecfg's "\fBadd device\fR" subcommand. However, its physical properties can only be modified by the global administrator.
+.sp
.LP
For more information about \fBzonecfg\fR syntax, see \fBzonecfg\fR(1M).
+.sp
.LP
After a dataset is delegated to a non-global zone, the "zoned" property is automatically set. A zoned file system cannot be mounted in the global zone, since the zone administrator might have to set the mount point to an unacceptable value.
+.sp
.LP
The global administrator can forcibly clear the "zoned" property, though this should be done with extreme care. The global administrator should verify that all the mount points are acceptable before clearing the property.
.SS "Native Properties"
+.sp
.LP
Properties are divided into two types, native properties and user defined properties. Native properties either export internal statistics or control \fBZFS\fR behavior. In addition, native properties are either editable or read-only. User properties have no effect on \fBZFS\fR behavior,
but you can use them to annotate datasets in a way that is meaningful in your environment. For more information about user properties, see the "User Properties" section.
+.sp
.LP
Every dataset has a set of properties that export statistics about the dataset as well as control various behavior. Properties are inherited from the parent unless overridden by the child. Snapshot properties can not be edited; they always inherit their inheritable properties. Properties
that are not applicable to snapshots are not displayed.
+.sp
.LP
The values of numeric properties can be specified using the following human-readable suffixes (for example, "k", "KB", "M", "Gb", etc, up to Z for zettabyte). The following are all valid (and equal) specifications:
.sp
@@ -279,19 +346,35 @@ The values of numeric properties can be specified using the following human-read
.in -2
.sp
+.sp
.LP
-The values of non-numeric properties are case sensitive and must be lowercase, except for "mountpoint" and "sharenfs".
+The values of non-numeric properties are case sensitive and must be lowercase, except for "mountpoint", "sharenfs" and "sharesmb".
+.sp
.LP
-The first set of properties consist of read-only statistics about the dataset. These properties cannot be set, nor are they inherited. Native properties apply to all dataset types unless otherwise noted.
+The following native properties consist of read-only statistics about the dataset. These properties cannot be set, nor are they inherited. Native properties apply to all dataset types unless otherwise noted.
.sp
.ne 2
.mk
.na
-\fBtype\fR
+\fBavailable\fR
.ad
-.RS 17n
-.rt
-The type of dataset: "filesystem", "volume", "snapshot", or "clone".
+.sp .6
+.RS 4n
+The amount of space available to the dataset and all its children, assuming that there is no other activity in the pool. Because space is shared within a pool, availability can be limited by any number of factors, including physical pool size, quotas, reservations, or other datasets
+within the pool.
+.sp
+This property can also be referred to by its shortened column name, "avail".
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fBcompressratio\fR
+.ad
+.sp .6
+.RS 4n
+The compression ratio achieved for this dataset, expressed as a multiplier. Compression can be turned on by running "zfs set compression=on \fIdataset\fR". The default value is "off".
.RE
.sp
@@ -300,8 +383,8 @@ The type of dataset: "filesystem", "volume", "snapshot", or "clone".
.na
\fBcreation\fR
.ad
-.RS 17n
-.rt
+.sp .6
+.RS 4n
The time this dataset was created.
.RE
@@ -309,31 +392,22 @@ The time this dataset was created.
.ne 2
.mk
.na
-\fBused\fR
+\fBmounted\fR
.ad
-.RS 17n
-.rt
-The amount of space consumed by this dataset and all its descendants. This is the value that is checked against this dataset's quota and reservation. The space used does not include this dataset's reservation, but does take into account the reservations of any descendant datasets.
-The amount of space that a dataset consumes from its parent, as well as the amount of space that will be freed if this dataset is recursively destroyed, is the greater of its space used and its reservation.
-.sp
-When snapshots (see the "Snapshots" section) are created, their space is initially shared between the snapshot and the file system, and possibly with previous snapshots. As the file system changes, space that was previously shared becomes unique to the snapshot, and counted in
-the snapshot's space used. Additionally, deleting snapshots can increase the amount of space unique to (and used by) other snapshots.
-.sp
-The amount of space used, available, or referenced does not take into account pending changes. Pending changes are generally accounted for within a few seconds. Committing a change to a disk using \fBfsync\fR(3c) or \fBO_SYNC\fR does not necessarily guarantee that the space usage information is updated immediately.
+.sp .6
+.RS 4n
+For file systems, indicates whether the file system is currently mounted. This property can be either "yes" or "no".
.RE
.sp
.ne 2
.mk
.na
-\fBavailable\fR
+\fBorigin\fR
.ad
-.RS 17n
-.rt
-The amount of space available to the dataset and all its children, assuming that there is no other activity in the pool. Because space is shared within a pool, availability can be limited by any number of factors, including physical pool size, quotas, reservations, or other datasets
-within the pool.
-.sp
-This property can also be referred to by its shortened column name, "avail".
+.sp .6
+.RS 4n
+For cloned file systems or volumes, the snapshot from which the clone was created. The origin cannot be destroyed (even with the \fB-r\fR or \fB-f\fR options) so long as a clone exists.
.RE
.sp
@@ -342,8 +416,8 @@ This property can also be referred to by its shortened column name, "avail".
.na
\fBreferenced\fR
.ad
-.RS 17n
-.rt
+.sp .6
+.RS 4n
The amount of data that is accessible by this dataset, which may or may not be shared with other datasets in the pool. When a snapshot or clone is created, it initially references the same amount of space as the file system or snapshot it was created from, since its contents are
identical.
.sp
@@ -354,116 +428,166 @@ This property can also be referred to by its shortened column name, "refer".
.ne 2
.mk
.na
-\fBcompressratio\fR
+\fBtype\fR
.ad
-.RS 17n
-.rt
-The compression ratio achieved for this dataset, expressed as a multiplier. Compression can be turned on by running "zfs set compression=on \fIdataset\fR". The default value is "off".
+.sp .6
+.RS 4n
+The type of dataset: "filesystem", "volume", "snapshot", or "clone".
.RE
.sp
.ne 2
.mk
.na
-\fBmounted\fR
+\fBused\fR
.ad
-.RS 17n
-.rt
-For file systems, indicates whether the file system is currently mounted. This property can be either "yes" or "no".
+.sp .6
+.RS 4n
+The amount of space consumed by this dataset and all its descendents. This is the value that is checked against this dataset's quota and reservation. The space used does not include this dataset's reservation, but does take into account the reservations of any descendent datasets.
+The amount of space that a dataset consumes from its parent, as well as the amount of space that are freed if this dataset is recursively destroyed, is the greater of its space used and its reservation.
+.sp
+When snapshots (see the "Snapshots" section) are created, their space is initially shared between the snapshot and the file system, and possibly with previous snapshots. As the file system changes, space that was previously shared becomes unique to the snapshot, and counted in
+the snapshot's space used. Additionally, deleting snapshots can increase the amount of space unique to (and used by) other snapshots.
+.sp
+The amount of space used, available, or referenced does not take into account pending changes. Pending changes are generally accounted for within a few seconds. Committing a change to a disk using \fBfsync\fR(3c) or \fBO_SYNC\fR does not necessarily guarantee that the space usage information is updated immediately.
.RE
.sp
.ne 2
.mk
.na
-\fBorigin\fR
+\fBvolblocksize=\fIblocksize\fR\fR
.ad
-.RS 17n
-.rt
-For cloned file systems or volumes, the snapshot from which the clone was created. The origin cannot be destroyed (even with the \fB-r\fR or \fB-f\fR options) so long as a clone exists.
+.sp .6
+.RS 4n
+For volumes, specifies the block size of the volume. The \fBblocksize\fR cannot be changed once the volume has been written, so it should be set at volume creation time. The default \fBblocksize\fR for volumes is 8 Kbytes. Any power of 2 from 512 bytes
+to 128 Kbytes is valid.
+.sp
+This property can also be referred to by its shortened column name, "volblock".
.RE
+.sp
.LP
-The following two properties can be set to control the way space is allocated between datasets. These properties are not inherited, but do affect their descendants.
+The following native properties can be used to change the behavior of a \fBZFS\fR dataset.
.sp
.ne 2
.mk
.na
-\fBquota=\fIsize\fR | \fInone\fR\fR
+\fBaclinherit=\fBdiscard\fR | \fBnoallow\fR | \fBrestricted\fR | \fBpassthrough\fR\fR
.ad
.sp .6
.RS 4n
-Limits the amount of space a dataset and its descendants can consume. This property enforces a hard limit on the amount of space used. This includes all space consumed by descendants, including file systems and snapshots. Setting a quota on a descendant of a dataset that already
-has a quota does not override the ancestor's quota, but rather imposes an additional limit.
+Controls how \fBACL\fR entries are inherited when files and directories are created. A file system with an "aclinherit" property of "\fBdiscard\fR" does not inherit any \fBACL\fR entries. A file system with an "aclinherit"
+property value of "\fBnoallow\fR" only inherits inheritable \fBACL\fR entries that specify "deny" permissions. The property value "\fBrestricted\fR" (the default) removes the "\fBwrite_acl\fR" and "\fBwrite_owner\fR" permissions when the \fBACL\fR entry is inherited. A file system with an "aclinherit" property value of "\fBpassthrough\fR" inherits all inheritable \fBACL\fR entries without any modifications made to the \fBACL\fR entries when they are inherited.
.sp
-Quotas cannot be set on volumes, as the "volsize" property acts as an implicit quota.
+When the property value is set to "\fBpassthrough\fR," files are created with a mode determined by the inheritable \fBACE\fRs. If no inheritable \fBACE\fRs exist that affect the mode, then the mode is set in accordance to the requested mode
+from the application.
.RE
.sp
.ne 2
.mk
.na
-\fBreservation=\fIsize\fR | \fInone\fR\fR
+\fBaclmode=\fBdiscard\fR | \fBgroupmask\fR | \fBpassthrough\fR\fR
.ad
.sp .6
.RS 4n
-The minimum amount of space guaranteed to a dataset and its descendants. When the amount of space used is below this value, the dataset is treated as if it were taking up the amount of space specified by its reservation. Reservations are accounted for in the parent datasets' space
-used, and count against the parent datasets' quotas and reservations.
+Controls how an \fBACL\fR is modified during \fBchmod\fR(2). A file system with an "aclmode" property of "\fBdiscard\fR"
+deletes all \fBACL\fR entries that do not represent the mode of the file. An "aclmode" property of "\fBgroupmask\fR" (the default) reduces 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 \fBUID\fR as the owner of the file or directory. In this case, the \fBACL\fR permissions are reduced so that they are no greater than owner permission bits. A file system with an "aclmode" property of "\fBpassthrough\fR" indicates that no changes are made to the \fBACL\fR other than generating the necessary \fBACL\fR entries to represent the new mode of the file or directory.
+.RE
+
.sp
-This property can also be referred to by its shortened column name, "reserv".
+.ne 2
+.mk
+.na
+\fBatime=\fIon\fR | \fIoff\fR\fR
+.ad
+.sp .6
+.RS 4n
+Controls whether the access time for files is updated when they are read. Turning this property off avoids producing write traffic when reading files and can result in significant performance gains, though it might confuse mailers and other similar utilities. The default value
+is "on".
.RE
.sp
.ne 2
.mk
.na
-\fBvolsize=\fIsize\fR\fR
+\fBcanmount=\fBon\fR | \fBoff\fR | \fBnoauto\fR\fR
.ad
.sp .6
.RS 4n
-For volumes, specifies the logical size of the volume. By default, creating a volume establishes a reservation of equal size. Any changes to \fBvolsize\fR are reflected in an equivalent change to the reservation. The \fBvolsize\fR can only be set to a
-multiple of \fBvolblocksize\fR, and cannot be zero.
+If this property is set to "\fBoff\fR", the file system cannot be mounted, and is ignored by "\fBzfs mount -a\fR". Setting this property to "\fBoff\fR" is similar to setting the "mountpoint"
+property to "\fBnone\fR", except that the dataset still has a normal "mountpoint" property, which can be inherited. Setting this property to "\fBoff\fR" allows datasets to be used solely as a mechanism to inherit properties. One example
+of setting canmount=\fBoff\fR is to have two datasets with the same mountpoint, so that the children of both datasets appear in the same directory, but might have different inherited characteristics.
.sp
-The reservation is kept equal to the volume's logical size to prevent unexpected behavior for consumers. Without the reservation, the volume could run out of space, resulting in undefined behavior or data corruption, depending on how the volume is used. These effects can also occur when
-the volume size is changed while it is in use (particularly when shrinking the size). Extreme care should be used when adjusting the volume size.
+When the "\fBnoauto\fR" option is set, a dataset can only be mounted and unmounted explicitly. The dataset is not mounted automatically when the dataset is created or imported, nor is it mounted by the "\fBzfs mount -a\fR" command or unmounted
+by the "\fBzfs unmount -a\fR" command.
.sp
-Though not recommended, a "sparse volume" (also known as "thin provisioning") can be created by specifying the \fB-s\fR option to the "\fBzfs create -V\fR" command, or by changing the reservation after the volume has been created.
-A "sparse volume" is a volume where the reservation is less then the volume size. Consequently, writes to a sparse volume can fail with \fBENOSPC\fR when the pool is low on space. For a sparse volume, changes to \fBvolsize\fR are not reflected in the reservation.
+This property is not inherited.
.RE
.sp
.ne 2
.mk
.na
-\fBvolblocksize=\fIblocksize\fR\fR
+\fBchecksum=\fIon\fR | \fIoff\fR | \fIfletcher2\fR, | \fIfletcher4\fR | \fIsha256\fR\fR
.ad
.sp .6
.RS 4n
-For volumes, specifies the block size of the volume. The \fBblocksize\fR cannot be changed once the volume has been written, so it should be set at volume creation time. The default \fBblocksize\fR for volumes is 8 Kbytes. Any power of 2 from 512 bytes
-to 128 Kbytes is valid.
-.sp
-This property can also be referred to by its shortened column name, "volblock".
+Controls the checksum used to verify data integrity. The default value is "on", which automatically selects an appropriate algorithm (currently, \fIfletcher2\fR, but this may change in future releases). The value "off" disables integrity
+checking on user data. Disabling checksums is NOT a recommended practice.
.RE
.sp
.ne 2
.mk
.na
-\fBrecordsize=\fIsize\fR\fR
+\fBcompression=\fIon\fR | \fIoff\fR | \fIlzjb\fR | \fIgzip\fR | \fIgzip-N\fR\fR
.ad
.sp .6
.RS 4n
-Specifies a suggested block size for files in the file system. This property is designed solely for use with database workloads that access files in fixed-size records. \fBZFS\fR automatically tunes block sizes according to internal algorithms optimized for typical
-access patterns.
+Controls the compression algorithm used for this dataset. The "lzjb" compression algorithm is optimized for performance while providing decent data compression. Setting compression to "on" uses the "lzjb" compression algorithm. The "gzip"
+compression algorithm uses the same compression as the \fBgzip\fR(1) command. You can specify the "gzip" level by using the value "gzip-\fIN\fR" where \fIN\fR is
+an integer from 1 (fastest) to 9 (best compression ratio). Currently, "gzip" is equivalent to "gzip-6" (which is also the default for \fBgzip\fR(1)).
.sp
-For databases that create very large files but access them in small random chunks, these algorithms may be suboptimal. Specifying a "recordsize" greater than or equal to the record size of the database can result in significant performance gains. Use of this property for general
-purpose file systems is strongly discouraged, and may adversely affect performance.
+This property can also be referred to by its shortened column name "compress".
+.RE
+
.sp
-The size specified must be a power of two greater than or equal to 512 and less than or equal to 128 Kbytes.
+.ne 2
+.mk
+.na
+\fBcopies=\fB1\fR | \fB2\fR | \fB3\fR\fR
+.ad
+.sp .6
+.RS 4n
+Controls the number of copies of data stored for this dataset. These copies are in addition to any redundancy provided by the pool, for example, mirroring or raid-z. The copies are stored on different disks, if possible. The space used by multiple copies is charged to the associated
+file and dataset, changing the "used" property and counting against quotas and reservations.
.sp
-Changing the file system's \fBrecordsize\fR only affects files created afterward; existing files are unaffected.
+Changing this property only affects newly-written data. Therefore, set this property at file system creation time by using the "\fB-o\fR copies=" option.
+.RE
+
.sp
-This property can also be referred to by its shortened column name, "recsize".
+.ne 2
+.mk
+.na
+\fBdevices=\fIon\fR | \fIoff\fR\fR
+.ad
+.sp .6
+.RS 4n
+Controls whether device nodes can be opened on this file system. The default value is "on".
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fBexec=\fIon\fR | \fIoff\fR\fR
+.ad
+.sp .6
+.RS 4n
+Controls whether processes can be executed from within this file system. The default value is "on".
.RE
.sp
@@ -484,89 +608,100 @@ previously "legacy" or "none", or if they were mounted before the property was c
.ne 2
.mk
.na
-\fBsharenfs=\fIon\fR | \fIoff\fR | \fIopts\fR\fR
+\fBnbmand=\fIon\fR | \fIoff\fR\fR
.ad
.sp .6
.RS 4n
-Controls whether the file system is shared via \fBNFS\fR, and what options are used. A file system with a sharenfs property of "off" is managed through traditional tools such as \fBshare\fR(1M), \fBunshare\fR(1M), and \fBdfstab\fR(4). Otherwise, the file system is automatically shared and unshared with the "\fBzfs share\fR" and "\fBzfs unshare\fR" commands. If the property is set to "on", the \fBshare\fR(1M) command is invoked with no options. Otherwise, the \fBshare\fR(1M) command is invoked with options equivalent to the contents of this property.
-.sp
-When the "sharenfs" property is changed for a dataset, the dataset and any children inheriting the property are re-shared with the new options, only if the property was previously "off", or if they were shared before the property was changed. If the new property is "off",
-the file systems are unshared.
+Controls whether the file system should be mounted with "\fBnbmand\fR" (Non Blocking mandatory locks). This is used for \fBCIFS\fR clients. Changes to this property only take effect when the file system is umounted and remounted. See \fBmount\fR(1M) for more information on "\fBnbmand\fR" mounts.
.RE
.sp
.ne 2
.mk
.na
-\fBshareiscsi=\fIon\fR | \fIoff\fR\fR
+\fBquota=\fIsize\fR | \fInone\fR\fR
.ad
.sp .6
.RS 4n
-Like the "sharenfs" property, "shareiscsi" indicates whether a \fBZFS\fR volume is exported as an \fBiSCSI\fR target. The acceptable values for this property are "on", "off", and "type=disk".
-The default value is "off". In the future, other target types might be supported. For example, "tape".
+Limits the amount of space a dataset and its descendents can consume. This property enforces a hard limit on the amount of space used. This includes all space consumed by descendents, including file systems and snapshots. Setting a quota on a descendent of a dataset that already
+has a quota does not override the ancestor's quota, but rather imposes an additional limit.
.sp
-You might want to set "shareiscsi=on" for a file system so that all \fBZFS\fR volumes within the file system are shared by default. Setting this property on a file system has no direct effect, however.
+Quotas cannot be set on volumes, as the "volsize" property acts as an implicit quota.
.RE
.sp
.ne 2
.mk
.na
-\fBchecksum=\fIon\fR | \fIoff\fR | \fIfletcher2\fR, | \fIfletcher4\fR | \fIsha256\fR\fR
+\fBreadonly=\fIon\fR | \fIoff\fR\fR
.ad
.sp .6
.RS 4n
-Controls the checksum used to verify data integrity. The default value is "on", which automatically selects an appropriate algorithm (currently, \fIfletcher2\fR, but this may change in future releases). The value "off" disables integrity
-checking on user data. Disabling checksums is NOT a recommended practice.
+Controls whether this dataset can be modified. The default value is "off".
+.sp
+This property can also be referred to by its shortened column name, "rdonly".
.RE
.sp
.ne 2
.mk
.na
-\fBcompression=\fIon\fR | \fIoff\fR | \fIlzjb\fR | \fIgzip\fR | \fIgzip-N\fR\fR
+\fBrecordsize=\fIsize\fR\fR
.ad
.sp .6
.RS 4n
-Controls the compression algorithm used for this dataset. The "lzjb" compression algorithm is optimized for performance while providing decent data compression. Setting compression to "on" uses the "lzjb" compression algorithm. The "gzip"
-compression algorithm uses the same compression as the \fBgzip\fR(1) command. You can specify the "gzip" level by using the value "gzip-\fIN\fR",
-where \fIN\fR is an integer from 1 (fastest) to 9 (best compression ratio). Currently, "gzip" is equivalent to "gzip-6" (which is also the default for \fBgzip\fR(1)).
+Specifies a suggested block size for files in the file system. This property is designed solely for use with database workloads that access files in fixed-size records. \fBZFS\fR automatically tunes block sizes according to internal algorithms optimized for typical
+access patterns.
.sp
-This property can also be referred to by its shortened column name "compress".
+For databases that create very large files but access them in small random chunks, these algorithms may be suboptimal. Specifying a "recordsize" greater than or equal to the record size of the database can result in significant performance gains. Use of this property for general
+purpose file systems is strongly discouraged, and may adversely affect performance.
+.sp
+The size specified must be a power of two greater than or equal to 512 and less than or equal to 128 Kbytes.
+.sp
+Changing the file system's \fBrecordsize\fR only affects files created afterward; existing files are unaffected.
+.sp
+This property can also be referred to by its shortened column name, "recsize".
.RE
.sp
.ne 2
.mk
.na
-\fBatime=\fIon\fR | \fIoff\fR\fR
+\fBrefquota=\fIsize\fR | \fInone\fR\fR
.ad
.sp .6
.RS 4n
-Controls whether the access time for files is updated when they are read. Turning this property off avoids producing write traffic when reading files and can result in significant performance gains, though it might confuse mailers and other similar utilities. The default value
-is "on".
+Limits the amount of space a dataset can consume. This property enforces a hard limit on the amount of space used. This hard limit does not include space used by descendents, including file systems and snapshots.
.RE
.sp
.ne 2
.mk
.na
-\fBdevices=\fIon\fR | \fIoff\fR\fR
+\fBrefreservation=\fIsize\fR | \fInone\fR\fR
.ad
.sp .6
.RS 4n
-Controls whether device nodes can be opened on this file system. The default value is "on".
+The minimum amount of space guaranteed to a dataset, not including its descendents. When the amount of space used is below this value, the dataset is treated as if it were taking up the amount of space specified by \fBrefreservation\fR. The \fBrefreservation\fR reservation
+is accounted for in the parent datasets' space used, and counts against the parent datasets' quotas and reservations.
+.sp
+If \fBrefreservation\fR is set, a snapshot is only allowed if there is enough free pool space outside of this reservation to accommodate the current number of "referenced" bytes in the dataset.
+.sp
+This property can also be referred to by its shortened column name, "refreserv".
.RE
.sp
.ne 2
.mk
.na
-\fBexec=\fIon\fR | \fIoff\fR\fR
+\fBreservation=\fIsize\fR | \fInone\fR\fR
.ad
.sp .6
.RS 4n
-Controls whether processes can be executed from within this file system. The default value is "on".
+The minimum amount of space guaranteed to a dataset and its descendents. When the amount of space used is below this value, the dataset is treated as if it were taking up the amount of space specified by its reservation. Reservations are accounted for in the parent datasets' space
+used, and count against the parent datasets' quotas and reservations.
+.sp
+This property can also be referred to by its shortened column name, "reserv".
.RE
.sp
@@ -584,24 +719,48 @@ Controls whether the set-\fBUID\fR bit is respected for the file system. The def
.ne 2
.mk
.na
-\fBreadonly=\fIon\fR | \fIoff\fR\fR
+\fBshareiscsi=\fIon\fR | \fIoff\fR\fR
.ad
.sp .6
.RS 4n
-Controls whether this dataset can be modified. The default value is "off".
+Like the "sharenfs" property, "shareiscsi" indicates whether a \fBZFS\fR volume is exported as an \fBiSCSI\fR target. The acceptable values for this property are "on", "off", and "type=disk".
+The default value is "off". In the future, other target types might be supported. For example, "tape".
.sp
-This property can also be referred to by its shortened column name, "rdonly".
+You might want to set "shareiscsi=on" for a file system so that all \fBZFS\fR volumes within the file system are shared by default. Setting this property on a file system has no direct effect, however.
.RE
.sp
.ne 2
.mk
.na
-\fBzoned=\fIon\fR | \fIoff\fR\fR
+\fBsharesmb=\fIon\fR | \fIoff\fR | \fIopts\fR\fR
.ad
.sp .6
.RS 4n
-Controls whether the dataset is managed from a non-global zone. See the "Zones" section for more information. The default value is "off".
+Controls whether the file system is shared by using the Solaris \fBCIFS\fR service, and what options are to be used. A file system with the "\fBsharesmb\fR" property set to "off" is managed through traditional tools such as \fBsharemgr\fR(1M). Otherwise, the file system is automatically shared and unshared with the "zfs share" and "zfs unshare" commands. If the property is set to "on",
+the \fBsharemgr\fR(1M) command is invoked with no options. Otherwise, the \fBsharemgr\fR(1M) command is invoked with options equivalent to the contents of this property.
+.sp
+Because \fBSMB\fR shares requires a resource name, a unique resource name is constructed from the dataset name. The constructed name is a copy of the dataset name except that the characters in the dataset name, which would be illegal in the resource name, are replaced with underscore
+(_) characters. A pseudo property "name" is also supported that allows you to replace the data set name with a specified name. The specified name is then used to replace the prefix dataset in the case of inheritance. For example, if the dataset "\fBdata/home/john\fR"
+is set to "name=john", then "\fBdata/home/john\fR" has a resource name of "john". If a child dataset of "\fBdata/home/john/backups\fR", it has a resource name of "john_backups".
+.sp
+When the "sharesmb" property is changed for a dataset, the dataset and any children inheriting the property are re-shared with the new options, only if the property was previously set to "off", or if they were shared before the property was changed. If the new property
+is set to "off", the file systems are unshared.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fBsharenfs=\fIon\fR | \fIoff\fR | \fIopts\fR\fR
+.ad
+.sp .6
+.RS 4n
+Controls whether the file system is shared via \fBNFS\fR, and what options are used. A file system with a"\fBsharenfs\fR" property of "off" is managed through traditional tools such as \fBshare\fR(1M), \fBunshare\fR(1M), and \fBdfstab\fR(4). Otherwise, the file system is automatically shared and unshared with the "\fBzfs share\fR" and "\fBzfs unshare\fR" commands. If the property is set to "on",
+the \fBshare\fR(1M) command is invoked with no options. Otherwise, the \fBshare\fR(1M) command is invoked with options equivalent to the contents of this property.
+.sp
+When the "sharenfs" property is changed for a dataset, the dataset and any children inheriting the property are re-shared with the new options, only if the property was previously "off", or if they were shared before the property was changed. If the new property is "off",
+the file systems are unshared.
.RE
.sp
@@ -619,40 +778,40 @@ Controls whether the ".zfs" directory is hidden or visible in the root of the fi
.ne 2
.mk
.na
-\fBaclmode=\fBdiscard\fR | \fBgroupmask\fR | \fBpassthrough\fR\fR
+\fBversion=\fB1\fR|\fB2\fR|\fBcurrent\fR\fR
.ad
.sp .6
.RS 4n
-Controls how an \fBACL\fR is modified during \fBchmod\fR(2). A file system with an "aclmode" property of "\fBdiscard\fR"
-deletes all \fBACL\fR entries that do not represent the mode of the file. An "aclmode" property of "\fBgroupmask\fR" (the default) reduces 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 \fBUID\fR as the owner of the file or directory. In this case, the \fBACL\fR permissions are reduced so that they are no greater than owner permission bits. A file system with an "aclmode" property of "\fBpassthrough\fR" indicates that no changes will be made to the \fBACL\fR other than generating the necessary \fBACL\fR entries to represent the new mode of the file or directory.
+The on-disk version of this file system, which is independent of the pool version. This property can only be set to later supported versions. See "\fBzfs upgrade\fR".
.RE
.sp
.ne 2
.mk
.na
-\fBaclinherit=\fBdiscard\fR | \fBnoallow\fR | \fBsecure\fR | \fBpassthrough\fR\fR
+\fBvolsize=\fIsize\fR\fR
.ad
.sp .6
.RS 4n
-Controls how \fBACL\fR entries are inherited when files and directories are created. A file system with an "aclinherit" property of "\fBdiscard\fR" does not inherit any \fBACL\fR entries. A file system with an "aclinherit"
-property value of "\fBnoallow\fR" only inherits inheritable \fBACL\fR entries that specify "deny" permissions. The property value "\fBsecure\fR" (the default) removes the "\fBwrite_acl\fR" and "\fBwrite_owner\fR" permissions when the \fBACL\fR entry is inherited. A file system with an "aclinherit" property value of "\fBpassthrough\fR" inherits all inheritable \fBACL\fR entries without any modifications made to the \fBACL\fR entries when they are inherited.
+For volumes, specifies the logical size of the volume. By default, creating a volume establishes a reservation of equal size. For storage pools with a version number of 9 or higher, a \fBrefreservation\fR is set instead. Any changes to \fBvolsize\fR are
+reflected in an equivalent change to the reservation (or \fBrefreservation\fR). The \fBvolsize\fR can only be set to a multiple of \fBvolblocksize\fR, and cannot be zero.
+.sp
+The reservation is kept equal to the volume's logical size to prevent unexpected behavior for consumers. Without the reservation, the volume could run out of space, resulting in undefined behavior or data corruption, depending on how the volume is used. These effects can also occur when
+the volume size is changed while it is in use (particularly when shrinking the size). Extreme care should be used when adjusting the volume size.
+.sp
+Though not recommended, a "sparse volume" (also known as "thin provisioning") can be created by specifying the \fB-s\fR option to the "\fBzfs create -V\fR" command, or by changing the reservation after the volume has been created.
+A "sparse volume" is a volume where the reservation is less then the volume size. Consequently, writes to a sparse volume can fail with \fBENOSPC\fR when the pool is low on space. For a sparse volume, changes to \fBvolsize\fR are not reflected in the reservation.
.RE
.sp
.ne 2
.mk
.na
-\fBcanmount=\fBon\fR | \fBoff\fR\fR
+\fBvscan=\fBon\fR|\fBoff\fR\fR
.ad
.sp .6
.RS 4n
-If this property is set to "\fBoff\fR", the file system cannot be mounted, and is ignored by "\fBzfs mount -a\fR". This is similar to setting the "mountpoint" property to "\fBnone\fR", except
-that the dataset still has a normal "mountpoint" property which can be inherited. This allows datasets to be used solely as a mechanism to inherit properties. One use case is to have two logically separate datasets have the same mountpoint, so that the children of both datasets appear
-in the same directory, but may have different inherited characteristics. The default value is "\fBon\fR".
-.sp
-This property is not inherited.
+Controls whether regular files should be scanned for viruses when a file is opened and closed. In addition to enabling this property, the virus scan service must also be enabled for virus scanning to occur. The default value is "off".
.RE
.sp
@@ -670,31 +829,74 @@ Controls whether extended attributes are enabled for this file system. The defau
.ne 2
.mk
.na
-\fBcopies=\fB1\fR | \fB2\fR | \fB3\fR\fR
+\fBzoned=\fIon\fR | \fIoff\fR\fR
.ad
.sp .6
.RS 4n
-Controls the number of copies of data stored for this dataset. These copies are in addition to any redundancy provided by the pool, for example, mirroring or raid-z. The copies are stored on different disks, if possible. The space used by multiple copies is charged to the associated
-file and dataset, changing the "used" property and counting against quotas and reservations.
+Controls whether the dataset is managed from a non-global zone. See the "Zones" section for more information. The default value is "off".
+.RE
+
.sp
-Changing this property only affects newly-written data. Therefore, set this property at file system creation time by using the "\fB-o\fR copies=" option.
+.LP
+The following three properties cannot be changed after the file system is created, and therefore, should be set when the file system is created. If the properties are not set with the "\fBzfs create\fR" command, these properties are inherited from the parent dataset.
+If the parent dataset lacks these properties due to having been created prior to these features being supported, the new file system will have the default values for these properties.
+.sp
+.ne 2
+.mk
+.na
+\fBcasesensitivity = \fBsensitive\fR | \fBinsensitive\fR | \fBmixed\fR\fR
+.ad
+.sp .6
+.RS 4n
+Indicates whether the file name matching algorithm used by the file system should be case-sensitive, case-insensitive, or allow a combination of both styles of matching. The default value for the "\fBcasesensitivity\fR" property is "\fBsensitive\fR."
+Traditionally, UNIX and POSIX file systems have case-sensitive file names.
+.sp
+The "\fBmixed\fR" value for the "\fBcasesensitivity\fR" property indicates that the file system can support requests for both case-sensitive and case-insensitive matching behavior. Currently, case-insensitive matching behavior on a file system
+that supports mixed behavior is limited to the Solaris CIFS server product. For more information about the "mixed" value behavior, see the \fIZFS Administration Guide\fR.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fBnormalization =\fBnone\fR | \fBformD\fR | \fBformKCf\fR\fR
+.ad
+.sp .6
+.RS 4n
+Indicates whether the file system should perform a \fBunicode\fR normalization of file names whenever two file names are compared, and which normalization algorithm should be used. File names are always stored unmodified, names are normalized as part of any comparison
+process. If this property is set to a legal value other than "\fBnone\fR," and the "\fButf8only\fR" property was left unspecified, the "\fButf8only\fR" property is automatically set to "\fBon\fR."
+The default value of the "\fBnormalization\fR" property is "\fBnone\fR." This property cannot be changed after the file system is created.
.RE
.sp
.ne 2
.mk
.na
-\fBjailed=\fIon\fR | \fIoff\fR\fR
+\fBjailed =\fIon\fR | \fIoff\fR\fR
.ad
.sp .6
.RS 4n
Controls whether the dataset is managed from within a jail. The default value is "off".
.RE
-.SS "iscsioptions"
+.sp
+.ne 2
+.mk
+.na
+\fButf8only =\fBon\fR | \fBoff\fR\fR
+.ad
+.sp .6
+.RS 4n
+Indicates whether the file system should reject file names that include characters that are not present in the \fBUTF-8\fR character code set. If this property is explicitly set to "\fBoff\fR," the normalization property must either not be
+explicitly set or be set to "\fBnone\fR." The default value for the "\fButf8only\fR" property is "off." This property cannot be changed after the file system is created.
+.RE
+
+.sp
.LP
-This read-only property, which is hidden, is used by the \fBiSCSI\fR target daemon to store persistent information, such as the \fBIQN\fR. It cannot be viewed or modified using the \fBzfs\fR command. The contents are not intended for external consumers.
+The "\fBcasesensitivity\fR," "\fBnormalization\fR," and "\fButf8only\fR" properties are also new permissions that can be assigned to non-privileged users by using the \fBZFS\fR delegated administration
+feature.
.SS "Temporary Mount Point Properties"
+.sp
.LP
When a file system is mounted, either through \fBmount\fR(1M) for legacy mounts or the "\fBzfs mount\fR" command for normal file systems,
its mount options are set according to its properties. The correlation between properties and mount options is as follows:
@@ -711,30 +913,39 @@ its mount options are set according to its properties. The correlation between p
.in -2
.sp
+.sp
.LP
In addition, these options can be set on a per-mount basis using the \fB-o\fR option, without affecting the property that is stored on disk. The values specified on the command line override the values stored in the dataset. The \fB-nosuid\fR option is an alias for "nodevices,nosetuid".
These properties are reported as "temporary" by the "\fBzfs get\fR" command. If the properties are changed while the dataset is mounted, the new setting overrides any temporary settings.
.SS "User Properties"
+.sp
.LP
In addition to the standard native properties, \fBZFS\fR supports arbitrary user properties. User properties have no effect on \fBZFS\fR behavior, but applications or administrators can use them to annotate datasets.
+.sp
.LP
User property names must contain a colon (":") character, to distinguish them from native properties. They might contain lowercase letters, numbers, and the following punctuation characters: colon (":"), dash ("-"), period ("."), and underscore
("_"). The expected convention is that the property name is divided into two portions such as "\fImodule\fR:\fIproperty\fR", but this namespace is not enforced by \fBZFS\fR. User property names can be at most 256 characters,
and cannot begin with a dash ("-").
+.sp
.LP
When making programmatic use of user properties, it is strongly suggested to use a reversed \fBDNS\fR domain name for the \fImodule\fR component of property names to reduce the chance that two independently-developed packages use the same property name for
different purposes. Property names beginning with "com.sun." are reserved for use by Sun Microsystems.
+.sp
.LP
The values of user properties are arbitrary strings, are always inherited, and are never validated. All of the commands that operate on properties ("zfs list", "zfs get", "zfs set", etc.) can be used to manipulate both native properties and user properties.
Use the "\fBzfs inherit\fR" command to clear a user property . If the property is not defined in any parent dataset, it is removed entirely. Property values are limited to 1024 characters.
.SS "Volumes as Swap or Dump Devices"
+.sp
.LP
To set up a swap area, create a \fBZFS\fR volume of a specific size and then enable swap on that device. For more information, see the EXAMPLES section.
+.sp
.LP
Do not swap to a file on a \fBZFS\fR file system. A \fBZFS\fR swap file configuration is not supported.
+.sp
.LP
Using a \fBZFS\fR volume as a dump device is not supported.
.SH SUBCOMMANDS
+.sp
.LP
All subcommands that modify state are logged persistently to the pool in their original form.
.sp
@@ -752,7 +963,7 @@ Displays a help message.
.ne 2
.mk
.na
-\fB\fBzfs create\fR [[\fB-o\fR property=value]...] \fIfilesystem\fR\fR
+\fB\fBzfs create\fR [\fB-p\fR] [\fB-o\fR \fIproperty\fR=\fIvalue\fR] ... \fIfilesystem\fR\fR
.ad
.sp .6
.RS 4n
@@ -761,7 +972,19 @@ Creates a new \fBZFS\fR file system. The file system is automatically mounted ac
.ne 2
.mk
.na
-\fB\fB-o\fR property=value\fR
+\fB\fB-p\fR\fR
+.ad
+.RS 21n
+.rt
+Creates all the non-existing parent datasets. Datasets created in this manner are automatically mounted according to the "mountpoint" property inherited from their parent. Any property specified on the command line using the \fB-o\fR option is ignored. If
+the target filesystem already exists, the operation completes successfully.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-o\fR \fIproperty\fR=\fIvalue\fR\fR
.ad
.RS 21n
.rt
@@ -775,7 +998,7 @@ error results if the same property is specified in multiple \fB-o\fR options.
.ne 2
.mk
.na
-\fB\fBzfs create\fR [\fB-s\fR] [\fB-b\fR \fIblocksize\fR] [[\fB-o\fR property=value]...] \fB-V\fR \fIsize\fR \fIvolume\fR\fR
+\fB\fBzfs create\fR [\fB-ps\fR] [\fB-b\fR \fIblocksize\fR] [\fB-o\fR \fIproperty\fR=\fIvalue\fR] ... \fB-V\fR \fIsize\fR \fIvolume\fR\fR
.ad
.sp .6
.RS 4n
@@ -787,6 +1010,18 @@ the logical size as exported by the device. By default, a reservation of equal s
.ne 2
.mk
.na
+\fB\fB-p\fR\fR
+.ad
+.RS 21n
+.rt
+Creates all the non-existing parent datasets. Datasets created in this manner are automatically mounted according to the "mountpoint" property inherited from their parent. Any property specified on the command line using the \fB-o\fR option is ignored. If
+the target filesystem already exists, the operation completes successfully.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
\fB\fB-s\fR\fR
.ad
.RS 21n
@@ -798,7 +1033,7 @@ Creates a sparse volume with no reservation. See "volsize" in the Native Propert
.ne 2
.mk
.na
-\fB\fB-o\fR property=value\fR
+\fB\fB-o\fR \fIproperty\fR=\fIvalue\fR\fR
.ad
.RS 21n
.rt
@@ -837,7 +1072,7 @@ Destroys the given dataset. By default, the command unshares any file systems th
.ad
.RS 6n
.rt
-Recursively destroy all children. If a snapshot is specified, destroy all snapshots with this name in descendant file systems.
+Recursively destroy all children. If a snapshot is specified, destroy all snapshots with this name in descendent file systems.
.RE
.sp
@@ -848,7 +1083,7 @@ Recursively destroy all children. If a snapshot is specified, destroy all snapsh
.ad
.RS 6n
.rt
-Recursively destroy all dependents, including cloned file systems outside the target hierarchy. If a snapshot is specified, destroy all snapshots with this name in descendant file systems.
+Recursively destroy all dependents, including cloned file systems outside the target hierarchy. If a snapshot is specified, destroy all snapshots with this name in descendent file systems.
.RE
.sp
@@ -869,58 +1104,65 @@ Extreme care should be taken when applying either the \fB-r\fR or the \fB-f\fR o
.ne 2
.mk
.na
-\fB\fBzfs clone\fR \fIsnapshot\fR \fIfilesystem\fR|\fIvolume\fR\fR
+\fB\fBzfs snapshot\fR [\fB-r\fR] \fIfilesystem@snapname\fR|\fIvolume@snapname\fR\fR
.ad
.sp .6
.RS 4n
-Creates a clone of the given snapshot. See the "Clones" section for details. The target dataset can be located anywhere in the \fBZFS\fR hierarchy, and is created as the same type as the original.
-.RE
-
+Creates a snapshot with the given name. See the "Snapshots" section for details.
.sp
.ne 2
.mk
.na
-\fB\fBzfs promote\fR \fIfilesystem\fR\fR
+\fB\fB-r\fR\fR
.ad
-.sp .6
-.RS 4n
-Promotes a clone file system to no longer be dependent on its "origin" snapshot. This makes it possible to destroy the file system that the clone was created from. The clone parent-child dependency relationship is reversed, so that the "origin" file system
-becomes a clone of the specified file system.
-.sp
-The snaphot that was cloned, and any snapshots previous to this snapshot, are now owned by the promoted clone. The space they use moves from the "origin" file system to the promoted clone, so enough space must be available to accommodate these snapshots. No new space is consumed
-by this operation, but the space accounting is adjusted. The promoted clone must not have any conflicting snapshot names of its own. The "\fBrename\fR" subcommand can be used to rename any conflicting snapshots.
+.RS 6n
+.rt
+Recursively create snapshots of all descendent datasets. Snapshots are taken atomically, so that all recursive snapshots correspond to the same moment in time.
+.RE
+
.RE
.sp
.ne 2
.mk
.na
-\fB\fBzfs rename\fR \fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR \fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR\fR
+\fB\fBzfs rollback\fR [\fB-rRf\fR] \fIsnapshot\fR\fR
.ad
.sp .6
.RS 4n
-Renames the given dataset. The new target can be located anywhere in the \fBZFS\fR hierarchy, with the exception of snapshots. Snapshots can only be renamed within the parent file system or volume. When renaming a snapshot, the parent file system of the snapshot does
-not need to be specified as part of the second argument. Renamed file systems can inherit new mount points, in which case they are unmounted and remounted at the new mount point.
+Roll back the given dataset to a previous snapshot. When a dataset is rolled back, all data that has changed since the snapshot is discarded, and the dataset reverts to the state at the time of the snapshot. By default, the command refuses to roll back to a snapshot other than
+the most recent one. In order to do so, all intermediate snapshots must be destroyed by specifying the \fB-r\fR option.
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-r\fR\fR
+.ad
+.RS 6n
+.rt
+Recursively destroy any snapshots more recent than the one specified.
.RE
.sp
.ne 2
.mk
.na
-\fB\fBzfs snapshot\fR [\fB-r\fR] \fIfilesystem@name\fR|\fIvolume@name\fR\fR
+\fB\fB-R\fR\fR
.ad
-.sp .6
-.RS 4n
-Creates a snapshot with the given name. See the "Snapshots" section for details.
+.RS 6n
+.rt
+Recursively destroy any more recent snapshots, as well as any clones of those snapshots.
+.RE
+
.sp
.ne 2
.mk
.na
-\fB\fB-r\fR\fR
+\fB\fB-f\fR\fR
.ad
.RS 6n
.rt
-Recursively create snapshots of all descendant datasets. Snapshots are taken atomically, so that all recursive snapshots correspond to the same moment in time.
+Used with the \fB-R\fR option to force an unmount of any clone file systems that are to be destroyed.
.RE
.RE
@@ -929,43 +1171,67 @@ Recursively create snapshots of all descendant datasets. Snapshots are taken ato
.ne 2
.mk
.na
-\fB\fBzfs rollback\fR [\fB-rRf\fR] \fIsnapshot\fR\fR
+\fB\fBzfs clone\fR [\fB-p\fR] \fIsnapshot\fR \fIfilesystem\fR|\fIvolume\fR\fR
.ad
.sp .6
.RS 4n
-Roll back the given dataset to a previous snapshot. When a dataset is rolled back, all data that has changed since the snapshot is discarded, and the dataset reverts to the state at the time of the snapshot. By default, the command refuses to roll back to a snapshot other than
-the most recent one. In order to do so, all intermediate snapshots must be destroyed by specifying the \fB-r\fR option. The file system is unmounted and remounted, if necessary.
+Creates a clone of the given snapshot. See the "Clones" section for details. The target dataset can be located anywhere in the \fBZFS\fR hierarchy, and is created as the same type as the original.
.sp
.ne 2
.mk
.na
-\fB\fB-r\fR\fR
+\fB\fB-p\fR\fR
.ad
.RS 6n
.rt
-Recursively destroy any snapshots more recent than the one specified.
+Creates all the non-existing parent datasets. Datasets created in this manner are automatically mounted according to the "mountpoint" property inherited from their parent. If the target filesystem or volume already exists, the operation completes successfully.
+.RE
+
.RE
.sp
.ne 2
.mk
.na
-\fB\fB-R\fR\fR
+\fB\fBzfs promote\fR \fIclone-filesystem\fR\fR
.ad
-.RS 6n
-.rt
-Recursively destroy any more recent snapshots, as well as any clones of those snapshots.
+.sp .6
+.RS 4n
+Promotes a clone file system to no longer be dependent on its "origin" snapshot. This makes it possible to destroy the file system that the clone was created from. The clone parent-child dependency relationship is reversed, so that the "origin" file system
+becomes a clone of the specified file system.
+.sp
+The snapshot that was cloned, and any snapshots previous to this snapshot, are now owned by the promoted clone. The space they use moves from the "origin" file system to the promoted clone, so enough space must be available to accommodate these snapshots. No new space is consumed
+by this operation, but the space accounting is adjusted. The promoted clone must not have any conflicting snapshot names of its own. The "\fBrename\fR" subcommand can be used to rename any conflicting snapshots.
.RE
.sp
.ne 2
.mk
.na
-\fB\fB-f\fR\fR
+\fB\fBzfs rename\fR \fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR\fR
+.ad
+.br
+.na
+\fB\fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR\fR
+.ad
+.br
+.na
+\fB\fBzfs
+rename\fR [\fB-p\fR] \fIfilesystem\fR|\fIvolume\fR \fIfilesystem\fR|\fIvolume\fR\fR
+.ad
+.sp .6
+.RS 4n
+Renames the given dataset. The new target can be located anywhere in the \fBZFS\fR hierarchy, with the exception of snapshots. Snapshots can only be renamed within the parent file system or volume. When renaming a snapshot, the parent file system of the snapshot does
+not need to be specified as part of the second argument. Renamed file systems can inherit new mount points, in which case they are unmounted and remounted at the new mount point.
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-p\fR\fR
.ad
.RS 6n
.rt
-Force an unmount of any file systems using the "\fBunmount -f\fR" command.
+Creates all the non-existing parent datasets. Datasets created in this manner are automatically mounted according to the "mountpoint" property inherited from their parent.
.RE
.RE
@@ -974,7 +1240,22 @@ Force an unmount of any file systems using the "\fBunmount -f\fR" command.
.ne 2
.mk
.na
-\fB\fBzfs\fR \fBlist\fR [\fB-rH\fR] [\fB-o\fR \fIprop\fR[,\fIprop\fR] ]... [ \fB-t\fR \fItype\fR[,\fItype\fR]...] [ \fB-s\fR \fIprop\fR [\fB-s\fR \fIprop\fR]... [ \fB-S\fR \fIprop\fR [\fB-S\fR \fIprop\fR]... [\fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR|\fI/pathname\fR|.\fI/pathname\fR ...\fR
+\fB\fBzfs rename\fR \fB-r\fR \fIsnapshot\fR \fIsnapshot\fR\fR
+.ad
+.sp .6
+.RS 4n
+Recursively rename the snapshots of all descendent datasets. Snapshots are the only dataset that can be renamed recursively.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fBzfs\fR \fBlist\fR [\fB-rH\fR] [\fB-o\fR \fIproperty\fR[,\fI\&...\fR]] [ \fB-t\fR \fItype\fR[,\fI\&...\fR]]\fR
+.ad
+.br
+.na
+\fB[ \fB-s\fR \fIproperty\fR ] ... [ \fB-S\fR \fIproperty\fR ] ... [\fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR] ...\fR
.ad
.sp .6
.RS 4n
@@ -993,7 +1274,7 @@ name,used,available,referenced,mountpoint
.na
\fB\fB-H\fR\fR
.ad
-.RS 11n
+.RS 15n
.rt
Used for scripting mode. Do not print headers and separate fields by a single tab instead of arbitrary whitespace.
.RE
@@ -1004,7 +1285,7 @@ Used for scripting mode. Do not print headers and separate fields by a single ta
.na
\fB\fB-r\fR\fR
.ad
-.RS 11n
+.RS 15n
.rt
Recursively display any children of the dataset on the command line.
.RE
@@ -1013,9 +1294,9 @@ Recursively display any children of the dataset on the command line.
.ne 2
.mk
.na
-\fB\fB-o\fR \fIprop\fR\fR
+\fB\fB-o\fR \fIproperty\fR\fR
.ad
-.RS 11n
+.RS 15n
.rt
A comma-separated list of properties to display. The property must be one of the properties described in the "Native Properties" section, or the special value "name" to display the dataset name.
.RE
@@ -1024,12 +1305,12 @@ A comma-separated list of properties to display. The property must be one of the
.ne 2
.mk
.na
-\fB\fB-s\fR \fIprop\fR\fR
+\fB\fB-s\fR \fIproperty\fR\fR
.ad
-.RS 11n
+.RS 15n
.rt
A property to use for sorting the output by column in ascending order based on the value of the property. The property must be one of the properties described in the "Properties" section, or the special value "name" to sort by the dataset name. Multiple
-properties can be specified at one time using multiple \fB-s\fR property options. Multiple \fB-s\fR options are evaluated from left to right in decreasing order of importance.
+properties can be specified at one time using multiple \fB-s\fR property options. Multiple \fB-s\fR options are evaluated from left to right in decreasing order of importance.
.sp
The following is a list of sorting criteria:
.RS +4
@@ -1062,9 +1343,9 @@ If no sorting options are specified the existing behavior of "\fBzfs list\fR" is
.ne 2
.mk
.na
-\fB\fB-S\fR \fIprop\fR\fR
+\fB\fB-S\fR \fIproperty\fR\fR
.ad
-.RS 11n
+.RS 15n
.rt
Same as the \fB-s\fR option, but sorts by property in descending order.
.RE
@@ -1075,7 +1356,7 @@ Same as the \fB-s\fR option, but sorts by property in descending order.
.na
\fB\fB-t\fR \fItype\fR\fR
.ad
-.RS 11n
+.RS 15n
.rt
A comma-separated list of types to display, where "type" is one of "filesystem", "snapshot" or "volume". For example, specifying "\fB-t snapshot\fR" displays only snapshots.
.RE
@@ -1098,7 +1379,7 @@ form with a suffix of "B", "K", "M", "G", "T", "P", "E", "Z" (for bytes, Kbytes,
.ne 2
.mk
.na
-\fB\fBzfs get\fR [\fB-rHp\fR] [\fB-o\fR \fIfield\fR[,\fIfield\fR]...] [\fB-s\fR \fIsource\fR[,\fIsource\fR]...] \fIall\fR | \fIproperty\fR[,\fIproperty\fR]... \fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR ...\fR
+\fB\fBzfs get\fR [\fB-rHp\fR] [\fB-o\fR \fIfield\fR[,...] [\fB-s\fR \fIsource\fR[,...] "\fIall\fR" | \fIproperty\fR[,...] \fIfilesystem\fR|\fIvolume\fR|\fIsnapshot\fR ...\fR
.ad
.sp .6
.RS 4n
@@ -1201,42 +1482,70 @@ Recursively inherit the given property for all children.
.ne 2
.mk
.na
-\fB\fBzfs mount\fR\fR
+\fB\fBzfs upgrade\fR [\fB-v\fR]\fR
.ad
.sp .6
.RS 4n
-Displays all \fBZFS\fR file systems currently mounted.
+Displays a list of file systems that are not the most recent version.
.RE
.sp
.ne 2
.mk
.na
-\fB\fBzfs mount\fR[\fB-o\fR \fIopts\fR] [\fB-O\fR] \fB-a\fR\fR
+\fB\fBzfs upgrade\fR [\fB-r\fR] [\fB-V\fR \fIversion\fR] [\fB-a\fR | \fIfilesystem\fR]\fR
.ad
.sp .6
.RS 4n
-Mounts all available \fBZFS\fR file systems. Invoked automatically as part of the boot process.
+Upgrades file systems to a new on-disk version. Once this is done, the file systems will no longer be accessible on systems running older versions of the software. "\fBzfs send\fR" streams generated from new snapshots of these file systems can not be accessed
+on systems running older versions of the software.
+.sp
+The file system version is independent of the pool version (see \fBzpool\fR(1M) for information on the "\fBzpool upgrade\fR" command).
+.sp
+The file system version does not have to be upgraded when the pool version is upgraded, and vice versa.
.sp
.ne 2
.mk
.na
-\fB\fB-o\fR \fIopts\fR\fR
+\fB\fB-a\fR\fR
.ad
-.RS 11n
+.RS 14n
.rt
-An optional comma-separated list of mount options to use temporarily for the duration of the mount. See the "Temporary Mount Point Properties" section for details.
+Upgrade all file systems on all imported pools.
.RE
.sp
.ne 2
.mk
.na
-\fB\fB-O\fR\fR
+\fB\fIfilesystem\fR\fR
.ad
-.RS 11n
+.RS 14n
.rt
-Perform an overlay mount. See \fBmount\fR(1M) for more information.
+Upgrade the specified file system.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-r\fR\fR
+.ad
+.RS 14n
+.rt
+Upgrade the specified file system and all descendent file systems
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-V\fR \fIversion\fR\fR
+.ad
+.RS 14n
+.rt
+Upgrade to the specified \fIversion\fR. If the \fB-V\fR flag is not specified, this command upgrades to the most recent version. This option can only be used to increase the version number, and only up to the most recent version supported by this
+software.
.RE
.RE
@@ -1245,18 +1554,29 @@ Perform an overlay mount. See \fBmount\fR(1M) for more information.
.ne 2
.mk
.na
-\fB\fBzfs mount\fR [\fB-o\fR \fIopts\fR] [\fB-O\fR] \fIfilesystem\fR\fR
+\fB\fBzfs mount\fR\fR
.ad
.sp .6
.RS 4n
-Mounts a specific \fBZFS\fR file system. This is typically not necessary, as file systems are automatically mounted when they are created or the mountpoint property has changed. See the "Mount Points" section for details.
+Displays all \fBZFS\fR file systems currently mounted.
+.RE
+
.sp
.ne 2
.mk
.na
-\fB\fB-o\fR \fIopts\fR\fR
+\fB\fBzfs mount\fR [\fB-vO\fR] [\fB-o\fR \fIoptions\fR] \fB-a\fR | \fIfilesystem\fR\fR
.ad
-.RS 11n
+.sp .6
+.RS 4n
+Mounts \fBZFS\fR file systems. Invoked automatically as part of the boot process.
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-o\fR \fIoptions\fR\fR
+.ad
+.RS 14n
.rt
An optional comma-separated list of mount options to use temporarily for the duration of the mount. See the "Temporary Mount Point Properties" section for details.
.RE
@@ -1267,97 +1587,152 @@ An optional comma-separated list of mount options to use temporarily for the dur
.na
\fB\fB-O\fR\fR
.ad
-.RS 11n
+.RS 14n
.rt
Perform an overlay mount. See \fBmount\fR(1M) for more information.
.RE
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-v\fR\fR
+.ad
+.RS 14n
+.rt
+Report mount progress.
.RE
.sp
.ne 2
.mk
.na
-\fB\fBzfs unmount\fR \fB-a\fR\fR
+\fB\fB-a\fR\fR
.ad
-.sp .6
-.RS 4n
-Unmounts all currently mounted \fBZFS\fR file systems. Invoked automatically as part of the shutdown process.
+.RS 14n
+.rt
+Mount all available \fBZFS\fR file systems. Invoked automatically as part of the boot process.
.RE
.sp
.ne 2
.mk
.na
-\fB\fBzfs unmount\fR [\fB-f\fR] \fIfilesystem\fR|\fImountpoint\fR\fR
+\fB\fIfilesystem\fR\fR
+.ad
+.RS 14n
+.rt
+Mount the specified filesystem.
+.RE
+
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fBzfs unmount\fR [\fB-f\fR] \fB-a\fR | \fIfilesystem\fR|\fImountpoint\fR\fR
.ad
.sp .6
.RS 4n
-Unmounts the given file system. The command can also be given a path to a \fBZFS\fR file system mount point on the system.
+Unmounts currently mounted \fBZFS\fR file systems. Invoked automatically as part of the shutdown process.
.sp
.ne 2
.mk
.na
\fB\fB-f\fR\fR
.ad
-.RS 6n
+.RS 25n
.rt
Forcefully unmount the file system, even if it is currently in use.
.RE
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-a\fR\fR
+.ad
+.RS 25n
+.rt
+Unmount all available \fBZFS\fR file systems. Invoked automatically as part of the boot process.
.RE
.sp
.ne 2
.mk
.na
-\fB\fBzfs share\fR \fB-a\fR\fR
+\fB\fIfilesystem\fR|\fImountpoint\fR\fR
.ad
-.sp .6
-.RS 4n
-Shares all available \fBZFS\fR file systems. This is invoked automatically as part of the boot process.
+.RS 25n
+.rt
+Unmount the specified filesystem. The command can also be given a path to a \fBZFS\fR file system mount point on the system.
+.RE
+
.RE
.sp
.ne 2
.mk
.na
-\fB\fBzfs share\fR \fIfilesystem\fR\fR
+\fB\fBzfs share\fR \fB-a\fR | \fIfilesystem\fR\fR
.ad
.sp .6
.RS 4n
-Shares a specific \fBZFS\fR file system according to the "sharenfs" property. File systems are shared when the "sharenfs" property is set.
+Shares available \fBZFS\fR file systems.
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-a\fR\fR
+.ad
+.RS 14n
+.rt
+Share all available \fBZFS\fR file systems. Invoked automatically as part of the boot process.
.RE
.sp
.ne 2
.mk
.na
-\fB\fBzfs unshare\fR \fB-a\fR\fR
+\fB\fIfilesystem\fR\fR
.ad
-.sp .6
-.RS 4n
-Unshares all currently shared \fBZFS\fR file systems. This is invoked automatically as part of the shutdown process.
+.RS 14n
+.rt
+Share the specified filesystem according to the "sharenfs" and "sharesmb" properties. File systems are shared when the "sharenfs" or "sharesmb" property is set.
+.RE
+
.RE
.sp
.ne 2
.mk
.na
-\fB\fBzfs unshare\fR [\fB-F\fR] \fIfilesystem\fR|\fImountpoint\fR\fR
+\fB\fBzfs unshare\fR \fB-a\fR | \fIfilesystem\fR|\fImountpoint\fR\fR
.ad
.sp .6
.RS 4n
-Unshares the given file system. The command can also be given a path to a \fBZFS\fR file system shared on the system.
+Unshares currently shared \fBZFS\fR file systems. This is invoked automatically as part of the shutdown process.
.sp
.ne 2
.mk
.na
-\fB\fB-F\fR\fR
+\fB\fB-a\fR\fR
.ad
-.RS 6n
+.RS 25n
.rt
-Forcefully unshare the file system, even if it is currently in use.
+Unshare all available \fBZFS\fR file systems. Invoked automatically as part of the boot process.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fIfilesystem\fR|\fImountpoint\fR\fR
+.ad
+.RS 25n
+.rt
+Unshare the specified filesystem. The command can also be given a path to a \fBZFS\fR file system shared on the system.
.RE
.RE
@@ -1366,27 +1741,64 @@ Forcefully unshare the file system, even if it is currently in use.
.ne 2
.mk
.na
-\fB\fBzfs send\fR [\fB-i\fR \fIsnapshot1\fR] \fIsnapshot2\fR\fR
+\fB\fBzfs send\fR [\fB-vR\fR] [\fB-\fR[\fB-iI\fR] \fIsnapshot\fR] \fIsnapshot\fR\fR
.ad
.sp .6
.RS 4n
-Creates a stream representation of snapshot2, which is written to standard output. The output can be redirected to a file or to a different system (for example, using \fBssh\fR(1). By default, a full stream is generated.
+Creates a stream representation of the second \fIsnapshot\fR, which is written to standard output. The output can be redirected to a file or to a different system (for example, using \fBssh\fR(1). By default, a full stream is generated.
.sp
.ne 2
.mk
.na
-\fB\fB-i\fR \fIsnapshot1\fR\fR
+\fB\fB-i\fR \fIsnapshot\fR\fR
.ad
-.RS 16n
+.RS 15n
.rt
-Generate an incremental stream from \fIsnapshot1\fR to \fIsnapshot2\fR. The incremental source \fIsnapshot1\fR can be specified as the last component of the snapshot name (for example, the part after the "@"),
-and it is assumed to be from the same file system as \fIsnapshot2\fR.
+Generate an incremental stream from the first \fIsnapshot\fR to the second \fIsnapshot\fR. The incremental source (the first \fIsnapshot\fR) can be specified as the last component of the snapshot name (for example,
+the part after the "@"), and it is assumed to be from the same file system as the second \fIsnapshot\fR.
+.sp
+If the destination is a clone, the source may be the origin snapshot, which must be fully specified (for example, "pool/fs@origin", not just "@origin").
.RE
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-I\fR \fIsnapshot\fR\fR
+.ad
+.RS 15n
+.rt
+Generate a stream package that sends all intermediary snapshots from the first snapshot to the second snapshot. For example, "\fB-I @a fs@d\fR" is similar to "\fB-i @a fs@b; -i @b fs@c; -i @c fs@d\fR". The incremental source snapshot
+may be specified as with the \fB-i\fR option.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-R\fR\fR
+.ad
+.RS 15n
+.rt
+Generate a replication stream package, which will replicate the specified filesystem, and all descendant file systems, up to the named snapshot. When received, all properties, snapshots, descendent file systems, and clones are preserved.
+.sp
+If the \fB-i\fR or \fB-I\fR flags are used in conjunction with the \fB-R\fR flag, an incremental replication stream is generated. The current values of properties, and current snapshot and file system names are set when the stream is received. If the \fB-F\fR flag is specified when this stream is recieved, snapshots and file systems that do not exist on the sending side are destroyed.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-v\fR\fR
+.ad
+.RS 15n
+.rt
+Print verbose information about the stream package generated.
.RE
-.LP
The format of the stream is evolving. No backwards compatibility is guaranteed. You may not be able to receive your streams on future versions of \fBZFS\fR.
+.RE
+
.sp
.ne 2
.mk
@@ -1402,7 +1814,8 @@ The format of the stream is evolving. No backwards compatibility is guaranteed.
Creates a snapshot whose contents are as specified in the stream provided on standard input. If a full stream is received, then a new file system is created as well. Streams are created using the "\fBzfs send\fR" subcommand, which by default creates a full
stream. "\fBzfs recv\fR" can be used as an alias for "\fBzfs receive\fR".
.sp
-If an incremental stream is received, then the destination file system must already exist, and its most recent snapshot must match the incremental stream's source. The destination file system and all of its child file systems are unmounted and cannot be accessed during the receive operation.
+If an incremental stream is received, then the destination file system must already exist, and its most recent snapshot must match the incremental stream's source. For \fBzvols\fR, the destination device link is destroyed and re-created, which means the \fBzvol\fR cannot
+be accessed during the \fBreceive\fR operation.
.sp
The name of the snapshot (and file system, if a full stream is received) that this subcommand creates depends on the argument type and the \fB-d\fR option.
.sp
@@ -1438,7 +1851,7 @@ Print verbose information about the stream and the time required to perform the
.ad
.RS 6n
.rt
-Do not actually receive the stream. This can be useful in conjunction with the \fB-v\fR option to determine what name the receive operation would use.
+Do not actually receive the stream. This can be useful in conjunction with the \fB-v\fR option to verify the name the receive operation would use.
.RE
.sp
@@ -1449,7 +1862,8 @@ Do not actually receive the stream. This can be useful in conjunction with the \
.ad
.RS 6n
.rt
-Force a rollback of the \fIfilesystem\fR to the most recent snapshot before performing the receive operation.
+Force a rollback of the file system to the most recent snapshot before performing the receive operation. If receiving an incremental replication stream (for example, one generated by "z\fBfs send -R -[iI]\fR"), destroy snapshots and file systems that do
+not exist on the sending side.
.RE
.RE
@@ -1458,6 +1872,186 @@ Force a rollback of the \fIfilesystem\fR to the most recent snapshot before perf
.ne 2
.mk
.na
+\fB\fBzfs allow\fR [\fB-ldug\fR] "\fIeveryone\fR"|\fIuser\fR|\fIgroup\fR[,...] \fIperm\fR|@\fIsetname\fR[,...] \fIfilesystem\fR| \fIvolume\fR\fR
+.ad
+.br
+.na
+\fB\fBzfs allow\fR [\fB-ld\fR] \fB-e\fR \fIperm\fR|@\fIsetname\fR[,...] \fIfilesystem\fR|\fIvolume\fR\fR
+.ad
+.sp .6
+.RS 4n
+Delegates \fBZFS\fR administration permission for the file systems to non-privileged users.
+.sp
+.ne 2
+.mk
+.na
+\fB[\fB-ug\fR] "\fIeveryone\fR"|\fIuser\fR|\fIgroup\fR[,...]\fR
+.ad
+.sp .6
+.RS 4n
+Specifies to whom the permissions are delegated. Multiple entities can be specified as a comma-separated list. If neither of the \fB-ug\fR options are specified, then the argument is interpreted preferentially as the keyword "everyone", then as a user name,
+and lastly as a group name. To specify a user or group named "everyone", use the \fB-u\fR or \fB-g\fR options. To specify a group with the same name as a user, use the \fB-g\fR options.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB[\fB-e\fR] \fIperm\fR|@\fIsetname\fR[,...]\fR
+.ad
+.sp .6
+.RS 4n
+Specifies that the permissions be delegated to "everyone." Multiple permissions may be specified as a comma-separated list. Permission names are the same as \fBZFS\fR subcommand and property names. See the property list below. Property set names, which
+begin with an "at sign" ("@") , may be specified. See the \fB-s\fR form below for details.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB[\fB-ld\fR] \fIfilesystem\fR|\fIvolume\fR\fR
+.ad
+.sp .6
+.RS 4n
+Specifies where the permissions are delegated. If neither of the \fB-ld\fR options are specified, or both are, then the permissions are allowed for the file system or volume, and all of its descendents. If only the \fB-l\fR option is used, then is allowed "locally"
+only for the specified file system. If only the \fB-d\fR option is used, then is allowed only for the descendent file systems.
+.RE
+
+.RE
+
+.sp
+.LP
+Permissions are generally the ability to use a \fBZFS\fR subcommand or change a \fBZFS\fR property. The following permissions are available:
+.sp
+.in +2
+.nf
+NAME TYPE NOTES
+allow subcommand Must also have the permission
+ that is being allowed.
+clone subcommand Must also have the 'create' ability
+ and the 'mount' ability in the origin
+ file system.
+create subcommand Must also have the 'mount' ability.
+destroy subcommand Must also have the 'mount' ability.
+mount subcommand Allows mount, unmount, and
+ create/remove zvol device links.
+promote subcommand Must also have the 'mount' ability and
+ 'promote' ability in the origin file system.
+receive subcommand Must also have the 'mount' ability and
+ the 'create' ability.
+rename subcommand Must also have the 'mount' ability and
+ the 'create' ability in the new parent.
+rollback subcommand Must also have the 'mount' ability.
+snapshot subcommand Must also have the 'mount' ability.
+share subcommand Allows share and unshare.
+send subcommand
+
+
+aclinherit property
+aclmode property
+atime property
+canmount property
+checksum property
+compression property
+copies property
+devices property
+exec property
+mountpoint property
+quota property
+readonly property
+recordsize property
+reservation property
+setuid property
+shareiscsi property
+sharenfs property
+snapdir property
+version property
+volsize property
+xattr property
+zoned property
+userprop other Allows changing any user property.
+.fi
+.in -2
+.sp
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fBzfs allow\fR \fB-c\fR \fIperm\fR|@\fIsetname\fR[,...] \fIfilesystem\fR|\fIvolume\fR\fR
+.ad
+.sp .6
+.RS 4n
+Sets "create time" permissions. These permissions are granted (locally) to the creator of any newly-created descendent file system.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fBzfs allow\fR \fB-s\fR @setname \fIperm\fR|@\fIsetname\fR[,...] \fIfilesystem\fR|\fIvolume\fR\fR
+.ad
+.sp .6
+.RS 4n
+Defines or adds permissions to a permission set. The set can be used by other \fBzfs allow\fR commands for the specified file system and its descendents. Sets are evaluated dynamically, so changes to a set are immediately reflected. Permission sets follow the same
+naming restrictions as ZFS file systems, but the name must begin with an "at sign" ("@"), and can be no more than 64 characters long.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fBzfs unallow\fR [\fB-rldug\fR] "\fIeveryone\fR"|\fIuser\fR|\fIgroup\fR[,...] [\fIperm\fR|@\fIsetname\fR[, ...]] \fIfilesystem\fR|\fIvolume\fR\fR
+.ad
+.br
+.na
+\fB\fBzfs unallow\fR [\fB-rld\fR] \fB-e\fR [\fIperm\fR|@\fIsetname\fR [,...]] \fIfilesystem\fR|\fIvolume\fR\fR
+.ad
+.br
+.na
+\fB\fBzfs unallow\fR [\fB-r\fR] \fB-c\fR [\fIperm\fR|@\fIsetname\fR[,...]]\fR
+.ad
+.br
+.na
+\fB\fIfilesystem\fR|\fIvolume\fR\fR
+.ad
+.sp .6
+.RS 4n
+Removes permissions that were granted with the "\fBzfs allow\fR" command. No permissions are explicitly denied, so other permissions granted are still in effect. For example, if the permission is granted by an ancestor. If no permissions are specified,
+then all permissions for the specified \fIuser\fR, \fIgroup\fR, or \fIeveryone\fR are removed. Specifying "everyone" (or using the \fB-e\fR option) only removes the permissions that were granted to "everyone",
+not all permissions for every user and group. See the "\fBzfs allow\fR" command for a description of the \fB-ldugec\fR options.
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-r\fR\fR
+.ad
+.RS 6n
+.rt
+Recursively remove the permissions from this file system and all descendents.
+.RE
+
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fBzfs unallow\fR [\fB-r\fR] \fB-s\fR @setname [\fIperm\fR|@\fIsetname\fR[,...]]\fR
+.ad
+.br
+.na
+\fB\fIfilesystem\fR|\fIvolume\fR\fR
+.ad
+.sp .6
+.RS 4n
+Removes permissions from a permission set. If no permissions are specified, then all permissions are removed, thus removing the set entirely.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
\fB\fBzfs jail\fR \fIjailid\fR \fIfilesystem\fR\fR
.ad
.sp .6
@@ -1480,6 +2074,7 @@ Detaches the given file system from the given jail.
.SH EXAMPLES
.LP
\fBExample 1 \fRCreating a ZFS File System Hierarchy
+.sp
.LP
The following commands create a file system named "\fBpool/home\fR" and a file system named "\fBpool/home/bob\fR". The mount point "\fB/export/home\fR" is set for the parent file system, and automatically inherited
by the child file system.
@@ -1496,6 +2091,7 @@ by the child file system.
.LP
\fBExample 2 \fRCreating a ZFS Snapshot
+.sp
.LP
The following command creates a snapshot named "yesterday". This snapshot is mounted on demand in the ".zfs/snapshot" directory at the root of the "\fBpool/home/bob\fR" file system.
@@ -1509,8 +2105,9 @@ The following command creates a snapshot named "yesterday". This snapshot is mou
.LP
\fBExample 3 \fRTaking and destroying multiple snapshots
+.sp
.LP
-The following command creates snapshots named "\fByesterday\fR" of "\fBpool/home\fR" and all of its descendant file systems. Each snapshot is mounted on demand in the ".zfs/snapshot" directory at the root of its file system. The
+The following command creates snapshots named "\fByesterday\fR" of "\fBpool/home\fR" and all of its descendent file systems. Each snapshot is mounted on demand in the ".zfs/snapshot" directory at the root of its file system. The
second command destroys the newly created snapshots.
.sp
@@ -1524,6 +2121,7 @@ second command destroys the newly created snapshots.
.LP
\fBExample 4 \fRTurning Off Compression
+.sp
.LP
The following commands turn compression off for all file systems under "\fBpool/home\fR", but explicitly turns it on for "\fBpool/home/anne\fR".
@@ -1538,6 +2136,7 @@ The following commands turn compression off for all file systems under "\fBpool/
.LP
\fBExample 5 \fRListing ZFS Datasets
+.sp
.LP
The following command lists all active file systems and volumes in the system.
@@ -1548,17 +2147,18 @@ The following command lists all active file systems and volumes in the system.
NAME USED AVAIL REFER MOUNTPOINT
- pool 100G 60G - /pool
- pool/home 100G 60G - /export/home
- pool/home/bob 40G 60G 40G /export/home/bob
- pool/home/bob@yesterday 3M - 40G -
- pool/home/anne 60G 60G 40G /export/home/anne
+ pool 450K 457G 18K /pool
+ pool/home 315K 457G 21K /export/home
+ pool/home/anne 18K 457G 18K /export/home/anne
+ pool/home/bob 276K 457G 276K /export/home/bob
+ pool/home/bob@yesterday 0 - 276K -
.fi
.in -2
.sp
.LP
\fBExample 6 \fRSetting a Quota on a ZFS File System
+.sp
.LP
The following command sets a quota of 50 gbytes for "\fBpool/home/bob\fR".
@@ -1572,6 +2172,7 @@ The following command sets a quota of 50 gbytes for "\fBpool/home/bob\fR".
.LP
\fBExample 7 \fRListing ZFS Properties
+.sp
.LP
The following command lists all properties for "\fBpool/home/bob\fR".
@@ -1583,18 +2184,17 @@ The following command lists all properties for "\fBpool/home/bob\fR".
NAME PROPERTY VALUE SOURCE
pool/home/bob type filesystem -
- pool/home/bob creation Fri Feb 23 14:20 2007 -
- pool/home/bob used 24.5K -
+ pool/home/bob creation Thu Jul 12 14:44 2007 -
+ pool/home/bob used 276K -
pool/home/bob available 50.0G -
- pool/home/bob referenced 24.5K -
+ pool/home/bob referenced 276K -
pool/home/bob compressratio 1.00x -
pool/home/bob mounted yes -
pool/home/bob quota 50G local
pool/home/bob reservation none default
pool/home/bob recordsize 128K default
- pool/home/bob mountpoint /pool/home/bob default
- pool/home/bob sharenfs off default
- pool/home/bob shareiscsi off default
+ pool/home/bob mountpoint /export/home/bob inherited from
+ pool/home
pool/home/bob checksum on default
pool/home/bob compression off default
pool/home/bob atime on default
@@ -1605,15 +2205,24 @@ The following command lists all properties for "\fBpool/home/bob\fR".
pool/home/bob zoned off default
pool/home/bob snapdir hidden default
pool/home/bob aclmode groupmask default
- pool/home/bob aclinherit secure default
+ pool/home/bob aclinherit restricted default
pool/home/bob canmount on default
+ pool/home/bob nbmand off default
+ pool/home/bob shareiscsi off default
+ pool/home/bob sharesmb off default
+ pool/home/bob sharenfs off default
pool/home/bob xattr on default
+ pool/home/bob refquota 10M local
+ pool/home/bob refreservation none default
+ pool/home/bob copies 1 default
+ pool/home/bob version 1 -
.fi
.in -2
.sp
+.sp
.LP
The following command gets a single property value.
@@ -1626,6 +2235,7 @@ on
.in -2
.sp
+.sp
.LP
The following command lists all properties with local settings for "\fBpool/home/bob\fR".
@@ -1643,6 +2253,7 @@ The following command lists all properties with local settings for "\fBpool/home
.LP
\fBExample 8 \fRRolling Back a ZFS File System
+.sp
.LP
The following command reverts the contents of "\fBpool/home/anne\fR" to the snapshot named "\fByesterday\fR", deleting all intermediate snapshots.
@@ -1656,6 +2267,7 @@ The following command reverts the contents of "\fBpool/home/anne\fR" to the snap
.LP
\fBExample 9 \fRCreating a ZFS Clone
+.sp
.LP
The following command creates a writable file system whose initial contents are the same as "\fBpool/home/bob@yesterday\fR".
@@ -1669,6 +2281,7 @@ The following command creates a writable file system whose initial contents are
.LP
\fBExample 10 \fRPromoting a ZFS Clone
+.sp
.LP
The following commands illustrate how to test out changes to a file system, and then replace the original file system with the changed one, using clones, clone promotion, and renaming:
@@ -1692,6 +2305,7 @@ The following commands illustrate how to test out changes to a file system, and
.LP
\fBExample 11 \fRInheriting ZFS Properties
+.sp
.LP
The following command causes "\fBpool/home/bob\fR" and "\fBpool/home/anne\fR" to inherit the "checksum" property from their parent.
@@ -1705,6 +2319,7 @@ The following command causes "\fBpool/home/bob\fR" and "\fBpool/home/anne\fR" to
.LP
\fBExample 12 \fRRemotely Replicating ZFS Data
+.sp
.LP
The following commands send a full stream and then an incremental stream to a remote machine, restoring them into "\fBpoolB/received/fs\fR@a" and "\fBpoolB/received/fs@b\fR", respectively. "\fBpoolB\fR" must contain
the file system "\fBpoolB/received\fR", and must not initially contain "\fBpoolB/received/fs\fR".
@@ -1721,30 +2336,31 @@ the file system "\fBpoolB/received\fR", and must not initially contain "\fBpoolB
.sp
.LP
-\fBExample 13 \fRUsing the zfs receive -d Option
+\fBExample 13 \fRUsing the zfs receive -d Option
+.sp
.LP
The following command sends a full stream of "\fBpoolA/fsA/fsB@snap\fR" to a remote machine, receiving it into "\fBpoolB/received/fsA/fsB@snap\fR". The "\fBfsA/fsB@snap\fR" portion of the received snapshot's name
-is determined from the name of the sent snapshot. "\fBpoolB\fR" must contain the file system "\fBpoolB/received\fR". If "\fBpoolB/received/fsA\fR" does not exist, it will be created as an empty file system.
+is determined from the name of the sent snapshot. "\fBpoolB\fR" must contain the file system "\fBpoolB/received\fR". If "\fBpoolB/received/fsA\fR" does not exist, it is be created as an empty file system.
.sp
.in +2
.nf
\fB# zfs send poolA/fsA/fsB@snap | \e
- ssh host zfs receive -d poolB/received
- \fR
+ ssh host zfs receive -d poolB/received\fR
.fi
.in -2
.sp
.LP
\fBExample 14 \fRCreating a ZFS volume as a Swap Device
+.sp
.LP
The following example shows how to create a 5-Gbyte ZFS volume and then add the volume as a swap device.
.sp
.in +2
.nf
-\fB# zfs create -V 5gb tank/vol
+\fB# zfs create -V 5gb tank/vol
# swap -a /dev/zvol/dsk/tank/vol\fR
.fi
.in -2
@@ -1752,6 +2368,7 @@ The following example shows how to create a 5-Gbyte ZFS volume and then add the
.LP
\fBExample 15 \fRSetting User Properties
+.sp
.LP
The following example sets the user defined "com.example:department" property for a dataset.
@@ -1765,6 +2382,7 @@ The following example sets the user defined "com.example:department" property fo
.LP
\fBExample 16 \fRCreating a ZFS Volume as a iSCSI Target Device
+.sp
.LP
The following example shows how to create a \fBZFS\fR volume as an \fBiSCSI\fR target.
@@ -1782,9 +2400,173 @@ Connections: 0
.in -2
.sp
+.sp
.LP
After the \fBiSCSI\fR target is created, set up the \fBiSCSI\fR initiator. For more information about the Solaris \fBiSCSI\fR initiator, see the Solaris Administration Guide: Devices and File Systems.
+.LP
+\fBExample 17 \fRPerforming a Rolling Snapshot
+.sp
+.LP
+The following example shows how to maintain a history of snapshots with a consistent naming scheme. To keep a week's worth of snapshots, the user destroys the oldest snapshot, renames the remaining snapshots, and then creates a new snapshot, as follows:
+
+.sp
+.in +2
+.nf
+\fB# zfs destroy -r pool/users@7daysago
+# zfs rename -r pool/users@6daysago @7daysago
+# zfs rename -r pool/users@5daysago @6daysago
+\&...
+# zfs rename -r pool/users@yesterday @2daysago
+# zfs rename -r pool/users@today @yesterday
+# zfs snapshot -r pool/users@today\fR
+.fi
+.in -2
+.sp
+
+.LP
+\fBExample 18 \fRSetting sharenfs Property Options on a ZFS File System
+.sp
+.LP
+The following commands show how to set "sharenfs" property options to enable \fBrw\fR access for a set of \fBIP\fR addresses and to enable root access for system \fBneo\fR on the \fBtank/home\fR file system.
+
+.sp
+.in +2
+.nf
+\fB# zfs set sharenfs='rw=@123.123.0.0/16,root=neo' tank/home\fR
+
+.fi
+.in -2
+.sp
+
+.sp
+.LP
+If you are using \fBDNS\fR for host name resolution, specify the fully qualified hostname.
+
+.LP
+\fBExample 19 \fRDelegating ZFS Administration Permissions on a ZFS Dataset
+.sp
+.LP
+The following example shows how to set permissions so that user "\fBcindys\fR" can create, destroy, mount and take snapshots on \fBtank/cindys\fR. The permissions on \fBtank/cindys\fR are also displayed.
+
+.sp
+.in +2
+.nf
+\fB# zfs allow cindys create,destroy,mount,snapshot tank/cindys
+# zfs allow tank/cindys\fR
+-------------------------------------------------------------
+Local+Descendent permissions on (tank/cindys)
+ user cindys create,destroy,mount,snapshot
+-------------------------------------------------------------
+
+.fi
+.in -2
+.sp
+
+.sp
+.LP
+Because the \fBtank/cindys\fR mount point permission is set to 755 by default, user \fBcindys\fR will be unable to mount file systems under \fBtank/cindys\fR. Set an \fBACL\fR similar to the following syntax to provide mount point access:
+.sp
+.in +2
+.nf
+# chmod A+user:cindys:add_subdirectory:allow /tank/cindys
+.fi
+.in -2
+.sp
+
+.LP
+\fBExample 20 \fRDelegating Create Time Permissions on a ZFS Dataset
+.sp
+.LP
+The following example shows how to grant anyone in the group \fBstaff\fR to create file systems in \fBtank/users\fR. This syntax also allows staff members to destroy their own file systems, but not destroy anyone else's file system. The permissions on \fBtank/users\fR are also displayed.
+
+.sp
+.in +2
+.nf
+\fB# zfs allow staff create,mount tank/users
+# zfs allow -c destroy tank/users
+# zfs allow tank/users\fR
+-------------------------------------------------------------
+Create time permissions on (tank/users)
+ create,destroy
+Local+Descendent permissions on (tank/users)
+ group staff create,mount
+-------------------------------------------------------------
+.fi
+.in -2
+.sp
+
+.LP
+\fBExample 21 \fRDefining and Granting a Permission Set on a ZFS Dataset
+.sp
+.LP
+The following example shows how to define and grant a permission set on the \fBtank/users\fR file system. The permissions on \fBtank/users\fR are also displayed.
+
+.sp
+.in +2
+.nf
+\fB# zfs allow -s @pset create,destroy,snapshot,mount tank/users
+# zfs allow staff @pset tank/users
+# zfs allow tank/users
+-------------------------------------------------------------
+Permission sets on (tank/users)
+ @pset create,destroy,mount,snapshot
+Create time permissions on (tank/users)
+ create,destroy
+Local+Descendent permissions on (tank/users)
+ group staff @pset,create,mount
+-------------------------------------------------------------\fR
+.fi
+.in -2
+.sp
+
+.LP
+\fBExample 22 \fRDelegating Property Permissions on a ZFS Dataset
+.sp
+.LP
+The following example shows to grant the ability to set quotas and reservations on the \fBusers/home\fR file system. The permissions on \fBusers/home\fR are also displayed.
+
+.sp
+.in +2
+.nf
+\fB# zfs allow cindys quota,reservation users/home
+# zfs allow users/home\fR
+-------------------------------------------------------------
+Local+Descendent permissions on (users/home)
+ user cindys quota,reservation
+-------------------------------------------------------------
+cindys% zfs set quota=10G users/home/marks
+cindys% zfs get quota users/home/marks
+NAME PROPERTY VALUE SOURCE
+users/home/marks quota 10G local
+.fi
+.in -2
+.sp
+
+.LP
+\fBExample 23 \fRRemoving ZFS Delegated Permissions on a ZFS Dataset
+.sp
+.LP
+The following example shows how to remove the snapshot permission from the \fBstaff\fR group on the \fBtank/users\fR file system. The permissions on \fBtank/users\fR are also displayed.
+
+.sp
+.in +2
+.nf
+\fB# zfs unallow staff snapshot tank/users
+# zfs allow tank/users\fR
+-------------------------------------------------------------
+Permission sets on (tank/users)
+ @pset create,destroy,mount,snapshot
+Create time permissions on (tank/users)
+ create,destroy
+Local+Descendent permissions on (tank/users)
+ group staff @pset,create,mount
+-------------------------------------------------------------
+.fi
+.in -2
+.sp
+
.SH EXIT STATUS
+.sp
.LP
The following exit values are returned:
.sp
@@ -1821,6 +2603,7 @@ Invalid command line options were specified.
.RE
.SH ATTRIBUTES
+.sp
.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -1835,9 +2618,13 @@ ATTRIBUTE TYPEATTRIBUTE VALUE
_
AvailabilitySUNWzfsu
_
-Interface StabilityEvolving
+Interface StabilityCommitted
.TE
.SH SEE ALSO
+.sp
+.LP
+\fBgzip\fR(1), \fBssh\fR(1), \fBmount\fR(1M), \fBshare\fR(1M), \fBsharemgr\fR(1M), \fBunshare\fR(1M), \fBzonecfg\fR(1M), \fBzpool\fR(1M), \fBchmod\fR(2), \fBstat\fR(2), \fBfsync\fR(3c), \fBdfstab\fR(4), \fBattributes\fR(5)
+.sp
.LP
-\fBgzip\fR(1), \fBssh\fR(1), \fBmount\fR(1M), \fBshare\fR(1M), \fBunshare\fR(1M), \fBzonecfg\fR(1M), \fBzpool\fR(1M), \fBchmod\fR(2), \fBstat\fR(2), \fBfsync\fR(3c), \fBdfstab\fR(4), \fBattributes\fR(5)
+For information about using the \fBZFS\fR web-based management tool and other \fBZFS\fR features, see the \fIZFS Administration Guide\fR.
diff --git a/cddl/contrib/opensolaris/cmd/zfs/zfs_iter.c b/cddl/contrib/opensolaris/cmd/zfs/zfs_iter.c
index eb6b8b1..a22370a 100644
--- a/cddl/contrib/opensolaris/cmd/zfs/zfs_iter.c
+++ b/cddl/contrib/opensolaris/cmd/zfs/zfs_iter.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <libintl.h>
#include <libuutil.h>
#include <stddef.h>
@@ -56,28 +54,43 @@ typedef struct zfs_node {
typedef struct callback_data {
uu_avl_t *cb_avl;
- int cb_recurse;
+ int cb_flags;
zfs_type_t cb_types;
zfs_sort_column_t *cb_sortcol;
- zfs_proplist_t **cb_proplist;
+ zprop_list_t **cb_proplist;
} callback_data_t;
uu_avl_pool_t *avl_pool;
/*
- * Called for each dataset. If the object the object is of an appropriate type,
+ * Include snaps if they were requested or if this a zfs list where types
+ * were not specified and the "listsnapshots" property is set on this pool.
+ */
+static int
+zfs_include_snapshots(zfs_handle_t *zhp, callback_data_t *cb)
+{
+ zpool_handle_t *zph;
+
+ if ((cb->cb_flags & ZFS_ITER_PROP_LISTSNAPS) == 0)
+ return (cb->cb_types & ZFS_TYPE_SNAPSHOT);
+
+ zph = zfs_get_pool_handle(zhp);
+ return (zpool_get_prop_int(zph, ZPOOL_PROP_LISTSNAPS, NULL));
+}
+
+/*
+ * Called for each dataset. If the object is of an appropriate type,
* add it to the avl tree and recurse over any children as necessary.
*/
-int
+static int
zfs_callback(zfs_handle_t *zhp, void *data)
{
callback_data_t *cb = data;
int dontclose = 0;
+ int include_snaps = zfs_include_snapshots(zhp, cb);
- /*
- * If this object is of the appropriate type, add it to the AVL tree.
- */
- if (zfs_get_type(zhp) & cb->cb_types) {
+ if ((zfs_get_type(zhp) & cb->cb_types) ||
+ ((zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) && include_snaps)) {
uu_avl_index_t idx;
zfs_node_t *node = safe_malloc(sizeof (zfs_node_t));
@@ -100,10 +113,12 @@ zfs_callback(zfs_handle_t *zhp, void *data)
/*
* Recurse if necessary.
*/
- if (cb->cb_recurse && (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM ||
- (zfs_get_type(zhp) == ZFS_TYPE_VOLUME && (cb->cb_types &
- ZFS_TYPE_SNAPSHOT))))
- (void) zfs_iter_children(zhp, zfs_callback, data);
+ if (cb->cb_flags & ZFS_ITER_RECURSE) {
+ if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM)
+ (void) zfs_iter_filesystems(zhp, zfs_callback, data);
+ if ((zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) && include_snaps)
+ (void) zfs_iter_snapshots(zhp, zfs_callback, data);
+ }
if (!dontclose)
zfs_close(zhp);
@@ -118,7 +133,7 @@ zfs_add_sort_column(zfs_sort_column_t **sc, const char *name,
zfs_sort_column_t *col;
zfs_prop_t prop;
- if ((prop = zfs_name_to_prop(name)) == ZFS_PROP_INVAL &&
+ if ((prop = zfs_name_to_prop(name)) == ZPROP_INVAL &&
!zfs_prop_user(name))
return (-1);
@@ -126,7 +141,7 @@ zfs_add_sort_column(zfs_sort_column_t **sc, const char *name,
col->sc_prop = prop;
col->sc_reverse = reverse;
- if (prop == ZFS_PROP_INVAL) {
+ if (prop == ZPROP_INVAL) {
col->sc_user_prop = safe_malloc(strlen(name) + 1);
(void) strcpy(col->sc_user_prop, name);
}
@@ -243,7 +258,7 @@ zfs_sort(const void *larg, const void *rarg, void *data)
* Otherwise, we compare 'lnum' and 'rnum'.
*/
lstr = rstr = NULL;
- if (psc->sc_prop == ZFS_PROP_INVAL) {
+ if (psc->sc_prop == ZPROP_INVAL) {
nvlist_t *luser, *ruser;
nvlist_t *lval, *rval;
@@ -257,10 +272,10 @@ zfs_sort(const void *larg, const void *rarg, void *data)
if (lvalid)
verify(nvlist_lookup_string(lval,
- ZFS_PROP_VALUE, &lstr) == 0);
+ ZPROP_VALUE, &lstr) == 0);
if (rvalid)
verify(nvlist_lookup_string(rval,
- ZFS_PROP_VALUE, &rstr) == 0);
+ ZPROP_VALUE, &rstr) == 0);
} else if (zfs_prop_is_string(psc->sc_prop)) {
lvalid = (zfs_prop_get(l, psc->sc_prop, lbuf,
@@ -293,7 +308,7 @@ zfs_sort(const void *larg, const void *rarg, void *data)
if (lstr)
ret = strcmp(lstr, rstr);
- if (lnum < rnum)
+ else if (lnum < rnum)
ret = -1;
else if (lnum > rnum)
ret = 1;
@@ -309,9 +324,9 @@ zfs_sort(const void *larg, const void *rarg, void *data)
}
int
-zfs_for_each(int argc, char **argv, boolean_t recurse, zfs_type_t types,
- zfs_sort_column_t *sortcol, zfs_proplist_t **proplist, zfs_iter_f callback,
- void *data, boolean_t args_can_be_paths)
+zfs_for_each(int argc, char **argv, int flags, zfs_type_t types,
+ zfs_sort_column_t *sortcol, zprop_list_t **proplist,
+ zfs_iter_f callback, void *data)
{
callback_data_t cb;
int ret = 0;
@@ -328,7 +343,7 @@ zfs_for_each(int argc, char **argv, boolean_t recurse, zfs_type_t types,
}
cb.cb_sortcol = sortcol;
- cb.cb_recurse = recurse;
+ cb.cb_flags = flags;
cb.cb_proplist = proplist;
cb.cb_types = types;
if ((cb.cb_avl = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) {
@@ -341,7 +356,7 @@ zfs_for_each(int argc, char **argv, boolean_t recurse, zfs_type_t types,
/*
* If given no arguments, iterate over all datasets.
*/
- cb.cb_recurse = 1;
+ cb.cb_flags |= ZFS_ITER_RECURSE;
ret = zfs_iter_root(g_zfs, zfs_callback, &cb);
} else {
int i;
@@ -354,14 +369,14 @@ zfs_for_each(int argc, char **argv, boolean_t recurse, zfs_type_t types,
* can take volumes as well.
*/
argtype = types;
- if (recurse) {
+ if (flags & ZFS_ITER_RECURSE) {
argtype |= ZFS_TYPE_FILESYSTEM;
if (types & ZFS_TYPE_SNAPSHOT)
argtype |= ZFS_TYPE_VOLUME;
}
for (i = 0; i < argc; i++) {
- if (args_can_be_paths) {
+ if (flags & ZFS_ITER_ARGS_CAN_BE_PATHS) {
zhp = zfs_path_to_zhandle(g_zfs, argv[i],
argtype);
} else {
diff --git a/cddl/contrib/opensolaris/cmd/zfs/zfs_iter.h b/cddl/contrib/opensolaris/cmd/zfs/zfs_iter.h
index 1f0ce8e..76a1108 100644
--- a/cddl/contrib/opensolaris/cmd/zfs/zfs_iter.h
+++ b/cddl/contrib/opensolaris/cmd/zfs/zfs_iter.h
@@ -19,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef ZFS_ITER_H
#define ZFS_ITER_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -40,8 +38,12 @@ typedef struct zfs_sort_column {
boolean_t sc_reverse;
} zfs_sort_column_t;
-int zfs_for_each(int, char **, boolean_t, zfs_type_t, zfs_sort_column_t *,
- zfs_proplist_t **, zfs_iter_f, void *, boolean_t);
+#define ZFS_ITER_RECURSE (1 << 0)
+#define ZFS_ITER_ARGS_CAN_BE_PATHS (1 << 1)
+#define ZFS_ITER_PROP_LISTSNAPS (1 << 2)
+
+int zfs_for_each(int, char **, int options, zfs_type_t,
+ zfs_sort_column_t *, zprop_list_t **, zfs_iter_f, void *);
int zfs_add_sort_column(zfs_sort_column_t **, const char *, boolean_t);
void zfs_free_sort_columns(zfs_sort_column_t *);
diff --git a/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c b/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c
index de15b00..6f5f92e 100644
--- a/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c
+++ b/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c
@@ -20,18 +20,17 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <libgen.h>
#include <libintl.h>
#include <libuutil.h>
+#include <libnvpair.h>
#include <locale.h>
#include <stddef.h>
#include <stdio.h>
@@ -44,8 +43,10 @@
#include <sys/mnttab.h>
#include <sys/mount.h>
#include <sys/stat.h>
+#include <sys/avl.h>
#include <libzfs.h>
+#include <libuutil.h>
#include "zfs_iter.h"
#include "zfs_util.h"
@@ -53,6 +54,7 @@
libzfs_handle_t *g_zfs;
static FILE *mnttab_file;
+static char history_str[HIS_MAX_RECORD_LEN];
static int zfs_do_clone(int argc, char **argv);
static int zfs_do_create(int argc, char **argv);
@@ -64,6 +66,7 @@ static int zfs_do_mount(int argc, char **argv);
static int zfs_do_rename(int argc, char **argv);
static int zfs_do_rollback(int argc, char **argv);
static int zfs_do_set(int argc, char **argv);
+static int zfs_do_upgrade(int argc, char **argv);
static int zfs_do_snapshot(int argc, char **argv);
static int zfs_do_unmount(int argc, char **argv);
static int zfs_do_share(int argc, char **argv);
@@ -71,13 +74,16 @@ static int zfs_do_unshare(int argc, char **argv);
static int zfs_do_send(int argc, char **argv);
static int zfs_do_receive(int argc, char **argv);
static int zfs_do_promote(int argc, char **argv);
+static int zfs_do_allow(int argc, char **argv);
+static int zfs_do_unallow(int argc, char **argv);
static int zfs_do_jail(int argc, char **argv);
static int zfs_do_unjail(int argc, char **argv);
/*
- * These libumem hooks provide a reasonable set of defaults for the allocator's
- * debugging facilities.
+ * Enable a reasonable set of defaults for libumem debugging on DEBUG builds.
*/
+
+#ifdef DEBUG
const char *
_umem_debug_init(void)
{
@@ -89,6 +95,7 @@ _umem_logging_init(void)
{
return ("fail,contents"); /* $UMEM_LOGGING setting */
}
+#endif
typedef enum {
HELP_CLONE,
@@ -96,6 +103,7 @@ typedef enum {
HELP_DESTROY,
HELP_GET,
HELP_INHERIT,
+ HELP_UPGRADE,
HELP_JAIL,
HELP_UNJAIL,
HELP_LIST,
@@ -109,7 +117,9 @@ typedef enum {
HELP_SHARE,
HELP_SNAPSHOT,
HELP_UNMOUNT,
- HELP_UNSHARE
+ HELP_UNSHARE,
+ HELP_ALLOW,
+ HELP_UNALLOW
} zfs_help_t;
typedef struct zfs_command {
@@ -142,18 +152,20 @@ static zfs_command_t command_table[] = {
{ "set", zfs_do_set, HELP_SET },
{ "get", zfs_do_get, HELP_GET },
{ "inherit", zfs_do_inherit, HELP_INHERIT },
+ { "upgrade", zfs_do_upgrade, HELP_UPGRADE },
{ NULL },
{ "mount", zfs_do_mount, HELP_MOUNT },
- { NULL },
{ "unmount", zfs_do_unmount, HELP_UNMOUNT },
- { NULL },
{ "share", zfs_do_share, HELP_SHARE },
- { NULL },
{ "unshare", zfs_do_unshare, HELP_UNSHARE },
{ NULL },
{ "send", zfs_do_send, HELP_SEND },
{ "receive", zfs_do_receive, HELP_RECEIVE },
{ NULL },
+ { "allow", zfs_do_allow, HELP_ALLOW },
+ { NULL },
+ { "unallow", zfs_do_unallow, HELP_UNALLOW },
+ { NULL },
{ "jail", zfs_do_jail, HELP_JAIL },
{ "unjail", zfs_do_unjail, HELP_UNJAIL },
};
@@ -167,39 +179,41 @@ get_usage(zfs_help_t idx)
{
switch (idx) {
case HELP_CLONE:
- return (gettext("\tclone <snapshot> <filesystem|volume>\n"));
+ return (gettext("\tclone [-p] [-o property=value] ... "
+ "<snapshot> <filesystem|volume>\n"));
case HELP_CREATE:
- return (gettext("\tcreate [[-o property=value] ... ] "
+ return (gettext("\tcreate [-p] [-o property=value] ... "
"<filesystem>\n"
- "\tcreate [-s] [-b blocksize] [[-o property=value] ...]\n"
- "\t -V <size> <volume>\n"));
+ "\tcreate [-ps] [-b blocksize] [-o property=value] ... "
+ "-V <size> <volume>\n"));
case HELP_DESTROY:
return (gettext("\tdestroy [-rRf] "
"<filesystem|volume|snapshot>\n"));
case HELP_GET:
- return (gettext("\tget [-rHp] [-o field[,field]...] "
- "[-s source[,source]...]\n"
- "\t <all | property[,property]...> "
+ return (gettext("\tget [-rHp] [-o field[,...]] "
+ "[-s source[,...]]\n"
+ "\t <\"all\" | property[,...]> "
"[filesystem|volume|snapshot] ...\n"));
case HELP_INHERIT:
return (gettext("\tinherit [-r] <property> "
- "<filesystem|volume> ...\n"));
+ "<filesystem|volume|snapshot> ...\n"));
+ case HELP_UPGRADE:
+ return (gettext("\tupgrade [-v]\n"
+ "\tupgrade [-r] [-V version] <-a | filesystem ...>\n"));
case HELP_JAIL:
return (gettext("\tjail <jailid> <filesystem>\n"));
case HELP_UNJAIL:
return (gettext("\tunjail <jailid> <filesystem>\n"));
case HELP_LIST:
- return (gettext("\tlist [-rH] [-o property[,property]...] "
- "[-t type[,type]...]\n"
- "\t [-s property [-s property]...]"
- " [-S property [-S property]...]\n"
- "\t [filesystem|volume|snapshot] ...\n"));
+ return (gettext("\tlist [-rH] [-o property[,...]] "
+ "[-t type[,...]] [-s property] ...\n"
+ "\t [-S property] ... "
+ "[filesystem|volume|snapshot] ...\n"));
case HELP_MOUNT:
return (gettext("\tmount\n"
- "\tmount [-o opts] [-O] -a\n"
- "\tmount [-o opts] [-O] <filesystem>\n"));
+ "\tmount [-vO] [-o opts] <-a | filesystem>\n"));
case HELP_PROMOTE:
- return (gettext("\tpromote <clone filesystem>\n"));
+ return (gettext("\tpromote <clone-filesystem>\n"));
case HELP_RECEIVE:
return (gettext("\treceive [-vnF] <filesystem|volume|"
"snapshot>\n"
@@ -207,26 +221,45 @@ get_usage(zfs_help_t idx)
case HELP_RENAME:
return (gettext("\trename <filesystem|volume|snapshot> "
"<filesystem|volume|snapshot>\n"
+ "\trename -p <filesystem|volume> <filesystem|volume>\n"
"\trename -r <snapshot> <snapshot>"));
case HELP_ROLLBACK:
return (gettext("\trollback [-rRf] <snapshot>\n"));
case HELP_SEND:
- return (gettext("\tsend [-i <snapshot>] <snapshot>\n"));
+ return (gettext("\tsend [-R] [-[iI] snapshot] <snapshot>\n"));
case HELP_SET:
return (gettext("\tset <property=value> "
- "<filesystem|volume> ...\n"));
+ "<filesystem|volume|snapshot> ...\n"));
case HELP_SHARE:
- return (gettext("\tshare -a\n"
- "\tshare <filesystem>\n"));
+ return (gettext("\tshare <-a | filesystem>\n"));
case HELP_SNAPSHOT:
- return (gettext("\tsnapshot [-r] "
- "<filesystem@name|volume@name>\n"));
+ return (gettext("\tsnapshot [-r] [-o property=value] ... "
+ "<filesystem@snapname|volume@snapname>\n"));
case HELP_UNMOUNT:
- return (gettext("\tunmount [-f] -a\n"
- "\tunmount [-f] <filesystem|mountpoint>\n"));
+ return (gettext("\tunmount [-f] "
+ "<-a | filesystem|mountpoint>\n"));
case HELP_UNSHARE:
- return (gettext("\tunshare [-f] -a\n"
- "\tunshare [-f] <filesystem|mountpoint>\n"));
+ return (gettext("\tunshare [-f] "
+ "<-a | filesystem|mountpoint>\n"));
+ case HELP_ALLOW:
+ return (gettext("\tallow [-ldug] "
+ "<\"everyone\"|user|group>[,...] <perm|@setname>[,...]\n"
+ "\t <filesystem|volume>\n"
+ "\tallow [-ld] -e <perm|@setname>[,...] "
+ "<filesystem|volume>\n"
+ "\tallow -c <perm|@setname>[,...] <filesystem|volume>\n"
+ "\tallow -s @setname <perm|@setname>[,...] "
+ "<filesystem|volume>\n"));
+ case HELP_UNALLOW:
+ return (gettext("\tunallow [-rldug] "
+ "<\"everyone\"|user|group>[,...]\n"
+ "\t [<perm|@setname>[,...]] <filesystem|volume>\n"
+ "\tunallow [-rld] -e [<perm|@setname>[,...]] "
+ "<filesystem|volume>\n"
+ "\tunallow [-r] -c [<perm|@setname>[,...]] "
+ "<filesystem|volume>\n"
+ "\tunallow [-r] -s @setname [<perm|@setname>[,...]] "
+ "<filesystem|volume>\n"));
}
abort();
@@ -250,20 +283,20 @@ safe_malloc(size_t size)
}
/*
- * Callback routinue that will print out information for each of the
+ * Callback routine that will print out information for each of
* the properties.
*/
-static zfs_prop_t
-usage_prop_cb(zfs_prop_t prop, void *cb)
+static int
+usage_prop_cb(int prop, void *cb)
{
FILE *fp = cb;
- (void) fprintf(fp, "\t%-13s ", zfs_prop_to_name(prop));
+ (void) fprintf(fp, "\t%-15s ", zfs_prop_to_name(prop));
if (zfs_prop_readonly(prop))
- (void) fprintf(fp, " NO ");
+ (void) fprintf(fp, " NO ");
else
- (void) fprintf(fp, " YES ");
+ (void) fprintf(fp, "YES ");
if (zfs_prop_inheritable(prop))
(void) fprintf(fp, " YES ");
@@ -275,7 +308,7 @@ usage_prop_cb(zfs_prop_t prop, void *cb)
else
(void) fprintf(fp, "%s\n", zfs_prop_values(prop));
- return (ZFS_PROP_CONT);
+ return (ZPROP_CONT);
}
/*
@@ -288,6 +321,7 @@ usage(boolean_t requested)
{
int i;
boolean_t show_properties = B_FALSE;
+ boolean_t show_permissions = B_FALSE;
FILE *fp = requested ? stdout : stderr;
if (current_command == NULL) {
@@ -318,29 +352,46 @@ usage(boolean_t requested)
strcmp(current_command->name, "list") == 0))
show_properties = B_TRUE;
+ if (current_command != NULL &&
+ (strcmp(current_command->name, "allow") == 0 ||
+ strcmp(current_command->name, "unallow") == 0))
+ show_permissions = B_TRUE;
+
if (show_properties) {
(void) fprintf(fp,
gettext("\nThe following properties are supported:\n"));
- (void) fprintf(fp, "\n\t%-13s %s %s %s\n\n",
+ (void) fprintf(fp, "\n\t%-14s %s %s %s\n\n",
"PROPERTY", "EDIT", "INHERIT", "VALUES");
/* Iterate over all properties */
- (void) zfs_prop_iter(usage_prop_cb, fp, B_FALSE);
+ (void) zprop_iter(usage_prop_cb, fp, B_FALSE, B_TRUE,
+ ZFS_TYPE_DATASET);
(void) fprintf(fp, gettext("\nSizes are specified in bytes "
"with standard units such as K, M, G, etc.\n"));
- (void) fprintf(fp, gettext("\n\nUser-defined properties can "
+ (void) fprintf(fp, gettext("\nUser-defined properties can "
"be specified by using a name containing a colon (:).\n"));
+
+ } else if (show_permissions) {
+ (void) fprintf(fp,
+ gettext("\nThe following permissions are supported:\n"));
+
+ zfs_deleg_permissions();
} else {
/*
* TRANSLATION NOTE:
* "zfs set|get" must not be localised this is the
* command name and arguments.
*/
+
(void) fprintf(fp,
gettext("\nFor the property list, run: zfs set|get\n"));
+
+ (void) fprintf(fp,
+ gettext("\nFor the delegated permission list, run:"
+ " zfs allow|unallow\n"));
}
/*
@@ -354,69 +405,143 @@ usage(boolean_t requested)
exit(requested ? 0 : 2);
}
+static int
+parseprop(nvlist_t *props)
+{
+ char *propname = optarg;
+ char *propval, *strval;
+
+ if ((propval = strchr(propname, '=')) == NULL) {
+ (void) fprintf(stderr, gettext("missing "
+ "'=' for -o option\n"));
+ return (-1);
+ }
+ *propval = '\0';
+ propval++;
+ if (nvlist_lookup_string(props, propname, &strval) == 0) {
+ (void) fprintf(stderr, gettext("property '%s' "
+ "specified multiple times\n"), propname);
+ return (-1);
+ }
+ if (nvlist_add_string(props, propname, propval) != 0) {
+ (void) fprintf(stderr, gettext("internal "
+ "error: out of memory\n"));
+ return (-1);
+ }
+ return (0);
+
+}
+
/*
- * zfs clone <fs, snap, vol> fs
+ * zfs clone [-p] [-o prop=value] ... <snap> <fs | vol>
*
* Given an existing dataset, create a writable copy whose initial contents
* are the same as the source. The newly created dataset maintains a
* dependency on the original; the original cannot be destroyed so long as
* the clone exists.
+ *
+ * The '-p' flag creates all the non-existing ancestors of the target first.
*/
static int
zfs_do_clone(int argc, char **argv)
{
- zfs_handle_t *zhp;
+ zfs_handle_t *zhp = NULL;
+ boolean_t parents = B_FALSE;
+ nvlist_t *props;
int ret;
+ int c;
+
+ if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) {
+ (void) fprintf(stderr, gettext("internal error: "
+ "out of memory\n"));
+ return (1);
+ }
/* check options */
- if (argc > 1 && argv[1][0] == '-') {
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- argv[1][1]);
- usage(B_FALSE);
+ while ((c = getopt(argc, argv, "o:p")) != -1) {
+ switch (c) {
+ case 'o':
+ if (parseprop(props))
+ return (1);
+ break;
+ case 'p':
+ parents = B_TRUE;
+ break;
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ goto usage;
+ }
}
+ argc -= optind;
+ argv += optind;
+
/* check number of arguments */
- if (argc < 2) {
+ if (argc < 1) {
(void) fprintf(stderr, gettext("missing source dataset "
"argument\n"));
- usage(B_FALSE);
+ goto usage;
}
- if (argc < 3) {
+ if (argc < 2) {
(void) fprintf(stderr, gettext("missing target dataset "
"argument\n"));
- usage(B_FALSE);
+ goto usage;
}
- if (argc > 3) {
+ if (argc > 2) {
(void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
+ goto usage;
}
/* open the source dataset */
- if ((zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_SNAPSHOT)) == NULL)
+ if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL)
return (1);
+ if (parents && zfs_name_valid(argv[1], ZFS_TYPE_FILESYSTEM |
+ ZFS_TYPE_VOLUME)) {
+ /*
+ * Now create the ancestors of the target dataset. If the
+ * target already exists and '-p' option was used we should not
+ * complain.
+ */
+ if (zfs_dataset_exists(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM |
+ ZFS_TYPE_VOLUME))
+ return (0);
+ if (zfs_create_ancestors(g_zfs, argv[1]) != 0)
+ return (1);
+ }
+
/* pass to libzfs */
- ret = zfs_clone(zhp, argv[2], NULL);
+ ret = zfs_clone(zhp, argv[1], props);
/* create the mountpoint if necessary */
if (ret == 0) {
- zfs_handle_t *clone = zfs_open(g_zfs, argv[2], ZFS_TYPE_ANY);
+ zfs_handle_t *clone;
+
+ clone = zfs_open(g_zfs, argv[1], ZFS_TYPE_DATASET);
if (clone != NULL) {
if ((ret = zfs_mount(clone, NULL, 0)) == 0)
ret = zfs_share(clone);
zfs_close(clone);
}
- zpool_log_history(g_zfs, argc, argv, argv[2], B_FALSE, B_FALSE);
}
zfs_close(zhp);
+ nvlist_free(props);
- return (ret == 0 ? 0 : 1);
+ return (!!ret);
+
+usage:
+ if (zhp)
+ zfs_close(zhp);
+ nvlist_free(props);
+ usage(B_FALSE);
+ return (-1);
}
/*
- * zfs create [-o prop=value] ... fs
- * zfs create [-s] [-b blocksize] [-o prop=value] ... -V vol size
+ * zfs create [-p] [-o prop=value] ... fs
+ * zfs create [-ps] [-b blocksize] [-o prop=value] ... -V vol size
*
* Create a new dataset. This command can be used to create filesystems
* and volumes. Snapshot creation is handled by 'zfs snapshot'.
@@ -424,7 +549,10 @@ zfs_do_clone(int argc, char **argv)
*
* The '-s' flag applies only to volumes, and indicates that we should not try
* to set the reservation for this volume. By default we set a reservation
- * equal to the size for any volume.
+ * equal to the size for any volume. For pools with SPA_VERSION >=
+ * SPA_VERSION_REFRESERVATION, we set a refreservation instead.
+ *
+ * The '-p' flag creates all the non-existing ancestors of the target first.
*/
static int
zfs_do_create(int argc, char **argv)
@@ -434,12 +562,12 @@ zfs_do_create(int argc, char **argv)
uint64_t volsize;
int c;
boolean_t noreserve = B_FALSE;
+ boolean_t bflag = B_FALSE;
+ boolean_t parents = B_FALSE;
int ret = 1;
- nvlist_t *props = NULL;
+ nvlist_t *props;
uint64_t intval;
- char *propname;
- char *propval = NULL;
- char *strval;
+ int canmount;
if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) {
(void) fprintf(stderr, gettext("internal error: "
@@ -448,7 +576,7 @@ zfs_do_create(int argc, char **argv)
}
/* check options */
- while ((c = getopt(argc, argv, ":V:b:so:")) != -1) {
+ while ((c = getopt(argc, argv, ":V:b:so:p")) != -1) {
switch (c) {
case 'V':
type = ZFS_TYPE_VOLUME;
@@ -468,7 +596,11 @@ zfs_do_create(int argc, char **argv)
}
volsize = intval;
break;
+ case 'p':
+ parents = B_TRUE;
+ break;
case 'b':
+ bflag = B_TRUE;
if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {
(void) fprintf(stderr, gettext("bad volume "
"block size '%s': %s\n"), optarg,
@@ -485,25 +617,8 @@ zfs_do_create(int argc, char **argv)
}
break;
case 'o':
- propname = optarg;
- if ((propval = strchr(propname, '=')) == NULL) {
- (void) fprintf(stderr, gettext("missing "
- "'=' for -o option\n"));
- goto error;
- }
- *propval = '\0';
- propval++;
- if (nvlist_lookup_string(props, propname,
- &strval) == 0) {
- (void) fprintf(stderr, gettext("property '%s' "
- "specified multiple times\n"), propname);
- goto error;
- }
- if (nvlist_add_string(props, propname, propval) != 0) {
- (void) fprintf(stderr, gettext("internal "
- "error: out of memory\n"));
+ if (parseprop(props))
goto error;
- }
break;
case 's':
noreserve = B_TRUE;
@@ -520,9 +635,9 @@ zfs_do_create(int argc, char **argv)
}
}
- if (noreserve && type != ZFS_TYPE_VOLUME) {
- (void) fprintf(stderr, gettext("'-s' can only be used when "
- "creating a volume\n"));
+ if ((bflag || noreserve) && type != ZFS_TYPE_VOLUME) {
+ (void) fprintf(stderr, gettext("'-s' and '-b' can only be "
+ "used when creating a volume\n"));
goto badusage;
}
@@ -540,46 +655,83 @@ zfs_do_create(int argc, char **argv)
goto badusage;
}
- if (type == ZFS_TYPE_VOLUME && !noreserve &&
- nvlist_lookup_string(props, zfs_prop_to_name(ZFS_PROP_RESERVATION),
- &strval) != 0) {
- if (nvlist_add_uint64(props,
- zfs_prop_to_name(ZFS_PROP_RESERVATION),
- volsize) != 0) {
- (void) fprintf(stderr, gettext("internal "
- "error: out of memory\n"));
- nvlist_free(props);
- return (1);
+ if (type == ZFS_TYPE_VOLUME && !noreserve) {
+ zpool_handle_t *zpool_handle;
+ uint64_t spa_version;
+ char *p;
+ zfs_prop_t resv_prop;
+ char *strval;
+
+ if (p = strchr(argv[0], '/'))
+ *p = '\0';
+ zpool_handle = zpool_open(g_zfs, argv[0]);
+ if (p != NULL)
+ *p = '/';
+ if (zpool_handle == NULL)
+ goto error;
+ spa_version = zpool_get_prop_int(zpool_handle,
+ ZPOOL_PROP_VERSION, NULL);
+ zpool_close(zpool_handle);
+ if (spa_version >= SPA_VERSION_REFRESERVATION)
+ resv_prop = ZFS_PROP_REFRESERVATION;
+ else
+ resv_prop = ZFS_PROP_RESERVATION;
+
+ if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop),
+ &strval) != 0) {
+ if (nvlist_add_uint64(props,
+ zfs_prop_to_name(resv_prop), volsize) != 0) {
+ (void) fprintf(stderr, gettext("internal "
+ "error: out of memory\n"));
+ nvlist_free(props);
+ return (1);
+ }
}
}
+ if (parents && zfs_name_valid(argv[0], type)) {
+ /*
+ * Now create the ancestors of target dataset. If the target
+ * already exists and '-p' option was used we should not
+ * complain.
+ */
+ if (zfs_dataset_exists(g_zfs, argv[0], type)) {
+ ret = 0;
+ goto error;
+ }
+ if (zfs_create_ancestors(g_zfs, argv[0]) != 0)
+ goto error;
+ }
+
/* pass to libzfs */
if (zfs_create(g_zfs, argv[0], type, props) != 0)
goto error;
- if (propval != NULL)
- *(propval - 1) = '=';
- zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0],
- B_FALSE, B_FALSE);
-
- if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL)
+ if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL)
goto error;
+ /*
+ * if the user doesn't want the dataset automatically mounted,
+ * then skip the mount/share step
+ */
+
+ canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT);
/*
* Mount and/or share the new filesystem as appropriate. We provide a
* verbose error message to let the user know that their filesystem was
* in fact created, even if we failed to mount or share it.
*/
- if (zfs_mount(zhp, NULL, 0) != 0) {
- (void) fprintf(stderr, gettext("filesystem successfully "
- "created, but not mounted\n"));
- ret = 1;
- } else if (zfs_share(zhp) != 0) {
- (void) fprintf(stderr, gettext("filesystem successfully "
- "created, but not shared\n"));
- ret = 1;
- } else {
- ret = 0;
+ ret = 0;
+ if (canmount == ZFS_CANMOUNT_ON) {
+ if (zfs_mount(zhp, NULL, 0) != 0) {
+ (void) fprintf(stderr, gettext("filesystem "
+ "successfully created, but not mounted\n"));
+ ret = 1;
+ } else if (zfs_share(zhp) != 0) {
+ (void) fprintf(stderr, gettext("filesystem "
+ "successfully created, but not shared\n"));
+ ret = 1;
+ }
}
error:
@@ -789,7 +941,7 @@ zfs_do_destroy(int argc, char **argv)
int ret;
*cp = '\0';
- if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL)
+ if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL)
return (1);
*cp = '@';
cp++;
@@ -807,16 +959,13 @@ zfs_do_destroy(int argc, char **argv)
if (ret) {
(void) fprintf(stderr,
gettext("no snapshots destroyed\n"));
- } else {
- zpool_log_history(g_zfs, argc + optind, argv - optind,
- argv[0], B_FALSE, B_FALSE);
}
return (ret != 0);
}
/* Open the given dataset */
- if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL)
+ if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL)
return (1);
cb.cb_target = zhp;
@@ -849,7 +998,6 @@ zfs_do_destroy(int argc, char **argv)
return (1);
}
-
if (cb.cb_error ||
zfs_iter_dependents(zhp, B_FALSE, destroy_callback, &cb) != 0) {
zfs_close(zhp);
@@ -860,11 +1008,10 @@ zfs_do_destroy(int argc, char **argv)
* Do the real thing. The callback will close the handle regardless of
* whether it succeeds or not.
*/
+
if (destroy_callback(zhp, &cb) != 0)
return (1);
- zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0],
- B_FALSE, B_FALSE);
return (0);
}
@@ -894,11 +1041,11 @@ static int
get_callback(zfs_handle_t *zhp, void *data)
{
char buf[ZFS_MAXPROPLEN];
- zfs_source_t sourcetype;
+ zprop_source_t sourcetype;
char source[ZFS_MAXNAMELEN];
- libzfs_get_cbdata_t *cbp = data;
+ zprop_get_cbdata_t *cbp = data;
nvlist_t *userprop = zfs_get_user_props(zhp);
- zfs_proplist_t *pl = cbp->cb_proplist;
+ zprop_list_t *pl = cbp->cb_proplist;
nvlist_t *propval;
char *strval;
char *sourceval;
@@ -912,7 +1059,7 @@ get_callback(zfs_handle_t *zhp, void *data)
pl == cbp->cb_proplist)
continue;
- if (pl->pl_prop != ZFS_PROP_INVAL) {
+ if (pl->pl_prop != ZPROP_INVAL) {
if (zfs_prop_get(zhp, pl->pl_prop, buf,
sizeof (buf), &sourcetype, source,
sizeof (source),
@@ -920,17 +1067,17 @@ get_callback(zfs_handle_t *zhp, void *data)
if (pl->pl_all)
continue;
if (!zfs_prop_valid_for_type(pl->pl_prop,
- ZFS_TYPE_ANY)) {
+ ZFS_TYPE_DATASET)) {
(void) fprintf(stderr,
gettext("No such property '%s'\n"),
zfs_prop_to_name(pl->pl_prop));
continue;
}
- sourcetype = ZFS_SRC_NONE;
+ sourcetype = ZPROP_SRC_NONE;
(void) strlcpy(buf, "-", sizeof (buf));
}
- libzfs_print_one_property(zfs_get_name(zhp), cbp,
+ zprop_print_one_property(zfs_get_name(zhp), cbp,
zfs_prop_to_name(pl->pl_prop),
buf, sourcetype, source);
} else {
@@ -938,25 +1085,25 @@ get_callback(zfs_handle_t *zhp, void *data)
pl->pl_user_prop, &propval) != 0) {
if (pl->pl_all)
continue;
- sourcetype = ZFS_SRC_NONE;
+ sourcetype = ZPROP_SRC_NONE;
strval = "-";
} else {
verify(nvlist_lookup_string(propval,
- ZFS_PROP_VALUE, &strval) == 0);
+ ZPROP_VALUE, &strval) == 0);
verify(nvlist_lookup_string(propval,
- ZFS_PROP_SOURCE, &sourceval) == 0);
+ ZPROP_SOURCE, &sourceval) == 0);
if (strcmp(sourceval,
zfs_get_name(zhp)) == 0) {
- sourcetype = ZFS_SRC_LOCAL;
+ sourcetype = ZPROP_SRC_LOCAL;
} else {
- sourcetype = ZFS_SRC_INHERITED;
+ sourcetype = ZPROP_SRC_INHERITED;
(void) strlcpy(source,
sourceval, sizeof (source));
}
}
- libzfs_print_one_property(zfs_get_name(zhp), cbp,
+ zprop_print_one_property(zfs_get_name(zhp), cbp,
pl->pl_user_prop, strval, sourcetype,
source);
}
@@ -968,21 +1115,21 @@ get_callback(zfs_handle_t *zhp, void *data)
static int
zfs_do_get(int argc, char **argv)
{
- libzfs_get_cbdata_t cb = { 0 };
- boolean_t recurse = B_FALSE;
- int i, c;
+ zprop_get_cbdata_t cb = { 0 };
+ int i, c, flags = 0;
char *value, *fields;
int ret;
- zfs_proplist_t fake_name = { 0 };
+ zprop_list_t fake_name = { 0 };
/*
* Set up default columns and sources.
*/
- cb.cb_sources = ZFS_SRC_ALL;
+ cb.cb_sources = ZPROP_SRC_ALL;
cb.cb_columns[0] = GET_COL_NAME;
cb.cb_columns[1] = GET_COL_PROPERTY;
cb.cb_columns[2] = GET_COL_VALUE;
cb.cb_columns[3] = GET_COL_SOURCE;
+ cb.cb_type = ZFS_TYPE_DATASET;
/* check options */
while ((c = getopt(argc, argv, ":o:s:rHp")) != -1) {
@@ -991,7 +1138,7 @@ zfs_do_get(int argc, char **argv)
cb.cb_literal = B_TRUE;
break;
case 'r':
- recurse = B_TRUE;
+ flags |= ZFS_ITER_RECURSE;
break;
case 'H':
cb.cb_scripted = B_TRUE;
@@ -1053,19 +1200,19 @@ zfs_do_get(int argc, char **argv)
switch (getsubopt(&optarg, source_subopts,
&value)) {
case 0:
- cb.cb_sources |= ZFS_SRC_LOCAL;
+ cb.cb_sources |= ZPROP_SRC_LOCAL;
break;
case 1:
- cb.cb_sources |= ZFS_SRC_DEFAULT;
+ cb.cb_sources |= ZPROP_SRC_DEFAULT;
break;
case 2:
- cb.cb_sources |= ZFS_SRC_INHERITED;
+ cb.cb_sources |= ZPROP_SRC_INHERITED;
break;
case 3:
- cb.cb_sources |= ZFS_SRC_TEMPORARY;
+ cb.cb_sources |= ZPROP_SRC_TEMPORARY;
break;
case 4:
- cb.cb_sources |= ZFS_SRC_NONE;
+ cb.cb_sources |= ZPROP_SRC_NONE;
break;
default:
(void) fprintf(stderr,
@@ -1094,7 +1241,8 @@ zfs_do_get(int argc, char **argv)
fields = argv[0];
- if (zfs_get_proplist(g_zfs, fields, &cb.cb_proplist) != 0)
+ if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET)
+ != 0)
usage(B_FALSE);
argc--;
@@ -1118,13 +1266,13 @@ zfs_do_get(int argc, char **argv)
cb.cb_first = B_TRUE;
/* run for each object */
- ret = zfs_for_each(argc, argv, recurse, ZFS_TYPE_ANY, NULL,
- &cb.cb_proplist, get_callback, &cb, B_FALSE);
+ ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET, NULL,
+ &cb.cb_proplist, get_callback, &cb);
if (cb.cb_proplist == &fake_name)
- zfs_free_proplist(fake_name.pl_next);
+ zprop_free_list(fake_name.pl_next);
else
- zfs_free_proplist(cb.cb_proplist);
+ zprop_free_list(cb.cb_proplist);
return (ret);
}
@@ -1140,37 +1288,46 @@ zfs_do_get(int argc, char **argv)
* useful for setting a property on a hierarchy-wide basis, regardless of any
* local modifications for each dataset.
*/
-typedef struct inherit_cbdata {
- char *cb_propname;
- boolean_t cb_any_successful;
-} inherit_cbdata_t;
static int
-inherit_callback(zfs_handle_t *zhp, void *data)
+inherit_recurse_cb(zfs_handle_t *zhp, void *data)
{
- inherit_cbdata_t *cbp = data;
- int ret;
+ char *propname = data;
+ zfs_prop_t prop = zfs_name_to_prop(propname);
- ret = zfs_prop_inherit(zhp, cbp->cb_propname);
- if (ret == 0)
- cbp->cb_any_successful = B_TRUE;
- return (ret != 0);
+ /*
+ * If we're doing it recursively, then ignore properties that
+ * are not valid for this type of dataset.
+ */
+ if (prop != ZPROP_INVAL &&
+ !zfs_prop_valid_for_type(prop, zfs_get_type(zhp)))
+ return (0);
+
+ return (zfs_prop_inherit(zhp, propname) != 0);
+}
+
+static int
+inherit_cb(zfs_handle_t *zhp, void *data)
+{
+ char *propname = data;
+
+ return (zfs_prop_inherit(zhp, propname) != 0);
}
static int
zfs_do_inherit(int argc, char **argv)
{
- boolean_t recurse = B_FALSE;
int c;
zfs_prop_t prop;
- inherit_cbdata_t cb;
+ char *propname;
int ret;
+ int flags = 0;
/* check options */
while ((c = getopt(argc, argv, "r")) != -1) {
switch (c) {
case 'r':
- recurse = B_TRUE;
+ flags |= ZFS_ITER_RECURSE;
break;
case '?':
default:
@@ -1193,42 +1350,265 @@ zfs_do_inherit(int argc, char **argv)
usage(B_FALSE);
}
- cb.cb_propname = argv[0];
+ propname = argv[0];
argc--;
argv++;
- if ((prop = zfs_name_to_prop(cb.cb_propname)) != ZFS_PROP_INVAL) {
+ if ((prop = zfs_name_to_prop(propname)) != ZPROP_INVAL) {
if (zfs_prop_readonly(prop)) {
(void) fprintf(stderr, gettext(
"%s property is read-only\n"),
- cb.cb_propname);
+ propname);
return (1);
}
if (!zfs_prop_inheritable(prop)) {
(void) fprintf(stderr, gettext("'%s' property cannot "
- "be inherited\n"), cb.cb_propname);
+ "be inherited\n"), propname);
if (prop == ZFS_PROP_QUOTA ||
- prop == ZFS_PROP_RESERVATION)
+ prop == ZFS_PROP_RESERVATION ||
+ prop == ZFS_PROP_REFQUOTA ||
+ prop == ZFS_PROP_REFRESERVATION)
(void) fprintf(stderr, gettext("use 'zfs set "
- "%s=none' to clear\n"), cb.cb_propname);
+ "%s=none' to clear\n"), propname);
return (1);
}
- } else if (!zfs_prop_user(cb.cb_propname)) {
- (void) fprintf(stderr, gettext(
- "invalid property '%s'\n"),
- cb.cb_propname);
+ } else if (!zfs_prop_user(propname)) {
+ (void) fprintf(stderr, gettext("invalid property '%s'\n"),
+ propname);
usage(B_FALSE);
}
- cb.cb_any_successful = B_FALSE;
+ if (flags & ZFS_ITER_RECURSE) {
+ ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET,
+ NULL, NULL, inherit_recurse_cb, propname);
+ } else {
+ ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET,
+ NULL, NULL, inherit_cb, propname);
+ }
+
+ return (ret);
+}
+
+typedef struct upgrade_cbdata {
+ uint64_t cb_numupgraded;
+ uint64_t cb_numsamegraded;
+ uint64_t cb_numfailed;
+ uint64_t cb_version;
+ boolean_t cb_newer;
+ boolean_t cb_foundone;
+ char cb_lastfs[ZFS_MAXNAMELEN];
+} upgrade_cbdata_t;
+
+static int
+same_pool(zfs_handle_t *zhp, const char *name)
+{
+ int len1 = strcspn(name, "/@");
+ const char *zhname = zfs_get_name(zhp);
+ int len2 = strcspn(zhname, "/@");
+
+ if (len1 != len2)
+ return (B_FALSE);
+ return (strncmp(name, zhname, len1) == 0);
+}
+
+static int
+upgrade_list_callback(zfs_handle_t *zhp, void *data)
+{
+ upgrade_cbdata_t *cb = data;
+ int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
+
+ /* list if it's old/new */
+ if ((!cb->cb_newer && version < ZPL_VERSION) ||
+ (cb->cb_newer && version > ZPL_VERSION)) {
+ char *str;
+ if (cb->cb_newer) {
+ str = gettext("The following filesystems are "
+ "formatted using a newer software version and\n"
+ "cannot be accessed on the current system.\n\n");
+ } else {
+ str = gettext("The following filesystems are "
+ "out of date, and can be upgraded. After being\n"
+ "upgraded, these filesystems (and any 'zfs send' "
+ "streams generated from\n"
+ "subsequent snapshots) will no longer be "
+ "accessible by older software versions.\n\n");
+ }
+
+ if (!cb->cb_foundone) {
+ (void) puts(str);
+ (void) printf(gettext("VER FILESYSTEM\n"));
+ (void) printf(gettext("--- ------------\n"));
+ cb->cb_foundone = B_TRUE;
+ }
+
+ (void) printf("%2u %s\n", version, zfs_get_name(zhp));
+ }
+
+ return (0);
+}
+
+static int
+upgrade_set_callback(zfs_handle_t *zhp, void *data)
+{
+ upgrade_cbdata_t *cb = data;
+ int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
+
+ if (cb->cb_version >= ZPL_VERSION_FUID) {
+ int spa_version;
+
+ if (zfs_spa_version(zhp, &spa_version) < 0)
+ return (-1);
+
+ if (spa_version < SPA_VERSION_FUID) {
+ /* can't upgrade */
+ (void) printf(gettext("%s: can not be upgraded; "
+ "the pool version needs to first be upgraded\nto "
+ "version %d\n\n"),
+ zfs_get_name(zhp), SPA_VERSION_FUID);
+ cb->cb_numfailed++;
+ return (0);
+ }
+ }
+
+ /* upgrade */
+ if (version < cb->cb_version) {
+ char verstr[16];
+ (void) snprintf(verstr, sizeof (verstr),
+ "%llu", cb->cb_version);
+ if (cb->cb_lastfs[0] && !same_pool(zhp, cb->cb_lastfs)) {
+ /*
+ * If they did "zfs upgrade -a", then we could
+ * be doing ioctls to different pools. We need
+ * to log this history once to each pool.
+ */
+ verify(zpool_stage_history(g_zfs, history_str) == 0);
+ }
+ if (zfs_prop_set(zhp, "version", verstr) == 0)
+ cb->cb_numupgraded++;
+ else
+ cb->cb_numfailed++;
+ (void) strcpy(cb->cb_lastfs, zfs_get_name(zhp));
+ } else if (version > cb->cb_version) {
+ /* can't downgrade */
+ (void) printf(gettext("%s: can not be downgraded; "
+ "it is already at version %u\n"),
+ zfs_get_name(zhp), version);
+ cb->cb_numfailed++;
+ } else {
+ cb->cb_numsamegraded++;
+ }
+ return (0);
+}
- ret = zfs_for_each(argc, argv, recurse,
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, NULL,
- inherit_callback, &cb, B_FALSE);
+/*
+ * zfs upgrade
+ * zfs upgrade -v
+ * zfs upgrade [-r] [-V <version>] <-a | filesystem>
+ */
+static int
+zfs_do_upgrade(int argc, char **argv)
+{
+ boolean_t all = B_FALSE;
+ boolean_t showversions = B_FALSE;
+ int ret;
+ upgrade_cbdata_t cb = { 0 };
+ char c;
+ int flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
- if (cb.cb_any_successful) {
- zpool_log_history(g_zfs, argc + optind + 1, argv - optind - 1,
- argv[0], B_FALSE, B_FALSE);
+ /* check options */
+ while ((c = getopt(argc, argv, "rvV:a")) != -1) {
+ switch (c) {
+ case 'r':
+ flags |= ZFS_ITER_RECURSE;
+ break;
+ case 'v':
+ showversions = B_TRUE;
+ break;
+ case 'V':
+ if (zfs_prop_string_to_index(ZFS_PROP_VERSION,
+ optarg, &cb.cb_version) != 0) {
+ (void) fprintf(stderr,
+ gettext("invalid version %s\n"), optarg);
+ usage(B_FALSE);
+ }
+ break;
+ case 'a':
+ all = B_TRUE;
+ break;
+ case '?':
+ default:
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if ((!all && !argc) && ((flags & ZFS_ITER_RECURSE) | cb.cb_version))
+ usage(B_FALSE);
+ if (showversions && (flags & ZFS_ITER_RECURSE || all ||
+ cb.cb_version || argc))
+ usage(B_FALSE);
+ if ((all || argc) && (showversions))
+ usage(B_FALSE);
+ if (all && argc)
+ usage(B_FALSE);
+
+ if (showversions) {
+ /* Show info on available versions. */
+ (void) printf(gettext("The following filesystem versions are "
+ "supported:\n\n"));
+ (void) printf(gettext("VER DESCRIPTION\n"));
+ (void) printf("--- -----------------------------------------"
+ "---------------\n");
+ (void) printf(gettext(" 1 Initial ZFS filesystem version\n"));
+ (void) printf(gettext(" 2 Enhanced directory entries\n"));
+ (void) printf(gettext(" 3 Case insensitive and File system "
+ "unique identifer (FUID)\n"));
+ (void) printf(gettext("\nFor more information on a particular "
+ "version, including supported releases, see:\n\n"));
+ (void) printf("http://www.opensolaris.org/os/community/zfs/"
+ "version/zpl/N\n\n");
+ (void) printf(gettext("Where 'N' is the version number.\n"));
+ ret = 0;
+ } else if (argc || all) {
+ /* Upgrade filesystems */
+ if (cb.cb_version == 0)
+ cb.cb_version = ZPL_VERSION;
+ ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_FILESYSTEM,
+ NULL, NULL, upgrade_set_callback, &cb);
+ (void) printf(gettext("%llu filesystems upgraded\n"),
+ cb.cb_numupgraded);
+ if (cb.cb_numsamegraded) {
+ (void) printf(gettext("%llu filesystems already at "
+ "this version\n"),
+ cb.cb_numsamegraded);
+ }
+ if (cb.cb_numfailed != 0)
+ ret = 1;
+ } else {
+ /* List old-version filesytems */
+ boolean_t found;
+ (void) printf(gettext("This system is currently running "
+ "ZFS filesystem version %llu.\n\n"), ZPL_VERSION);
+
+ flags |= ZFS_ITER_RECURSE;
+ ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM,
+ NULL, NULL, upgrade_list_callback, &cb);
+
+ found = cb.cb_foundone;
+ cb.cb_foundone = B_FALSE;
+ cb.cb_newer = B_TRUE;
+
+ ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM,
+ NULL, NULL, upgrade_list_callback, &cb);
+
+ if (!cb.cb_foundone && !found) {
+ (void) printf(gettext("All filesystems are "
+ "formatted with the current version.\n"));
+ }
}
return (ret);
@@ -1240,7 +1620,7 @@ zfs_do_inherit(int argc, char **argv)
* <dataset> ...
*
* -r Recurse over all children
- * -H Scripted mode; elide headers and separate colums by tabs
+ * -H Scripted mode; elide headers and separate columns by tabs
* -o Control which fields to display.
* -t Control which object types to display.
* -s Specify sort columns, descending order.
@@ -1253,14 +1633,14 @@ zfs_do_inherit(int argc, char **argv)
typedef struct list_cbdata {
boolean_t cb_first;
boolean_t cb_scripted;
- zfs_proplist_t *cb_proplist;
+ zprop_list_t *cb_proplist;
} list_cbdata_t;
/*
* Given a list of columns to display, output appropriate headers for each one.
*/
static void
-print_header(zfs_proplist_t *pl)
+print_header(zprop_list_t *pl)
{
char headerbuf[ZFS_MAXPROPLEN];
const char *header;
@@ -1276,7 +1656,7 @@ print_header(zfs_proplist_t *pl)
}
right_justify = B_FALSE;
- if (pl->pl_prop != ZFS_PROP_INVAL) {
+ if (pl->pl_prop != ZPROP_INVAL) {
header = zfs_prop_column_name(pl->pl_prop);
right_justify = zfs_prop_align_right(pl->pl_prop);
} else {
@@ -1302,7 +1682,7 @@ print_header(zfs_proplist_t *pl)
* to the described layout.
*/
static void
-print_dataset(zfs_handle_t *zhp, zfs_proplist_t *pl, int scripted)
+print_dataset(zfs_handle_t *zhp, zprop_list_t *pl, boolean_t scripted)
{
boolean_t first = B_TRUE;
char property[ZFS_MAXPROPLEN];
@@ -1323,7 +1703,7 @@ print_dataset(zfs_handle_t *zhp, zfs_proplist_t *pl, int scripted)
}
right_justify = B_FALSE;
- if (pl->pl_prop != ZFS_PROP_INVAL) {
+ if (pl->pl_prop != ZPROP_INVAL) {
if (zfs_prop_get(zhp, pl->pl_prop, property,
sizeof (property), NULL, NULL, 0, B_FALSE) != 0)
propstr = "-";
@@ -1337,7 +1717,7 @@ print_dataset(zfs_handle_t *zhp, zfs_proplist_t *pl, int scripted)
propstr = "-";
else
verify(nvlist_lookup_string(propval,
- ZFS_PROP_VALUE, &propstr) == 0);
+ ZPROP_VALUE, &propstr) == 0);
}
width = pl->pl_width;
@@ -1381,18 +1761,17 @@ static int
zfs_do_list(int argc, char **argv)
{
int c;
- boolean_t recurse = B_FALSE;
boolean_t scripted = B_FALSE;
static char default_fields[] =
"name,used,available,referenced,mountpoint";
- int types = ZFS_TYPE_ANY;
+ int types = ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;
+ boolean_t types_specified = B_FALSE;
char *fields = NULL;
- char *basic_fields = default_fields;
list_cbdata_t cb = { 0 };
char *value;
int ret;
- char *type_subopts[] = { "filesystem", "volume", "snapshot", NULL };
zfs_sort_column_t *sortcol = NULL;
+ int flags = ZFS_ITER_PROP_LISTSNAPS | ZFS_ITER_ARGS_CAN_BE_PATHS;
/* check options */
while ((c = getopt(argc, argv, ":o:rt:Hs:S:")) != -1) {
@@ -1401,7 +1780,7 @@ zfs_do_list(int argc, char **argv)
fields = optarg;
break;
case 'r':
- recurse = B_TRUE;
+ flags |= ZFS_ITER_RECURSE;
break;
case 'H':
scripted = B_TRUE;
@@ -1424,7 +1803,12 @@ zfs_do_list(int argc, char **argv)
break;
case 't':
types = 0;
+ types_specified = B_TRUE;
+ flags &= ~ZFS_ITER_PROP_LISTSNAPS;
while (*optarg != '\0') {
+ static char *type_subopts[] = { "filesystem",
+ "volume", "snapshot", "all", NULL };
+
switch (getsubopt(&optarg, type_subopts,
&value)) {
case 0:
@@ -1436,6 +1820,10 @@ zfs_do_list(int argc, char **argv)
case 2:
types |= ZFS_TYPE_SNAPSHOT;
break;
+ case 3:
+ types = ZFS_TYPE_DATASET;
+ break;
+
default:
(void) fprintf(stderr,
gettext("invalid type '%s'\n"),
@@ -1460,35 +1848,46 @@ zfs_do_list(int argc, char **argv)
argv += optind;
if (fields == NULL)
- fields = basic_fields;
+ fields = default_fields;
/*
- * If the user specifies '-o all', the zfs_get_proplist() doesn't
+ * If "-o space" and no types were specified, don't display snapshots.
+ */
+ if (strcmp(fields, "space") == 0 && types_specified == B_FALSE)
+ types &= ~ZFS_TYPE_SNAPSHOT;
+
+ /*
+ * If the user specifies '-o all', the zprop_get_list() doesn't
* normally include the name of the dataset. For 'zfs list', we always
* want this property to be first.
*/
- if (zfs_get_proplist(g_zfs, fields, &cb.cb_proplist) != 0)
+ if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET)
+ != 0)
usage(B_FALSE);
cb.cb_scripted = scripted;
cb.cb_first = B_TRUE;
- ret = zfs_for_each(argc, argv, recurse, types, sortcol, &cb.cb_proplist,
- list_callback, &cb, B_TRUE);
+ ret = zfs_for_each(argc, argv, flags, types, sortcol, &cb.cb_proplist,
+ list_callback, &cb);
- zfs_free_proplist(cb.cb_proplist);
+ zprop_free_list(cb.cb_proplist);
zfs_free_sort_columns(sortcol);
- if (ret == 0 && cb.cb_first)
+ if (ret == 0 && cb.cb_first && !cb.cb_scripted)
(void) printf(gettext("no datasets available\n"));
return (ret);
}
/*
- * zfs rename [-r] <fs | snap | vol> <fs | snap | vol>
+ * zfs rename <fs | snap | vol> <fs | snap | vol>
+ * zfs rename -p <fs | vol> <fs | vol>
+ * zfs rename -r <snap> <snap>
*
* Renames the given dataset to another of the same type.
+ *
+ * The '-p' flag creates all the non-existing ancestors of the target first.
*/
/* ARGSUSED */
static int
@@ -1497,13 +1896,17 @@ zfs_do_rename(int argc, char **argv)
zfs_handle_t *zhp;
int c;
int ret;
- int recurse = 0;
+ boolean_t recurse = B_FALSE;
+ boolean_t parents = B_FALSE;
/* check options */
- while ((c = getopt(argc, argv, "r")) != -1) {
+ while ((c = getopt(argc, argv, "pr")) != -1) {
switch (c) {
+ case 'p':
+ parents = B_TRUE;
+ break;
case 'r':
- recurse = 1;
+ recurse = B_TRUE;
break;
case '?':
default:
@@ -1532,20 +1935,30 @@ zfs_do_rename(int argc, char **argv)
usage(B_FALSE);
}
+ if (recurse && parents) {
+ (void) fprintf(stderr, gettext("-p and -r options are mutually "
+ "exclusive\n"));
+ usage(B_FALSE);
+ }
+
if (recurse && strchr(argv[0], '@') == 0) {
(void) fprintf(stderr, gettext("source dataset for recursive "
"rename must be a snapshot\n"));
usage(B_FALSE);
}
- if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL)
+ if ((zhp = zfs_open(g_zfs, argv[0], parents ? ZFS_TYPE_FILESYSTEM |
+ ZFS_TYPE_VOLUME : ZFS_TYPE_DATASET)) == NULL)
return (1);
- ret = (zfs_rename(zhp, argv[1], recurse) != 0);
+ /* If we were asked and the name looks good, try to create ancestors. */
+ if (parents && zfs_name_valid(argv[1], zfs_get_type(zhp)) &&
+ zfs_create_ancestors(g_zfs, argv[1]) != 0) {
+ zfs_close(zhp);
+ return (1);
+ }
- if (!ret)
- zpool_log_history(g_zfs, argc + optind, argv - optind, argv[1],
- B_FALSE, B_FALSE);
+ ret = (zfs_rename(zhp, argv[1], recurse) != 0);
zfs_close(zhp);
return (ret);
@@ -1587,19 +2000,17 @@ zfs_do_promote(int argc, char **argv)
ret = (zfs_promote(zhp) != 0);
- if (!ret)
- zpool_log_history(g_zfs, argc, argv, argv[1], B_FALSE, B_FALSE);
zfs_close(zhp);
return (ret);
}
/*
- * zfs rollback [-rfR] <snapshot>
+ * zfs rollback [-rRf] <snapshot>
*
* -r Delete any intervening snapshots before doing rollback
* -R Delete any snapshots and their clones
- * -f Force unmount filesystems, even if they are in use.
+ * -f ignored for backwards compatability
*
* Given a filesystem, rollback to a specific snapshot, discarding any changes
* since then and making it the active dataset. If more recent snapshots exist,
@@ -1686,18 +2097,15 @@ zfs_do_rollback(int argc, char **argv)
{
int ret;
int c;
+ boolean_t force = B_FALSE;
rollback_cbdata_t cb = { 0 };
zfs_handle_t *zhp, *snap;
char parentname[ZFS_MAXNAMELEN];
char *delim;
- int force = 0;
/* check options */
- while ((c = getopt(argc, argv, "rfR")) != -1) {
+ while ((c = getopt(argc, argv, "rRf")) != -1) {
switch (c) {
- case 'f':
- force = 1;
- break;
case 'r':
cb.cb_recurse = 1;
break;
@@ -1705,6 +2113,9 @@ zfs_do_rollback(int argc, char **argv)
cb.cb_recurse = 1;
cb.cb_doclones = 1;
break;
+ case 'f':
+ force = B_TRUE;
+ break;
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
@@ -1733,7 +2144,7 @@ zfs_do_rollback(int argc, char **argv)
(void) strlcpy(parentname, argv[0], sizeof (parentname));
verify((delim = strrchr(parentname, '@')) != NULL);
*delim = '\0';
- if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_ANY)) == NULL) {
+ if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_DATASET)) == NULL) {
zfs_close(snap);
return (1);
}
@@ -1757,11 +2168,6 @@ zfs_do_rollback(int argc, char **argv)
*/
ret = zfs_rollback(zhp, snap, force);
- if (!ret) {
- zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0],
- B_FALSE, B_FALSE);
- }
-
out:
zfs_close(snap);
zfs_close(zhp);
@@ -1780,7 +2186,6 @@ out:
typedef struct set_cbdata {
char *cb_propname;
char *cb_value;
- boolean_t cb_any_successful;
} set_cbdata_t;
static int
@@ -1801,7 +2206,6 @@ set_callback(zfs_handle_t *zhp, void *data)
}
return (1);
}
- cbp->cb_any_successful = B_TRUE;
return (0);
}
@@ -1831,7 +2235,8 @@ zfs_do_set(int argc, char **argv)
/* validate property=value argument */
cb.cb_propname = argv[1];
- if ((cb.cb_value = strchr(cb.cb_propname, '=')) == NULL) {
+ if (((cb.cb_value = strchr(cb.cb_propname, '=')) == NULL) ||
+ (cb.cb_value[1] == '\0')) {
(void) fprintf(stderr, gettext("missing value in "
"property=value argument\n"));
usage(B_FALSE);
@@ -1839,7 +2244,6 @@ zfs_do_set(int argc, char **argv)
*cb.cb_value = '\0';
cb.cb_value++;
- cb.cb_any_successful = B_FALSE;
if (*cb.cb_propname == '\0') {
(void) fprintf(stderr,
@@ -1847,40 +2251,46 @@ zfs_do_set(int argc, char **argv)
usage(B_FALSE);
}
- ret = zfs_for_each(argc - 2, argv + 2, B_FALSE,
- ZFS_TYPE_ANY, NULL, NULL, set_callback, &cb, B_FALSE);
-
- if (cb.cb_any_successful) {
- *(cb.cb_value - 1) = '=';
- zpool_log_history(g_zfs, argc, argv, argv[2], B_FALSE, B_FALSE);
- }
+ ret = zfs_for_each(argc - 2, argv + 2, NULL,
+ ZFS_TYPE_DATASET, NULL, NULL, set_callback, &cb);
return (ret);
}
/*
- * zfs snapshot [-r] <fs@snap>
+ * zfs snapshot [-r] [-o prop=value] ... <fs@snap>
*
* Creates a snapshot with the given name. While functionally equivalent to
- * 'zfs create', it is a separate command to diffferentiate intent.
+ * 'zfs create', it is a separate command to differentiate intent.
*/
static int
zfs_do_snapshot(int argc, char **argv)
{
- int recursive = B_FALSE;
+ boolean_t recursive = B_FALSE;
int ret;
char c;
+ nvlist_t *props;
+
+ if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) {
+ (void) fprintf(stderr, gettext("internal error: "
+ "out of memory\n"));
+ return (1);
+ }
/* check options */
- while ((c = getopt(argc, argv, ":r")) != -1) {
+ while ((c = getopt(argc, argv, "ro:")) != -1) {
switch (c) {
+ case 'o':
+ if (parseprop(props))
+ return (1);
+ break;
case 'r':
recursive = B_TRUE;
break;
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
- usage(B_FALSE);
+ goto usage;
}
}
@@ -1890,25 +2300,28 @@ zfs_do_snapshot(int argc, char **argv)
/* check number of arguments */
if (argc < 1) {
(void) fprintf(stderr, gettext("missing snapshot argument\n"));
- usage(B_FALSE);
+ goto usage;
}
if (argc > 1) {
(void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
+ goto usage;
}
- ret = zfs_snapshot(g_zfs, argv[0], recursive);
+ ret = zfs_snapshot(g_zfs, argv[0], recursive, props);
+ nvlist_free(props);
if (ret && recursive)
(void) fprintf(stderr, gettext("no snapshots were created\n"));
- if (!ret) {
- zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0],
- B_FALSE, B_FALSE);
- }
return (ret != 0);
+
+usage:
+ nvlist_free(props);
+ usage(B_FALSE);
+ return (-1);
}
/*
- * zfs send [-i <@snap>] <fs@snap>
+ * zfs send [-v] -R [-i|-I <@snap>] <fs@snap>
+ * zfs send [-v] [-i|-I <@snap>] <fs@snap>
*
* Send a backup stream to stdout.
*/
@@ -1916,18 +2329,35 @@ static int
zfs_do_send(int argc, char **argv)
{
char *fromname = NULL;
+ char *toname = NULL;
char *cp;
zfs_handle_t *zhp;
+ boolean_t doall = B_FALSE;
+ boolean_t replicate = B_FALSE;
+ boolean_t fromorigin = B_FALSE;
+ boolean_t verbose = B_FALSE;
int c, err;
/* check options */
- while ((c = getopt(argc, argv, ":i:")) != -1) {
+ while ((c = getopt(argc, argv, ":i:I:Rv")) != -1) {
switch (c) {
case 'i':
if (fromname)
usage(B_FALSE);
fromname = optarg;
break;
+ case 'I':
+ if (fromname)
+ usage(B_FALSE);
+ fromname = optarg;
+ doall = B_TRUE;
+ break;
+ case 'R':
+ replicate = B_TRUE;
+ break;
+ case 'v':
+ verbose = B_TRUE;
+ break;
case ':':
(void) fprintf(stderr, gettext("missing argument for "
"'%c' option\n"), optopt);
@@ -1960,37 +2390,62 @@ zfs_do_send(int argc, char **argv)
return (1);
}
- if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL)
+ cp = strchr(argv[0], '@');
+ if (cp == NULL) {
+ (void) fprintf(stderr,
+ gettext("argument must be a snapshot\n"));
+ usage(B_FALSE);
+ }
+ *cp = '\0';
+ toname = cp + 1;
+ zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
+ if (zhp == NULL)
return (1);
/*
* If they specified the full path to the snapshot, chop off
- * everything except the short name of the snapshot.
+ * everything except the short name of the snapshot, but special
+ * case if they specify the origin.
*/
if (fromname && (cp = strchr(fromname, '@')) != NULL) {
- if (cp != fromname &&
- strncmp(argv[0], fromname, cp - fromname + 1)) {
- (void) fprintf(stderr,
- gettext("incremental source must be "
- "in same filesystem\n"));
- usage(B_FALSE);
- }
- fromname = cp + 1;
- if (strchr(fromname, '@') || strchr(fromname, '/')) {
- (void) fprintf(stderr,
- gettext("invalid incremental source\n"));
- usage(B_FALSE);
+ char origin[ZFS_MAXNAMELEN];
+ zprop_source_t src;
+
+ (void) zfs_prop_get(zhp, ZFS_PROP_ORIGIN,
+ origin, sizeof (origin), &src, NULL, 0, B_FALSE);
+
+ if (strcmp(origin, fromname) == 0) {
+ fromname = NULL;
+ fromorigin = B_TRUE;
+ } else {
+ *cp = '\0';
+ if (cp != fromname && strcmp(argv[0], fromname)) {
+ (void) fprintf(stderr,
+ gettext("incremental source must be "
+ "in same filesystem\n"));
+ usage(B_FALSE);
+ }
+ fromname = cp + 1;
+ if (strchr(fromname, '@') || strchr(fromname, '/')) {
+ (void) fprintf(stderr,
+ gettext("invalid incremental source\n"));
+ usage(B_FALSE);
+ }
}
}
- err = zfs_send(zhp, fromname, STDOUT_FILENO);
+ if (replicate && fromname == NULL)
+ doall = B_TRUE;
+
+ err = zfs_send(zhp, fromname, toname, replicate, doall, fromorigin,
+ verbose, STDOUT_FILENO);
zfs_close(zhp);
return (err != 0);
}
/*
- * zfs receive <fs@snap>
+ * zfs receive [-dnvF] <fs@snap>
*
* Restore a backup stream from stdin.
*/
@@ -1998,25 +2453,23 @@ static int
zfs_do_receive(int argc, char **argv)
{
int c, err;
- boolean_t isprefix = B_FALSE;
- boolean_t dryrun = B_FALSE;
- boolean_t verbose = B_FALSE;
- boolean_t force = B_FALSE;
+ recvflags_t flags;
+ bzero(&flags, sizeof (recvflags_t));
/* check options */
while ((c = getopt(argc, argv, ":dnvF")) != -1) {
switch (c) {
case 'd':
- isprefix = B_TRUE;
+ flags.isprefix = B_TRUE;
break;
case 'n':
- dryrun = B_TRUE;
+ flags.dryrun = B_TRUE;
break;
case 'v':
- verbose = B_TRUE;
+ flags.verbose = B_TRUE;
break;
case 'F':
- force = B_TRUE;
+ flags.force = B_TRUE;
break;
case ':':
(void) fprintf(stderr, gettext("missing argument for "
@@ -2051,15 +2504,391 @@ zfs_do_receive(int argc, char **argv)
return (1);
}
- err = zfs_receive(g_zfs, argv[0], isprefix, verbose, dryrun, force,
- STDIN_FILENO);
+ err = zfs_receive(g_zfs, argv[0], flags, STDIN_FILENO, NULL);
+
+ return (err != 0);
+}
+
+typedef struct allow_cb {
+ int a_permcnt;
+ size_t a_treeoffset;
+} allow_cb_t;
- if (!err) {
- zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0],
- B_FALSE, B_FALSE);
+static void
+zfs_print_perms(avl_tree_t *tree)
+{
+ zfs_perm_node_t *permnode;
+
+ permnode = avl_first(tree);
+ while (permnode != NULL) {
+ (void) printf("%s", permnode->z_pname);
+ permnode = AVL_NEXT(tree, permnode);
+ if (permnode)
+ (void) printf(",");
+ else
+ (void) printf("\n");
}
+}
- return (err != 0);
+/*
+ * Iterate over user/groups/everyone/... and the call perm_iter
+ * function to print actual permission when tree has >0 nodes.
+ */
+static void
+zfs_iter_perms(avl_tree_t *tree, const char *banner, allow_cb_t *cb)
+{
+ zfs_allow_node_t *item;
+ avl_tree_t *ptree;
+
+ item = avl_first(tree);
+ while (item) {
+ ptree = (void *)((char *)item + cb->a_treeoffset);
+ if (avl_numnodes(ptree)) {
+ if (cb->a_permcnt++ == 0)
+ (void) printf("%s\n", banner);
+ (void) printf("\t%s", item->z_key);
+ /*
+ * Avoid an extra space being printed
+ * for "everyone" which is keyed with a null
+ * string
+ */
+ if (item->z_key[0] != '\0')
+ (void) printf(" ");
+ zfs_print_perms(ptree);
+ }
+ item = AVL_NEXT(tree, item);
+ }
+}
+
+#define LINES "-------------------------------------------------------------\n"
+static int
+zfs_print_allows(char *ds)
+{
+ zfs_allow_t *curperms, *perms;
+ zfs_handle_t *zhp;
+ allow_cb_t allowcb = { 0 };
+ char banner[MAXPATHLEN];
+
+ if (ds[0] == '-')
+ usage(B_FALSE);
+
+ if (strrchr(ds, '@')) {
+ (void) fprintf(stderr, gettext("Snapshots don't have 'allow'"
+ " permissions\n"));
+ return (1);
+ }
+ if ((zhp = zfs_open(g_zfs, ds, ZFS_TYPE_DATASET)) == NULL)
+ return (1);
+
+ if (zfs_perm_get(zhp, &perms)) {
+ (void) fprintf(stderr,
+ gettext("Failed to retrieve 'allows' on %s\n"), ds);
+ zfs_close(zhp);
+ return (1);
+ }
+
+ zfs_close(zhp);
+
+ if (perms != NULL)
+ (void) printf("%s", LINES);
+ for (curperms = perms; curperms; curperms = curperms->z_next) {
+
+ (void) snprintf(banner, sizeof (banner),
+ "Permission sets on (%s)", curperms->z_setpoint);
+ allowcb.a_treeoffset =
+ offsetof(zfs_allow_node_t, z_localdescend);
+ allowcb.a_permcnt = 0;
+ zfs_iter_perms(&curperms->z_sets, banner, &allowcb);
+
+ (void) snprintf(banner, sizeof (banner),
+ "Create time permissions on (%s)", curperms->z_setpoint);
+ allowcb.a_treeoffset =
+ offsetof(zfs_allow_node_t, z_localdescend);
+ allowcb.a_permcnt = 0;
+ zfs_iter_perms(&curperms->z_crperms, banner, &allowcb);
+
+
+ (void) snprintf(banner, sizeof (banner),
+ "Local permissions on (%s)", curperms->z_setpoint);
+ allowcb.a_treeoffset = offsetof(zfs_allow_node_t, z_local);
+ allowcb.a_permcnt = 0;
+ zfs_iter_perms(&curperms->z_user, banner, &allowcb);
+ zfs_iter_perms(&curperms->z_group, banner, &allowcb);
+ zfs_iter_perms(&curperms->z_everyone, banner, &allowcb);
+
+ (void) snprintf(banner, sizeof (banner),
+ "Descendent permissions on (%s)", curperms->z_setpoint);
+ allowcb.a_treeoffset = offsetof(zfs_allow_node_t, z_descend);
+ allowcb.a_permcnt = 0;
+ zfs_iter_perms(&curperms->z_user, banner, &allowcb);
+ zfs_iter_perms(&curperms->z_group, banner, &allowcb);
+ zfs_iter_perms(&curperms->z_everyone, banner, &allowcb);
+
+ (void) snprintf(banner, sizeof (banner),
+ "Local+Descendent permissions on (%s)",
+ curperms->z_setpoint);
+ allowcb.a_treeoffset =
+ offsetof(zfs_allow_node_t, z_localdescend);
+ allowcb.a_permcnt = 0;
+ zfs_iter_perms(&curperms->z_user, banner, &allowcb);
+ zfs_iter_perms(&curperms->z_group, banner, &allowcb);
+ zfs_iter_perms(&curperms->z_everyone, banner, &allowcb);
+
+ (void) printf("%s", LINES);
+ }
+ zfs_free_allows(perms);
+ return (0);
+}
+
+#define ALLOWOPTIONS "ldcsu:g:e"
+#define UNALLOWOPTIONS "ldcsu:g:er"
+
+/*
+ * Validate options, and build necessary datastructure to display/remove/add
+ * permissions.
+ * Returns 0 - If permissions should be added/removed
+ * Returns 1 - If permissions should be displayed.
+ * Returns -1 - on failure
+ */
+int
+parse_allow_args(int *argc, char **argv[], boolean_t unallow,
+ char **ds, int *recurse, nvlist_t **zperms)
+{
+ int c;
+ char *options = unallow ? UNALLOWOPTIONS : ALLOWOPTIONS;
+ zfs_deleg_inherit_t deleg_type = ZFS_DELEG_NONE;
+ zfs_deleg_who_type_t who_type = ZFS_DELEG_WHO_UNKNOWN;
+ char *who = NULL;
+ char *perms = NULL;
+ zfs_handle_t *zhp;
+
+ while ((c = getopt(*argc, *argv, options)) != -1) {
+ switch (c) {
+ case 'l':
+ if (who_type == ZFS_DELEG_CREATE ||
+ who_type == ZFS_DELEG_NAMED_SET)
+ usage(B_FALSE);
+
+ deleg_type |= ZFS_DELEG_PERM_LOCAL;
+ break;
+ case 'd':
+ if (who_type == ZFS_DELEG_CREATE ||
+ who_type == ZFS_DELEG_NAMED_SET)
+ usage(B_FALSE);
+
+ deleg_type |= ZFS_DELEG_PERM_DESCENDENT;
+ break;
+ case 'r':
+ *recurse = B_TRUE;
+ break;
+ case 'c':
+ if (who_type != ZFS_DELEG_WHO_UNKNOWN)
+ usage(B_FALSE);
+ if (deleg_type)
+ usage(B_FALSE);
+ who_type = ZFS_DELEG_CREATE;
+ break;
+ case 's':
+ if (who_type != ZFS_DELEG_WHO_UNKNOWN)
+ usage(B_FALSE);
+ if (deleg_type)
+ usage(B_FALSE);
+ who_type = ZFS_DELEG_NAMED_SET;
+ break;
+ case 'u':
+ if (who_type != ZFS_DELEG_WHO_UNKNOWN)
+ usage(B_FALSE);
+ who_type = ZFS_DELEG_USER;
+ who = optarg;
+ break;
+ case 'g':
+ if (who_type != ZFS_DELEG_WHO_UNKNOWN)
+ usage(B_FALSE);
+ who_type = ZFS_DELEG_GROUP;
+ who = optarg;
+ break;
+ case 'e':
+ if (who_type != ZFS_DELEG_WHO_UNKNOWN)
+ usage(B_FALSE);
+ who_type = ZFS_DELEG_EVERYONE;
+ break;
+ default:
+ usage(B_FALSE);
+ break;
+ }
+ }
+
+ if (deleg_type == 0)
+ deleg_type = ZFS_DELEG_PERM_LOCALDESCENDENT;
+
+ *argc -= optind;
+ *argv += optind;
+
+ if (unallow == B_FALSE && *argc == 1) {
+ /*
+ * Only print permissions if no options were processed
+ */
+ if (optind == 1)
+ return (1);
+ else
+ usage(B_FALSE);
+ }
+
+ /*
+ * initialize variables for zfs_build_perms based on number
+ * of arguments.
+ * 3 arguments ==> zfs [un]allow joe perm,perm,perm <dataset> or
+ * zfs [un]allow -s @set1 perm,perm <dataset>
+ * 2 arguments ==> zfs [un]allow -c perm,perm <dataset> or
+ * zfs [un]allow -u|-g <name> perm <dataset> or
+ * zfs [un]allow -e perm,perm <dataset>
+ * zfs unallow joe <dataset>
+ * zfs unallow -s @set1 <dataset>
+ * 1 argument ==> zfs [un]allow -e <dataset> or
+ * zfs [un]allow -c <dataset>
+ */
+
+ switch (*argc) {
+ case 3:
+ perms = (*argv)[1];
+ who = (*argv)[0];
+ *ds = (*argv)[2];
+
+ /*
+ * advance argc/argv for do_allow cases.
+ * for do_allow case make sure who have a know who type
+ * and its not a permission set.
+ */
+ if (unallow == B_TRUE) {
+ *argc -= 2;
+ *argv += 2;
+ } else if (who_type != ZFS_DELEG_WHO_UNKNOWN &&
+ who_type != ZFS_DELEG_NAMED_SET)
+ usage(B_FALSE);
+ break;
+
+ case 2:
+ if (unallow == B_TRUE && (who_type == ZFS_DELEG_EVERYONE ||
+ who_type == ZFS_DELEG_CREATE || who != NULL)) {
+ perms = (*argv)[0];
+ *ds = (*argv)[1];
+ } else {
+ if (unallow == B_FALSE &&
+ (who_type == ZFS_DELEG_WHO_UNKNOWN ||
+ who_type == ZFS_DELEG_NAMED_SET))
+ usage(B_FALSE);
+ else if (who_type == ZFS_DELEG_WHO_UNKNOWN ||
+ who_type == ZFS_DELEG_NAMED_SET)
+ who = (*argv)[0];
+ else if (who_type != ZFS_DELEG_NAMED_SET)
+ perms = (*argv)[0];
+ *ds = (*argv)[1];
+ }
+ if (unallow == B_TRUE) {
+ (*argc)--;
+ (*argv)++;
+ }
+ break;
+
+ case 1:
+ if (unallow == B_FALSE)
+ usage(B_FALSE);
+ if (who == NULL && who_type != ZFS_DELEG_CREATE &&
+ who_type != ZFS_DELEG_EVERYONE)
+ usage(B_FALSE);
+ *ds = (*argv)[0];
+ break;
+
+ default:
+ usage(B_FALSE);
+ }
+
+ if (strrchr(*ds, '@')) {
+ (void) fprintf(stderr,
+ gettext("Can't set or remove 'allow' permissions "
+ "on snapshots.\n"));
+ return (-1);
+ }
+
+ if ((zhp = zfs_open(g_zfs, *ds, ZFS_TYPE_DATASET)) == NULL)
+ return (-1);
+
+ if ((zfs_build_perms(zhp, who, perms,
+ who_type, deleg_type, zperms)) != 0) {
+ zfs_close(zhp);
+ return (-1);
+ }
+ zfs_close(zhp);
+ return (0);
+}
+
+static int
+zfs_do_allow(int argc, char **argv)
+{
+ char *ds;
+ nvlist_t *zperms = NULL;
+ zfs_handle_t *zhp;
+ int unused;
+ int ret;
+
+ if ((ret = parse_allow_args(&argc, &argv, B_FALSE, &ds,
+ &unused, &zperms)) == -1)
+ return (1);
+
+ if (ret == 1)
+ return (zfs_print_allows(argv[0]));
+
+ if ((zhp = zfs_open(g_zfs, ds, ZFS_TYPE_DATASET)) == NULL)
+ return (1);
+
+ if (zfs_perm_set(zhp, zperms)) {
+ zfs_close(zhp);
+ nvlist_free(zperms);
+ return (1);
+ }
+ nvlist_free(zperms);
+ zfs_close(zhp);
+
+ return (0);
+}
+
+static int
+unallow_callback(zfs_handle_t *zhp, void *data)
+{
+ nvlist_t *nvp = (nvlist_t *)data;
+ int error;
+
+ error = zfs_perm_remove(zhp, nvp);
+ if (error) {
+ (void) fprintf(stderr, gettext("Failed to remove permissions "
+ "on %s\n"), zfs_get_name(zhp));
+ }
+ return (error);
+}
+
+static int
+zfs_do_unallow(int argc, char **argv)
+{
+ int recurse = B_FALSE;
+ char *ds;
+ int error;
+ nvlist_t *zperms = NULL;
+ int flags = 0;
+
+ if (parse_allow_args(&argc, &argv, B_TRUE,
+ &ds, &recurse, &zperms) == -1)
+ return (1);
+
+ if (recurse)
+ flags |= ZFS_ITER_RECURSE;
+ error = zfs_for_each(argc, argv, flags,
+ ZFS_TYPE_FILESYSTEM|ZFS_TYPE_VOLUME, NULL,
+ NULL, unallow_callback, (void *)zperms);
+
+ if (zperms)
+ nvlist_free(zperms);
+
+ return (error);
}
typedef struct get_all_cbdata {
@@ -2067,14 +2896,35 @@ typedef struct get_all_cbdata {
size_t cb_alloc;
size_t cb_used;
uint_t cb_types;
+ boolean_t cb_verbose;
} get_all_cbdata_t;
+#define CHECK_SPINNER 30
+#define SPINNER_TIME 3 /* seconds */
+#define MOUNT_TIME 5 /* seconds */
+
static int
get_one_dataset(zfs_handle_t *zhp, void *data)
{
+ static char spin[] = { '-', '\\', '|', '/' };
+ static int spinval = 0;
+ static int spincheck = 0;
+ static time_t last_spin_time = (time_t)0;
get_all_cbdata_t *cbp = data;
zfs_type_t type = zfs_get_type(zhp);
+ if (cbp->cb_verbose) {
+ if (--spincheck < 0) {
+ time_t now = time(NULL);
+ if (last_spin_time + SPINNER_TIME < now) {
+ (void) printf("\b%c", spin[spinval++ % 4]);
+ (void) fflush(stdout);
+ last_spin_time = now;
+ }
+ spincheck = CHECK_SPINNER;
+ }
+ }
+
/*
* Interate over any nested datasets.
*/
@@ -2117,15 +2967,26 @@ get_one_dataset(zfs_handle_t *zhp, void *data)
}
static void
-get_all_datasets(uint_t types, zfs_handle_t ***dslist, size_t *count)
+get_all_datasets(uint_t types, zfs_handle_t ***dslist, size_t *count,
+ boolean_t verbose)
{
get_all_cbdata_t cb = { 0 };
cb.cb_types = types;
+ cb.cb_verbose = verbose;
+
+ if (verbose) {
+ (void) printf("%s: *", gettext("Reading ZFS config"));
+ (void) fflush(stdout);
+ }
(void) zfs_iter_root(g_zfs, get_one_dataset, &cb);
*dslist = cb.cb_handles;
*count = cb.cb_used;
+
+ if (verbose) {
+ (void) printf("\b%s\n", gettext("done."));
+ }
}
static int
@@ -2167,15 +3028,17 @@ dataset_cmp(const void *a, const void *b)
* Share or mount a dataset.
*/
static int
-share_mount_one(zfs_handle_t *zhp, int op, int flags, boolean_t explicit,
- const char *options)
+share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol,
+ boolean_t explicit, const char *options)
{
char mountpoint[ZFS_MAXPROPLEN];
char shareopts[ZFS_MAXPROPLEN];
+ char smbshareopts[ZFS_MAXPROPLEN];
const char *cmdname = op == OP_SHARE ? "share" : "mount";
struct mnttab mnt;
uint64_t zoned, canmount;
zfs_type_t type = zfs_get_type(zhp);
+ boolean_t shared_nfs, shared_smb;
assert(type & (ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME));
@@ -2216,9 +3079,12 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, boolean_t explicit,
sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0);
verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts,
sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0);
+ verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshareopts,
+ sizeof (smbshareopts), NULL, NULL, 0, B_FALSE) == 0);
canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT);
- if (op == OP_SHARE && strcmp(shareopts, "off") == 0) {
+ if (op == OP_SHARE && strcmp(shareopts, "off") == 0 &&
+ strcmp(smbshareopts, "off") == 0) {
if (!explicit)
return (0);
@@ -2240,9 +3106,8 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, boolean_t explicit,
(void) fprintf(stderr, gettext("cannot %s '%s': "
"legacy mountpoint\n"), cmdname, zfs_get_name(zhp));
- (void) fprintf(stderr, gettext("use %s to "
- "%s this filesystem\n"), op == OP_SHARE ?
- "share(1M)" : "mount(1M)", cmdname);
+ (void) fprintf(stderr, gettext("use %s(1M) to "
+ "%s this filesystem\n"), cmdname, cmdname);
return (1);
}
@@ -2255,7 +3120,16 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, boolean_t explicit,
return (1);
}
- if (!canmount) {
+ /*
+ * canmount explicit outcome
+ * on no pass through
+ * on yes pass through
+ * off no return 0
+ * off yes display error, return 1
+ * noauto no return 0
+ * noauto yes pass through
+ */
+ if (canmount == ZFS_CANMOUNT_OFF) {
if (!explicit)
return (0);
@@ -2263,6 +3137,8 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, boolean_t explicit,
"'canmount' property is set to 'off'\n"), cmdname,
zfs_get_name(zhp));
return (1);
+ } else if (canmount == ZFS_CANMOUNT_NOAUTO && !explicit) {
+ return (0);
}
/*
@@ -2274,7 +3150,15 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, boolean_t explicit,
*/
switch (op) {
case OP_SHARE:
- if (zfs_is_shared_nfs(zhp, NULL)) {
+
+ shared_nfs = zfs_is_shared_nfs(zhp, NULL);
+ shared_smb = zfs_is_shared_smb(zhp, NULL);
+
+ if (shared_nfs && shared_smb ||
+ (shared_nfs && strcmp(shareopts, "on") == 0 &&
+ strcmp(smbshareopts, "off") == 0) ||
+ (shared_smb && strcmp(smbshareopts, "on") == 0 &&
+ strcmp(shareopts, "off") == 0)) {
if (!explicit)
return (0);
@@ -2288,8 +3172,23 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, boolean_t explicit,
zfs_mount(zhp, NULL, 0) != 0)
return (1);
- if (zfs_share_nfs(zhp) != 0)
+ if (protocol == NULL) {
+ if (zfs_shareall(zhp) != 0)
+ return (1);
+ } else if (strcmp(protocol, "nfs") == 0) {
+ if (zfs_share_nfs(zhp))
+ return (1);
+ } else if (strcmp(protocol, "smb") == 0) {
+ if (zfs_share_smb(zhp))
+ return (1);
+ } else {
+ (void) fprintf(stderr, gettext("cannot share "
+ "'%s': invalid share type '%s' "
+ "specified\n"),
+ zfs_get_name(zhp), protocol);
return (1);
+ }
+
break;
case OP_MOUNT:
@@ -2352,24 +3251,93 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, boolean_t explicit,
return (0);
}
+/*
+ * Reports progress in the form "(current/total)". Not thread-safe.
+ */
+static void
+report_mount_progress(int current, int total)
+{
+ static int len;
+ static char *reverse = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
+ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
+ static time_t last_progress_time;
+ time_t now = time(NULL);
+
+ /* report 1..n instead of 0..n-1 */
+ ++current;
+
+ /* display header if we're here for the first time */
+ if (current == 1) {
+ (void) printf(gettext("Mounting ZFS filesystems: "));
+ len = 0;
+ } else if (current != total && last_progress_time + MOUNT_TIME >= now) {
+ /* too soon to report again */
+ return;
+ }
+
+ last_progress_time = now;
+
+ /* back up to prepare for overwriting */
+ if (len)
+ (void) printf("%*.*s", len, len, reverse);
+
+ /* We put a newline at the end if this is the last one. */
+ len = printf("(%d/%d)%s", current, total, current == total ? "\n" : "");
+ (void) fflush(stdout);
+}
+
+static void
+append_options(char *mntopts, char *newopts)
+{
+ int len = strlen(mntopts);
+
+ /* original length plus new string to append plus 1 for the comma */
+ if (len + 1 + strlen(newopts) >= MNT_LINE_MAX) {
+ (void) fprintf(stderr, gettext("the opts argument for "
+ "'%c' option is too long (more than %d chars)\n"),
+ "-o", MNT_LINE_MAX);
+ usage(B_FALSE);
+ }
+
+ if (*mntopts)
+ mntopts[len++] = ',';
+
+ (void) strcpy(&mntopts[len], newopts);
+}
+
static int
share_mount(int op, int argc, char **argv)
{
int do_all = 0;
+ boolean_t verbose = B_FALSE;
int c, ret = 0;
- const char *options = NULL;
+ char *options = NULL;
int types, flags = 0;
/* check options */
- while ((c = getopt(argc, argv, op == OP_MOUNT ? ":ao:O" : "a"))
+ while ((c = getopt(argc, argv, op == OP_MOUNT ? ":avo:O" : "a"))
!= -1) {
switch (c) {
case 'a':
do_all = 1;
break;
+ case 'v':
+ verbose = B_TRUE;
+ break;
case 'o':
- options = optarg;
+ if (*optarg == '\0') {
+ (void) fprintf(stderr, gettext("empty mount "
+ "options (-o) specified\n"));
+ usage(B_FALSE);
+ }
+
+ if (options == NULL)
+ options = safe_malloc(MNT_LINE_MAX + 1);
+
+ /* option validation is done later */
+ append_options(options, optarg);
break;
+
case 'O':
warnx("no overlay mounts support on FreeBSD, ignoring");
break;
@@ -2392,20 +3360,22 @@ share_mount(int op, int argc, char **argv)
if (do_all) {
zfs_handle_t **dslist = NULL;
size_t i, count = 0;
+ char *protocol = NULL;
if (op == OP_MOUNT) {
types = ZFS_TYPE_FILESYSTEM;
} else if (argc > 0) {
- if (strcmp(argv[0], "nfs") == 0) {
+ if (strcmp(argv[0], "nfs") == 0 ||
+ strcmp(argv[0], "smb") == 0) {
types = ZFS_TYPE_FILESYSTEM;
} else if (strcmp(argv[0], "iscsi") == 0) {
types = ZFS_TYPE_VOLUME;
} else {
(void) fprintf(stderr, gettext("share type "
- "must be 'nfs' or 'iscsi'\n"));
+ "must be 'nfs', 'smb' or 'iscsi'\n"));
usage(B_FALSE);
}
-
+ protocol = argv[0];
argc--;
argv++;
} else {
@@ -2417,7 +3387,7 @@ share_mount(int op, int argc, char **argv)
usage(B_FALSE);
}
- get_all_datasets(types, &dslist, &count);
+ get_all_datasets(types, &dslist, &count, verbose);
if (count == 0)
return (0);
@@ -2425,8 +3395,11 @@ share_mount(int op, int argc, char **argv)
qsort(dslist, count, sizeof (void *), dataset_cmp);
for (i = 0; i < count; i++) {
- if (share_mount_one(dslist[i], op, flags, B_FALSE,
- options) != 0)
+ if (verbose)
+ report_mount_progress(i, count);
+
+ if (share_mount_one(dslist[i], op, flags, protocol,
+ B_FALSE, options) != 0)
ret = 1;
zfs_close(dslist[i]);
}
@@ -2436,9 +3409,9 @@ share_mount(int op, int argc, char **argv)
struct statfs *sfs;
int i, n;
- if (op == OP_SHARE) {
+ if ((op == OP_SHARE) || (options != NULL)) {
(void) fprintf(stderr, gettext("missing filesystem "
- "argument\n"));
+ "argument (specify -a for all)\n"));
usage(B_FALSE);
}
@@ -2476,7 +3449,7 @@ share_mount(int op, int argc, char **argv)
if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL) {
ret = 1;
} else {
- ret = share_mount_one(zhp, op, flags, B_TRUE,
+ ret = share_mount_one(zhp, op, flags, NULL, B_TRUE,
options);
zfs_close(zhp);
}
@@ -2498,7 +3471,7 @@ zfs_do_mount(int argc, char **argv)
}
/*
- * zfs share -a [nfs | iscsi]
+ * zfs share -a [nfs | iscsi | smb]
* zfs share filesystem
*
* Share all filesystems, or share the given filesystem.
@@ -2535,9 +3508,23 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
{
zfs_handle_t *zhp;
int ret;
+ struct stat64 statbuf;
struct mnttab search = { 0 }, entry;
const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount";
- char property[ZFS_MAXPROPLEN];
+ ino_t path_inode;
+
+ /*
+ * Search for the path in /etc/mnttab. Rather than looking for the
+ * specific path, which can be fooled by non-standard paths (i.e. ".."
+ * or "//"), we stat() the path and search for the corresponding
+ * (major,minor) device pair.
+ */
+ if (stat64(path, &statbuf) != 0) {
+ (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"),
+ cmdname, path, strerror(errno));
+ return (1);
+ }
+ path_inode = statbuf.st_ino;
/*
* Search for the given (major,minor) pair in the mount table.
@@ -2545,9 +3532,17 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
search.mnt_mountp = path;
rewind(mnttab_file);
if (getmntany(mnttab_file, &entry, &search) != 0) {
- (void) fprintf(stderr, gettext("cannot %s '%s': not "
- "currently mounted\n"), cmdname, path);
- return (1);
+ if (op == OP_SHARE) {
+ (void) fprintf(stderr, gettext("cannot %s '%s': not "
+ "currently mounted\n"), cmdname, path);
+ return (1);
+ }
+ (void) fprintf(stderr, gettext("warning: %s not in mnttab\n"),
+ path);
+ if ((ret = umount2(path, flags)) != 0)
+ (void) fprintf(stderr, gettext("%s: %s\n"), path,
+ strerror(errno));
+ return (ret != 0);
}
if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) {
@@ -2560,39 +3555,58 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
ZFS_TYPE_FILESYSTEM)) == NULL)
return (1);
- verify(zfs_prop_get(zhp, op == OP_SHARE ?
- ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property,
- sizeof (property), NULL, NULL, 0, B_FALSE) == 0);
+ ret = 1;
+ if (stat64(entry.mnt_mountp, &statbuf) != 0) {
+ (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"),
+ cmdname, path, strerror(errno));
+ goto out;
+ } else if (statbuf.st_ino != path_inode) {
+ (void) fprintf(stderr, gettext("cannot "
+ "%s '%s': not a mountpoint\n"), cmdname, path);
+ goto out;
+ }
if (op == OP_SHARE) {
- if (strcmp(property, "off") == 0) {
+ char nfs_mnt_prop[ZFS_MAXPROPLEN];
+ char smbshare_prop[ZFS_MAXPROPLEN];
+
+ verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, nfs_mnt_prop,
+ sizeof (nfs_mnt_prop), NULL, NULL, 0, B_FALSE) == 0);
+ verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshare_prop,
+ sizeof (smbshare_prop), NULL, NULL, 0, B_FALSE) == 0);
+
+ if (strcmp(nfs_mnt_prop, "off") == 0 &&
+ strcmp(smbshare_prop, "off") == 0) {
(void) fprintf(stderr, gettext("cannot unshare "
"'%s': legacy share\n"), path);
(void) fprintf(stderr, gettext("use "
"unshare(1M) to unshare this filesystem\n"));
- ret = 1;
- } else if (!zfs_is_shared_nfs(zhp, NULL)) {
+ } else if (!zfs_is_shared(zhp)) {
(void) fprintf(stderr, gettext("cannot unshare '%s': "
"not currently shared\n"), path);
- ret = 1;
} else {
- ret = zfs_unshareall_nfs(zhp);
+ ret = zfs_unshareall_bypath(zhp, path);
}
} else {
+ char mtpt_prop[ZFS_MAXPROPLEN];
+
+ verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mtpt_prop,
+ sizeof (mtpt_prop), NULL, NULL, 0, B_FALSE) == 0);
+
if (is_manual) {
ret = zfs_unmount(zhp, NULL, flags);
- } else if (strcmp(property, "legacy") == 0) {
+ } else if (strcmp(mtpt_prop, "legacy") == 0) {
(void) fprintf(stderr, gettext("cannot unmount "
"'%s': legacy mountpoint\n"),
zfs_get_name(zhp));
(void) fprintf(stderr, gettext("use umount(1M) "
"to unmount this filesystem\n"));
- ret = 1;
} else {
ret = zfs_unmountall(zhp, flags);
}
}
+out:
zfs_close(zhp);
return (ret != 0);
@@ -2609,7 +3623,8 @@ unshare_unmount(int op, int argc, char **argv)
int ret = 0;
int types, c;
zfs_handle_t *zhp;
- char property[ZFS_MAXPROPLEN];
+ char nfsiscsi_mnt_prop[ZFS_MAXPROPLEN];
+ char sharesmb[ZFS_MAXPROPLEN];
/* check options */
while ((c = getopt(argc, argv, op == OP_SHARE ? "a" : "af")) != -1) {
@@ -2695,18 +3710,35 @@ unshare_unmount(int op, int argc, char **argv)
continue;
}
- verify(zfs_prop_get(zhp, op == OP_SHARE ?
- ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT,
- property, sizeof (property), NULL, NULL,
- 0, B_FALSE) == 0);
-
- /* Ignore legacy mounts and shares */
- if ((op == OP_SHARE &&
- strcmp(property, "off") == 0) ||
- (op == OP_MOUNT &&
- strcmp(property, "legacy") == 0)) {
- zfs_close(zhp);
- continue;
+ switch (op) {
+ case OP_SHARE:
+ verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,
+ nfsiscsi_mnt_prop,
+ sizeof (nfsiscsi_mnt_prop),
+ NULL, NULL, 0, B_FALSE) == 0);
+ if (strcmp(nfsiscsi_mnt_prop, "off") != 0)
+ break;
+ verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,
+ nfsiscsi_mnt_prop,
+ sizeof (nfsiscsi_mnt_prop),
+ NULL, NULL, 0, B_FALSE) == 0);
+ if (strcmp(nfsiscsi_mnt_prop, "off") == 0)
+ continue;
+ break;
+ case OP_MOUNT:
+ /* Ignore legacy mounts */
+ verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
+ nfsiscsi_mnt_prop,
+ sizeof (nfsiscsi_mnt_prop),
+ NULL, NULL, 0, B_FALSE) == 0);
+ if (strcmp(nfsiscsi_mnt_prop, "legacy") == 0)
+ continue;
+ /* Ignore canmount=noauto mounts */
+ if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) ==
+ ZFS_CANMOUNT_NOAUTO)
+ continue;
+ default:
+ break;
}
node = safe_malloc(sizeof (unshare_unmount_node_t));
@@ -2746,7 +3778,7 @@ unshare_unmount(int op, int argc, char **argv)
switch (op) {
case OP_SHARE:
- if (zfs_unshare_nfs(node->un_zhp,
+ if (zfs_unshareall_bypath(node->un_zhp,
node->un_mountp) != 0)
ret = 1;
break;
@@ -2774,7 +3806,8 @@ unshare_unmount(int op, int argc, char **argv)
zfs_handle_t **dslist = NULL;
size_t i, count = 0;
- get_all_datasets(ZFS_TYPE_VOLUME, &dslist, &count);
+ get_all_datasets(ZFS_TYPE_VOLUME, &dslist, &count,
+ B_FALSE);
if (count != 0) {
qsort(dslist, count, sizeof (void *),
@@ -2819,12 +3852,22 @@ unshare_unmount(int op, int argc, char **argv)
if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
verify(zfs_prop_get(zhp, op == OP_SHARE ?
- ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property,
- sizeof (property), NULL, NULL, 0, B_FALSE) == 0);
+ ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT,
+ nfsiscsi_mnt_prop, sizeof (nfsiscsi_mnt_prop), NULL,
+ NULL, 0, B_FALSE) == 0);
switch (op) {
case OP_SHARE:
- if (strcmp(property, "off") == 0) {
+ verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,
+ nfsiscsi_mnt_prop,
+ sizeof (nfsiscsi_mnt_prop),
+ NULL, NULL, 0, B_FALSE) == 0);
+ verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,
+ sharesmb, sizeof (sharesmb), NULL, NULL,
+ 0, B_FALSE) == 0);
+
+ if (strcmp(nfsiscsi_mnt_prop, "off") == 0 &&
+ strcmp(sharesmb, "off") == 0) {
(void) fprintf(stderr, gettext("cannot "
"unshare '%s': legacy share\n"),
zfs_get_name(zhp));
@@ -2832,18 +3875,18 @@ unshare_unmount(int op, int argc, char **argv)
"unshare(1M) to unshare this "
"filesystem\n"));
ret = 1;
- } else if (!zfs_is_shared_nfs(zhp, NULL)) {
+ } else if (!zfs_is_shared(zhp)) {
(void) fprintf(stderr, gettext("cannot "
"unshare '%s': not currently "
"shared\n"), zfs_get_name(zhp));
ret = 1;
- } else if (zfs_unshareall_nfs(zhp) != 0) {
+ } else if (zfs_unshareall(zhp) != 0) {
ret = 1;
}
break;
case OP_MOUNT:
- if (strcmp(property, "legacy") == 0) {
+ if (strcmp(nfsiscsi_mnt_prop, "legacy") == 0) {
(void) fprintf(stderr, gettext("cannot "
"unmount '%s': legacy "
"mountpoint\n"), zfs_get_name(zhp));
@@ -2865,10 +3908,11 @@ unshare_unmount(int op, int argc, char **argv)
} else {
assert(op == OP_SHARE);
- verify(zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, property,
- sizeof (property), NULL, NULL, 0, B_FALSE) == 0);
+ verify(zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI,
+ nfsiscsi_mnt_prop, sizeof (nfsiscsi_mnt_prop),
+ NULL, NULL, 0, B_FALSE) == 0);
- if (strcmp(property, "off") == 0) {
+ if (strcmp(nfsiscsi_mnt_prop, "off") == 0) {
(void) fprintf(stderr, gettext("cannot unshare "
"'%s': 'shareiscsi' property not set\n"),
zfs_get_name(zhp));
@@ -2948,9 +3992,6 @@ do_jail(int argc, char **argv, int attach)
ret = (zfs_jail(zhp, jailid, attach) != 0);
- if (!ret)
- zpool_log_history(g_zfs, argc, argv, argv[2], B_FALSE, B_FALSE);
-
zfs_close(zhp);
return (ret);
}
@@ -3141,6 +4182,23 @@ do_volcheck(boolean_t isinit)
return (zpool_iter(g_zfs, volcheck, &isinit) ? 1 : 0);
}
+static int
+find_command_idx(char *command, int *idx)
+{
+ int i;
+
+ for (i = 0; i < NCOMMAND; i++) {
+ if (command_table[i].name == NULL)
+ continue;
+
+ if (strcmp(command, command_table[i].name) == 0) {
+ *idx = i;
+ return (0);
+ }
+ }
+ return (1);
+}
+
int
main(int argc, char **argv)
{
@@ -3160,6 +4218,9 @@ main(int argc, char **argv)
return (1);
}
+ zpool_set_history_str("zfs", argc, argv, history_str);
+ verify(zpool_stage_history(g_zfs, history_str) == 0);
+
libzfs_print_on_error(g_zfs, B_TRUE);
if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) {
@@ -3218,18 +4279,14 @@ main(int argc, char **argv)
/*
* Run the appropriate command.
*/
- for (i = 0; i < NCOMMAND; i++) {
- if (command_table[i].name == NULL)
- continue;
-
- if (strcmp(cmdname, command_table[i].name) == 0) {
- current_command = &command_table[i];
- ret = command_table[i].func(argc - 1, argv + 1);
- break;
- }
- }
-
- if (i == NCOMMAND) {
+ if (find_command_idx(cmdname, &i) == 0) {
+ current_command = &command_table[i];
+ ret = command_table[i].func(argc - 1, argv + 1);
+ } else if (strchr(cmdname, '=') != NULL) {
+ verify(find_command_idx("set", &i) == 0);
+ current_command = &command_table[i];
+ ret = command_table[i].func(argc, argv);
+ } else {
(void) fprintf(stderr, gettext("unrecognized "
"command '%s'\n"), cmdname);
usage(B_FALSE);
diff --git a/cddl/contrib/opensolaris/cmd/zinject/translate.c b/cddl/contrib/opensolaris/cmd/zinject/translate.c
new file mode 100644
index 0000000..da26cd6
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/zinject/translate.c
@@ -0,0 +1,460 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL 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.
+ */
+
+#include <libzfs.h>
+
+#undef verify /* both libzfs.h and zfs_context.h want to define this */
+
+#include <sys/zfs_context.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/file.h>
+#include <sys/mntent.h>
+#include <sys/mnttab.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <sys/dmu.h>
+#include <sys/dmu_objset.h>
+#include <sys/dnode.h>
+#include <sys/vdev_impl.h>
+
+#include "zinject.h"
+
+#include <assert.h>
+#define verify assert
+
+extern void kernel_init(int);
+extern void kernel_fini(void);
+
+static int debug;
+
+static void
+ziprintf(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!debug)
+ return;
+
+ va_start(ap, fmt);
+ (void) vprintf(fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Given a full path to a file, translate into a dataset name and a relative
+ * path within the dataset. 'dataset' must be at least MAXNAMELEN characters,
+ * and 'relpath' must be at least MAXPATHLEN characters. We also pass a stat64
+ * buffer, which we need later to get the object ID.
+ */
+static int
+parse_pathname(const char *fullpath, char *dataset, char *relpath,
+ struct stat64 *statbuf)
+{
+ struct statfs sfs;
+ const char *rel;
+
+ if (fullpath[0] != '/') {
+ (void) fprintf(stderr, "invalid object '%s': must be full "
+ "path\n", fullpath);
+ usage();
+ return (-1);
+ }
+
+ if (strlen(fullpath) >= MAXPATHLEN) {
+ (void) fprintf(stderr, "invalid object; pathname too long\n");
+ return (-1);
+ }
+
+ if (stat64(fullpath, statbuf) != 0) {
+ (void) fprintf(stderr, "cannot open '%s': %s\n",
+ fullpath, strerror(errno));
+ return (-1);
+ }
+
+ if (statfs(fullpath, &sfs) == -1) {
+ (void) fprintf(stderr, "cannot find mountpoint for '%s': %s\n",
+ fullpath, strerror(errno));
+ return (-1);
+ }
+
+ if (strcmp(sfs.f_fstypename, MNTTYPE_ZFS) != 0) {
+ (void) fprintf(stderr, "invalid path '%s': not a ZFS "
+ "filesystem\n", fullpath);
+ return (-1);
+ }
+
+ if (strncmp(fullpath, sfs.f_mntonname, strlen(sfs.f_mntonname)) != 0) {
+ (void) fprintf(stderr, "invalid path '%s': mountpoint "
+ "doesn't match path\n", fullpath);
+ return (-1);
+ }
+
+ (void) strcpy(dataset, sfs.f_mntfromname);
+
+ rel = fullpath + strlen(sfs.f_mntonname);
+ if (rel[0] == '/')
+ rel++;
+ (void) strcpy(relpath, rel);
+
+ return (0);
+}
+
+/*
+ * Convert from a (dataset, path) pair into a (objset, object) pair. Note that
+ * we grab the object number from the inode number, since looking this up via
+ * libzpool is a real pain.
+ */
+/* ARGSUSED */
+static int
+object_from_path(const char *dataset, const char *path, struct stat64 *statbuf,
+ zinject_record_t *record)
+{
+ objset_t *os;
+ int err;
+
+ /*
+ * Before doing any libzpool operations, call sync() to ensure that the
+ * on-disk state is consistent with the in-core state.
+ */
+ sync();
+
+ if ((err = dmu_objset_open(dataset, DMU_OST_ZFS,
+ DS_MODE_USER | DS_MODE_READONLY, &os)) != 0) {
+ (void) fprintf(stderr, "cannot open dataset '%s': %s\n",
+ dataset, strerror(err));
+ return (-1);
+ }
+
+ record->zi_objset = dmu_objset_id(os);
+ record->zi_object = statbuf->st_ino;
+
+ dmu_objset_close(os);
+
+ return (0);
+}
+
+/*
+ * Calculate the real range based on the type, level, and range given.
+ */
+static int
+calculate_range(const char *dataset, err_type_t type, int level, char *range,
+ zinject_record_t *record)
+{
+ objset_t *os = NULL;
+ dnode_t *dn = NULL;
+ int err;
+ int ret = -1;
+
+ /*
+ * Determine the numeric range from the string.
+ */
+ if (range == NULL) {
+ /*
+ * If range is unspecified, set the range to [0,-1], which
+ * indicates that the whole object should be treated as an
+ * error.
+ */
+ record->zi_start = 0;
+ record->zi_end = -1ULL;
+ } else {
+ char *end;
+
+ /* XXX add support for suffixes */
+ record->zi_start = strtoull(range, &end, 10);
+
+
+ if (*end == '\0')
+ record->zi_end = record->zi_start + 1;
+ else if (*end == ',')
+ record->zi_end = strtoull(end + 1, &end, 10);
+
+ if (*end != '\0') {
+ (void) fprintf(stderr, "invalid range '%s': must be "
+ "a numeric range of the form 'start[,end]'\n",
+ range);
+ goto out;
+ }
+ }
+
+ switch (type) {
+ case TYPE_DATA:
+ break;
+
+ case TYPE_DNODE:
+ /*
+ * If this is a request to inject faults into the dnode, then we
+ * must translate the current (objset,object) pair into an
+ * offset within the metadnode for the objset. Specifying any
+ * kind of range with type 'dnode' is illegal.
+ */
+ if (range != NULL) {
+ (void) fprintf(stderr, "range cannot be specified when "
+ "type is 'dnode'\n");
+ goto out;
+ }
+
+ record->zi_start = record->zi_object * sizeof (dnode_phys_t);
+ record->zi_end = record->zi_start + sizeof (dnode_phys_t);
+ record->zi_object = 0;
+ break;
+ }
+
+ /*
+ * Get the dnode associated with object, so we can calculate the block
+ * size.
+ */
+ if ((err = dmu_objset_open(dataset, DMU_OST_ANY,
+ DS_MODE_USER | DS_MODE_READONLY, &os)) != 0) {
+ (void) fprintf(stderr, "cannot open dataset '%s': %s\n",
+ dataset, strerror(err));
+ goto out;
+ }
+
+ if (record->zi_object == 0) {
+ dn = os->os->os_meta_dnode;
+ } else {
+ err = dnode_hold(os->os, record->zi_object, FTAG, &dn);
+ if (err != 0) {
+ (void) fprintf(stderr, "failed to hold dnode "
+ "for object %llu\n",
+ (u_longlong_t)record->zi_object);
+ goto out;
+ }
+ }
+
+
+ ziprintf("data shift: %d\n", (int)dn->dn_datablkshift);
+ ziprintf(" ind shift: %d\n", (int)dn->dn_indblkshift);
+
+ /*
+ * Translate range into block IDs.
+ */
+ if (record->zi_start != 0 || record->zi_end != -1ULL) {
+ record->zi_start >>= dn->dn_datablkshift;
+ record->zi_end >>= dn->dn_datablkshift;
+ }
+
+ /*
+ * Check level, and then translate level 0 blkids into ranges
+ * appropriate for level of indirection.
+ */
+ record->zi_level = level;
+ if (level > 0) {
+ ziprintf("level 0 blkid range: [%llu, %llu]\n",
+ record->zi_start, record->zi_end);
+
+ if (level >= dn->dn_nlevels) {
+ (void) fprintf(stderr, "level %d exceeds max level "
+ "of object (%d)\n", level, dn->dn_nlevels - 1);
+ goto out;
+ }
+
+ if (record->zi_start != 0 || record->zi_end != 0) {
+ int shift = dn->dn_indblkshift - SPA_BLKPTRSHIFT;
+
+ for (; level > 0; level--) {
+ record->zi_start >>= shift;
+ record->zi_end >>= shift;
+ }
+ }
+ }
+
+ ret = 0;
+out:
+ if (dn) {
+ if (dn != os->os->os_meta_dnode)
+ dnode_rele(dn, FTAG);
+ }
+ if (os)
+ dmu_objset_close(os);
+
+ return (ret);
+}
+
+int
+translate_record(err_type_t type, const char *object, const char *range,
+ int level, zinject_record_t *record, char *poolname, char *dataset)
+{
+ char path[MAXPATHLEN];
+ char *slash;
+ struct stat64 statbuf;
+ int ret = -1;
+
+ kernel_init(FREAD);
+
+ debug = (getenv("ZINJECT_DEBUG") != NULL);
+
+ ziprintf("translating: %s\n", object);
+
+ if (MOS_TYPE(type)) {
+ /*
+ * MOS objects are treated specially.
+ */
+ switch (type) {
+ case TYPE_MOS:
+ record->zi_type = 0;
+ break;
+ case TYPE_MOSDIR:
+ record->zi_type = DMU_OT_OBJECT_DIRECTORY;
+ break;
+ case TYPE_METASLAB:
+ record->zi_type = DMU_OT_OBJECT_ARRAY;
+ break;
+ case TYPE_CONFIG:
+ record->zi_type = DMU_OT_PACKED_NVLIST;
+ break;
+ case TYPE_BPLIST:
+ record->zi_type = DMU_OT_BPLIST;
+ break;
+ case TYPE_SPACEMAP:
+ record->zi_type = DMU_OT_SPACE_MAP;
+ break;
+ case TYPE_ERRLOG:
+ record->zi_type = DMU_OT_ERROR_LOG;
+ break;
+ }
+
+ dataset[0] = '\0';
+ (void) strcpy(poolname, object);
+ return (0);
+ }
+
+ /*
+ * Convert a full path into a (dataset, file) pair.
+ */
+ if (parse_pathname(object, dataset, path, &statbuf) != 0)
+ goto err;
+
+ ziprintf(" dataset: %s\n", dataset);
+ ziprintf(" path: %s\n", path);
+
+ /*
+ * Convert (dataset, file) into (objset, object)
+ */
+ if (object_from_path(dataset, path, &statbuf, record) != 0)
+ goto err;
+
+ ziprintf("raw objset: %llu\n", record->zi_objset);
+ ziprintf("raw object: %llu\n", record->zi_object);
+
+ /*
+ * For the given object, calculate the real (type, level, range)
+ */
+ if (calculate_range(dataset, type, level, (char *)range, record) != 0)
+ goto err;
+
+ ziprintf(" objset: %llu\n", record->zi_objset);
+ ziprintf(" object: %llu\n", record->zi_object);
+ if (record->zi_start == 0 &&
+ record->zi_end == -1ULL)
+ ziprintf(" range: all\n");
+ else
+ ziprintf(" range: [%llu, %llu]\n", record->zi_start,
+ record->zi_end);
+
+ /*
+ * Copy the pool name
+ */
+ (void) strcpy(poolname, dataset);
+ if ((slash = strchr(poolname, '/')) != NULL)
+ *slash = '\0';
+
+ ret = 0;
+
+err:
+ kernel_fini();
+ return (ret);
+}
+
+int
+translate_raw(const char *str, zinject_record_t *record)
+{
+ /*
+ * A raw bookmark of the form objset:object:level:blkid, where each
+ * number is a hexidecimal value.
+ */
+ if (sscanf(str, "%llx:%llx:%x:%llx", (u_longlong_t *)&record->zi_objset,
+ (u_longlong_t *)&record->zi_object, &record->zi_level,
+ (u_longlong_t *)&record->zi_start) != 4) {
+ (void) fprintf(stderr, "bad raw spec '%s': must be of the form "
+ "'objset:object:level:blkid'\n", str);
+ return (-1);
+ }
+
+ record->zi_end = record->zi_start;
+
+ return (0);
+}
+
+int
+translate_device(const char *pool, const char *device, err_type_t label_type,
+ zinject_record_t *record)
+{
+ char *end;
+ zpool_handle_t *zhp;
+ nvlist_t *tgt;
+ boolean_t isspare, iscache;
+
+ /*
+ * Given a device name or GUID, create an appropriate injection record
+ * with zi_guid set.
+ */
+ if ((zhp = zpool_open(g_zfs, pool)) == NULL)
+ return (-1);
+
+ record->zi_guid = strtoull(device, &end, 16);
+ if (record->zi_guid == 0 || *end != '\0') {
+ tgt = zpool_find_vdev(zhp, device, &isspare, &iscache, NULL);
+
+ if (tgt == NULL) {
+ (void) fprintf(stderr, "cannot find device '%s' in "
+ "pool '%s'\n", device, pool);
+ return (-1);
+ }
+
+ verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID,
+ &record->zi_guid) == 0);
+ }
+
+ switch (label_type) {
+ case TYPE_LABEL_UBERBLOCK:
+ record->zi_start = offsetof(vdev_label_t, vl_uberblock[0]);
+ record->zi_end = record->zi_start + VDEV_UBERBLOCK_RING - 1;
+ break;
+ case TYPE_LABEL_NVLIST:
+ record->zi_start = offsetof(vdev_label_t, vl_vdev_phys);
+ record->zi_end = record->zi_start + VDEV_PHYS_SIZE - 1;
+ break;
+ }
+ return (0);
+}
diff --git a/cddl/contrib/opensolaris/cmd/zinject/zinject.c b/cddl/contrib/opensolaris/cmd/zinject/zinject.c
new file mode 100644
index 0000000..2302dc4
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/zinject/zinject.c
@@ -0,0 +1,770 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL 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.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * ZFS Fault Injector
+ *
+ * This userland component takes a set of options and uses libzpool to translate
+ * from a user-visible object type and name to an internal representation.
+ * There are two basic types of faults: device faults and data faults.
+ *
+ *
+ * DEVICE FAULTS
+ *
+ * Errors can be injected into a particular vdev using the '-d' option. This
+ * option takes a path or vdev GUID to uniquely identify the device within a
+ * pool. There are two types of errors that can be injected, EIO and ENXIO,
+ * that can be controlled through the '-e' option. The default is ENXIO. For
+ * EIO failures, any attempt to read data from the device will return EIO, but
+ * subsequent attempt to reopen the device will succeed. For ENXIO failures,
+ * any attempt to read from the device will return EIO, but any attempt to
+ * reopen the device will also return ENXIO.
+ * For label faults, the -L option must be specified. This allows faults
+ * to be injected into either the nvlist or uberblock region of all the labels
+ * for the specified device.
+ *
+ * This form of the command looks like:
+ *
+ * zinject -d device [-e errno] [-L <uber | nvlist>] pool
+ *
+ *
+ * DATA FAULTS
+ *
+ * We begin with a tuple of the form:
+ *
+ * <type,level,range,object>
+ *
+ * type A string describing the type of data to target. Each type
+ * implicitly describes how to interpret 'object'. Currently,
+ * the following values are supported:
+ *
+ * data User data for a file
+ * dnode Dnode for a file or directory
+ *
+ * The following MOS objects are special. Instead of injecting
+ * errors on a particular object or blkid, we inject errors across
+ * all objects of the given type.
+ *
+ * mos Any data in the MOS
+ * mosdir object directory
+ * config pool configuration
+ * bplist blkptr list
+ * spacemap spacemap
+ * metaslab metaslab
+ * errlog persistent error log
+ *
+ * level Object level. Defaults to '0', not applicable to all types. If
+ * a range is given, this corresponds to the indirect block
+ * corresponding to the specific range.
+ *
+ * range A numerical range [start,end) within the object. Defaults to
+ * the full size of the file.
+ *
+ * object A string describing the logical location of the object. For
+ * files and directories (currently the only supported types),
+ * this is the path of the object on disk.
+ *
+ * This is translated, via libzpool, into the following internal representation:
+ *
+ * <type,objset,object,level,range>
+ *
+ * These types should be self-explanatory. This tuple is then passed to the
+ * kernel via a special ioctl() to initiate fault injection for the given
+ * object. Note that 'type' is not strictly necessary for fault injection, but
+ * is used when translating existing faults into a human-readable string.
+ *
+ *
+ * The command itself takes one of the forms:
+ *
+ * zinject
+ * zinject <-a | -u pool>
+ * zinject -c <id|all>
+ * zinject [-q] <-t type> [-f freq] [-u] [-a] [-m] [-e errno] [-l level]
+ * [-r range] <object>
+ * zinject [-f freq] [-a] [-m] [-u] -b objset:object:level:start:end pool
+ *
+ * With no arguments, the command prints all currently registered injection
+ * handlers, with their numeric identifiers.
+ *
+ * The '-c' option will clear the given handler, or all handlers if 'all' is
+ * specified.
+ *
+ * The '-e' option takes a string describing the errno to simulate. This must
+ * be either 'io' or 'checksum'. In most cases this will result in the same
+ * behavior, but RAID-Z will produce a different set of ereports for this
+ * situation.
+ *
+ * The '-a', '-u', and '-m' flags toggle internal flush behavior. If '-a' is
+ * specified, then the ARC cache is flushed appropriately. If '-u' is
+ * specified, then the underlying SPA is unloaded. Either of these flags can be
+ * specified independently of any other handlers. The '-m' flag automatically
+ * does an unmount and remount of the underlying dataset to aid in flushing the
+ * cache.
+ *
+ * The '-f' flag controls the frequency of errors injected, expressed as a
+ * integer percentage between 1 and 100. The default is 100.
+ *
+ * The this form is responsible for actually injecting the handler into the
+ * framework. It takes the arguments described above, translates them to the
+ * internal tuple using libzpool, and then issues an ioctl() to register the
+ * handler.
+ *
+ * The final form can target a specific bookmark, regardless of whether a
+ * human-readable interface has been designed. It allows developers to specify
+ * a particular block by number.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <sys/fs/zfs.h>
+#include <sys/mount.h>
+
+#include <libzfs.h>
+
+#undef verify /* both libzfs.h and zfs_context.h want to define this */
+
+#include "zinject.h"
+
+libzfs_handle_t *g_zfs;
+int zfs_fd;
+
+#ifndef ECKSUM
+#define ECKSUM EBADE
+#endif
+
+static const char *errtable[TYPE_INVAL] = {
+ "data",
+ "dnode",
+ "mos",
+ "mosdir",
+ "metaslab",
+ "config",
+ "bplist",
+ "spacemap",
+ "errlog",
+ "uber",
+ "nvlist"
+};
+
+static err_type_t
+name_to_type(const char *arg)
+{
+ int i;
+ for (i = 0; i < TYPE_INVAL; i++)
+ if (strcmp(errtable[i], arg) == 0)
+ return (i);
+
+ return (TYPE_INVAL);
+}
+
+static const char *
+type_to_name(uint64_t type)
+{
+ switch (type) {
+ case DMU_OT_OBJECT_DIRECTORY:
+ return ("mosdir");
+ case DMU_OT_OBJECT_ARRAY:
+ return ("metaslab");
+ case DMU_OT_PACKED_NVLIST:
+ return ("config");
+ case DMU_OT_BPLIST:
+ return ("bplist");
+ case DMU_OT_SPACE_MAP:
+ return ("spacemap");
+ case DMU_OT_ERROR_LOG:
+ return ("errlog");
+ default:
+ return ("-");
+ }
+}
+
+
+/*
+ * Print usage message.
+ */
+void
+usage(void)
+{
+ (void) printf(
+ "usage:\n"
+ "\n"
+ "\tzinject\n"
+ "\n"
+ "\t\tList all active injection records.\n"
+ "\n"
+ "\tzinject -c <id|all>\n"
+ "\n"
+ "\t\tClear the particular record (if given a numeric ID), or\n"
+ "\t\tall records if 'all' is specificed.\n"
+ "\n"
+ "\tzinject -d device [-e errno] [-L <nvlist|uber>] pool\n"
+ "\t\tInject a fault into a particular device or the device's\n"
+ "\t\tlabel. Label injection can either be 'nvlist' or 'uber'.\n"
+ "\t\t'errno' can either be 'nxio' (the default) or 'io'.\n"
+ "\n"
+ "\tzinject -b objset:object:level:blkid pool\n"
+ "\n"
+ "\t\tInject an error into pool 'pool' with the numeric bookmark\n"
+ "\t\tspecified by the remaining tuple. Each number is in\n"
+ "\t\thexidecimal, and only one block can be specified.\n"
+ "\n"
+ "\tzinject [-q] <-t type> [-e errno] [-l level] [-r range]\n"
+ "\t [-a] [-m] [-u] [-f freq] <object>\n"
+ "\n"
+ "\t\tInject an error into the object specified by the '-t' option\n"
+ "\t\tand the object descriptor. The 'object' parameter is\n"
+ "\t\tinterperted depending on the '-t' option.\n"
+ "\n"
+ "\t\t-q\tQuiet mode. Only print out the handler number added.\n"
+ "\t\t-e\tInject a specific error. Must be either 'io' or\n"
+ "\t\t\t'checksum'. Default is 'io'.\n"
+ "\t\t-l\tInject error at a particular block level. Default is "
+ "0.\n"
+ "\t\t-m\tAutomatically remount underlying filesystem.\n"
+ "\t\t-r\tInject error over a particular logical range of an\n"
+ "\t\t\tobject. Will be translated to the appropriate blkid\n"
+ "\t\t\trange according to the object's properties.\n"
+ "\t\t-a\tFlush the ARC cache. Can be specified without any\n"
+ "\t\t\tassociated object.\n"
+ "\t\t-u\tUnload the associated pool. Can be specified with only\n"
+ "\t\t\ta pool object.\n"
+ "\t\t-f\tOnly inject errors a fraction of the time. Expressed as\n"
+ "\t\t\ta percentage between 1 and 100.\n"
+ "\n"
+ "\t-t data\t\tInject an error into the plain file contents of a\n"
+ "\t\t\tfile. The object must be specified as a complete path\n"
+ "\t\t\tto a file on a ZFS filesystem.\n"
+ "\n"
+ "\t-t dnode\tInject an error into the metadnode in the block\n"
+ "\t\t\tcorresponding to the dnode for a file or directory. The\n"
+ "\t\t\t'-r' option is incompatible with this mode. The object\n"
+ "\t\t\tis specified as a complete path to a file or directory\n"
+ "\t\t\ton a ZFS filesystem.\n"
+ "\n"
+ "\t-t <mos>\tInject errors into the MOS for objects of the given\n"
+ "\t\t\ttype. Valid types are: mos, mosdir, config, bplist,\n"
+ "\t\t\tspacemap, metaslab, errlog. The only valid <object> is\n"
+ "\t\t\tthe poolname.\n");
+}
+
+static int
+iter_handlers(int (*func)(int, const char *, zinject_record_t *, void *),
+ void *data)
+{
+ zfs_cmd_t zc;
+ int ret;
+
+ zc.zc_guid = 0;
+
+ while (ioctl(zfs_fd, ZFS_IOC_INJECT_LIST_NEXT, &zc) == 0)
+ if ((ret = func((int)zc.zc_guid, zc.zc_name,
+ &zc.zc_inject_record, data)) != 0)
+ return (ret);
+
+ return (0);
+}
+
+static int
+print_data_handler(int id, const char *pool, zinject_record_t *record,
+ void *data)
+{
+ int *count = data;
+
+ if (record->zi_guid != 0)
+ return (0);
+
+ if (*count == 0) {
+ (void) printf("%3s %-15s %-6s %-6s %-8s %3s %-15s\n",
+ "ID", "POOL", "OBJSET", "OBJECT", "TYPE", "LVL", "RANGE");
+ (void) printf("--- --------------- ------ "
+ "------ -------- --- ---------------\n");
+ }
+
+ *count += 1;
+
+ (void) printf("%3d %-15s %-6llu %-6llu %-8s %3d ", id, pool,
+ (u_longlong_t)record->zi_objset, (u_longlong_t)record->zi_object,
+ type_to_name(record->zi_type), record->zi_level);
+
+ if (record->zi_start == 0 &&
+ record->zi_end == -1ULL)
+ (void) printf("all\n");
+ else
+ (void) printf("[%llu, %llu]\n", (u_longlong_t)record->zi_start,
+ (u_longlong_t)record->zi_end);
+
+ return (0);
+}
+
+static int
+print_device_handler(int id, const char *pool, zinject_record_t *record,
+ void *data)
+{
+ int *count = data;
+
+ if (record->zi_guid == 0)
+ return (0);
+
+ if (*count == 0) {
+ (void) printf("%3s %-15s %s\n", "ID", "POOL", "GUID");
+ (void) printf("--- --------------- ----------------\n");
+ }
+
+ *count += 1;
+
+ (void) printf("%3d %-15s %llx\n", id, pool,
+ (u_longlong_t)record->zi_guid);
+
+ return (0);
+}
+
+/*
+ * Print all registered error handlers. Returns the number of handlers
+ * registered.
+ */
+static int
+print_all_handlers(void)
+{
+ int count = 0;
+
+ (void) iter_handlers(print_device_handler, &count);
+ (void) printf("\n");
+ count = 0;
+ (void) iter_handlers(print_data_handler, &count);
+
+ return (count);
+}
+
+/* ARGSUSED */
+static int
+cancel_one_handler(int id, const char *pool, zinject_record_t *record,
+ void *data)
+{
+ zfs_cmd_t zc;
+
+ zc.zc_guid = (uint64_t)id;
+
+ if (ioctl(zfs_fd, ZFS_IOC_CLEAR_FAULT, &zc) != 0) {
+ (void) fprintf(stderr, "failed to remove handler %d: %s\n",
+ id, strerror(errno));
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Remove all fault injection handlers.
+ */
+static int
+cancel_all_handlers(void)
+{
+ int ret = iter_handlers(cancel_one_handler, NULL);
+
+ (void) printf("removed all registered handlers\n");
+
+ return (ret);
+}
+
+/*
+ * Remove a specific fault injection handler.
+ */
+static int
+cancel_handler(int id)
+{
+ zfs_cmd_t zc;
+
+ zc.zc_guid = (uint64_t)id;
+
+ if (ioctl(zfs_fd, ZFS_IOC_CLEAR_FAULT, &zc) != 0) {
+ (void) fprintf(stderr, "failed to remove handler %d: %s\n",
+ id, strerror(errno));
+ return (1);
+ }
+
+ (void) printf("removed handler %d\n", id);
+
+ return (0);
+}
+
+/*
+ * Register a new fault injection handler.
+ */
+static int
+register_handler(const char *pool, int flags, zinject_record_t *record,
+ int quiet)
+{
+ zfs_cmd_t zc;
+
+ (void) strcpy(zc.zc_name, pool);
+ zc.zc_inject_record = *record;
+ zc.zc_guid = flags;
+
+ if (ioctl(zfs_fd, ZFS_IOC_INJECT_FAULT, &zc) != 0) {
+ (void) fprintf(stderr, "failed to add handler: %s\n",
+ strerror(errno));
+ return (1);
+ }
+
+ if (flags & ZINJECT_NULL)
+ return (0);
+
+ if (quiet) {
+ (void) printf("%llu\n", (u_longlong_t)zc.zc_guid);
+ } else {
+ (void) printf("Added handler %llu with the following "
+ "properties:\n", (u_longlong_t)zc.zc_guid);
+ (void) printf(" pool: %s\n", pool);
+ if (record->zi_guid) {
+ (void) printf(" vdev: %llx\n",
+ (u_longlong_t)record->zi_guid);
+ } else {
+ (void) printf("objset: %llu\n",
+ (u_longlong_t)record->zi_objset);
+ (void) printf("object: %llu\n",
+ (u_longlong_t)record->zi_object);
+ (void) printf(" type: %llu\n",
+ (u_longlong_t)record->zi_type);
+ (void) printf(" level: %d\n", record->zi_level);
+ if (record->zi_start == 0 &&
+ record->zi_end == -1ULL)
+ (void) printf(" range: all\n");
+ else
+ (void) printf(" range: [%llu, %llu)\n",
+ (u_longlong_t)record->zi_start,
+ (u_longlong_t)record->zi_end);
+ }
+ }
+
+ return (0);
+}
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ char *range = NULL;
+ char *cancel = NULL;
+ char *end;
+ char *raw = NULL;
+ char *device = NULL;
+ int level = 0;
+ int quiet = 0;
+ int error = 0;
+ int domount = 0;
+ err_type_t type = TYPE_INVAL;
+ err_type_t label = TYPE_INVAL;
+ zinject_record_t record = { 0 };
+ char pool[MAXNAMELEN];
+ char dataset[MAXNAMELEN];
+ zfs_handle_t *zhp;
+ int ret;
+ int flags = 0;
+
+ if ((g_zfs = libzfs_init()) == NULL) {
+ (void) fprintf(stderr, "internal error: failed to "
+ "initialize ZFS library\n");
+ return (1);
+ }
+
+ libzfs_print_on_error(g_zfs, B_TRUE);
+
+ if ((zfs_fd = open(ZFS_DEV, O_RDWR)) < 0) {
+ (void) fprintf(stderr, "failed to open ZFS device\n");
+ return (1);
+ }
+
+ if (argc == 1) {
+ /*
+ * No arguments. Print the available handlers. If there are no
+ * available handlers, direct the user to '-h' for help
+ * information.
+ */
+ if (print_all_handlers() == 0) {
+ (void) printf("No handlers registered.\n");
+ (void) printf("Run 'zinject -h' for usage "
+ "information.\n");
+ }
+
+ return (0);
+ }
+
+ while ((c = getopt(argc, argv, ":ab:d:f:qhc:t:l:mr:e:uL:")) != -1) {
+ switch (c) {
+ case 'a':
+ flags |= ZINJECT_FLUSH_ARC;
+ break;
+ case 'b':
+ raw = optarg;
+ break;
+ case 'c':
+ cancel = optarg;
+ break;
+ case 'd':
+ device = optarg;
+ break;
+ case 'e':
+ if (strcasecmp(optarg, "io") == 0) {
+ error = EIO;
+ } else if (strcasecmp(optarg, "checksum") == 0) {
+ error = ECKSUM;
+ } else if (strcasecmp(optarg, "nxio") == 0) {
+ error = ENXIO;
+ } else {
+ (void) fprintf(stderr, "invalid error type "
+ "'%s': must be 'io', 'checksum' or "
+ "'nxio'\n", optarg);
+ usage();
+ return (1);
+ }
+ break;
+ case 'f':
+ record.zi_freq = atoi(optarg);
+ if (record.zi_freq < 1 || record.zi_freq > 100) {
+ (void) fprintf(stderr, "frequency range must "
+ "be in the range (0, 100]\n");
+ return (1);
+ }
+ break;
+ case 'h':
+ usage();
+ return (0);
+ case 'l':
+ level = (int)strtol(optarg, &end, 10);
+ if (*end != '\0') {
+ (void) fprintf(stderr, "invalid level '%s': "
+ "must be an integer\n", optarg);
+ usage();
+ return (1);
+ }
+ break;
+ case 'm':
+ domount = 1;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'r':
+ range = optarg;
+ break;
+ case 't':
+ if ((type = name_to_type(optarg)) == TYPE_INVAL &&
+ !MOS_TYPE(type)) {
+ (void) fprintf(stderr, "invalid type '%s'\n",
+ optarg);
+ usage();
+ return (1);
+ }
+ break;
+ case 'u':
+ flags |= ZINJECT_UNLOAD_SPA;
+ break;
+ case 'L':
+ if ((label = name_to_type(optarg)) == TYPE_INVAL &&
+ !LABEL_TYPE(type)) {
+ (void) fprintf(stderr, "invalid label type "
+ "'%s'\n", optarg);
+ usage();
+ return (1);
+ }
+ break;
+ case ':':
+ (void) fprintf(stderr, "option -%c requires an "
+ "operand\n", optopt);
+ usage();
+ return (1);
+ case '?':
+ (void) fprintf(stderr, "invalid option '%c'\n",
+ optopt);
+ usage();
+ return (2);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (cancel != NULL) {
+ /*
+ * '-c' is invalid with any other options.
+ */
+ if (raw != NULL || range != NULL || type != TYPE_INVAL ||
+ level != 0) {
+ (void) fprintf(stderr, "cancel (-c) incompatible with "
+ "any other options\n");
+ usage();
+ return (2);
+ }
+ if (argc != 0) {
+ (void) fprintf(stderr, "extraneous argument to '-c'\n");
+ usage();
+ return (2);
+ }
+
+ if (strcmp(cancel, "all") == 0) {
+ return (cancel_all_handlers());
+ } else {
+ int id = (int)strtol(cancel, &end, 10);
+ if (*end != '\0') {
+ (void) fprintf(stderr, "invalid handle id '%s':"
+ " must be an integer or 'all'\n", cancel);
+ usage();
+ return (1);
+ }
+ return (cancel_handler(id));
+ }
+ }
+
+ if (device != NULL) {
+ /*
+ * Device (-d) injection uses a completely different mechanism
+ * for doing injection, so handle it separately here.
+ */
+ if (raw != NULL || range != NULL || type != TYPE_INVAL ||
+ level != 0) {
+ (void) fprintf(stderr, "device (-d) incompatible with "
+ "data error injection\n");
+ usage();
+ return (2);
+ }
+
+ if (argc != 1) {
+ (void) fprintf(stderr, "device (-d) injection requires "
+ "a single pool name\n");
+ usage();
+ return (2);
+ }
+
+ (void) strcpy(pool, argv[0]);
+ dataset[0] = '\0';
+
+ if (error == ECKSUM) {
+ (void) fprintf(stderr, "device error type must be "
+ "'io' or 'nxio'\n");
+ return (1);
+ }
+
+ if (translate_device(pool, device, label, &record) != 0)
+ return (1);
+ if (!error)
+ error = ENXIO;
+ } else if (raw != NULL) {
+ if (range != NULL || type != TYPE_INVAL || level != 0) {
+ (void) fprintf(stderr, "raw (-b) format with "
+ "any other options\n");
+ usage();
+ return (2);
+ }
+
+ if (argc != 1) {
+ (void) fprintf(stderr, "raw (-b) format expects a "
+ "single pool name\n");
+ usage();
+ return (2);
+ }
+
+ (void) strcpy(pool, argv[0]);
+ dataset[0] = '\0';
+
+ if (error == ENXIO) {
+ (void) fprintf(stderr, "data error type must be "
+ "'checksum' or 'io'\n");
+ return (1);
+ }
+
+ if (translate_raw(raw, &record) != 0)
+ return (1);
+ if (!error)
+ error = EIO;
+ } else if (type == TYPE_INVAL) {
+ if (flags == 0) {
+ (void) fprintf(stderr, "at least one of '-b', '-d', "
+ "'-t', '-a', or '-u' must be specified\n");
+ usage();
+ return (2);
+ }
+
+ if (argc == 1 && (flags & ZINJECT_UNLOAD_SPA)) {
+ (void) strcpy(pool, argv[0]);
+ dataset[0] = '\0';
+ } else if (argc != 0) {
+ (void) fprintf(stderr, "extraneous argument for "
+ "'-f'\n");
+ usage();
+ return (2);
+ }
+
+ flags |= ZINJECT_NULL;
+ } else {
+ if (argc != 1) {
+ (void) fprintf(stderr, "missing object\n");
+ usage();
+ return (2);
+ }
+
+ if (error == ENXIO) {
+ (void) fprintf(stderr, "data error type must be "
+ "'checksum' or 'io'\n");
+ return (1);
+ }
+
+ if (translate_record(type, argv[0], range, level, &record, pool,
+ dataset) != 0)
+ return (1);
+ if (!error)
+ error = EIO;
+ }
+
+ /*
+ * If this is pool-wide metadata, unmount everything. The ioctl() will
+ * unload the pool, so that we trigger spa-wide reopen of metadata next
+ * time we access the pool.
+ */
+ if (dataset[0] != '\0' && domount) {
+ if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET)) == NULL)
+ return (1);
+
+ if (zfs_unmount(zhp, NULL, 0) != 0)
+ return (1);
+ }
+
+ record.zi_error = error;
+
+ ret = register_handler(pool, flags, &record, quiet);
+
+ if (dataset[0] != '\0' && domount)
+ ret = (zfs_mount(zhp, NULL, 0) != 0);
+
+ libzfs_fini(g_zfs);
+
+ return (ret);
+}
diff --git a/cddl/contrib/opensolaris/cmd/zinject/zinject.h b/cddl/contrib/opensolaris/cmd/zinject/zinject.h
new file mode 100644
index 0000000..adc3efe
--- /dev/null
+++ b/cddl/contrib/opensolaris/cmd/zinject/zinject.h
@@ -0,0 +1,71 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL 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.
+ */
+
+#ifndef _ZINJECT_H
+#define _ZINJECT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/zfs_ioctl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ TYPE_DATA, /* plain file contents */
+ TYPE_DNODE, /* metadnode contents */
+ TYPE_MOS, /* all MOS data */
+ TYPE_MOSDIR, /* MOS object directory */
+ TYPE_METASLAB, /* metaslab objects */
+ TYPE_CONFIG, /* MOS config */
+ TYPE_BPLIST, /* block pointer list */
+ TYPE_SPACEMAP, /* space map objects */
+ TYPE_ERRLOG, /* persistent error log */
+ TYPE_LABEL_UBERBLOCK, /* label specific uberblock */
+ TYPE_LABEL_NVLIST, /* label specific nvlist */
+ TYPE_INVAL
+} err_type_t;
+
+#define MOS_TYPE(t) \
+ ((t) >= TYPE_MOS && (t) < TYPE_LABEL_UBERBLOCK)
+
+#define LABEL_TYPE(t) \
+ ((t) >= TYPE_LABEL_UBERBLOCK && (t) < TYPE_INVAL)
+
+int translate_record(err_type_t type, const char *object, const char *range,
+ int level, zinject_record_t *record, char *poolname, char *dataset);
+int translate_raw(const char *raw, zinject_record_t *record);
+int translate_device(const char *pool, const char *device,
+ err_type_t label_type, zinject_record_t *record);
+void usage(void);
+
+extern libzfs_handle_t *g_zfs;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZINJECT_H */
diff --git a/cddl/contrib/opensolaris/cmd/zpool/zpool.8 b/cddl/contrib/opensolaris/cmd/zpool/zpool.8
index 95ae008..a7967d7 100644
--- a/cddl/contrib/opensolaris/cmd/zpool/zpool.8
+++ b/cddl/contrib/opensolaris/cmd/zpool/zpool.8
@@ -17,8 +17,8 @@
.\" information: Portions Copyright [yyyy] [name of copyright owner]
.\"
.\" CDDL HEADER END
-.\" Copyright (c) 2006, Sun Microsystems, Inc. All Rights Reserved.
-.TH zpool 1M "14 Nov 2006" "SunOS 5.11" "System Administration Commands"
+.\" Copyright (c) 2007, Sun Microsystems, Inc. All Rights Reserved.
+.TH zpool 1M "13 Nov 2007" "SunOS 5.11" "System Administration Commands"
.SH NAME
zpool \- configures ZFS storage pools
.SH SYNOPSIS
@@ -29,7 +29,8 @@ zpool \- configures ZFS storage pools
.LP
.nf
-\fBzpool create\fR [\fB-fn\fR] [\fB-R\fR \fIroot\fR] [\fB-m\fR \fImountpoint\fR] \fIpool\fR \fIvdev ...\fR
+\fBzpool create\fR [\fB-fn\fR] [\fB-o\fR \fIproperty=value\fR] ... [\fB-m\fR \fImountpoint\fR] [\fB-R\fR \fIroot\fR]
+ \fIpool\fR \fIvdev\fR ...
.fi
.LP
@@ -39,22 +40,22 @@ zpool \- configures ZFS storage pools
.LP
.nf
-\fBzpool add\fR [\fB-fn\fR] \fIpool\fR \fIvdev\fR
+\fBzpool add\fR [\fB-fn\fR] \fIpool\fR \fIvdev\fR ...
.fi
.LP
.nf
-\fBzpool remove\fR \fIpool\fR \fIvdev\fR
+\fBzpool remove\fR \fIpool\fR \fIdevice\fR ...
.fi
.LP
.nf
-\fBzpool \fR \fBlist\fR [\fB-H\fR] [\fB-o\fR \fIfield\fR[,\fIfield\fR]*] [\fIpool\fR] ...
+\fBzpool list\fR [\fB-H\fR] [\fB-o\fR \fIproperty\fR[,...]] [\fIpool\fR] ...
.fi
.LP
.nf
-\fBzpool iostat\fR [\fB-v\fR] [\fIpool\fR] ... [\fIinterval\fR [\fIcount\fR]]
+\fBzpool iostat\fR [\fB-v\fR] [\fIpool\fR] ... [\fIinterval\fR[\fIcount\fR]]
.fi
.LP
@@ -64,17 +65,17 @@ zpool \- configures ZFS storage pools
.LP
.nf
-\fBzpool offline\fR [\fB-t\fR] \fIpool\fR \fIdevice\fR ...
+\fBzpool online\fR \fIpool\fR \fIdevice\fR ...
.fi
.LP
.nf
-\fBzpool online\fR \fIpool\fR \fIdevice\fR ...
+\fBzpool offline\fR [\fB-t\fR] \fIpool\fR \fIdevice\fR ...
.fi
.LP
.nf
-\fBzpool clear\fR \fIpool\fR [\fIdevice\fR] ...
+\fBzpool clear\fR \fIpool\fR [\fIdevice\fR]
.fi
.LP
@@ -99,23 +100,24 @@ zpool \- configures ZFS storage pools
.LP
.nf
-\fBzpool export\fR [\fB-f\fR] \fIpool\fR
+\fBzpool import\fR [\fB-d\fR \fIdir\fR] [\fB-D\fR]
.fi
.LP
.nf
-\fBzpool import\fR [\fB-d\fR \fIdir\fR] [\fB-D\fR]
+\fBzpool import\fR [\fB-o \fImntopts\fR\fR] [\fB-p\fR \fIproperty=value\fR] ... [\fB-d\fR \fIdir\fR | \fB-c\fR \fIcachefile\fR]
+ [\fB-D\fR] [\fB-f\fR] [\fB-R\fR \fIroot\fR] \fB-a\fR
.fi
.LP
.nf
-\fBzpool import\fR [\fB-d\fR \fIdir\fR] [\fB-D\fR] [\fB-f\fR] [\fB-o \fIopts\fR\fR] [\fB-R \fR\fIroot\fR] \fIpool\fR | \fIid\fR
- [\fInewpool\fR]
+\fBzpool import\fR [\fB-o \fImntopts\fR\fR] [\fB-o\fR \fIproperty=value\fR] ... [\fB-d\fR \fIdir\fR | \fB-c\fR \fIcachefile\fR]
+ [\fB-D\fR] [\fB-f\fR] [\fB-R\fR \fIroot\fR] \fIpool\fR |\fIid\fR [\fInewpool\fR]
.fi
.LP
.nf
-\fBzpool import\fR [\fB-d\fR \fIdir\fR] [\fB-D\fR] [\fB-f\fR] [\fB-a\fR]
+\fBzpool export\fR [\fB-f\fR] \fIpool\fR ...
.fi
.LP
@@ -130,20 +132,33 @@ zpool \- configures ZFS storage pools
.LP
.nf
-\fBzpool upgrade\fR [\fB-a\fR | \fIpool\fR]
+\fBzpool upgrade\fR [\fB-V\fR \fIversion\fR] \fB-a\fR | \fIpool\fR ...
+.fi
+
+.LP
+.nf
+\fBzpool history\fR [\fB-il\fR] [\fIpool\fR] ...
+.fi
+
+.LP
+.nf
+\fBzpool get\fR "\fIall\fR" | \fIproperty\fR[,...] \fIpool\fR ...
.fi
.LP
.nf
-\fBzpool history\fR [\fIpool\fR] ...
+\fBzpool set\fR \fIproperty\fR=\fIvalue\fR \fIpool\fR
.fi
.SH DESCRIPTION
+.sp
.LP
The \fBzpool\fR command configures \fBZFS\fR storage pools. A storage pool is a collection of devices that provides physical storage and data replication for \fBZFS\fR datasets.
+.sp
.LP
All datasets within a storage pool share the same space. See \fBzfs\fR(1M) for information on managing datasets.
.SS "Virtual Devices (vdevs)"
+.sp
.LP
A "virtual device" describes a single device or a collection of devices organized according to certain performance and fault characteristics. The following virtual devices are supported:
.sp
@@ -202,8 +217,8 @@ A variation on \fBRAID-5\fR that allows for better distribution of parity and el
A \fBraidz\fR group can have either single- or double-parity, meaning that the \fBraidz\fR group can sustain one or two failures respectively without losing any data. The \fBraidz1\fR \fBvdev\fR type specifies a single-parity \fBraidz\fR group
and the \fBraidz2\fR \fBvdev\fR type specifies a double-parity \fBraidz\fR group. The \fBraidz\fR \fBvdev\fR type is an alias for \fBraidz1\fR.
.sp
-A \fBraidz\fR group with \fIN\fR disks of size \fIX\fR with \fIP\fR parity disks can hold approximately (\fIN-P\fR)*\fIX\fR bytes and can withstand one device failing before
-data integrity is compromised. The minimum number of devices in a \fBraidz\fR group is one more than the number of parity disks. The recommended number is between 3 and 9.
+A \fBraidz\fR group with \fIN\fR disks of size \fIX\fR with \fIP\fR parity disks can hold approximately (\fIN-P\fR)*\fIX\fR bytes and can withstand \fIP\fR device(s)
+failing before data integrity is compromised. The minimum number of devices in a \fBraidz\fR group is one more than the number of parity disks. The recommended number is between 3 and 9 to help increase performance.
.RE
.sp
@@ -217,11 +232,37 @@ data integrity is compromised. The minimum number of devices in a \fBraidz\fR gr
A special pseudo-\fBvdev\fR which keeps track of available hot spares for a pool. For more information, see the "Hot Spares" section.
.RE
+.sp
+.ne 2
+.mk
+.na
+\fBlog\fR
+.ad
+.RS 10n
+.rt
+A separate intent log device. If more than one log device is specified, then writes are load-balanced between devices. Log devices can be mirrored. However, \fBraidz\fR and \fBraidz2\fR are not supported for the intent log. For more information, see the "Intent
+Log" section.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fBcache\fR
+.ad
+.RS 10n
+.rt
+A device used to cache storage pool data. A cache device cannot be mirrored or part of a \fBraidz\fR or \fBraidz2\fR configuration. For more information, see the "Cache Devices" section.
+.RE
+
+.sp
.LP
Virtual devices cannot be nested, so a mirror or \fBraidz\fR virtual device can only contain files or disks. Mirrors of mirrors (or other combinations) are not allowed.
+.sp
.LP
A pool can have any number of virtual devices at the top of the configuration (known as "root vdevs"). Data is dynamically distributed across all top-level devices to balance data among devices. As new virtual devices are added, \fBZFS\fR automatically places data
on the newly available devices.
+.sp
.LP
Virtual devices are specified one at a time on the command line, separated by whitespace. The keywords "mirror" and "raidz" are used to distinguish where a group ends and another begins. For example, the following creates two root vdevs, each a mirror of two disks:
.sp
@@ -233,15 +274,119 @@ Virtual devices are specified one at a time on the command line, separated by wh
.sp
.SS "Device Failure and Recovery"
+.sp
.LP
\fBZFS\fR supports a rich set of mechanisms for handling device failure and data corruption. All metadata and data is checksummed, and \fBZFS\fR automatically repairs bad data from a good copy when corruption is detected.
+.sp
.LP
In order to take advantage of these features, a pool must make use of some form of redundancy, using either mirrored or \fBraidz\fR groups. While \fBZFS\fR supports running in a non-redundant configuration, where each root vdev is simply a disk or file, this is
strongly discouraged. A single case of bit corruption can render some or all of your data unavailable.
+.sp
.LP
A pool's health status is described by one of three states: online, degraded, or faulted. An online pool has all devices operating normally. A degraded pool is one in which one or more devices have failed, but the data is still available due to a redundant configuration. A faulted pool has
-one or more failed devices, and there is insufficient redundancy to replicate the missing data.
+corrupted metadata, or one or more faulted devices, and insufficient replicas to continue functioning.
+.sp
+.LP
+The health of the top-level vdev, such as mirror or \fBraidz\fR device, is potentially impacted by the state of its associated vdevs, or component devices. A top-level vdev or component device is in one of the following states:
+.sp
+.ne 2
+.mk
+.na
+\fB\fBDEGRADED\fR\fR
+.ad
+.RS 12n
+.rt
+One or more top-level vdevs is in the degraded state because one or more component devices are offline. Sufficient replicas exist to continue functioning.
+.sp
+One or more component devices is in the degraded or faulted state, but sufficient replicas exist to continue functioning. The underlying conditions are as follows:
+.RS +4
+.TP
+.ie t \(bu
+.el o
+The number of checksum errors exceeds acceptable levels and the device is degraded as an indication that something may be wrong. \fBZFS\fR continues to use the device as necessary.
+.RE
+.RS +4
+.TP
+.ie t \(bu
+.el o
+The number of I/O errors exceeds acceptable levels. The device could not be marked as faulted because there are insufficient replicas to continue functioning.
+.RE
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fBFAULTED\fR\fR
+.ad
+.RS 12n
+.rt
+One or more top-level vdevs is in the faulted state because one or more component devices are offline. Insufficient replicas exist to continue functioning.
+.sp
+One or more component devices is in the faulted state, and insufficient replicas exist to continue functioning. The underlying conditions are as follows:
+.RS +4
+.TP
+.ie t \(bu
+.el o
+The device could be opened, but the contents did not match expected values.
+.RE
+.RS +4
+.TP
+.ie t \(bu
+.el o
+The number of I/O errors exceeds acceptable levels and the device is faulted to prevent further use of the device.
+.RE
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fBOFFLINE\fR\fR
+.ad
+.RS 12n
+.rt
+The device was explicitly taken offline by the "\fBzpool offline\fR" command.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fBONLINE\fR\fR
+.ad
+.RS 12n
+.rt
+The device is online and functioning.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fBREMOVED\fR\fR
+.ad
+.RS 12n
+.rt
+The device was physically removed while the system was running. Device removal detection is hardware-dependent and may not be supported on all platforms.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fBUNAVAIL\fR\fR
+.ad
+.RS 12n
+.rt
+The device could not be opened. If a pool is imported when a device was unavailable, then the device will be identified by a unique identifier instead of its path since the path was never correct in the first place.
+.RE
+
+.sp
+.LP
+If a device is removed and later re-attached to the system, \fBZFS\fR attempts to put the device online automatically. Device attach detection is hardware-dependent and might not be supported on all platforms.
.SS "Hot Spares"
+.sp
.LP
\fBZFS\fR allows devices to be associated with pools as "hot spares". These devices are not actively used in the pool, but when an active device fails, it is automatically replaced by a hot spare. To create a pool with hot spares, specify a "spare" \fBvdev\fR with any number of devices. For example,
.sp
@@ -252,22 +397,261 @@ one or more failed devices, and there is insufficient redundancy to replicate th
.in -2
.sp
+.sp
.LP
-Spares can be shared across multiple pools, and can be added with the "zpool add" command and removed with the "zpool remove" command. Once a spare replacement is initiated, a new "spare" \fBvdev\fR is created within the configuration that
-will remain there until the original device is replaced. At this point, the hot spare becomes available again if another device fails.
+Spares can be shared across multiple pools, and can be added with the "\fBzpool add\fR" command and removed with the "\fBzpool remove\fR" command. Once a spare replacement is initiated, a new "spare" \fBvdev\fR is
+created within the configuration that will remain there until the original device is replaced. At this point, the hot spare becomes available again if another device fails.
+.sp
.LP
An in-progress spare replacement can be cancelled by detaching the hot spare. If the original faulted device is detached, then the hot spare assumes its place in the configuration, and is removed from the spare list of all active pools.
-.SS "Alternate Root Pools"
+.sp
+.LP
+Spares cannot replace log devices.
+.SS "Intent Log"
+.sp
+.LP
+The \fBZFS\fR Intent Log (\fBZIL\fR) satisfies \fBPOSIX\fR requirements for synchronous transactions. For instance, databases often require their transactions to be on stable storage devices when returning from a system call. \fBNFS\fR and
+other applications can also use \fBfsync\fR() to ensure data stability. By default, the intent log is allocated from blocks within the main pool. However, it might be possible to get better performance using separate intent log devices such as \fBNVRAM\fR or a dedicated
+disk. For example:
+.sp
+.in +2
+.nf
+\fB# zpool create pool c0d0 c1d0 log c2d0\fR
+.fi
+.in -2
+.sp
+
+.sp
+.LP
+Multiple log devices can also be specified, and they can be mirrored. See the EXAMPLES section for an example of mirroring multiple log devices.
+.sp
+.LP
+Log devices can be added, replaced, attached, detached, and imported and exported as part of the larger pool.
+.SS "Cache Devices"
+.sp
+.LP
+Devices can be added to a storage pool as "cache devices." These devices provide an additional layer of caching between main memory and disk. For read-heavy workloads, where the working set size is much larger than what can be cached in main memory, using cache devices allow
+much more of this working set to be served from low latency media. Using cache devices provides the greatest performance improvement for random read-workloads of mostly static content.
+.sp
+.LP
+To create a pool with cache devices, specify a "cache" \fBvdev\fR with any number of devices. For example:
+.sp
+.in +2
+.nf
+\fB# zpool create pool c0d0 c1d0 cache c2d0 c3d0\fR
+.fi
+.in -2
+.sp
+
+.sp
+.LP
+Cache devices cannot be mirrored or part of a \fBraidz\fR configuration. If a read error is encountered on a cache device, that read \fBI/O\fR is reissued to the original storage pool device, which might be part of a mirrored or \fBraidz\fR configuration.
+.sp
+.LP
+The content of the cache devices is considered volatile, as is the case with other system caches.
+.SS "Properties"
+.sp
+.LP
+Each pool has several properties associated with it. Some properties are read-only statistics while others are configurable and change the behavior of the pool. The following are read-only properties:
+.sp
+.ne 2
+.mk
+.na
+\fBavailable\fR
+.ad
+.RS 20n
+.rt
+Amount of storage available within the pool. This property can also be referred to by its shortened column name, "avail".
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fBcapacity\fR
+.ad
+.RS 20n
+.rt
+Percentage of pool space used. This property can also be referred to by its shortened column name, "cap".
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fBhealth\fR
+.ad
+.RS 20n
+.rt
+The current health of the pool. Health can be "\fBONLINE\fR", "\fBDEGRADED\fR", "\fBFAULTED\fR", " \fBOFFLINE\fR", "\fBREMOVED\fR", or "\fBUNAVAIL\fR".
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fBguid\fR
+.ad
+.RS 20n
+.rt
+A unique identifier for the pool.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fBsize\fR
+.ad
+.RS 20n
+.rt
+Total size of the storage pool.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fBused\fR
+.ad
+.RS 20n
+.rt
+Amount of storage space used within the pool.
+.RE
+
+.sp
.LP
-The "zpool create -R" and "zpool import -R" commands allow users to create and import a pool with a different root path. By default, whenever a pool is created or imported on a system, it is permanently added so that it is available whenever the system boots. For
-removable media, or when in recovery situations, this may not always be desirable. An alternate root pool does not persist on the system. Instead, it exists only until exported or the system is rebooted, at which point it will have to be imported again.
+These space usage properties report actual physical space available to the storage pool. The physical space can be different from the total amount of space that any contained datasets can actually use. The amount of space used in a \fBraidz\fR configuration depends on the characteristics
+of the data being written. In addition, \fBZFS\fR reserves some space for internal accounting that the \fBzfs\fR(1M) command takes into account, but the \fBzpool\fR command does not. For non-full pools of a reasonable size, these effects should be invisible. For small pools, or pools that are close to being completely full, these discrepancies may become more noticeable.
+.sp
.LP
-In addition, all mount points in the pool are prefixed with the given root, so a pool can be constrained to a particular area of the file system. This is most useful when importing unknown pools from removable media, as the mount points of any file systems cannot be trusted.
+The following property can be set at creation time and import time:
+.sp
+.ne 2
+.mk
+.na
+\fB\fBaltroot\fR\fR
+.ad
+.sp .6
+.RS 4n
+Alternate root directory. If set, this directory is prepended to any mount points within the pool. This can be used when examining an unknown pool where the mount points cannot be trusted, or in an alternate boot environment, where the typical paths are not valid. \fBaltroot\fR is
+not a persistent property. It is valid only while the system is up. Setting \fBaltroot\fR defaults to using \fBcachefile\fR=none, though this may be overridden using an explicit setting.
+.RE
+
+.sp
.LP
-When creating an alternate root pool, the default mount point is "/", rather than the normal default "/\fIpool\fR".
+The following properties can be set at creation time and import time, and later changed with the "\fBzpool set\fR" command:
+.sp
+.ne 2
+.mk
+.na
+\fB\fBautoreplace\fR=on | off\fR
+.ad
+.sp .6
+.RS 4n
+Controls automatic device replacement. If set to "\fBoff\fR", device replacement must be initiated by the administrator by using the "\fBzpool replace\fR" command. If set to "\fBon\fR", any new device, found
+in the same physical location as a device that previously belonged to the pool, is automatically formatted and replaced. The default behavior is "\fBoff\fR". This property can also be referred to by its shortened column name, "replace".
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fBbootfs\fR=\fIpool\fR/\fIdataset\fR\fR
+.ad
+.sp .6
+.RS 4n
+Identifies the default bootable dataset for the root pool. This property is expected to be set mainly by the installation and upgrade programs.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fBcachefile\fR=\fIpath\fR | "none"\fR
+.ad
+.sp .6
+.RS 4n
+Controls the location of where the pool configuration is cached. Discovering all pools on system startup requires a cached copy of the configuration data that is stored on the root file system. All pools in this cache are automatically imported when the system boots. Some environments,
+such as install and clustering, need to cache this information in a different location so that pools are not automatically imported. Setting this property caches the pool configuration in a different location that can later be imported with "\fBzpool import -c\fR". Setting
+it to the special value "\fBnone\fR" creates a temporary pool that is never cached, and the special value \fB\&''\fR (empty string) uses the default location.
+.sp
+Multiple pools can share the same cache file. Because the kernel destroys and recreates this file when pools are added and removed, care should be taken when attempting to access this file. When the last pool using a \fBcachefile\fR is exported or destroyed, the file is removed.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fBdelegation\fR=\fBon\fR | \fBoff\fR\fR
+.ad
+.sp .6
+.RS 4n
+Controls whether a non-privileged user is granted access based on the dataset permissions defined on the dataset. See \fBzfs\fR(1M) for more information
+on \fBZFS\fR delegated administration.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fBfailmode\fR=\fBwait\fR | \fBcontinue\fR | \fBpanic\fR\fR
+.ad
+.sp .6
+.RS 4n
+Controls the system behavior in the event of catastrophic pool failure. This condition is typically a result of a loss of connectivity to the underlying storage device(s) or a failure of all devices within the pool. The behavior of such an event is determined as follows:
+.sp
+.ne 2
+.mk
+.na
+\fBwait\fR
+.ad
+.RS 12n
+.rt
+Blocks all \fBI/O\fR access until the device connectivity is recovered and the errors are cleared. This is the default behavior.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fBcontinue\fR
+.ad
+.RS 12n
+.rt
+Returns \fBEIO\fR to any new write \fBI/O\fR requests but allows reads to any of the remaining healthy devices. Any write requests that have yet to be committed to disk would be blocked.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fBpanic\fR
+.ad
+.RS 12n
+.rt
+Prints out a message to the console and generates a system crash dump.
+.RE
+
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fBversion\fR=\fIversion\fR\fR
+.ad
+.sp .6
+.RS 4n
+The current on-disk version of the pool. This can be increased, but never decreased. The preferred method of updating pools is with the "\fBzpool upgrade\fR" command, though this property can be used when a specific version is needed for backwards compatibility.
+This property can be any number between 1 and the current version reported by "\fBzpool upgrade -v\fR". The special value "\fBcurrent\fR" is an alias for the latest supported version.
+.RE
+
.SS "Subcommands"
+.sp
.LP
All subcommands that modify state are logged persistently to the pool in their original form.
+.sp
.LP
The \fBzpool\fR command provides subcommands to create and destroy storage pools, add capacity to storage pools, and provide information about the storage pools. The following subcommands are supported:
.sp
@@ -285,12 +669,12 @@ Displays a help message.
.ne 2
.mk
.na
-\fB\fBzpool create\fR [\fB-fn\fR] [\fB-R\fR \fIroot\fR] [\fB-m\fR \fImountpoint\fR] \fIpool\fR \fIvdev ...\fR\fR
+\fB\fBzpool create\fR [\fB-fn\fR] [\fB-o\fR \fIproperty=value\fR] ... [\fB-m\fR \fImountpoint\fR] [\fB-R\fR \fIroot\fR] \fIpool\fR \fIvdev\fR ...\fR
.ad
.sp .6
.RS 4n
Creates a new storage pool containing the virtual devices specified on the command line. The pool name must begin with a letter, and can only contain alphanumeric characters as well as underscore ("_"), dash ("-"), and period ("."). The pool
-names "mirror", "raidz", and "spare" are reserved, as are names beginning with the pattern "c[0-9]". The \fBvdev\fR specification is described in the "Virtual Devices" section.
+names "mirror", "raidz", "spare" and "log" are reserved, as are names beginning with the pattern "c[0-9]". The \fBvdev\fR specification is described in the "Virtual Devices" section.
.sp
The command verifies that each device specified is accessible and not currently in use by another subsystem. There are some uses, such as being currently mounted, or specified as the dedicated dump device, that prevents a device from ever being used by \fBZFS\fR. Other uses,
such as having a preexisting \fBUFS\fR file system, can be overridden with the \fB-f\fR option.
@@ -305,8 +689,8 @@ Unless the \fB-R\fR option is specified, the default mount point is "/\fIpool\fR
.na
\fB\fB-f\fR\fR
.ad
-.RS 17n
-.rt
+.sp .6
+.RS 4n
Forces use of \fBvdev\fRs, even if they appear in use or specify a conflicting replication level. Not all devices can be overridden in this manner.
.RE
@@ -316,8 +700,8 @@ Forces use of \fBvdev\fRs, even if they appear in use or specify a conflicting r
.na
\fB\fB-n\fR\fR
.ad
-.RS 17n
-.rt
+.sp .6
+.RS 4n
Displays the configuration that would be used without actually creating the pool. The actual pool creation can still fail due to insufficient privileges or device sharing.
.RE
@@ -325,11 +709,22 @@ Displays the configuration that would be used without actually creating the pool
.ne 2
.mk
.na
+\fB\fB-o\fR \fIproperty=value\fR [\fB-o\fR \fIproperty=value\fR] ...\fR
+.ad
+.sp .6
+.RS 4n
+Sets the given pool properties. See the "Properties" section for a list of valid properties that can be set.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
\fB\fB-R\fR \fIroot\fR\fR
.ad
-.RS 17n
-.rt
-Creates the pool with an alternate \fIroot\fR. See the "Alternate Root Pools" section. The root dataset has its mount point set to "/" as part of this operation.
+.sp .6
+.RS 4n
+Equivalent to "-o cachefile=none,altroot=\fIroot\fR"
.RE
.sp
@@ -338,10 +733,9 @@ Creates the pool with an alternate \fIroot\fR. See the "Alternate Root Pools" se
.na
\fB\fB-m\fR \fImountpoint\fR\fR
.ad
-.RS 17n
-.rt
-Sets the mount point for the root dataset. The default mount point is "/\fIpool\fR". The mount point must be an absolute path, "\fBlegacy\fR", or "\fBnone\fR". For more information on dataset mount
-points, see \fBzfs\fR(1M).
+.sp .6
+.RS 4n
+Sets the mount point for the root dataset. The default mount point is "/\fIpool\fR" or "\fBaltroot\fR/\fIpool\fR" if \fBaltroot\fR is specified. The mount point must be an absolute path, "\fBlegacy\fR", or "\fBnone\fR". For more information on dataset mount points, see \fBzfs\fR(1M).
.RE
.RE
@@ -372,7 +766,7 @@ Forces any active datasets contained within the pool to be unmounted.
.ne 2
.mk
.na
-\fB\fBzpool add\fR [\fB-fn\fR] \fIpool\fR \fIvdev ...\fR\fR
+\fB\fBzpool add\fR [\fB-fn\fR] \fIpool\fR \fIvdev\fR ...\fR
.ad
.sp .6
.RS 4n
@@ -400,26 +794,26 @@ Forces use of \fBvdev\fRs, even if they appear in use or specify a conflicting r
Displays the configuration that would be used without actually adding the \fBvdev\fRs. The actual pool creation can still fail due to insufficient privileges or device sharing.
.RE
-Do not add a disk that is currently configured as a quorum device to a zpool. Once a disk is in a zpool, that disk can then be configured as a quorum device.
+Do not add a disk that is currently configured as a quorum device to a zpool. After a disk is in the pool, that disk can then be configured as a quorum device.
.RE
.sp
.ne 2
.mk
.na
-\fB\fBzpool remove\fR \fIpool\fR \fIvdev\fR\fR
+\fB\fBzpool remove\fR \fIpool\fR \fIdevice\fR ...\fR
.ad
.sp .6
.RS 4n
-Removes the given \fBvdev\fR from the pool. This command currently only supports removing hot spares. Devices which are part of a mirror can be removed using the "zpool detach" command. \fBRaidz\fR and top-level \fBvdevs\fR cannot
-be removed from a pool.
+Removes the specified device from the pool. This command currently only supports removing hot spares and cache devices. Devices that are part of a mirrored configuration can be removed using the "\fBzpool detach\fR" command. Non-redundant and \fBraidz\fR devices
+cannot be removed from a pool.
.RE
.sp
.ne 2
.mk
.na
-\fB\fBzpool list\fR [\fB-H\fR] [\fB-o\fR \fIfield\fR[,\fIfield*\fR]] [\fIpool\fR] ...\fR
+\fB\fBzpool list\fR [\fB-H\fR] [\fB-o\fR \fIprops\fR[,...]] [\fIpool\fR] ...\fR
.ad
.sp .6
.RS 4n
@@ -439,36 +833,20 @@ Scripted mode. Do not display headers, and separate fields by a single tab inste
.ne 2
.mk
.na
-\fB\fB-o\fR \fIfield\fR\fR
+\fB\fB-o\fR \fIprops\fR\fR
.ad
.RS 12n
.rt
-Comma-separated list of fields to display. Each field must be one of:
-.sp
-.in +2
-.nf
-name Pool name
-size Total size
-used Amount of space used
-available Amount of space available
-capacity Percentage of pool space used
-health Health status
-.fi
-.in -2
-.sp
-
-The default is all fields.
+Comma-separated list of properties to display. See the "Properties" section for a list of valid properties. The default list is "name, size, used, available, capacity, health, altroot"
.RE
-This command reports actual physical space available to the storage pool. The physical space can be different from the total amount of space that any contained datasets can actually use. The amount of space used in a \fBraidz\fR configuration depends on the characteristics of
-the data being written. In addition, \fBZFS\fR reserves some space for internal accounting that the \fBzfs\fR(1M) command takes into account, but the \fBzpool\fR command does not. For non-full pools of a reasonable size, these effects should be invisible. For small pools, or pools that are close to being completely full, these discrepancies may become more noticeable.
.RE
.sp
.ne 2
.mk
.na
-\fB\fBzpool iostat\fR [\fB-v\fR] [\fIpool\fR] ... [\fIinterval\fR [\fIcount\fR]]\fR
+\fB\fBzpool iostat\fR [\fB-v\fR] [\fIpool\fR] ... [\fIinterval\fR[\fIcount\fR]]\fR
.ad
.sp .6
.RS 4n
@@ -495,7 +873,7 @@ Verbose statistics. Reports usage statistics for individual \fIvdevs\fR within t
.ad
.sp .6
.RS 4n
-Displays the detailed health status for the given pools. If no \fIpool\fR is specified, then the status of each pool in the system is displayed.
+Displays the detailed health status for the given pools. If no \fIpool\fR is specified, then the status of each pool in the system is displayed. For more information on pool and device health, see the "Device Failure and Recovery" section.
.sp
If a scrub or resilver is in progress, this command reports the percentage done and the estimated time to completion. Both of these are only approximate, because the amount of data in the pool and the other workloads on the system can change.
.sp
@@ -526,13 +904,26 @@ Displays verbose data error information, printing out a complete list of all dat
.ne 2
.mk
.na
+\fB\fBzpool online\fR \fIpool\fR \fIdevice\fR ...\fR
+.ad
+.sp .6
+.RS 4n
+Brings the specified physical device online.
+.sp
+This command is not applicable to spares or cache devices.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
\fB\fBzpool offline\fR [\fB-t\fR] \fIpool\fR \fIdevice\fR ...\fR
.ad
.sp .6
.RS 4n
Takes the specified physical device offline. While the \fIdevice\fR is offline, no attempt is made to read or write to the device.
.sp
-This command is not applicable to spares.
+This command is not applicable to spares or cache devices.
.sp
.ne 2
.mk
@@ -550,19 +941,6 @@ Temporary. Upon reboot, the specified physical device reverts to its previous st
.ne 2
.mk
.na
-\fB\fBzpool online\fR \fIpool\fR \fIdevice\fR ...\fR
-.ad
-.sp .6
-.RS 4n
-Brings the specified physical device online.
-.sp
-This command is not applicable to spares.
-.RE
-
-.sp
-.ne 2
-.mk
-.na
\fB\fBzpool clear\fR \fIpool\fR [\fIdevice\fR] ...\fR
.ad
.sp .6
@@ -616,8 +994,8 @@ Replaces \fIold_device\fR with \fInew_device\fR. This is equivalent to attaching
.sp
The size of \fInew_device\fR must be greater than or equal to the minimum size of all the devices in a mirror or \fBraidz\fR configuration.
.sp
-If \fInew_device\fR is not specified, it defaults to \fIold_device\fR. This form of replacement is useful after an existing disk has failed and has been physically replaced. In this case, the new disk may have the same \fB/dev/dsk\fR path
-as the old device, even though it is actually a different disk. \fBZFS\fR recognizes this.
+\fInew_device\fR is required if the pool is not redundant. If \fInew_device\fR is not specified, it defaults to \fIold_device\fR. This form of replacement is useful after an existing disk has failed and has been physically replaced.
+In this case, the new disk may have the same \fB/dev/dsk\fR path as the old device, even though it is actually a different disk. \fBZFS\fR recognizes this.
.sp
.ne 2
.mk
@@ -663,24 +1041,45 @@ Stop scrubbing.
.ne 2
.mk
.na
-\fB\fBzpool export\fR [\fB-f\fR] \fIpool\fR ...\fR
+\fB\fBzpool import\fR [\fB-d\fR \fIdir\fR | \fB-c\fR \fIcachefile\fR] [\fB-D\fR]\fR
.ad
.sp .6
.RS 4n
-Exports the given pools from the system. All devices are marked as exported, but are still considered in use by other subsystems. The devices can be moved between systems (even those of different endianness) and imported as long as a sufficient number of devices are present.
+Lists pools available to import. If the \fB-d\fR option is not specified, this command searches for devices in "/dev/dsk". The \fB-d\fR option can be specified multiple times, and all directories are searched. If the device appears to be part of
+an exported pool, this command displays a summary of the pool with the name of the pool, a numeric identifier, as well as the \fIvdev\fR layout and current health of the device for each device or file. Destroyed pools, pools that were previously destroyed with the "\fBzpool destroy\fR" command, are not listed unless the \fB-D\fR option is specified.
.sp
-Before exporting the pool, all datasets within the pool are unmounted.
+The numeric identifier is unique, and can be used instead of the pool name when multiple exported pools of the same name are available.
.sp
-For pools to be portable, you must give the \fBzpool\fR command whole disks, not just slices, so that \fBZFS\fR can label the disks with portable \fBEFI\fR labels. Otherwise, disk drivers on platforms of different endianness will not recognize the disks.
+.ne 2
+.mk
+.na
+\fB\fB-c\fR \fIcachefile\fR\fR
+.ad
+.RS 16n
+.rt
+Reads configuration from the given \fBcachefile\fR that was created with the "\fBcachefile\fR" pool property. This \fBcachefile\fR is used instead of searching for devices.
+.RE
+
.sp
.ne 2
.mk
.na
-\fB\fB-f\fR\fR
+\fB\fB-d\fR \fIdir\fR\fR
.ad
-.RS 6n
+.RS 16n
.rt
-Forcefully unmount all datasets, using the "\fBunmount -f\fR" command.
+Searches for devices or files in \fIdir\fR. The \fB-d\fR option can be specified multiple times.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-D\fR\fR
+.ad
+.RS 16n
+.rt
+Lists destroyed pools only.
.RE
.RE
@@ -689,59 +1088,56 @@ Forcefully unmount all datasets, using the "\fBunmount -f\fR" command.
.ne 2
.mk
.na
-\fB\fBzpool import\fR [\fB-d\fR \fIdir\fR] [\fB-D\fR]\fR
+\fB\fBzpool import\fR [\fB-o\fR \fImntopts\fR] [ \fB-o\fR \fIproperty\fR=\fIvalue\fR] ... [\fB-d\fR \fIdir\fR | \fB-c\fR \fIcachefile\fR]
+[\fB-D\fR] [\fB-f\fR] [\fB-R\fR \fIroot\fR] \fB-a\fR\fR
.ad
.sp .6
.RS 4n
-Lists pools available to import. If the \fB-d\fR option is not specified, this command searches for devices in "/dev/dsk". The \fB-d\fR option can be specified multiple times, and all directories are searched. If the device appears to be part of
-an exported pool, this command displays a summary of the pool with the name of the pool, a numeric identifier, as well as the \fIvdev\fR layout and current health of the device for each device or file. Destroyed pools, pools that were previously destroyed with the "\fB-zpool destroy\fR" command, are not listed unless the \fB-D\fR option is specified.
-.sp
-The numeric identifier is unique, and can be used instead of the pool name when multiple exported pools of the same name are available.
+Imports all pools found in the search directories. Identical to the previous command, except that all pools with a sufficient number of devices available are imported. Destroyed pools, pools that were previously destroyed with the "\fBzpool destroy\fR"
+command, will not be imported unless the \fB-D\fR option is specified.
.sp
.ne 2
.mk
.na
-\fB\fB-d\fR \fIdir\fR\fR
+\fB\fB-o\fR \fImntopts\fR\fR
.ad
-.RS 10n
+.RS 21n
.rt
-Searches for devices or files in \fIdir\fR. The \fB-d\fR option can be specified multiple times.
+Comma-separated list of mount options to use when mounting datasets within the pool. See \fBzfs\fR(1M) for a description of dataset properties and mount
+options.
.RE
.sp
.ne 2
.mk
.na
-\fB\fB-D\fR\fR
+\fB\fB-o\fR \fIproperty=value\fR\fR
.ad
-.RS 10n
+.RS 21n
.rt
-Lists destroyed pools only.
-.RE
-
+Sets the specified property on the imported pool. See the "Properties" section for more information on the available pool properties.
.RE
.sp
.ne 2
.mk
.na
-\fB\fBzpool import\fR [\fB-d\fR \fIdir\fR] [\fB-D\fR] [\fB-f\fR] [\fB-o\fR \fIopts\fR] [\fB-R\fR \fIroot\fR] \fIpool\fR | \fIid\fR [\fInewpool\fR]\fR
+\fB\fB-c\fR \fIcachefile\fR\fR
.ad
-.sp .6
-.RS 4n
-Imports a specific pool. A pool can be identified by its name or the numeric identifier. If \fInewpool\fR is specified, the pool is imported using the name \fInewpool\fR. Otherwise, it is imported with the same name as its exported name.
-.sp
-If a device is removed from a system without running "\fBzpool export\fR" first, the device appears as potentially active. It cannot be determined if this was a failed export, or whether the device is really in use from another host. To import a pool in this state,
-the \fB-f\fR option is required.
+.RS 21n
+.rt
+Reads configuration from the given \fBcachefile\fR that was created with the "\fBcachefile\fR" pool property. This \fBcachefile\fR is used instead of searching for devices.
+.RE
+
.sp
.ne 2
.mk
.na
\fB\fB-d\fR \fIdir\fR\fR
.ad
-.RS 11n
+.RS 21n
.rt
-Searches for devices or files in \fIdir\fR. The \fB-d\fR option can be specified multiple times.
+Searches for devices or files in \fIdir\fR. The \fB-d\fR option can be specified multiple times. This option is incompatible with the \fB-c\fR option.
.RE
.sp
@@ -750,9 +1146,9 @@ Searches for devices or files in \fIdir\fR. The \fB-d\fR option can be specified
.na
\fB\fB-D\fR\fR
.ad
-.RS 11n
+.RS 21n
.rt
-Imports destroyed pool. The \fB-f\fR option is also required.
+Imports destroyed pools only. The \fB-f\fR option is also required.
.RE
.sp
@@ -761,7 +1157,7 @@ Imports destroyed pool. The \fB-f\fR option is also required.
.na
\fB\fB-f\fR\fR
.ad
-.RS 11n
+.RS 21n
.rt
Forces import, even if the pool appears to be potentially active.
.RE
@@ -770,12 +1166,11 @@ Forces import, even if the pool appears to be potentially active.
.ne 2
.mk
.na
-\fB\fB-o\fR \fIopts\fR\fR
+\fB\fB-a\fR\fR
.ad
-.RS 11n
+.RS 21n
.rt
-Comma-separated list of mount options to use when mounting datasets within the pool. See \fBzfs\fR(1M) for a description of dataset properties and mount
-options.
+Searches for and imports all pools found.
.RE
.sp
@@ -784,9 +1179,9 @@ options.
.na
\fB\fB-R\fR \fIroot\fR\fR
.ad
-.RS 11n
+.RS 21n
.rt
-Imports pool(s) with an alternate \fIroot\fR. See the "Alternate Root Pools" section.
+Sets the "\fBcachefile\fR" property to "\fBnone\fR" and the "\fIaltroot\fR" property to "\fIroot\fR".
.RE
.RE
@@ -795,21 +1190,58 @@ Imports pool(s) with an alternate \fIroot\fR. See the "Alternate Root Pools" sec
.ne 2
.mk
.na
-\fB\fBzpool import\fR [\fB-d\fR \fIdir\fR] [\fB-D\fR] [\fB-f\fR] [\fB-a\fR]\fR
+\fB\fBzpool import\fR [\fB-o\fR \fImntopts\fR] [ \fB-o\fR \fIproperty\fR=\fIvalue\fR] ... [\fB-d\fR \fIdir\fR | \fB-c\fR \fIcachefile\fR]
+[\fB-D\fR] [\fB-f\fR] [\fB-R\fR \fIroot\fR] \fIpool\fR | \fIid\fR [\fInewpool\fR]\fR
.ad
.sp .6
.RS 4n
-Imports all pools found in the search directories. Identical to the previous command, except that all pools with a sufficient number of devices available are imported. Destroyed pools, pools that were previously destroyed with the "\fB-zpool destroy\fR" command,
-will not be imported unless the \fB-D\fR option is specified.
+Imports a specific pool. A pool can be identified by its name or the numeric identifier. If \fInewpool\fR is specified, the pool is imported using the name \fInewpool\fR. Otherwise, it is imported with the same name as its exported name.
+.sp
+If a device is removed from a system without running "\fBzpool export\fR" first, the device appears as potentially active. It cannot be determined if this was a failed export, or whether the device is really in use from another host. To import a pool in this state,
+the \fB-f\fR option is required.
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-o\fR \fImntopts\fR\fR
+.ad
+.sp .6
+.RS 4n
+Comma-separated list of mount options to use when mounting datasets within the pool. See \fBzfs\fR(1M) for a description of dataset properties and mount
+options.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-o\fR \fIproperty=value\fR\fR
+.ad
+.sp .6
+.RS 4n
+Sets the specified property on the imported pool. See the "Properties" section for more information on the available pool properties.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-c\fR \fIcachefile\fR\fR
+.ad
+.sp .6
+.RS 4n
+Reads configuration from the given \fBcachefile\fR that was created with the "\fBcachefile\fR" pool property. This \fBcachefile\fR is used instead of searching for devices.
+.RE
+
.sp
.ne 2
.mk
.na
\fB\fB-d\fR \fIdir\fR\fR
.ad
-.RS 10n
-.rt
-Searches for devices or files in \fIdir\fR. The \fB-d\fR option can be specified multiple times.
+.sp .6
+.RS 4n
+Searches for devices or files in \fIdir\fR. The \fB-d\fR option can be specified multiple times. This option is incompatible with the \fB-c\fR option.
.RE
.sp
@@ -818,9 +1250,9 @@ Searches for devices or files in \fIdir\fR. The \fB-d\fR option can be specified
.na
\fB\fB-D\fR\fR
.ad
-.RS 10n
-.rt
-Imports destroyed pools only. The \fB-f\fR option is also required.
+.sp .6
+.RS 4n
+Imports destroyed pool. The \fB-f\fR option is also required.
.RE
.sp
@@ -829,11 +1261,48 @@ Imports destroyed pools only. The \fB-f\fR option is also required.
.na
\fB\fB-f\fR\fR
.ad
-.RS 10n
-.rt
+.sp .6
+.RS 4n
Forces import, even if the pool appears to be potentially active.
.RE
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-R\fR \fIroot\fR\fR
+.ad
+.sp .6
+.RS 4n
+Sets the "\fBcachefile\fR" property to "\fBnone\fR" and the "\fIaltroot\fR" property to "\fIroot\fR".
+.RE
+
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fBzpool export\fR [\fB-f\fR] \fIpool\fR ...\fR
+.ad
+.sp .6
+.RS 4n
+Exports the given pools from the system. All devices are marked as exported, but are still considered in use by other subsystems. The devices can be moved between systems (even those of different endianness) and imported as long as a sufficient number of devices are present.
+.sp
+Before exporting the pool, all datasets within the pool are unmounted.
+.sp
+For pools to be portable, you must give the \fBzpool\fR command whole disks, not just slices, so that \fBZFS\fR can label the disks with portable \fBEFI\fR labels. Otherwise, disk drivers on platforms of different endianness will not recognize the disks.
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-f\fR\fR
+.ad
+.RS 6n
+.rt
+Forcefully unmount all datasets, using the "\fBunmount -f\fR" command.
+.RE
+
.RE
.sp
@@ -856,14 +1325,14 @@ a more recent version are also displayed, although these pools will be inaccessi
.ad
.sp .6
.RS 4n
-Displays \fBZFS\fR versions supported by the current software. The current \fBZFS\fR versions and all previous supportedversions are displayed, along with an explanation of the features provided with each version.
+Displays \fBZFS\fR versions supported by the current software. The current \fBZFS\fR versions and all previous supported versions are displayed, along with an explanation of the features provided with each version.
.RE
.sp
.ne 2
.mk
.na
-\fB\fBzpool upgrade\fR [\fB-a\fR | \fIpool\fR]\fR
+\fB\fBzpool upgrade\fR [\fB-V\fR \fIversion\fR] \fB-a\fR | \fIpool\fR ...\fR
.ad
.sp .6
.RS 4n
@@ -874,27 +1343,95 @@ Upgrades the given pool to the latest on-disk version. Once this is done, the po
.na
\fB\fB-a\fR\fR
.ad
-.RS 6n
+.RS 14n
.rt
Upgrades all pools.
.RE
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-V\fR \fIversion\fR\fR
+.ad
+.RS 14n
+.rt
+Upgrade to the specified version. If the \fB-V\fR flag is not specified, the pool is upgraded to the most recent version. This option can only be used to increase the version number, and only up to the most recent version supported by this software.
+.RE
+
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fBzpool history\fR [\fB-il\fR] [\fIpool\fR] ...\fR
+.ad
+.sp .6
+.RS 4n
+Displays the command history of the specified pools or all pools if no pool is specified.
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-i\fR\fR
+.ad
+.RS 6n
+.rt
+Displays internally logged \fBZFS\fR events in addition to user initiated events.
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fB-l\fR\fR
+.ad
+.RS 6n
+.rt
+Displays log records in long format, which in addition to standard format includes, the user name, the hostname, and the zone in which the operation was performed.
+.RE
+
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB\fBzpool get\fR "\fIall\fR" | \fIproperty\fR[,...] \fIpool\fR ...\fR
+.ad
+.sp .6
+.RS 4n
+Retrieves the given list of properties (or all properties if "\fBall\fR" is used) for the specified storage pool(s). These properties are displayed with the following fields:
+.sp
+.in +2
+.nf
+ name Name of storage pool
+ property Property name
+ value Property value
+ source Property source, either 'default' or 'local'.
+.fi
+.in -2
+.sp
+
+See the "Properties" section for more information on the available pool properties.
.RE
.sp
.ne 2
.mk
.na
-\fB\fBzpool history\fR [\fIpool\fR] ...\fR
+\fB\fBzpool set\fR \fIproperty\fR=\fIvalue\fR \fIpool\fR \fR
.ad
.sp .6
.RS 4n
-Displays the command history of the specified pools (or all pools if no pool is specified).
+Sets the given property on the specified pool. See the "Properties" section for more information on what properties can be set and acceptable values.
.RE
.SH EXAMPLES
.LP
\fBExample 1 \fRCreating a RAID-Z Storage Pool
+.sp
.LP
The following command creates a pool with a single \fBraidz\fR root \fIvdev\fR that consists of six disks.
@@ -908,6 +1445,7 @@ The following command creates a pool with a single \fBraidz\fR root \fIvdev\fR t
.LP
\fBExample 2 \fRCreating a Mirrored Storage Pool
+.sp
.LP
The following command creates a pool with two mirrors, where each mirror contains two disks.
@@ -921,6 +1459,7 @@ The following command creates a pool with two mirrors, where each mirror contain
.LP
\fBExample 3 \fRCreating a ZFS Storage Pool by Using Slices
+.sp
.LP
The following command creates an unmirrored pool using two disk slices.
@@ -934,6 +1473,7 @@ The following command creates an unmirrored pool using two disk slices.
.LP
\fBExample 4 \fRCreating a ZFS Storage Pool by Using Files
+.sp
.LP
The following command creates an unmirrored pool using files. While not recommended, a pool based on files can be useful for experimental purposes.
@@ -947,6 +1487,7 @@ The following command creates an unmirrored pool using files. While not recommen
.LP
\fBExample 5 \fRAdding a Mirror to a ZFS Storage Pool
+.sp
.LP
The following command adds two mirrored disks to the pool "\fItank\fR", assuming the pool is already made up of two-way mirrors. The additional space is immediately available to any datasets within the pool.
@@ -960,9 +1501,11 @@ The following command adds two mirrored disks to the pool "\fItank\fR", assuming
.LP
\fBExample 6 \fRListing Available ZFS Storage Pools
+.sp
.LP
The following command lists all available pools on the system. In this case, the pool \fIzion\fR is faulted due to a missing device.
+.sp
.LP
The results from this command are similar to the following:
@@ -980,6 +1523,7 @@ The results from this command are similar to the following:
.LP
\fBExample 7 \fRDestroying a ZFS Storage Pool
+.sp
.LP
The following command destroys the pool "\fItank\fR" and any datasets contained within.
@@ -993,6 +1537,7 @@ The following command destroys the pool "\fItank\fR" and any datasets contained
.LP
\fBExample 8 \fRExporting a ZFS Storage Pool
+.sp
.LP
The following command exports the devices in pool \fItank\fR so that they can be relocated or later imported.
@@ -1006,9 +1551,11 @@ The following command exports the devices in pool \fItank\fR so that they can be
.LP
\fBExample 9 \fRImporting a ZFS Storage Pool
+.sp
.LP
The following command displays available pools, and then imports the pool "tank" for use on the system.
+.sp
.LP
The results from this command are similar to the following:
@@ -1034,6 +1581,7 @@ config:
.LP
\fBExample 10 \fRUpgrading All ZFS Storage Pools to the Current Version
+.sp
.LP
The following command upgrades all ZFS Storage pools to the current version of the software.
@@ -1048,6 +1596,7 @@ This system is currently running ZFS version 2.
.LP
\fBExample 11 \fRManaging Hot Spares
+.sp
.LP
The following command creates a new pool with an available hot spare:
@@ -1059,6 +1608,7 @@ The following command creates a new pool with an available hot spare:
.in -2
.sp
+.sp
.LP
If one of the disks were to fail, the pool would be reduced to the degraded state. The failed device can be replaced using the following command:
@@ -1070,8 +1620,9 @@ If one of the disks were to fail, the pool would be reduced to the degraded stat
.in -2
.sp
+.sp
.LP
-Once the data has been resilvered, the spare is automatically removed and is made available should another device fails. The hot spare can be permanently removed from the pool using the following command:
+Once the data has been resilvered, the spare is automatically removed and is made available should another device fails. The hot spare can be permanently removed from the pool using the following command:
.sp
.in +2
@@ -1081,7 +1632,48 @@ Once the data has been resilvered, the spare is automatically removed and is mad
.in -2
.sp
+.LP
+\fBExample 12 \fRCreating a ZFS Pool with Mirrored Separate Intent Logs
+.sp
+.LP
+The following command creates a ZFS storage pool consisting of two, two-way mirrors and mirrored log devices:
+
+.sp
+.in +2
+.nf
+\fB# zpool create pool mirror c0d0 c1d0 mirror c2d0 c3d0 log mirror \e
+ c4d0 c5d0\fR
+.fi
+.in -2
+.sp
+
+.LP
+\fBExample 13 \fRAdding Cache Devices to a ZFS Pool
+.sp
+.LP
+The following command adds two disks for use as cache devices to a ZFS storage pool:
+
+.sp
+.in +2
+.nf
+\fB# zpool add pool cache c2d0 c3d0\fR
+.fi
+.in -2
+.sp
+
+.sp
+.LP
+Once added, the cache devices gradually fill with content from main memory. Depending on the size of your cache devices, it could take over an hour for them to fill. Capacity and reads can be monitored using the \fBiostat\fR option as follows:
+.sp
+.in +2
+.nf
+\fB# zpool iostat -v pool 5\fR
+.fi
+.in -2
+.sp
+
.SH EXIT STATUS
+.sp
.LP
The following exit values are returned:
.sp
@@ -1118,6 +1710,7 @@ Invalid command line options were specified.
.RE
.SH ATTRIBUTES
+.sp
.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -1136,5 +1729,6 @@ Interface StabilityEvolving
.TE
.SH SEE ALSO
+.sp
.LP
\fBzfs\fR(1M), \fBattributes\fR(5)
diff --git a/cddl/contrib/opensolaris/cmd/zpool/zpool_iter.c b/cddl/contrib/opensolaris/cmd/zpool/zpool_iter.c
index f724179..6ba91b1 100644
--- a/cddl/contrib/opensolaris/cmd/zpool/zpool_iter.c
+++ b/cddl/contrib/opensolaris/cmd/zpool/zpool_iter.c
@@ -53,6 +53,7 @@ struct zpool_list {
boolean_t zl_findall;
uu_avl_t *zl_avl;
uu_avl_pool_t *zl_pool;
+ zprop_list_t **zl_proplist;
};
/* ARGSUSED */
@@ -81,6 +82,12 @@ add_pool(zpool_handle_t *zhp, void *data)
node->zn_handle = zhp;
uu_avl_node_init(node, &node->zn_avlnode, zlp->zl_pool);
if (uu_avl_find(zlp->zl_avl, node, NULL, &idx) == NULL) {
+ if (zlp->zl_proplist &&
+ zpool_expand_proplist(zhp, zlp->zl_proplist) != 0) {
+ zpool_close(zhp);
+ free(node);
+ return (-1);
+ }
uu_avl_insert(zlp->zl_avl, node, idx);
} else {
zpool_close(zhp);
@@ -98,7 +105,7 @@ add_pool(zpool_handle_t *zhp, void *data)
* line.
*/
zpool_list_t *
-pool_list_get(int argc, char **argv, zpool_proplist_t **proplist, int *err)
+pool_list_get(int argc, char **argv, zprop_list_t **proplist, int *err)
{
zpool_list_t *zlp;
@@ -114,6 +121,8 @@ pool_list_get(int argc, char **argv, zpool_proplist_t **proplist, int *err)
UU_DEFAULT)) == NULL)
zpool_no_memory();
+ zlp->zl_proplist = proplist;
+
if (argc == 0) {
(void) zpool_iter(g_zfs, add_pool, zlp);
zlp->zl_findall = B_TRUE;
@@ -123,13 +132,12 @@ pool_list_get(int argc, char **argv, zpool_proplist_t **proplist, int *err)
for (i = 0; i < argc; i++) {
zpool_handle_t *zhp;
- if ((zhp = zpool_open_canfail(g_zfs,
- argv[i])) != NULL && add_pool(zhp, zlp) == 0) {
- if (proplist &&
- zpool_expand_proplist(zhp, proplist) != 0)
+ if (zhp = zpool_open_canfail(g_zfs, argv[i])) {
+ if (add_pool(zhp, zlp) != 0)
*err = B_TRUE;
- } else
+ } else {
*err = B_TRUE;
+ }
}
}
@@ -228,7 +236,7 @@ pool_list_count(zpool_list_t *zlp)
*/
int
for_each_pool(int argc, char **argv, boolean_t unavail,
- zpool_proplist_t **proplist, zpool_iter_f func, void *data)
+ zprop_list_t **proplist, zpool_iter_f func, void *data)
{
zpool_list_t *list;
int ret = 0;
diff --git a/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c b/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c
index 5b1d856..2388df9 100644
--- a/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c
+++ b/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c
@@ -20,12 +20,10 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <solaris.h>
#include <assert.h>
#include <ctype.h>
@@ -42,6 +40,8 @@
#include <strings.h>
#include <unistd.h>
#include <priv.h>
+#include <pwd.h>
+#include <zone.h>
#include <sys/time.h>
#include <sys/fs/zfs.h>
@@ -50,6 +50,7 @@
#include <libzfs.h>
#include "zpool_util.h"
+#include "zfs_comutil.h"
static int zpool_do_create(int, char **);
static int zpool_do_destroy(int, char **);
@@ -85,6 +86,8 @@ static int zpool_do_set(int, char **);
* These libumem hooks provide a reasonable set of defaults for the allocator's
* debugging facilities.
*/
+
+#ifdef DEBUG
const char *
_umem_debug_init(void)
{
@@ -96,6 +99,7 @@ _umem_logging_init(void)
{
return ("fail,contents"); /* $UMEM_LOGGING setting */
}
+#endif
typedef enum {
HELP_ADD,
@@ -169,6 +173,7 @@ static zpool_command_t command_table[] = {
#define NCOMMAND (sizeof (command_table) / sizeof (command_table[0]))
zpool_command_t *current_command;
+static char history_str[HIS_MAX_RECORD_LEN];
static const char *
get_usage(zpool_help_t idx) {
@@ -177,12 +182,13 @@ get_usage(zpool_help_t idx) {
return (gettext("\tadd [-fn] <pool> <vdev> ...\n"));
case HELP_ATTACH:
return (gettext("\tattach [-f] <pool> <device> "
- "<new_device>\n"));
+ "<new-device>\n"));
case HELP_CLEAR:
return (gettext("\tclear <pool> [device]\n"));
case HELP_CREATE:
- return (gettext("\tcreate [-fn] [-R root] [-m mountpoint] "
- "<pool> <vdev> ...\n"));
+ return (gettext("\tcreate [-fn] [-o property=value] ... \n"
+ "\t [-O file-system-property=value] ... \n"
+ "\t [-m mountpoint] [-R root] <pool> <vdev> ...\n"));
case HELP_DESTROY:
return (gettext("\tdestroy [-f] <pool>\n"));
case HELP_DETACH:
@@ -190,17 +196,19 @@ get_usage(zpool_help_t idx) {
case HELP_EXPORT:
return (gettext("\texport [-f] <pool> ...\n"));
case HELP_HISTORY:
- return (gettext("\thistory [<pool>]\n"));
+ return (gettext("\thistory [-il] [<pool>] ...\n"));
case HELP_IMPORT:
return (gettext("\timport [-d dir] [-D]\n"
- "\timport [-d dir] [-D] [-f] [-o opts] [-R root] -a\n"
- "\timport [-d dir] [-D] [-f] [-o opts] [-R root ]"
- " <pool | id> [newpool]\n"));
+ "\timport [-o mntopts] [-o property=value] ... \n"
+ "\t [-d dir | -c cachefile] [-D] [-f] [-R root] -a\n"
+ "\timport [-o mntopts] [-o property=value] ... \n"
+ "\t [-d dir | -c cachefile] [-D] [-f] [-R root] "
+ "<pool | id> [newpool]\n"));
case HELP_IOSTAT:
return (gettext("\tiostat [-v] [pool] ... [interval "
"[count]]\n"));
case HELP_LIST:
- return (gettext("\tlist [-H] [-o field[,field]*] "
+ return (gettext("\tlist [-H] [-o property[,...]] "
"[pool] ...\n"));
case HELP_OFFLINE:
return (gettext("\toffline [-t] <pool> <device> ...\n"));
@@ -208,9 +216,9 @@ get_usage(zpool_help_t idx) {
return (gettext("\tonline <pool> <device> ...\n"));
case HELP_REPLACE:
return (gettext("\treplace [-f] <pool> <device> "
- "[new_device]\n"));
+ "[new-device]\n"));
case HELP_REMOVE:
- return (gettext("\tremove <pool> <device>\n"));
+ return (gettext("\tremove <pool> <device> ...\n"));
case HELP_SCRUB:
return (gettext("\tscrub [-s] <pool> ...\n"));
case HELP_STATUS:
@@ -218,9 +226,9 @@ get_usage(zpool_help_t idx) {
case HELP_UPGRADE:
return (gettext("\tupgrade\n"
"\tupgrade -v\n"
- "\tupgrade <-a | pool>\n"));
+ "\tupgrade [-V version] <-a | pool ...>\n"));
case HELP_GET:
- return (gettext("\tget <all | property[,property]...> "
+ return (gettext("\tget <\"all\" | property[,...]> "
"<pool> ...\n"));
case HELP_SET:
return (gettext("\tset <property=value> <pool> \n"));
@@ -230,67 +238,28 @@ get_usage(zpool_help_t idx) {
/* NOTREACHED */
}
-/*
- * Fields available for 'zpool list'.
- */
-typedef enum {
- ZPOOL_FIELD_NAME,
- ZPOOL_FIELD_SIZE,
- ZPOOL_FIELD_USED,
- ZPOOL_FIELD_AVAILABLE,
- ZPOOL_FIELD_CAPACITY,
- ZPOOL_FIELD_HEALTH,
- ZPOOL_FIELD_ROOT
-} zpool_field_t;
-
-#define MAX_FIELDS 10
-
-typedef struct column_def {
- const char *cd_title;
- size_t cd_width;
- enum {
- left_justify,
- right_justify
- } cd_justify;
-} column_def_t;
-
-static column_def_t column_table[] = {
- { "NAME", 20, left_justify },
- { "SIZE", 6, right_justify },
- { "USED", 6, right_justify },
- { "AVAIL", 6, right_justify },
- { "CAP", 5, right_justify },
- { "HEALTH", 9, left_justify },
- { "ALTROOT", 15, left_justify }
-};
-
-static char *column_subopts[] = {
- "name",
- "size",
- "used",
- "available",
- "capacity",
- "health",
- "root",
- NULL
-};
/*
* Callback routine that will print out a pool property value.
*/
-static zpool_prop_t
-print_prop_cb(zpool_prop_t prop, void *cb)
+static int
+print_prop_cb(int prop, void *cb)
{
FILE *fp = cb;
(void) fprintf(fp, "\t%-13s ", zpool_prop_to_name(prop));
+ if (zpool_prop_readonly(prop))
+ (void) fprintf(fp, " NO ");
+ else
+ (void) fprintf(fp, " YES ");
+
if (zpool_prop_values(prop) == NULL)
(void) fprintf(fp, "-\n");
else
(void) fprintf(fp, "%s\n", zpool_prop_values(prop));
- return (ZFS_PROP_CONT);
+ return (ZPROP_CONT);
}
/*
@@ -301,7 +270,6 @@ print_prop_cb(zpool_prop_t prop, void *cb)
void
usage(boolean_t requested)
{
- int i;
FILE *fp = requested ? stdout : stderr;
if (current_command == NULL) {
@@ -321,28 +289,22 @@ usage(boolean_t requested)
} else {
(void) fprintf(fp, gettext("usage:\n"));
(void) fprintf(fp, "%s", get_usage(current_command->usage));
-
- if (strcmp(current_command->name, "list") == 0) {
- (void) fprintf(fp, gettext("\nwhere 'field' is one "
- "of the following:\n\n"));
-
- for (i = 0; column_subopts[i] != NULL; i++)
- (void) fprintf(fp, "\t%s\n", column_subopts[i]);
- }
}
if (current_command != NULL &&
((strcmp(current_command->name, "set") == 0) ||
- (strcmp(current_command->name, "get") == 0))) {
+ (strcmp(current_command->name, "get") == 0) ||
+ (strcmp(current_command->name, "list") == 0))) {
(void) fprintf(fp,
gettext("\nthe following properties are supported:\n"));
- (void) fprintf(fp, "\n\t%-13s %s\n\n",
- "PROPERTY", "VALUES");
+ (void) fprintf(fp, "\n\t%-13s %s %s\n\n",
+ "PROPERTY", "EDIT", "VALUES");
/* Iterate over all properties */
- (void) zpool_prop_iter(print_prop_cb, fp, B_FALSE);
+ (void) zprop_iter(print_prop_cb, fp, B_FALSE, B_TRUE,
+ ZFS_TYPE_POOL);
}
/*
@@ -356,46 +318,9 @@ usage(boolean_t requested)
exit(requested ? 0 : 2);
}
-const char *
-state_to_health(int vs_state)
-{
- switch (vs_state) {
- case VDEV_STATE_CLOSED:
- case VDEV_STATE_CANT_OPEN:
- case VDEV_STATE_OFFLINE:
- return (dgettext(TEXT_DOMAIN, "FAULTED"));
- case VDEV_STATE_DEGRADED:
- return (dgettext(TEXT_DOMAIN, "DEGRADED"));
- case VDEV_STATE_HEALTHY:
- return (dgettext(TEXT_DOMAIN, "ONLINE"));
- }
-
- return (dgettext(TEXT_DOMAIN, "UNKNOWN"));
-}
-
-const char *
-state_to_name(vdev_stat_t *vs)
-{
- switch (vs->vs_state) {
- case VDEV_STATE_CLOSED:
- case VDEV_STATE_CANT_OPEN:
- if (vs->vs_aux == VDEV_AUX_CORRUPT_DATA)
- return (gettext("FAULTED"));
- else
- return (gettext("UNAVAIL"));
- case VDEV_STATE_OFFLINE:
- return (gettext("OFFLINE"));
- case VDEV_STATE_DEGRADED:
- return (gettext("DEGRADED"));
- case VDEV_STATE_HEALTHY:
- return (gettext("ONLINE"));
- }
-
- return (gettext("UNKNOWN"));
-}
-
void
-print_vdev_tree(zpool_handle_t *zhp, const char *name, nvlist_t *nv, int indent)
+print_vdev_tree(zpool_handle_t *zhp, const char *name, nvlist_t *nv, int indent,
+ boolean_t print_logs)
{
nvlist_t **child;
uint_t c, children;
@@ -409,13 +334,75 @@ print_vdev_tree(zpool_handle_t *zhp, const char *name, nvlist_t *nv, int indent)
return;
for (c = 0; c < children; c++) {
+ uint64_t is_log = B_FALSE;
+
+ (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG,
+ &is_log);
+ if ((is_log && !print_logs) || (!is_log && print_logs))
+ continue;
+
vname = zpool_vdev_name(g_zfs, zhp, child[c]);
- print_vdev_tree(zhp, vname, child[c], indent + 2);
+ print_vdev_tree(zhp, vname, child[c], indent + 2,
+ B_FALSE);
free(vname);
}
}
/*
+ * Add a property pair (name, string-value) into a property nvlist.
+ */
+static int
+add_prop_list(const char *propname, char *propval, nvlist_t **props,
+ boolean_t poolprop)
+{
+ zpool_prop_t prop = ZPROP_INVAL;
+ zfs_prop_t fprop;
+ nvlist_t *proplist;
+ const char *normnm;
+ char *strval;
+
+ if (*props == NULL &&
+ nvlist_alloc(props, NV_UNIQUE_NAME, 0) != 0) {
+ (void) fprintf(stderr,
+ gettext("internal error: out of memory\n"));
+ return (1);
+ }
+
+ proplist = *props;
+
+ if (poolprop) {
+ if ((prop = zpool_name_to_prop(propname)) == ZPROP_INVAL) {
+ (void) fprintf(stderr, gettext("property '%s' is "
+ "not a valid pool property\n"), propname);
+ return (2);
+ }
+ normnm = zpool_prop_to_name(prop);
+ } else {
+ if ((fprop = zfs_name_to_prop(propname)) == ZPROP_INVAL) {
+ (void) fprintf(stderr, gettext("property '%s' is "
+ "not a valid file system property\n"), propname);
+ return (2);
+ }
+ normnm = zfs_prop_to_name(fprop);
+ }
+
+ if (nvlist_lookup_string(proplist, normnm, &strval) == 0 &&
+ prop != ZPOOL_PROP_CACHEFILE) {
+ (void) fprintf(stderr, gettext("property '%s' "
+ "specified multiple times\n"), propname);
+ return (2);
+ }
+
+ if (nvlist_add_string(proplist, normnm, propval) != 0) {
+ (void) fprintf(stderr, gettext("internal "
+ "error: out of memory\n"));
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
* zpool add [-fn] <pool> <vdev> ...
*
* -f Force addition of devices, even if they appear in use
@@ -483,7 +470,8 @@ zpool_do_add(int argc, char **argv)
}
/* pass off to get_vdev_spec for processing */
- nvroot = make_root_vdev(config, force, !force, B_FALSE, argc, argv);
+ nvroot = make_root_vdev(zhp, force, !force, B_FALSE, dryrun,
+ argc, argv);
if (nvroot == NULL) {
zpool_close(zhp);
return (1);
@@ -498,16 +486,21 @@ zpool_do_add(int argc, char **argv)
(void) printf(gettext("would update '%s' to the following "
"configuration:\n"), zpool_get_name(zhp));
- print_vdev_tree(zhp, poolname, poolnvroot, 0);
- print_vdev_tree(zhp, NULL, nvroot, 0);
+ /* print original main pool and new tree */
+ print_vdev_tree(zhp, poolname, poolnvroot, 0, B_FALSE);
+ print_vdev_tree(zhp, NULL, nvroot, 0, B_FALSE);
+
+ /* Do the same for the logs */
+ if (num_logs(poolnvroot) > 0) {
+ print_vdev_tree(zhp, "logs", poolnvroot, 0, B_TRUE);
+ print_vdev_tree(zhp, NULL, nvroot, 0, B_TRUE);
+ } else if (num_logs(nvroot) > 0) {
+ print_vdev_tree(zhp, "logs", nvroot, 0, B_TRUE);
+ }
ret = 0;
} else {
ret = (zpool_add(zhp, nvroot) != 0);
- if (!ret) {
- zpool_log_history(g_zfs, argc + 1 + optind,
- argv - 1 - optind, poolname, B_TRUE, B_FALSE);
- }
}
nvlist_free(nvroot);
@@ -517,17 +510,17 @@ zpool_do_add(int argc, char **argv)
}
/*
- * zpool remove <pool> <vdev>
+ * zpool remove <pool> <vdev> ...
*
* Removes the given vdev from the pool. Currently, this only supports removing
- * spares from the pool. Eventually, we'll want to support removing leaf vdevs
- * (as an alias for 'detach') as well as toplevel vdevs.
+ * spares and cache devices from the pool. Eventually, we'll want to support
+ * removing leaf vdevs (as an alias for 'detach') as well as toplevel vdevs.
*/
int
zpool_do_remove(int argc, char **argv)
{
char *poolname;
- int ret;
+ int i, ret = 0;
zpool_handle_t *zhp;
argc--;
@@ -548,17 +541,18 @@ zpool_do_remove(int argc, char **argv)
if ((zhp = zpool_open(g_zfs, poolname)) == NULL)
return (1);
- ret = (zpool_vdev_remove(zhp, argv[1]) != 0);
- if (!ret) {
- zpool_log_history(g_zfs, ++argc, --argv, poolname, B_TRUE,
- B_FALSE);
+ for (i = 1; i < argc; i++) {
+ if (zpool_vdev_remove(zhp, argv[i]) != 0)
+ ret = 1;
}
return (ret);
}
/*
- * zpool create [-fn] [-R root] [-m mountpoint] <pool> <dev> ...
+ * zpool create [-fn] [-o property=value] ...
+ * [-O file-system-property=value] ...
+ * [-R root] [-m mountpoint] <pool> <dev> ...
*
* -f Force creation, even if devices appear in use
* -n Do not create the pool, but display the resulting layout if it
@@ -566,6 +560,8 @@ zpool_do_remove(int argc, char **argv)
* -R Create a pool under an alternate root
* -m Set default mountpoint for the root dataset. By default it's
* '/<pool>'
+ * -o Set property=value.
+ * -O Set fsproperty=value in the pool's root file system
*
* Creates the named pool according to the given vdev specification. The
* bulk of the vdev processing is done in get_vdev_spec() in zpool_vdev.c. Once
@@ -578,16 +574,17 @@ zpool_do_create(int argc, char **argv)
boolean_t force = B_FALSE;
boolean_t dryrun = B_FALSE;
int c;
- nvlist_t *nvroot;
+ nvlist_t *nvroot = NULL;
char *poolname;
- int ret;
+ int ret = 1;
char *altroot = NULL;
char *mountpoint = NULL;
- nvlist_t **child;
- uint_t children;
+ nvlist_t *fsprops = NULL;
+ nvlist_t *props = NULL;
+ char *propval;
/* check options */
- while ((c = getopt(argc, argv, ":fnR:m:")) != -1) {
+ while ((c = getopt(argc, argv, ":fnR:m:o:O:")) != -1) {
switch (c) {
case 'f':
force = B_TRUE;
@@ -597,19 +594,52 @@ zpool_do_create(int argc, char **argv)
break;
case 'R':
altroot = optarg;
+ if (add_prop_list(zpool_prop_to_name(
+ ZPOOL_PROP_ALTROOT), optarg, &props, B_TRUE))
+ goto errout;
+ if (nvlist_lookup_string(props,
+ zpool_prop_to_name(ZPOOL_PROP_CACHEFILE),
+ &propval) == 0)
+ break;
+ if (add_prop_list(zpool_prop_to_name(
+ ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE))
+ goto errout;
break;
case 'm':
mountpoint = optarg;
break;
+ case 'o':
+ if ((propval = strchr(optarg, '=')) == NULL) {
+ (void) fprintf(stderr, gettext("missing "
+ "'=' for -o option\n"));
+ goto errout;
+ }
+ *propval = '\0';
+ propval++;
+
+ if (add_prop_list(optarg, propval, &props, B_TRUE))
+ goto errout;
+ break;
+ case 'O':
+ if ((propval = strchr(optarg, '=')) == NULL) {
+ (void) fprintf(stderr, gettext("missing "
+ "'=' for -O option\n"));
+ goto errout;
+ }
+ *propval = '\0';
+ propval++;
+
+ if (add_prop_list(optarg, propval, &fsprops, B_FALSE))
+ goto errout;
+ break;
case ':':
(void) fprintf(stderr, gettext("missing argument for "
"'%c' option\n"), optopt);
- usage(B_FALSE);
- break;
+ goto badusage;
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
- usage(B_FALSE);
+ goto badusage;
}
}
@@ -619,11 +649,11 @@ zpool_do_create(int argc, char **argv)
/* get pool name and check number of arguments */
if (argc < 1) {
(void) fprintf(stderr, gettext("missing pool name argument\n"));
- usage(B_FALSE);
+ goto badusage;
}
if (argc < 2) {
(void) fprintf(stderr, gettext("missing vdev specification\n"));
- usage(B_FALSE);
+ goto badusage;
}
poolname = argv[0];
@@ -637,31 +667,28 @@ zpool_do_create(int argc, char **argv)
"character '/' in pool name\n"), poolname);
(void) fprintf(stderr, gettext("use 'zfs create' to "
"create a dataset\n"));
- return (1);
+ goto errout;
}
/* pass off to get_vdev_spec for bulk processing */
- nvroot = make_root_vdev(NULL, force, !force, B_FALSE, argc - 1,
- argv + 1);
+ nvroot = make_root_vdev(NULL, force, !force, B_FALSE, dryrun,
+ argc - 1, argv + 1);
if (nvroot == NULL)
- return (1);
+ goto errout;
/* make_root_vdev() allows 0 toplevel children if there are spares */
- verify(nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
- &child, &children) == 0);
- if (children == 0) {
+ if (!zfs_allocatable_devs(nvroot)) {
(void) fprintf(stderr, gettext("invalid vdev "
"specification: at least one toplevel vdev must be "
"specified\n"));
- return (1);
+ goto errout;
}
if (altroot != NULL && altroot[0] != '/') {
(void) fprintf(stderr, gettext("invalid alternate root '%s': "
"must be an absolute path\n"), altroot);
- nvlist_free(nvroot);
- return (1);
+ goto errout;
}
/*
@@ -672,14 +699,13 @@ zpool_do_create(int argc, char **argv)
(strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 &&
strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) != 0)) {
char buf[MAXPATHLEN];
- struct stat64 statbuf;
+ DIR *dirp;
if (mountpoint && mountpoint[0] != '/') {
(void) fprintf(stderr, gettext("invalid mountpoint "
"'%s': must be an absolute path, 'legacy', or "
"'none'\n"), mountpoint);
- nvlist_free(nvroot);
- return (1);
+ goto errout;
}
if (mountpoint == NULL) {
@@ -698,23 +724,30 @@ zpool_do_create(int argc, char **argv)
mountpoint);
}
- if (stat64(buf, &statbuf) == 0 &&
- statbuf.st_nlink != 2) {
- if (mountpoint == NULL)
- (void) fprintf(stderr, gettext("default "
- "mountpoint '%s' exists and is not "
- "empty\n"), buf);
- else
- (void) fprintf(stderr, gettext("mountpoint "
- "'%s' exists and is not empty\n"), buf);
+ if ((dirp = opendir(buf)) == NULL && errno != ENOENT) {
+ (void) fprintf(stderr, gettext("mountpoint '%s' : "
+ "%s\n"), buf, strerror(errno));
(void) fprintf(stderr, gettext("use '-m' "
"option to provide a different default\n"));
- nvlist_free(nvroot);
- return (1);
+ goto errout;
+ } else if (dirp) {
+ int count = 0;
+
+ while (count < 3 && readdir(dirp) != NULL)
+ count++;
+ (void) closedir(dirp);
+
+ if (count > 2) {
+ (void) fprintf(stderr, gettext("mountpoint "
+ "'%s' exists and is not empty\n"), buf);
+ (void) fprintf(stderr, gettext("use '-m' "
+ "option to provide a "
+ "different default\n"));
+ goto errout;
+ }
}
}
-
if (dryrun) {
/*
* For a dry run invocation, print out a basic message and run
@@ -724,15 +757,17 @@ zpool_do_create(int argc, char **argv)
(void) printf(gettext("would create '%s' with the "
"following layout:\n\n"), poolname);
- print_vdev_tree(NULL, poolname, nvroot, 0);
+ print_vdev_tree(NULL, poolname, nvroot, 0, B_FALSE);
+ if (num_logs(nvroot) > 0)
+ print_vdev_tree(NULL, "logs", nvroot, 0, B_TRUE);
ret = 0;
} else {
- ret = 1;
/*
* Hand off to libzfs.
*/
- if (zpool_create(g_zfs, poolname, nvroot, altroot) == 0) {
+ if (zpool_create(g_zfs, poolname,
+ nvroot, props, fsprops) == 0) {
zfs_handle_t *pool = zfs_open(g_zfs, poolname,
ZFS_TYPE_FILESYSTEM);
if (pool != NULL) {
@@ -742,20 +777,25 @@ zpool_do_create(int argc, char **argv)
ZFS_PROP_MOUNTPOINT),
mountpoint) == 0);
if (zfs_mount(pool, NULL, 0) == 0)
- ret = zfs_share_nfs(pool);
+ ret = zfs_shareall(pool);
zfs_close(pool);
}
- zpool_log_history(g_zfs, argc + optind, argv - optind,
- poolname, B_TRUE, B_TRUE);
} else if (libzfs_errno(g_zfs) == EZFS_INVALIDNAME) {
(void) fprintf(stderr, gettext("pool name may have "
"been omitted\n"));
}
}
+errout:
nvlist_free(nvroot);
-
+ nvlist_free(fsprops);
+ nvlist_free(props);
return (ret);
+badusage:
+ nvlist_free(fsprops);
+ nvlist_free(props);
+ usage(B_FALSE);
+ return (2);
}
/*
@@ -819,9 +859,6 @@ zpool_do_destroy(int argc, char **argv)
return (1);
}
- zpool_log_history(g_zfs, argc + optind, argv - optind, pool, B_TRUE,
- B_FALSE);
-
ret = (zpool_destroy(zhp) != 0);
zpool_close(zhp);
@@ -882,10 +919,7 @@ zpool_do_export(int argc, char **argv)
continue;
}
- zpool_log_history(g_zfs, argc + optind, argv - optind, argv[i],
- B_TRUE, B_FALSE);
-
- if (zpool_export(zhp) != 0)
+ if (zpool_export(zhp, force) != 0)
ret = 1;
zpool_close(zhp);
@@ -919,6 +953,14 @@ max_width(zpool_handle_t *zhp, nvlist_t *nv, int depth, int max)
max = ret;
}
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
+ &child, &children) == 0) {
+ for (c = 0; c < children; c++)
+ if ((ret = max_width(zhp, child[c], depth + 2,
+ max)) > max)
+ max = ret;
+ }
+
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
&child, &children) == 0) {
for (c = 0; c < children; c++)
@@ -937,7 +979,8 @@ max_width(zpool_handle_t *zhp, nvlist_t *nv, int depth, int max)
* pool, printing out the name and status for each one.
*/
void
-print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth)
+print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth,
+ boolean_t print_logs)
{
nvlist_t **child;
uint_t c, children;
@@ -952,9 +995,10 @@ print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth)
(uint64_t **)&vs, &c) == 0);
(void) printf("\t%*s%-*s", depth, "", namewidth - depth, name);
+ (void) printf(" %s", zpool_state_to_name(vs->vs_state, vs->vs_aux));
if (vs->vs_aux != 0) {
- (void) printf(" %-8s ", state_to_name(vs));
+ (void) printf(" ");
switch (vs->vs_aux) {
case VDEV_AUX_OPEN_FAILED:
@@ -973,12 +1017,14 @@ print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth)
(void) printf(gettext("newer version"));
break;
+ case VDEV_AUX_ERR_EXCEEDED:
+ (void) printf(gettext("too many errors"));
+ break;
+
default:
(void) printf(gettext("corrupted data"));
break;
}
- } else {
- (void) printf(" %s", state_to_name(vs));
}
(void) printf("\n");
@@ -987,21 +1033,37 @@ print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth)
return;
for (c = 0; c < children; c++) {
+ uint64_t is_log = B_FALSE;
+
+ (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG,
+ &is_log);
+ if ((is_log && !print_logs) || (!is_log && print_logs))
+ continue;
+
vname = zpool_vdev_name(g_zfs, NULL, child[c]);
print_import_config(vname, child[c],
- namewidth, depth + 2);
+ namewidth, depth + 2, B_FALSE);
free(vname);
}
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES,
- &child, &children) != 0)
- return;
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
+ &child, &children) == 0) {
+ (void) printf(gettext("\tcache\n"));
+ for (c = 0; c < children; c++) {
+ vname = zpool_vdev_name(g_zfs, NULL, child[c]);
+ (void) printf("\t %s\n", vname);
+ free(vname);
+ }
+ }
- (void) printf(gettext("\tspares\n"));
- for (c = 0; c < children; c++) {
- vname = zpool_vdev_name(g_zfs, NULL, child[c]);
- (void) printf("\t %s\n", vname);
- free(vname);
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES,
+ &child, &children) == 0) {
+ (void) printf(gettext("\tspares\n"));
+ for (c = 0; c < children; c++) {
+ vname = zpool_vdev_name(g_zfs, NULL, child[c]);
+ (void) printf("\t %s\n", vname);
+ free(vname);
+ }
}
}
@@ -1033,7 +1095,7 @@ show_import(nvlist_t *config)
verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS,
(uint64_t **)&vs, &vsc) == 0);
- health = state_to_health(vs->vs_state);
+ health = zpool_state_to_name(vs->vs_state, vs->vs_aux);
reason = zpool_import_status(config, &msgid);
@@ -1086,6 +1148,18 @@ show_import(nvlist_t *config)
(void) printf(gettext("status: The pool was last accessed by "
"another system.\n"));
break;
+
+ case ZPOOL_STATUS_FAULTED_DEV_R:
+ case ZPOOL_STATUS_FAULTED_DEV_NR:
+ (void) printf(gettext("status: One or more devices are "
+ "faulted.\n"));
+ break;
+
+ case ZPOOL_STATUS_BAD_LOG:
+ (void) printf(gettext("status: An intent log record cannot be "
+ "read.\n"));
+ break;
+
default:
/*
* No other status can be seen when importing pools.
@@ -1147,7 +1221,7 @@ show_import(nvlist_t *config)
"but can be imported using the '-Df' flags.\n"));
else if (pool_state != POOL_STATE_EXPORTED)
(void) printf(gettext("\tThe pool may be active on "
- "on another system, but can be imported using\n\t"
+ "another system, but can be imported using\n\t"
"the '-f' flag.\n"));
}
@@ -1160,7 +1234,12 @@ show_import(nvlist_t *config)
namewidth = max_width(NULL, nvroot, 0, 0);
if (namewidth < 10)
namewidth = 10;
- print_import_config(name, nvroot, namewidth, 0);
+
+ print_import_config(name, nvroot, namewidth, 0, B_FALSE);
+ if (num_logs(nvroot) > 0) {
+ (void) printf(gettext("\tlogs\n"));
+ print_import_config(name, nvroot, namewidth, 0, B_TRUE);
+ }
if (reason == ZPOOL_STATUS_BAD_GUID_SUM) {
(void) printf(gettext("\n\tAdditional devices are known to "
@@ -1171,17 +1250,18 @@ show_import(nvlist_t *config)
/*
* Perform the import for the given configuration. This passes the heavy
- * lifting off to zpool_import(), and then mounts the datasets contained within
- * the pool.
+ * lifting off to zpool_import_props(), and then mounts the datasets contained
+ * within the pool.
*/
static int
do_import(nvlist_t *config, const char *newname, const char *mntopts,
- const char *altroot, int force, int argc, char **argv)
+ int force, nvlist_t *props, boolean_t allowfaulted)
{
zpool_handle_t *zhp;
char *name;
uint64_t state;
uint64_t version;
+ int error = 0;
verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
&name) == 0);
@@ -1190,7 +1270,7 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
ZPOOL_CONFIG_POOL_STATE, &state) == 0);
verify(nvlist_lookup_uint64(config,
ZPOOL_CONFIG_VERSION, &version) == 0);
- if (version > ZFS_VERSION) {
+ if (version > SPA_VERSION) {
(void) fprintf(stderr, gettext("cannot import '%s': pool "
"is formatted using a newer ZFS version\n"), name);
return (1);
@@ -1228,15 +1308,14 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
}
}
- if (zpool_import(g_zfs, config, newname, altroot) != 0)
+ if (zpool_import_props(g_zfs, config, newname, props,
+ allowfaulted) != 0)
return (1);
if (newname != NULL)
name = (char *)newname;
- zpool_log_history(g_zfs, argc, argv, name, B_TRUE, B_FALSE);
-
- verify((zhp = zpool_open(g_zfs, name)) != NULL);
+ verify((zhp = zpool_open_canfail(g_zfs, name)) != NULL);
if (zpool_enable_datasets(zhp, mntopts, 0) != 0) {
zpool_close(zhp);
@@ -1244,13 +1323,18 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
}
zpool_close(zhp);
- return (0);
+ return (error);
}
/*
* zpool import [-d dir] [-D]
- * import [-R root] [-D] [-d dir] [-f] -a
- * import [-R root] [-D] [-d dir] [-f] <pool | id> [newpool]
+ * import [-o mntopts] [-o prop=value] ... [-R root] [-D]
+ * [-d dir | -c cachefile] [-f] -a
+ * import [-o mntopts] [-o prop=value] ... [-R root] [-D]
+ * [-d dir | -c cachefile] [-f] <pool | id> [newpool]
+ *
+ * -c Read pool information from a cachefile instead of searching
+ * devices.
*
* -d Scan in a specific directory, other than /dev/dsk. More than
* one directory can be specified using multiple '-d' options.
@@ -1264,8 +1348,15 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
*
* -f Force import, even if it appears that the pool is active.
*
+ * -F Import even in the presence of faulted vdevs. This is an
+ * intentionally undocumented option for testing purposes, and
+ * treats the pool configuration as complete, leaving any bad
+ * vdevs in the FAULTED state.
+ *
* -a Import all pools found.
*
+ * -o Set property=value and/or temporary mount options (without '=').
+ *
* The import command scans for pools to import, and import pools based on pool
* name and GUID. The pool can also be renamed as part of the import process.
*/
@@ -1276,26 +1367,32 @@ zpool_do_import(int argc, char **argv)
int nsearch = 0;
int c;
int err;
- nvlist_t *pools;
+ nvlist_t *pools = NULL;
boolean_t do_all = B_FALSE;
boolean_t do_destroyed = B_FALSE;
- char *altroot = NULL;
char *mntopts = NULL;
boolean_t do_force = B_FALSE;
nvpair_t *elem;
nvlist_t *config;
- uint64_t searchguid;
- char *searchname;
+ uint64_t searchguid = 0;
+ char *searchname = NULL;
+ char *propval;
nvlist_t *found_config;
+ nvlist_t *props = NULL;
boolean_t first;
+ boolean_t allow_faulted = B_FALSE;
uint64_t pool_state;
+ char *cachefile = NULL;
/* check options */
- while ((c = getopt(argc, argv, ":Dfd:R:ao:")) != -1) {
+ while ((c = getopt(argc, argv, ":ac:d:DfFo:p:R:")) != -1) {
switch (c) {
case 'a':
do_all = B_TRUE;
break;
+ case 'c':
+ cachefile = optarg;
+ break;
case 'd':
if (searchdirs == NULL) {
searchdirs = safe_malloc(sizeof (char *));
@@ -1315,11 +1412,31 @@ zpool_do_import(int argc, char **argv)
case 'f':
do_force = B_TRUE;
break;
+ case 'F':
+ allow_faulted = B_TRUE;
+ break;
case 'o':
- mntopts = optarg;
+ if ((propval = strchr(optarg, '=')) != NULL) {
+ *propval = '\0';
+ propval++;
+ if (add_prop_list(optarg, propval,
+ &props, B_TRUE))
+ goto error;
+ } else {
+ mntopts = optarg;
+ }
break;
case 'R':
- altroot = optarg;
+ if (add_prop_list(zpool_prop_to_name(
+ ZPOOL_PROP_ALTROOT), optarg, &props, B_TRUE))
+ goto error;
+ if (nvlist_lookup_string(props,
+ zpool_prop_to_name(ZPOOL_PROP_CACHEFILE),
+ &propval) == 0)
+ break;
+ if (add_prop_list(zpool_prop_to_name(
+ ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE))
+ goto error;
break;
case ':':
(void) fprintf(stderr, gettext("missing argument for "
@@ -1336,9 +1453,14 @@ zpool_do_import(int argc, char **argv)
argc -= optind;
argv += optind;
+ if (cachefile && nsearch != 0) {
+ (void) fprintf(stderr, gettext("-c is incompatible with -d\n"));
+ usage(B_FALSE);
+ }
+
if (searchdirs == NULL) {
searchdirs = safe_malloc(sizeof (char *));
- searchdirs[0] = "/dev";
+ searchdirs[0] = "/dev/dsk";
nsearch = 1;
}
@@ -1367,13 +1489,7 @@ zpool_do_import(int argc, char **argv)
}
}
- if ((pools = zpool_find_import(g_zfs, nsearch, searchdirs)) == NULL) {
- free(searchdirs);
- return (1);
- }
-
/*
- * We now have a list of all available pools in the given directories.
* Depending on the arguments given, we do one of the following:
*
* <none> Iterate through all pools and display information about
@@ -1393,11 +1509,38 @@ zpool_do_import(int argc, char **argv)
searchguid = strtoull(argv[0], &endptr, 10);
if (errno != 0 || *endptr != '\0')
searchname = argv[0];
- else
- searchname = NULL;
found_config = NULL;
}
+ if (cachefile) {
+ pools = zpool_find_import_cached(g_zfs, cachefile, searchname,
+ searchguid);
+ } else if (searchname != NULL) {
+ pools = zpool_find_import_byname(g_zfs, nsearch, searchdirs,
+ searchname);
+ } else {
+ /*
+ * It's OK to search by guid even if searchguid is 0.
+ */
+ pools = zpool_find_import_byguid(g_zfs, nsearch, searchdirs,
+ searchguid);
+ }
+
+ if (pools == NULL) {
+ if (argc != 0) {
+ (void) fprintf(stderr, gettext("cannot import '%s': "
+ "no such pool available\n"), argv[0]);
+ }
+ free(searchdirs);
+ return (1);
+ }
+
+ /*
+ * At this point we have a list of import candidate configs. Even if
+ * we were searching by pool name or guid, we still need to
+ * post-process the list to deal with pool state and possible
+ * duplicate names.
+ */
err = 0;
elem = NULL;
first = B_TRUE;
@@ -1420,8 +1563,7 @@ zpool_do_import(int argc, char **argv)
if (do_all)
err |= do_import(config, NULL, mntopts,
- altroot, do_force, argc + optind,
- argv - optind);
+ do_force, props, allow_faulted);
else
show_import(config);
} else if (searchname != NULL) {
@@ -1469,8 +1611,7 @@ zpool_do_import(int argc, char **argv)
err = B_TRUE;
} else {
err |= do_import(found_config, argc == 1 ? NULL :
- argv[1], mntopts, altroot, do_force, argc + optind,
- argv - optind);
+ argv[1], mntopts, do_force, props, allow_faulted);
}
}
@@ -1482,6 +1623,8 @@ zpool_do_import(int argc, char **argv)
(void) fprintf(stderr,
gettext("no pools available to import\n"));
+error:
+ nvlist_free(props);
nvlist_free(pools);
free(searchdirs);
@@ -1518,7 +1661,7 @@ print_iostat_header(iostat_cbdata_t *cb)
/*
* Display a single statistic.
*/
-void
+static void
print_one_stat(uint64_t value)
{
char buf[64];
@@ -1606,6 +1749,28 @@ print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv,
newchild[c], cb, depth + 2);
free(vname);
}
+
+ /*
+ * Include level 2 ARC devices in iostat output
+ */
+ if (nvlist_lookup_nvlist_array(newnv, ZPOOL_CONFIG_L2CACHE,
+ &newchild, &children) != 0)
+ return;
+
+ if (oldnv && nvlist_lookup_nvlist_array(oldnv, ZPOOL_CONFIG_L2CACHE,
+ &oldchild, &c) != 0)
+ return;
+
+ if (children > 0) {
+ (void) printf("%-*s - - - - - "
+ "-\n", cb->cb_namewidth, "cache");
+ for (c = 0; c < children; c++) {
+ vname = zpool_vdev_name(g_zfs, zhp, newchild[c]);
+ print_vdev_stats(zhp, vname, oldnv ? oldchild[c] : NULL,
+ newchild[c], cb, depth + 2);
+ free(vname);
+ }
+ }
}
static int
@@ -1870,160 +2035,122 @@ zpool_do_iostat(int argc, char **argv)
typedef struct list_cbdata {
boolean_t cb_scripted;
boolean_t cb_first;
- int cb_fields[MAX_FIELDS];
- int cb_fieldcount;
+ zprop_list_t *cb_proplist;
} list_cbdata_t;
/*
* Given a list of columns to display, output appropriate headers for each one.
*/
-void
-print_header(int *fields, size_t count)
+static void
+print_header(zprop_list_t *pl)
{
- int i;
- column_def_t *col;
- const char *fmt;
+ const char *header;
+ boolean_t first = B_TRUE;
+ boolean_t right_justify;
- for (i = 0; i < count; i++) {
- col = &column_table[fields[i]];
- if (i != 0)
+ for (; pl != NULL; pl = pl->pl_next) {
+ if (pl->pl_prop == ZPROP_INVAL)
+ continue;
+
+ if (!first)
(void) printf(" ");
- if (col->cd_justify == left_justify)
- fmt = "%-*s";
else
- fmt = "%*s";
+ first = B_FALSE;
+
+ header = zpool_prop_column_name(pl->pl_prop);
+ right_justify = zpool_prop_align_right(pl->pl_prop);
- (void) printf(fmt, i == count - 1 ? strlen(col->cd_title) :
- col->cd_width, col->cd_title);
+ if (pl->pl_next == NULL && !right_justify)
+ (void) printf("%s", header);
+ else if (right_justify)
+ (void) printf("%*s", pl->pl_width, header);
+ else
+ (void) printf("%-*s", pl->pl_width, header);
}
(void) printf("\n");
}
-int
-list_callback(zpool_handle_t *zhp, void *data)
+/*
+ * Given a pool and a list of properties, print out all the properties according
+ * to the described layout.
+ */
+static void
+print_pool(zpool_handle_t *zhp, zprop_list_t *pl, int scripted)
{
- list_cbdata_t *cbp = data;
- nvlist_t *config;
- int i;
- char buf[ZPOOL_MAXNAMELEN];
- uint64_t total;
- uint64_t used;
- const char *fmt;
- column_def_t *col;
-
- if (cbp->cb_first) {
- if (!cbp->cb_scripted)
- print_header(cbp->cb_fields, cbp->cb_fieldcount);
- cbp->cb_first = B_FALSE;
- }
-
- if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
- config = NULL;
- } else {
- config = zpool_get_config(zhp, NULL);
- total = zpool_get_space_total(zhp);
- used = zpool_get_space_used(zhp);
- }
-
- for (i = 0; i < cbp->cb_fieldcount; i++) {
- if (i != 0) {
- if (cbp->cb_scripted)
+ boolean_t first = B_TRUE;
+ char property[ZPOOL_MAXPROPLEN];
+ char *propstr;
+ boolean_t right_justify;
+ int width;
+
+ for (; pl != NULL; pl = pl->pl_next) {
+ if (!first) {
+ if (scripted)
(void) printf("\t");
else
(void) printf(" ");
+ } else {
+ first = B_FALSE;
}
- col = &column_table[cbp->cb_fields[i]];
-
- switch (cbp->cb_fields[i]) {
- case ZPOOL_FIELD_NAME:
- (void) strlcpy(buf, zpool_get_name(zhp), sizeof (buf));
- break;
-
- case ZPOOL_FIELD_SIZE:
- if (config == NULL)
- (void) strlcpy(buf, "-", sizeof (buf));
- else
- zfs_nicenum(total, buf, sizeof (buf));
- break;
-
- case ZPOOL_FIELD_USED:
- if (config == NULL)
- (void) strlcpy(buf, "-", sizeof (buf));
+ right_justify = B_FALSE;
+ if (pl->pl_prop != ZPROP_INVAL) {
+ if (zpool_get_prop(zhp, pl->pl_prop, property,
+ sizeof (property), NULL) != 0)
+ propstr = "-";
else
- zfs_nicenum(used, buf, sizeof (buf));
- break;
+ propstr = property;
- case ZPOOL_FIELD_AVAILABLE:
- if (config == NULL)
- (void) strlcpy(buf, "-", sizeof (buf));
- else
- zfs_nicenum(total - used, buf, sizeof (buf));
- break;
+ right_justify = zpool_prop_align_right(pl->pl_prop);
+ } else {
+ propstr = "-";
+ }
- case ZPOOL_FIELD_CAPACITY:
- if (config == NULL) {
- (void) strlcpy(buf, "-", sizeof (buf));
- } else {
- uint64_t capacity = (total == 0 ? 0 :
- (used * 100 / total));
- (void) snprintf(buf, sizeof (buf), "%llu%%",
- (u_longlong_t)capacity);
- }
- break;
+ width = pl->pl_width;
- case ZPOOL_FIELD_HEALTH:
- if (config == NULL) {
- (void) strlcpy(buf, "FAULTED", sizeof (buf));
- } else {
- nvlist_t *nvroot;
- vdev_stat_t *vs;
- uint_t vsc;
-
- verify(nvlist_lookup_nvlist(config,
- ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
- verify(nvlist_lookup_uint64_array(nvroot,
- ZPOOL_CONFIG_STATS, (uint64_t **)&vs,
- &vsc) == 0);
- (void) strlcpy(buf, state_to_name(vs),
- sizeof (buf));
- }
- break;
+ /*
+ * If this is being called in scripted mode, or if this is the
+ * last column and it is left-justified, don't include a width
+ * format specifier.
+ */
+ if (scripted || (pl->pl_next == NULL && !right_justify))
+ (void) printf("%s", propstr);
+ else if (right_justify)
+ (void) printf("%*s", width, propstr);
+ else
+ (void) printf("%-*s", width, propstr);
+ }
- case ZPOOL_FIELD_ROOT:
- if (config == NULL)
- (void) strlcpy(buf, "-", sizeof (buf));
- else if (zpool_get_root(zhp, buf, sizeof (buf)) != 0)
- (void) strlcpy(buf, "-", sizeof (buf));
- break;
- }
+ (void) printf("\n");
+}
- if (cbp->cb_scripted)
- (void) printf("%s", buf);
- else {
- if (col->cd_justify == left_justify)
- fmt = "%-*s";
- else
- fmt = "%*s";
+/*
+ * Generic callback function to list a pool.
+ */
+int
+list_callback(zpool_handle_t *zhp, void *data)
+{
+ list_cbdata_t *cbp = data;
- (void) printf(fmt, i == cbp->cb_fieldcount - 1 ?
- strlen(buf) : col->cd_width, buf);
- }
+ if (cbp->cb_first) {
+ if (!cbp->cb_scripted)
+ print_header(cbp->cb_proplist);
+ cbp->cb_first = B_FALSE;
}
- (void) printf("\n");
+ print_pool(zhp, cbp->cb_proplist, cbp->cb_scripted);
return (0);
}
/*
- * zpool list [-H] [-o field[,field]*] [pool] ...
+ * zpool list [-H] [-o prop[,prop]*] [pool] ...
*
- * -H Scripted mode. Don't display headers, and separate fields by
- * a single tab.
- * -o List of fields to display. Defaults to all fields, or
- * "name,size,used,available,capacity,health,root"
+ * -H Scripted mode. Don't display headers, and separate properties
+ * by a single tab.
+ * -o List of properties to display. Defaults to
+ * "name,size,used,available,capacity,health,altroot"
*
* List all pools in the system, whether or not they're healthy. Output space
* statistics for each one, as well as health status summary.
@@ -2034,10 +2161,9 @@ zpool_do_list(int argc, char **argv)
int c;
int ret;
list_cbdata_t cb = { 0 };
- static char default_fields[] =
- "name,size,used,available,capacity,health,root";
- char *fields = default_fields;
- char *value;
+ static char default_props[] =
+ "name,size,used,available,capacity,health,altroot";
+ char *props = default_props;
/* check options */
while ((c = getopt(argc, argv, ":Ho:")) != -1) {
@@ -2046,7 +2172,7 @@ zpool_do_list(int argc, char **argv)
cb.cb_scripted = B_TRUE;
break;
case 'o':
- fields = optarg;
+ props = optarg;
break;
case ':':
(void) fprintf(stderr, gettext("missing argument for "
@@ -2063,29 +2189,17 @@ zpool_do_list(int argc, char **argv)
argc -= optind;
argv += optind;
- while (*fields != '\0') {
- if (cb.cb_fieldcount == MAX_FIELDS) {
- (void) fprintf(stderr, gettext("too many "
- "properties given to -o option\n"));
- usage(B_FALSE);
- }
-
- if ((cb.cb_fields[cb.cb_fieldcount] = getsubopt(&fields,
- column_subopts, &value)) == -1) {
- (void) fprintf(stderr, gettext("invalid property "
- "'%s'\n"), value);
- usage(B_FALSE);
- }
-
- cb.cb_fieldcount++;
- }
-
+ if (zprop_get_list(g_zfs, props, &cb.cb_proplist, ZFS_TYPE_POOL) != 0)
+ usage(B_FALSE);
cb.cb_first = B_TRUE;
- ret = for_each_pool(argc, argv, B_TRUE, NULL, list_callback, &cb);
+ ret = for_each_pool(argc, argv, B_TRUE, &cb.cb_proplist,
+ list_callback, &cb);
- if (argc == 0 && cb.cb_first) {
+ zprop_free_list(cb.cb_proplist);
+
+ if (argc == 0 && cb.cb_first && !cb.cb_scripted) {
(void) printf(gettext("no pools available\n"));
return (0);
}
@@ -2128,10 +2242,7 @@ zpool_do_attach_or_replace(int argc, char **argv, int replacing)
nvlist_t *nvroot;
char *poolname, *old_disk, *new_disk;
zpool_handle_t *zhp;
- nvlist_t *config;
int ret;
- int log_argc;
- char **log_argv;
/* check options */
while ((c = getopt(argc, argv, "f")) != -1) {
@@ -2146,8 +2257,6 @@ zpool_do_attach_or_replace(int argc, char **argv, int replacing)
}
}
- log_argc = argc;
- log_argv = argv;
argc -= optind;
argv += optind;
@@ -2190,14 +2299,15 @@ zpool_do_attach_or_replace(int argc, char **argv, int replacing)
if ((zhp = zpool_open(g_zfs, poolname)) == NULL)
return (1);
- if ((config = zpool_get_config(zhp, NULL)) == NULL) {
+ if (zpool_get_config(zhp, NULL) == NULL) {
(void) fprintf(stderr, gettext("pool '%s' is unavailable\n"),
poolname);
zpool_close(zhp);
return (1);
}
- nvroot = make_root_vdev(config, force, B_FALSE, replacing, argc, argv);
+ nvroot = make_root_vdev(zhp, force, B_FALSE, replacing, B_FALSE,
+ argc, argv);
if (nvroot == NULL) {
zpool_close(zhp);
return (1);
@@ -2205,11 +2315,6 @@ zpool_do_attach_or_replace(int argc, char **argv, int replacing)
ret = zpool_vdev_attach(zhp, old_disk, new_disk, nvroot, replacing);
- if (!ret) {
- zpool_log_history(g_zfs, log_argc, log_argv, poolname, B_TRUE,
- B_FALSE);
- }
-
nvlist_free(nvroot);
zpool_close(zhp);
@@ -2299,10 +2404,6 @@ zpool_do_detach(int argc, char **argv)
ret = zpool_vdev_detach(zhp, path);
- if (!ret) {
- zpool_log_history(g_zfs, argc + optind, argv - optind, poolname,
- B_TRUE, B_FALSE);
- }
zpool_close(zhp);
return (ret);
@@ -2311,7 +2412,6 @@ zpool_do_detach(int argc, char **argv)
/*
* zpool online <pool> <device> ...
*/
-/* ARGSUSED */
int
zpool_do_online(int argc, char **argv)
{
@@ -2319,6 +2419,7 @@ zpool_do_online(int argc, char **argv)
char *poolname;
zpool_handle_t *zhp;
int ret = 0;
+ vdev_state_t newstate;
/* check options */
while ((c = getopt(argc, argv, "t")) != -1) {
@@ -2349,17 +2450,26 @@ zpool_do_online(int argc, char **argv)
if ((zhp = zpool_open(g_zfs, poolname)) == NULL)
return (1);
- for (i = 1; i < argc; i++)
- if (zpool_vdev_online(zhp, argv[i]) == 0)
- (void) printf(gettext("Bringing device %s online\n"),
- argv[i]);
- else
+ for (i = 1; i < argc; i++) {
+ if (zpool_vdev_online(zhp, argv[i], 0, &newstate) == 0) {
+ if (newstate != VDEV_STATE_HEALTHY) {
+ (void) printf(gettext("warning: device '%s' "
+ "onlined, but remains in faulted state\n"),
+ argv[i]);
+ if (newstate == VDEV_STATE_FAULTED)
+ (void) printf(gettext("use 'zpool "
+ "clear' to restore a faulted "
+ "device\n"));
+ else
+ (void) printf(gettext("use 'zpool "
+ "replace' to replace devices "
+ "that are no longer present\n"));
+ }
+ } else {
ret = 1;
-
- if (!ret) {
- zpool_log_history(g_zfs, argc + optind, argv - optind, poolname,
- B_TRUE, B_FALSE);
+ }
}
+
zpool_close(zhp);
return (ret);
@@ -2417,17 +2527,11 @@ zpool_do_offline(int argc, char **argv)
if ((zhp = zpool_open(g_zfs, poolname)) == NULL)
return (1);
- for (i = 1; i < argc; i++)
- if (zpool_vdev_offline(zhp, argv[i], istmp) == 0)
- (void) printf(gettext("Bringing device %s offline\n"),
- argv[i]);
- else
+ for (i = 1; i < argc; i++) {
+ if (zpool_vdev_offline(zhp, argv[i], istmp) != 0)
ret = 1;
-
- if (!ret) {
- zpool_log_history(g_zfs, argc + optind, argv - optind, poolname,
- B_TRUE, B_FALSE);
}
+
zpool_close(zhp);
return (ret);
@@ -2458,14 +2562,12 @@ zpool_do_clear(int argc, char **argv)
pool = argv[1];
device = argc == 3 ? argv[2] : NULL;
- if ((zhp = zpool_open(g_zfs, pool)) == NULL)
+ if ((zhp = zpool_open_canfail(g_zfs, pool)) == NULL)
return (1);
if (zpool_clear(zhp, device) != 0)
ret = 1;
- if (!ret)
- zpool_log_history(g_zfs, argc, argv, pool, B_TRUE, B_FALSE);
zpool_close(zhp);
return (ret);
@@ -2494,11 +2596,6 @@ scrub_callback(zpool_handle_t *zhp, void *data)
err = zpool_scrub(zhp, cb->cb_type);
- if (!err) {
- zpool_log_history(g_zfs, cb->cb_argc, cb->cb_argv,
- zpool_get_name(zhp), B_TRUE, B_FALSE);
- }
-
return (err != 0);
}
@@ -2559,7 +2656,7 @@ print_scrub_status(nvlist_t *nvroot)
uint_t vsc;
time_t start, end, now;
double fraction_done;
- uint64_t examined, total, minutes_left;
+ uint64_t examined, total, minutes_left, minutes_taken;
char *scrub_type;
verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS,
@@ -2583,8 +2680,13 @@ print_scrub_status(nvlist_t *nvroot)
total = vs->vs_alloc;
if (end != 0) {
- (void) printf(gettext("%s %s with %llu errors on %s"),
+ minutes_taken = (uint64_t)((end - start) / 60);
+
+ (void) printf(gettext("%s %s after %lluh%um with %llu errors "
+ "on %s"),
scrub_type, vs->vs_scrub_complete ? "completed" : "stopped",
+ (u_longlong_t)(minutes_taken / 60),
+ (uint_t)(minutes_taken % 60),
(u_longlong_t)vs->vs_scrub_errors, ctime(&end));
return;
}
@@ -2597,9 +2699,12 @@ print_scrub_status(nvlist_t *nvroot)
fraction_done = (double)examined / total;
minutes_left = (uint64_t)((now - start) *
(1 - fraction_done) / fraction_done / 60);
+ minutes_taken = (uint64_t)((now - start) / 60);
- (void) printf(gettext("%s in progress, %.2f%% done, %lluh%um to go\n"),
- scrub_type, 100 * fraction_done,
+ (void) printf(gettext("%s in progress for %lluh%um, %.2f%% done, "
+ "%lluh%um to go\n"),
+ scrub_type, (u_longlong_t)(minutes_taken / 60),
+ (uint_t)(minutes_taken % 60), 100 * fraction_done,
(u_longlong_t)(minutes_left / 60), (uint_t)(minutes_left % 60));
}
@@ -2653,7 +2758,7 @@ find_spare(zpool_handle_t *zhp, void *data)
*/
void
print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
- int namewidth, int depth, boolean_t isspare)
+ int namewidth, int depth, boolean_t isspare, boolean_t print_logs)
{
nvlist_t **child;
uint_t c, children;
@@ -2662,7 +2767,7 @@ print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
char *vname;
uint64_t notpresent;
spare_cbdata_t cb;
- const char *state;
+ char *state;
verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_STATS,
(uint64_t **)&vs, &c) == 0);
@@ -2671,7 +2776,7 @@ print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
&child, &children) != 0)
children = 0;
- state = state_to_name(vs);
+ state = zpool_state_to_name(vs->vs_state, vs->vs_aux);
if (isspare) {
/*
* For hot spares, we use the terms 'INUSE' and 'AVAILABLE' for
@@ -2736,6 +2841,18 @@ print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
}
break;
+ case VDEV_AUX_ERR_EXCEEDED:
+ (void) printf(gettext("too many errors"));
+ break;
+
+ case VDEV_AUX_IO_FAILURE:
+ (void) printf(gettext("experienced I/O failures"));
+ break;
+
+ case VDEV_AUX_BAD_LOG:
+ (void) printf(gettext("bad intent log"));
+ break;
+
default:
(void) printf(gettext("corrupted data"));
break;
@@ -2753,9 +2870,15 @@ print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
(void) printf("\n");
for (c = 0; c < children; c++) {
+ uint64_t is_log = B_FALSE;
+
+ (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG,
+ &is_log);
+ if ((is_log && !print_logs) || (!is_log && print_logs))
+ continue;
vname = zpool_vdev_name(g_zfs, zhp, child[c]);
print_status_config(zhp, vname, child[c],
- namewidth, depth + 2, isspare);
+ namewidth, depth + 2, isspare, B_FALSE);
free(vname);
}
}
@@ -2763,7 +2886,7 @@ print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
static void
print_error_log(zpool_handle_t *zhp)
{
- nvlist_t *nverrlist;
+ nvlist_t *nverrlist = NULL;
nvpair_t *elem;
char *pathname;
size_t len = MAXPATHLEN * 2;
@@ -2810,7 +2933,27 @@ print_spares(zpool_handle_t *zhp, nvlist_t **spares, uint_t nspares,
for (i = 0; i < nspares; i++) {
name = zpool_vdev_name(g_zfs, zhp, spares[i]);
print_status_config(zhp, name, spares[i],
- namewidth, 2, B_TRUE);
+ namewidth, 2, B_TRUE, B_FALSE);
+ free(name);
+ }
+}
+
+static void
+print_l2cache(zpool_handle_t *zhp, nvlist_t **l2cache, uint_t nl2cache,
+ int namewidth)
+{
+ uint_t i;
+ char *name;
+
+ if (nl2cache == 0)
+ return;
+
+ (void) printf(gettext("\tcache\n"));
+
+ for (i = 0; i < nl2cache; i++) {
+ name = zpool_vdev_name(g_zfs, zhp, l2cache[i]);
+ print_status_config(zhp, name, l2cache[i],
+ namewidth, 2, B_FALSE, B_FALSE);
free(name);
}
}
@@ -2869,7 +3012,7 @@ status_callback(zpool_handle_t *zhp, void *data)
&nvroot) == 0);
verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS,
(uint64_t **)&vs, &c) == 0);
- health = state_to_name(vs);
+ health = zpool_state_to_name(vs->vs_state, vs->vs_aux);
(void) printf(gettext(" pool: %s\n"), zpool_get_name(zhp));
(void) printf(gettext(" state: %s\n"), health);
@@ -2972,6 +3115,45 @@ status_callback(zpool_handle_t *zhp, void *data)
"backup.\n"));
break;
+ case ZPOOL_STATUS_FAULTED_DEV_R:
+ (void) printf(gettext("status: One or more devices are "
+ "faulted in response to persistent errors.\n\tSufficient "
+ "replicas exist for the pool to continue functioning "
+ "in a\n\tdegraded state.\n"));
+ (void) printf(gettext("action: Replace the faulted device, "
+ "or use 'zpool clear' to mark the device\n\trepaired.\n"));
+ break;
+
+ case ZPOOL_STATUS_FAULTED_DEV_NR:
+ (void) printf(gettext("status: One or more devices are "
+ "faulted in response to persistent errors. There are "
+ "insufficient replicas for the pool to\n\tcontinue "
+ "functioning.\n"));
+ (void) printf(gettext("action: Destroy and re-create the pool "
+ "from a backup source. Manually marking the device\n"
+ "\trepaired using 'zpool clear' may allow some data "
+ "to be recovered.\n"));
+ break;
+
+ case ZPOOL_STATUS_IO_FAILURE_WAIT:
+ case ZPOOL_STATUS_IO_FAILURE_CONTINUE:
+ (void) printf(gettext("status: One or more devices are "
+ "faulted in response to IO failures.\n"));
+ (void) printf(gettext("action: Make sure the affected devices "
+ "are connected, then run 'zpool clear'.\n"));
+ break;
+
+ case ZPOOL_STATUS_BAD_LOG:
+ (void) printf(gettext("status: An intent log record "
+ "could not be read.\n"
+ "\tWaiting for adminstrator intervention to fix the "
+ "faulted pool.\n"));
+ (void) printf(gettext("action: Either restore the affected "
+ "device(s) and run 'zpool online',\n"
+ "\tor ignore the intent log records by running "
+ "'zpool clear'.\n"));
+ break;
+
default:
/*
* The remaining errors can't actually be generated, yet.
@@ -2986,8 +3168,8 @@ status_callback(zpool_handle_t *zhp, void *data)
if (config != NULL) {
int namewidth;
uint64_t nerr;
- nvlist_t **spares;
- uint_t nspares;
+ nvlist_t **spares, **l2cache;
+ uint_t nspares, nl2cache;
(void) printf(gettext(" scrub: "));
@@ -3001,7 +3183,14 @@ status_callback(zpool_handle_t *zhp, void *data)
(void) printf(gettext("\t%-*s %-8s %5s %5s %5s\n"), namewidth,
"NAME", "STATE", "READ", "WRITE", "CKSUM");
print_status_config(zhp, zpool_get_name(zhp), nvroot,
- namewidth, 0, B_FALSE);
+ namewidth, 0, B_FALSE, B_FALSE);
+ if (num_logs(nvroot) > 0)
+ print_status_config(zhp, "logs", nvroot, namewidth, 0,
+ B_FALSE, B_TRUE);
+
+ if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE,
+ &l2cache, &nl2cache) == 0)
+ print_l2cache(zhp, l2cache, nl2cache, namewidth);
if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
&spares, &nspares) == 0)
@@ -3016,7 +3205,7 @@ status_callback(zpool_handle_t *zhp, void *data)
* precise count by fetching the entire log and
* uniquifying the results.
*/
- if (nerr < 100 && !cbp->cb_verbose &&
+ if (nerr > 0 && nerr < 100 && !cbp->cb_verbose &&
zpool_get_errlog(zhp, &nverrlist) == 0) {
nvpair_t *elem;
@@ -3103,6 +3292,7 @@ typedef struct upgrade_cbdata {
int cb_first;
int cb_newer;
int cb_argc;
+ uint64_t cb_version;
char **cb_argv;
} upgrade_cbdata_t;
@@ -3118,7 +3308,7 @@ upgrade_cb(zpool_handle_t *zhp, void *arg)
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
&version) == 0);
- if (!cbp->cb_newer && version < ZFS_VERSION) {
+ if (!cbp->cb_newer && version < SPA_VERSION) {
if (!cbp->cb_all) {
if (cbp->cb_first) {
(void) printf(gettext("The following pools are "
@@ -3135,16 +3325,13 @@ upgrade_cb(zpool_handle_t *zhp, void *arg)
zpool_get_name(zhp));
} else {
cbp->cb_first = B_FALSE;
- ret = zpool_upgrade(zhp);
+ ret = zpool_upgrade(zhp, cbp->cb_version);
if (!ret) {
- zpool_log_history(g_zfs, cbp->cb_argc,
- cbp->cb_argv, zpool_get_name(zhp), B_TRUE,
- B_FALSE);
(void) printf(gettext("Successfully upgraded "
- "'%s'\n"), zpool_get_name(zhp));
+ "'%s'\n\n"), zpool_get_name(zhp));
}
}
- } else if (cbp->cb_newer && version > ZFS_VERSION) {
+ } else if (cbp->cb_newer && version > SPA_VERSION) {
assert(!cbp->cb_all);
if (cbp->cb_first) {
@@ -3168,29 +3355,37 @@ upgrade_cb(zpool_handle_t *zhp, void *arg)
static int
upgrade_one(zpool_handle_t *zhp, void *data)
{
- nvlist_t *config;
- uint64_t version;
- int ret;
upgrade_cbdata_t *cbp = data;
+ uint64_t cur_version;
+ int ret;
- config = zpool_get_config(zhp, NULL);
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
- &version) == 0);
+ if (strcmp("log", zpool_get_name(zhp)) == 0) {
+ (void) printf(gettext("'log' is now a reserved word\n"
+ "Pool 'log' must be renamed using export and import"
+ " to upgrade.\n"));
+ return (1);
+ }
- if (version == ZFS_VERSION) {
+ cur_version = zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL);
+ if (cur_version > cbp->cb_version) {
+ (void) printf(gettext("Pool '%s' is already formatted "
+ "using more current version '%llu'.\n"),
+ zpool_get_name(zhp), cur_version);
+ return (0);
+ }
+ if (cur_version == cbp->cb_version) {
(void) printf(gettext("Pool '%s' is already formatted "
"using the current version.\n"), zpool_get_name(zhp));
return (0);
}
- ret = zpool_upgrade(zhp);
+ ret = zpool_upgrade(zhp, cbp->cb_version);
if (!ret) {
- zpool_log_history(g_zfs, cbp->cb_argc, cbp->cb_argv,
- zpool_get_name(zhp), B_TRUE, B_FALSE);
(void) printf(gettext("Successfully upgraded '%s' "
- "from version %llu to version %llu\n"), zpool_get_name(zhp),
- (u_longlong_t)version, (u_longlong_t)ZFS_VERSION);
+ "from version %llu to version %llu\n\n"),
+ zpool_get_name(zhp), (u_longlong_t)cur_version,
+ (u_longlong_t)cbp->cb_version);
}
return (ret != 0);
@@ -3199,7 +3394,7 @@ upgrade_one(zpool_handle_t *zhp, void *data)
/*
* zpool upgrade
* zpool upgrade -v
- * zpool upgrade <-a | pool>
+ * zpool upgrade [-V version] <-a | pool ...>
*
* With no arguments, display downrev'd ZFS pool available for upgrade.
* Individual pools can be upgraded by specifying the pool, and '-a' will
@@ -3212,9 +3407,11 @@ zpool_do_upgrade(int argc, char **argv)
upgrade_cbdata_t cb = { 0 };
int ret = 0;
boolean_t showversions = B_FALSE;
+ char *end;
+
/* check options */
- while ((c = getopt(argc, argv, "av")) != -1) {
+ while ((c = getopt(argc, argv, "avV:")) != -1) {
switch (c) {
case 'a':
cb.cb_all = B_TRUE;
@@ -3222,6 +3419,15 @@ zpool_do_upgrade(int argc, char **argv)
case 'v':
showversions = B_TRUE;
break;
+ case 'V':
+ cb.cb_version = strtoll(optarg, &end, 10);
+ if (*end != '\0' || cb.cb_version > SPA_VERSION ||
+ cb.cb_version < SPA_VERSION_1) {
+ (void) fprintf(stderr,
+ gettext("invalid version '%s'\n"), optarg);
+ usage(B_FALSE);
+ }
+ break;
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
@@ -3234,6 +3440,14 @@ zpool_do_upgrade(int argc, char **argv)
argc -= optind;
argv += optind;
+ if (cb.cb_version == 0) {
+ cb.cb_version = SPA_VERSION;
+ } else if (!cb.cb_all && argc == 0) {
+ (void) fprintf(stderr, gettext("-V option is "
+ "incompatible with other arguments\n"));
+ usage(B_FALSE);
+ }
+
if (showversions) {
if (cb.cb_all || argc != 0) {
(void) fprintf(stderr, gettext("-v option is "
@@ -3242,14 +3456,14 @@ zpool_do_upgrade(int argc, char **argv)
}
} else if (cb.cb_all) {
if (argc != 0) {
- (void) fprintf(stderr, gettext("-a option is "
- "incompatible with other arguments\n"));
+ (void) fprintf(stderr, gettext("-a option should not "
+ "be used along with a pool name\n"));
usage(B_FALSE);
}
}
- (void) printf(gettext("This system is currently running ZFS version "
- "%llu.\n\n"), ZFS_VERSION);
+ (void) printf(gettext("This system is currently running "
+ "ZFS pool version %llu.\n\n"), SPA_VERSION);
cb.cb_first = B_TRUE;
if (showversions) {
(void) printf(gettext("The following versions are "
@@ -3265,8 +3479,16 @@ zpool_do_upgrade(int argc, char **argv)
(void) printf(gettext(" 4 zpool history\n"));
(void) printf(gettext(" 5 Compression using the gzip "
"algorithm\n"));
- (void) printf(gettext(" 6 bootfs pool property "));
- (void) printf(gettext("\nFor more information on a particular "
+ (void) printf(gettext(" 6 bootfs pool property\n"));
+ (void) printf(gettext(" 7 Separate intent log devices\n"));
+ (void) printf(gettext(" 8 Delegated administration\n"));
+ (void) printf(gettext(" 9 refquota and refreservation "
+ "properties\n"));
+ (void) printf(gettext(" 10 Cache devices\n"));
+ (void) printf(gettext(" 11 Improved scrub performance\n"));
+ (void) printf(gettext(" 12 Snapshot properties\n"));
+ (void) printf(gettext(" 13 snapused property\n"));
+ (void) printf(gettext("For more information on a particular "
"version, including supported releases, see:\n\n"));
(void) printf("http://www.opensolaris.org/os/community/zfs/"
"version/N\n\n");
@@ -3306,6 +3528,53 @@ zpool_do_upgrade(int argc, char **argv)
return (ret);
}
+typedef struct hist_cbdata {
+ boolean_t first;
+ int longfmt;
+ int internal;
+} hist_cbdata_t;
+
+char *hist_event_table[LOG_END] = {
+ "invalid event",
+ "pool create",
+ "vdev add",
+ "pool remove",
+ "pool destroy",
+ "pool export",
+ "pool import",
+ "vdev attach",
+ "vdev replace",
+ "vdev detach",
+ "vdev online",
+ "vdev offline",
+ "vdev upgrade",
+ "pool clear",
+ "pool scrub",
+ "pool property set",
+ "create",
+ "clone",
+ "destroy",
+ "destroy_begin_sync",
+ "inherit",
+ "property set",
+ "quota set",
+ "permission update",
+ "permission remove",
+ "permission who remove",
+ "promote",
+ "receive",
+ "rename",
+ "reservation set",
+ "replay_inc_sync",
+ "replay_full_sync",
+ "rollback",
+ "snapshot",
+ "filesystem version upgrade",
+ "refquota set",
+ "refreservation set",
+ "pool scrub done",
+};
+
/*
* Print out the command history for a specific pool.
*/
@@ -3316,13 +3585,22 @@ get_history_one(zpool_handle_t *zhp, void *data)
nvlist_t **records;
uint_t numrecords;
char *cmdstr;
+ char *pathstr;
uint64_t dst_time;
time_t tsec;
struct tm t;
char tbuf[30];
int ret, i;
+ uint64_t who;
+ struct passwd *pwd;
+ char *hostname;
+ char *zonename;
+ char internalstr[MAXPATHLEN];
+ hist_cbdata_t *cb = (hist_cbdata_t *)data;
+ uint64_t txg;
+ uint64_t ievent;
- *(boolean_t *)data = B_FALSE;
+ cb->first = B_FALSE;
(void) printf(gettext("History for '%s':\n"), zpool_get_name(zhp));
@@ -3333,14 +3611,65 @@ get_history_one(zpool_handle_t *zhp, void *data)
&records, &numrecords) == 0);
for (i = 0; i < numrecords; i++) {
if (nvlist_lookup_uint64(records[i], ZPOOL_HIST_TIME,
- &dst_time) == 0) {
- verify(nvlist_lookup_string(records[i], ZPOOL_HIST_CMD,
- &cmdstr) == 0);
- tsec = dst_time;
- (void) localtime_r(&tsec, &t);
- (void) strftime(tbuf, sizeof (tbuf), "%F.%T", &t);
- (void) printf("%s %s\n", tbuf, cmdstr);
+ &dst_time) != 0)
+ continue;
+
+ /* is it an internal event or a standard event? */
+ if (nvlist_lookup_string(records[i], ZPOOL_HIST_CMD,
+ &cmdstr) != 0) {
+ if (cb->internal == 0)
+ continue;
+
+ if (nvlist_lookup_uint64(records[i],
+ ZPOOL_HIST_INT_EVENT, &ievent) != 0)
+ continue;
+ verify(nvlist_lookup_uint64(records[i],
+ ZPOOL_HIST_TXG, &txg) == 0);
+ verify(nvlist_lookup_string(records[i],
+ ZPOOL_HIST_INT_STR, &pathstr) == 0);
+ if (ievent >= LOG_END)
+ continue;
+ (void) snprintf(internalstr,
+ sizeof (internalstr),
+ "[internal %s txg:%lld] %s",
+ hist_event_table[ievent], txg,
+ pathstr);
+ cmdstr = internalstr;
+ }
+ tsec = dst_time;
+ (void) localtime_r(&tsec, &t);
+ (void) strftime(tbuf, sizeof (tbuf), "%F.%T", &t);
+ (void) printf("%s %s", tbuf, cmdstr);
+
+ if (!cb->longfmt) {
+ (void) printf("\n");
+ continue;
}
+ (void) printf(" [");
+ if (nvlist_lookup_uint64(records[i],
+ ZPOOL_HIST_WHO, &who) == 0) {
+ pwd = getpwuid((uid_t)who);
+ if (pwd)
+ (void) printf("user %s on",
+ pwd->pw_name);
+ else
+ (void) printf("user %d on",
+ (int)who);
+ } else {
+ (void) printf(gettext("no info]\n"));
+ continue;
+ }
+ if (nvlist_lookup_string(records[i],
+ ZPOOL_HIST_HOST, &hostname) == 0) {
+ (void) printf(" %s", hostname);
+ }
+ if (nvlist_lookup_string(records[i],
+ ZPOOL_HIST_ZONE, &zonename) == 0) {
+ (void) printf(":%s", zonename);
+ }
+
+ (void) printf("]");
+ (void) printf("\n");
}
(void) printf("\n");
nvlist_free(nvhis);
@@ -3353,19 +3682,38 @@ get_history_one(zpool_handle_t *zhp, void *data)
*
* Displays the history of commands that modified pools.
*/
+
+
int
zpool_do_history(int argc, char **argv)
{
- boolean_t first = B_TRUE;
+ hist_cbdata_t cbdata = { 0 };
int ret;
+ int c;
+ cbdata.first = B_TRUE;
+ /* check options */
+ while ((c = getopt(argc, argv, "li")) != -1) {
+ switch (c) {
+ case 'l':
+ cbdata.longfmt = 1;
+ break;
+ case 'i':
+ cbdata.internal = 1;
+ break;
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
+ }
argc -= optind;
argv += optind;
ret = for_each_pool(argc, argv, B_FALSE, NULL, get_history_one,
- &first);
+ &cbdata);
- if (argc == 0 && first == B_TRUE) {
+ if (argc == 0 && cbdata.first == B_TRUE) {
(void) printf(gettext("no pools available\n"));
return (0);
}
@@ -3376,17 +3724,18 @@ zpool_do_history(int argc, char **argv)
static int
get_callback(zpool_handle_t *zhp, void *data)
{
- libzfs_get_cbdata_t *cbp = (libzfs_get_cbdata_t *)data;
+ zprop_get_cbdata_t *cbp = (zprop_get_cbdata_t *)data;
char value[MAXNAMELEN];
- zfs_source_t srctype;
- zpool_proplist_t *pl;
+ zprop_source_t srctype;
+ zprop_list_t *pl;
for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) {
/*
- * Skip the special fake placeholder.
+ * Skip the special fake placeholder. This will also skip
+ * over the name property when 'all' is specified.
*/
- if (pl->pl_prop == ZFS_PROP_NAME &&
+ if (pl->pl_prop == ZPOOL_PROP_NAME &&
pl == cbp->cb_proplist)
continue;
@@ -3394,7 +3743,7 @@ get_callback(zpool_handle_t *zhp, void *data)
value, sizeof (value), &srctype) != 0)
continue;
- libzfs_print_one_property(zpool_get_name(zhp), cbp,
+ zprop_print_one_property(zpool_get_name(zhp), cbp,
zpool_prop_to_name(pl->pl_prop), value, srctype, NULL);
}
return (0);
@@ -3403,25 +3752,27 @@ get_callback(zpool_handle_t *zhp, void *data)
int
zpool_do_get(int argc, char **argv)
{
- libzfs_get_cbdata_t cb = { 0 };
- zpool_proplist_t fake_name = { 0 };
+ zprop_get_cbdata_t cb = { 0 };
+ zprop_list_t fake_name = { 0 };
int ret;
if (argc < 3)
usage(B_FALSE);
cb.cb_first = B_TRUE;
- cb.cb_sources = ZFS_SRC_ALL;
+ cb.cb_sources = ZPROP_SRC_ALL;
cb.cb_columns[0] = GET_COL_NAME;
cb.cb_columns[1] = GET_COL_PROPERTY;
cb.cb_columns[2] = GET_COL_VALUE;
cb.cb_columns[3] = GET_COL_SOURCE;
+ cb.cb_type = ZFS_TYPE_POOL;
- if (zpool_get_proplist(g_zfs, argv[1], &cb.cb_proplist) != 0)
+ if (zprop_get_list(g_zfs, argv[1], &cb.cb_proplist,
+ ZFS_TYPE_POOL) != 0)
usage(B_FALSE);
if (cb.cb_proplist != NULL) {
- fake_name.pl_prop = ZFS_PROP_NAME;
+ fake_name.pl_prop = ZPOOL_PROP_NAME;
fake_name.pl_width = strlen(gettext("NAME"));
fake_name.pl_next = cb.cb_proplist;
cb.cb_proplist = &fake_name;
@@ -3431,9 +3782,9 @@ zpool_do_get(int argc, char **argv)
get_callback, &cb);
if (cb.cb_proplist == &fake_name)
- zfs_free_proplist(fake_name.pl_next);
+ zprop_free_list(fake_name.pl_next);
else
- zfs_free_proplist(cb.cb_proplist);
+ zprop_free_list(cb.cb_proplist);
return (ret);
}
@@ -3500,11 +3851,6 @@ zpool_do_set(int argc, char **argv)
error = for_each_pool(argc - 2, argv + 2, B_TRUE, NULL,
set_callback, &cb);
- if (cb.cb_any_successful) {
- *(cb.cb_value - 1) = '=';
- zpool_log_history(g_zfs, argc, argv, argv[2], B_FALSE, B_FALSE);
- }
-
return (error);
}
@@ -3531,7 +3877,6 @@ main(int argc, char **argv)
int ret;
int i;
char *cmdname;
- int found = 0;
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
@@ -3562,26 +3907,29 @@ main(int argc, char **argv)
if (strcmp(cmdname, "-?") == 0)
usage(B_TRUE);
+ zpool_set_history_str("zpool", argc, argv, history_str);
+ verify(zpool_stage_history(g_zfs, history_str) == 0);
+
/*
* Run the appropriate command.
*/
if (find_command_idx(cmdname, &i) == 0) {
current_command = &command_table[i];
ret = command_table[i].func(argc - 1, argv + 1);
- found++;
- }
-
- /*
- * 'freeze' is a vile debugging abomination, so we treat it as such.
- */
- if (strcmp(cmdname, "freeze") == 0 && argc == 3) {
+ } else if (strchr(cmdname, '=')) {
+ verify(find_command_idx("set", &i) == 0);
+ current_command = &command_table[i];
+ ret = command_table[i].func(argc, argv);
+ } else if (strcmp(cmdname, "freeze") == 0 && argc == 3) {
+ /*
+ * 'freeze' is a vile debugging abomination, so we treat
+ * it as such.
+ */
char buf[16384];
int fd = open(ZFS_DEV, O_RDWR);
(void) strcpy((void *)buf, argv[2]);
return (!!ioctl(fd, ZFS_IOC_POOL_FREEZE, buf));
- }
-
- if (!found) {
+ } else {
(void) fprintf(stderr, gettext("unrecognized "
"command '%s'\n"), cmdname);
usage(B_FALSE);
diff --git a/cddl/contrib/opensolaris/cmd/zpool/zpool_util.c b/cddl/contrib/opensolaris/cmd/zpool/zpool_util.c
index 8eb9c81..f44da4f 100644
--- a/cddl/contrib/opensolaris/cmd/zpool/zpool_util.c
+++ b/cddl/contrib/opensolaris/cmd/zpool/zpool_util.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -77,3 +77,28 @@ zpool_no_memory(void)
gettext("internal error: out of memory\n"));
exit(1);
}
+
+/*
+ * Return the number of logs in supplied nvlist
+ */
+uint_t
+num_logs(nvlist_t *nv)
+{
+ uint_t nlogs = 0;
+ uint_t c, children;
+ nvlist_t **child;
+
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
+ &child, &children) != 0)
+ return (0);
+
+ for (c = 0; c < children; c++) {
+ uint64_t is_log = B_FALSE;
+
+ (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG,
+ &is_log);
+ if (is_log)
+ nlogs++;
+ }
+ return (nlogs);
+}
diff --git a/cddl/contrib/opensolaris/cmd/zpool/zpool_util.h b/cddl/contrib/opensolaris/cmd/zpool/zpool_util.h
index cb05bda..e82f320 100644
--- a/cddl/contrib/opensolaris/cmd/zpool/zpool_util.h
+++ b/cddl/contrib/opensolaris/cmd/zpool/zpool_util.h
@@ -19,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef ZPOOL_UTIL_H
#define ZPOOL_UTIL_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <libnvpair.h>
#include <libzfs.h>
@@ -41,22 +39,24 @@ extern "C" {
void *safe_malloc(size_t);
char *safe_strdup(const char *);
void zpool_no_memory(void);
+uint_t num_logs(nvlist_t *nv);
/*
* Virtual device functions
*/
-nvlist_t *make_root_vdev(nvlist_t *poolconfig, int force, int check_rep,
- boolean_t isreplace, int argc, char **argv);
+
+nvlist_t *make_root_vdev(zpool_handle_t *zhp, int force, int check_rep,
+ boolean_t isreplace, boolean_t dryrun, int argc, char **argv);
/*
* Pool list functions
*/
-int for_each_pool(int, char **, boolean_t unavail, zpool_proplist_t **,
+int for_each_pool(int, char **, boolean_t unavail, zprop_list_t **,
zpool_iter_f, void *);
typedef struct zpool_list zpool_list_t;
-zpool_list_t *pool_list_get(int, char **, zpool_proplist_t **, int *);
+zpool_list_t *pool_list_get(int, char **, zprop_list_t **, int *);
void pool_list_update(zpool_list_t *);
int pool_list_iter(zpool_list_t *, int unavail, zpool_iter_f, void *);
void pool_list_free(zpool_list_t *);
diff --git a/cddl/contrib/opensolaris/cmd/zpool/zpool_vdev.c b/cddl/contrib/opensolaris/cmd/zpool/zpool_vdev.c
index cfed1f0..35a636c 100644
--- a/cddl/contrib/opensolaris/cmd/zpool/zpool_vdev.c
+++ b/cddl/contrib/opensolaris/cmd/zpool/zpool_vdev.c
@@ -20,12 +20,10 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* Functions to convert between a list of vdevs and an nvlist representing the
* configuration. Each entry in the list can be one of:
@@ -48,8 +46,8 @@
* Hot spares are a special case, and passed down as an array of disk vdevs, at
* the same level as the root of the vdev tree.
*
- * The only function exported by this file is 'get_vdev_spec'. The function
- * performs several passes:
+ * The only function exported by this file is 'make_root_vdev'. The
+ * function performs several passes:
*
* 1. Construct the vdev specification. Performs syntax validation and
* makes sure each device is valid.
@@ -59,6 +57,7 @@
* 3. Check for replication errors if the 'force' flag is not specified.
* validates that the replication level is consistent across the
* entire pool.
+ * 4. Call libzfs to label any whole disks with an EFI label.
*/
#include <assert.h>
@@ -76,8 +75,6 @@
#include <sys/mntent.h>
#include <libgeom.h>
-#include <libzfs.h>
-
#include "zpool_util.h"
/*
@@ -111,53 +108,105 @@ vdev_error(const char *fmt, ...)
}
/*
- * Validate a GEOM provider.
+ * Check that a file is valid. All we can do in this case is check that it's
+ * not in use by another pool, and not in use by swap.
*/
static int
-check_provider(const char *name, boolean_t force, boolean_t isspare)
+check_file(const char *file, boolean_t force, boolean_t isspare)
{
- struct gmesh mesh;
- struct gclass *mp;
- struct ggeom *gp;
- struct gprovider *pp;
- int rv;
-
- /* XXX: What to do with isspare? */
-
- if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
- name += sizeof(_PATH_DEV) - 1;
-
- rv = geom_gettree(&mesh);
- assert(rv == 0);
-
- pp = NULL;
- LIST_FOREACH(mp, &mesh.lg_class, lg_class) {
- LIST_FOREACH(gp, &mp->lg_geom, lg_geom) {
- LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
- if (strcmp(pp->lg_name, name) == 0)
- goto out;
+ char *name;
+ int fd;
+ int ret = 0;
+ int err;
+ pool_state_t state;
+ boolean_t inuse;
+
+#if 0
+ if (dm_inuse_swap(file, &err)) {
+ if (err)
+ libdiskmgt_error(err);
+ else
+ vdev_error(gettext("%s is currently used by swap. "
+ "Please see swap(1M).\n"), file);
+ return (-1);
+ }
+#endif
+
+ if ((fd = open(file, O_RDONLY)) < 0)
+ return (0);
+
+ if (zpool_in_use(g_zfs, fd, &state, &name, &inuse) == 0 && inuse) {
+ const char *desc;
+
+ switch (state) {
+ case POOL_STATE_ACTIVE:
+ desc = gettext("active");
+ break;
+
+ case POOL_STATE_EXPORTED:
+ desc = gettext("exported");
+ break;
+
+ case POOL_STATE_POTENTIALLY_ACTIVE:
+ desc = gettext("potentially active");
+ break;
+
+ default:
+ desc = gettext("unknown");
+ break;
+ }
+
+ /*
+ * Allow hot spares to be shared between pools.
+ */
+ if (state == POOL_STATE_SPARE && isspare)
+ return (0);
+
+ if (state == POOL_STATE_ACTIVE ||
+ state == POOL_STATE_SPARE || !force) {
+ switch (state) {
+ case POOL_STATE_SPARE:
+ vdev_error(gettext("%s is reserved as a hot "
+ "spare for pool %s\n"), file, name);
+ break;
+ default:
+ vdev_error(gettext("%s is part of %s pool "
+ "'%s'\n"), file, desc, name);
+ break;
}
+ ret = -1;
}
+
+ free(name);
}
-out:
- rv = -1;
- if (pp == NULL)
- vdev_error("no such provider %s\n", name);
- else {
- int acr, acw, ace;
-
- VERIFY(sscanf(pp->lg_mode, "r%dw%de%d", &acr, &acw, &ace) == 3);
- if (acw == 0 && ace == 0)
- rv = 0;
- else
- vdev_error("%s is in use (%s)\n", name, pp->lg_mode);
- }
- geom_deletetree(&mesh);
- return (rv);
+
+ (void) close(fd);
+ return (ret);
+}
+
+static int
+check_provider(const char *name, boolean_t force, boolean_t isspare)
+{
+ char path[MAXPATHLEN];
+
+ if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) != 0)
+ snprintf(path, sizeof(path), "%s%s", _PATH_DEV, name);
+ else
+ strlcpy(path, name, sizeof(path));
+
+ return (check_file(path, force, isspare));
}
+/*
+ * By "whole disk" we mean an entire physical disk (something we can
+ * label, toggle the write cache on, etc.) as opposed to the full
+ * capacity of a pseudo-device such as lofi or did. We act as if we
+ * are labeling the disk, which should be a pretty good test of whether
+ * it's a viable device or not. Returns B_TRUE if it is and B_FALSE if
+ * it isn't.
+ */
static boolean_t
-is_provider(const char *name)
+is_whole_disk(const char *name)
{
int fd;
@@ -167,8 +216,8 @@ is_provider(const char *name)
return (B_TRUE);
}
return (B_FALSE);
-
}
+
/*
* Create a leaf vdev. Determine if this is a GEOM provider.
* Valid forms for a leaf vdev are:
@@ -176,25 +225,81 @@ is_provider(const char *name)
* /dev/xxx Complete path to a GEOM provider
* xxx Shorthand for /dev/xxx
*/
-nvlist_t *
-make_leaf_vdev(const char *arg)
+static nvlist_t *
+make_leaf_vdev(const char *arg, uint64_t is_log)
{
- char ident[DISK_IDENT_SIZE], path[MAXPATHLEN];
+ char path[MAXPATHLEN];
struct stat64 statbuf;
nvlist_t *vdev = NULL;
char *type = NULL;
boolean_t wholedisk = B_FALSE;
- if (strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
- strlcpy(path, arg, sizeof (path));
- else
- snprintf(path, sizeof (path), "%s%s", _PATH_DEV, arg);
+ /*
+ * Determine what type of vdev this is, and put the full path into
+ * 'path'. We detect whether this is a device of file afterwards by
+ * checking the st_mode of the file.
+ */
+ if (arg[0] == '/') {
+ /*
+ * Complete device or file path. Exact type is determined by
+ * examining the file descriptor afterwards.
+ */
+ wholedisk = is_whole_disk(arg);
+ if (!wholedisk && (stat64(arg, &statbuf) != 0)) {
+ (void) fprintf(stderr,
+ gettext("cannot open '%s': %s\n"),
+ arg, strerror(errno));
+ return (NULL);
+ }
+
+ (void) strlcpy(path, arg, sizeof (path));
+ } else {
+ /*
+ * This may be a short path for a device, or it could be total
+ * gibberish. Check to see if it's a known device in
+ * /dev/dsk/. As part of this check, see if we've been given a
+ * an entire disk (minus the slice number).
+ */
+ if (strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
+ strlcpy(path, arg, sizeof (path));
+ else
+ snprintf(path, sizeof (path), "%s%s", _PATH_DEV, arg);
+ wholedisk = is_whole_disk(path);
+ if (!wholedisk && (stat64(path, &statbuf) != 0)) {
+ /*
+ * If we got ENOENT, then the user gave us
+ * gibberish, so try to direct them with a
+ * reasonable error message. Otherwise,
+ * regurgitate strerror() since it's the best we
+ * can do.
+ */
+ if (errno == ENOENT) {
+ (void) fprintf(stderr,
+ gettext("cannot open '%s': no such "
+ "GEOM provider\n"), arg);
+ (void) fprintf(stderr,
+ gettext("must be a full path or "
+ "shorthand device name\n"));
+ return (NULL);
+ } else {
+ (void) fprintf(stderr,
+ gettext("cannot open '%s': %s\n"),
+ path, strerror(errno));
+ return (NULL);
+ }
+ }
+ }
- if (is_provider(path))
+ /*
+ * Determine whether this is a device or a file.
+ */
+ if (wholedisk) {
type = VDEV_TYPE_DISK;
- else {
+ } else if (S_ISREG(statbuf.st_mode)) {
+ type = VDEV_TYPE_FILE;
+ } else {
(void) fprintf(stderr, gettext("cannot use '%s': must be a "
- "GEOM provider\n"), path);
+ "GEOM provider or regular file\n"), path);
return (NULL);
}
@@ -206,6 +311,7 @@ make_leaf_vdev(const char *arg)
verify(nvlist_alloc(&vdev, NV_UNIQUE_NAME, 0) == 0);
verify(nvlist_add_string(vdev, ZPOOL_CONFIG_PATH, path) == 0);
verify(nvlist_add_string(vdev, ZPOOL_CONFIG_TYPE, type) == 0);
+ verify(nvlist_add_uint64(vdev, ZPOOL_CONFIG_IS_LOG, is_log) == 0);
if (strcmp(type, VDEV_TYPE_DISK) == 0)
verify(nvlist_add_uint64(vdev, ZPOOL_CONFIG_WHOLE_DISK,
(uint64_t)B_FALSE) == 0);
@@ -267,12 +373,14 @@ typedef struct replication_level {
uint64_t zprl_parity;
} replication_level_t;
+#define ZPOOL_FUZZ (16 * 1024 * 1024)
+
/*
* Given a list of toplevel vdevs, return the current replication level. If
* the config is inconsistent, then NULL is returned. If 'fatal' is set, then
* an error message will be displayed for each self-inconsistent vdev.
*/
-replication_level_t *
+static replication_level_t *
get_replication(nvlist_t *nvroot, boolean_t fatal)
{
nvlist_t **top;
@@ -291,10 +399,20 @@ get_replication(nvlist_t *nvroot, boolean_t fatal)
lastrep.zprl_type = NULL;
for (t = 0; t < toplevels; t++) {
+ uint64_t is_log = B_FALSE;
+
nv = top[t];
- verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
+ /*
+ * For separate logs we ignore the top level vdev replication
+ * constraints.
+ */
+ (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_IS_LOG, &is_log);
+ if (is_log)
+ continue;
+ verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE,
+ &type) == 0);
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
&child, &children) != 0) {
/*
@@ -328,7 +446,7 @@ get_replication(nvlist_t *nvroot, boolean_t fatal)
}
/*
- * The 'dontreport' variable indicatest that we've
+ * The 'dontreport' variable indicates that we've
* already reported an error for this spec, so don't
* bother doing it again.
*/
@@ -349,7 +467,7 @@ get_replication(nvlist_t *nvroot, boolean_t fatal)
ZPOOL_CONFIG_TYPE, &childtype) == 0);
/*
- * If this is a a replacing or spare vdev, then
+ * If this is a replacing or spare vdev, then
* get the real first child of the vdev.
*/
if (strcmp(childtype,
@@ -409,22 +527,30 @@ get_replication(nvlist_t *nvroot, boolean_t fatal)
*/
if ((fd = open(path, O_RDONLY)) >= 0) {
err = fstat64(fd, &statbuf);
+ if (err == 0 &&
+ S_ISCHR(statbuf.st_mode)) {
+ err = ioctl(fd, DIOCGMEDIASIZE,
+ &statbuf.st_size);
+ }
(void) close(fd);
} else {
err = stat64(path, &statbuf);
}
-
if (err != 0 || statbuf.st_size == 0)
continue;
size = statbuf.st_size;
/*
- * Also check the size of each device. If they
- * differ, then report an error.
+ * Also make sure that devices and
+ * slices have a consistent size. If
+ * they differ by a significant amount
+ * (~16MB) then report an error.
*/
- if (!dontreport && vdev_size != -1ULL &&
- size != vdev_size) {
+ if (!dontreport &&
+ (vdev_size != -1ULL &&
+ (labs(size - vdev_size) >
+ ZPOOL_FUZZ))) {
if (ret != NULL)
free(ret);
ret = NULL;
@@ -506,9 +632,11 @@ get_replication(nvlist_t *nvroot, boolean_t fatal)
* has a consistent replication level, then we ignore any errors. Otherwise,
* report any difference between the two.
*/
-int
+static int
check_replication(nvlist_t *config, nvlist_t *newroot)
{
+ nvlist_t **child;
+ uint_t children;
replication_level_t *current = NULL, *new;
int ret;
@@ -524,6 +652,23 @@ check_replication(nvlist_t *config, nvlist_t *newroot)
if ((current = get_replication(nvroot, B_FALSE)) == NULL)
return (0);
}
+ /*
+ * for spares there may be no children, and therefore no
+ * replication level to check
+ */
+ if ((nvlist_lookup_nvlist_array(newroot, ZPOOL_CONFIG_CHILDREN,
+ &child, &children) != 0) || (children == 0)) {
+ free(current);
+ return (0);
+ }
+
+ /*
+ * If all we have is logs then there's no replication level to check.
+ */
+ if (num_logs(newroot) == children) {
+ free(current);
+ return (0);
+ }
/*
* Get the replication level of the new vdev spec, reporting any
@@ -621,7 +766,7 @@ is_spare(nvlist_t *config, const char *path)
* Go through and find any devices that are in use. We rely on libdiskmgt for
* the majority of this task.
*/
-int
+static int
check_in_use(nvlist_t *config, nvlist_t *nv, int force, int isreplacing,
int isspare)
{
@@ -653,6 +798,9 @@ check_in_use(nvlist_t *config, nvlist_t *nv, int force, int isreplacing,
if (strcmp(type, VDEV_TYPE_DISK) == 0)
ret = check_provider(path, force, isspare);
+ if (strcmp(type, VDEV_TYPE_FILE) == 0)
+ ret = check_file(path, force, isspare);
+
return (ret);
}
@@ -668,10 +816,17 @@ check_in_use(nvlist_t *config, nvlist_t *nv, int force, int isreplacing,
isreplacing, B_TRUE)) != 0)
return (ret);
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
+ &child, &children) == 0)
+ for (c = 0; c < children; c++)
+ if ((ret = check_in_use(config, child[c], force,
+ isreplacing, B_FALSE)) != 0)
+ return (ret);
+
return (0);
}
-const char *
+static const char *
is_grouping(const char *type, int *mindev)
{
if (strcmp(type, "raidz") == 0 || strcmp(type, "raidz1") == 0) {
@@ -698,6 +853,18 @@ is_grouping(const char *type, int *mindev)
return (VDEV_TYPE_SPARE);
}
+ if (strcmp(type, "log") == 0) {
+ if (mindev != NULL)
+ *mindev = 1;
+ return (VDEV_TYPE_LOG);
+ }
+
+ if (strcmp(type, "cache") == 0) {
+ if (mindev != NULL)
+ *mindev = 1;
+ return (VDEV_TYPE_L2CACHE);
+ }
+
return (NULL);
}
@@ -710,14 +877,21 @@ is_grouping(const char *type, int *mindev)
nvlist_t *
construct_spec(int argc, char **argv)
{
- nvlist_t *nvroot, *nv, **top, **spares;
- int t, toplevels, mindev, nspares;
+ nvlist_t *nvroot, *nv, **top, **spares, **l2cache;
+ int t, toplevels, mindev, nspares, nlogs, nl2cache;
const char *type;
+ uint64_t is_log;
+ boolean_t seen_logs;
top = NULL;
toplevels = 0;
spares = NULL;
+ l2cache = NULL;
nspares = 0;
+ nlogs = 0;
+ nl2cache = 0;
+ is_log = B_FALSE;
+ seen_logs = B_FALSE;
while (argc > 0) {
nv = NULL;
@@ -730,12 +904,56 @@ construct_spec(int argc, char **argv)
nvlist_t **child = NULL;
int c, children = 0;
- if (strcmp(type, VDEV_TYPE_SPARE) == 0 &&
- spares != NULL) {
- (void) fprintf(stderr, gettext("invalid vdev "
- "specification: 'spare' can be "
- "specified only once\n"));
- return (NULL);
+ if (strcmp(type, VDEV_TYPE_SPARE) == 0) {
+ if (spares != NULL) {
+ (void) fprintf(stderr,
+ gettext("invalid vdev "
+ "specification: 'spare' can be "
+ "specified only once\n"));
+ return (NULL);
+ }
+ is_log = B_FALSE;
+ }
+
+ if (strcmp(type, VDEV_TYPE_LOG) == 0) {
+ if (seen_logs) {
+ (void) fprintf(stderr,
+ gettext("invalid vdev "
+ "specification: 'log' can be "
+ "specified only once\n"));
+ return (NULL);
+ }
+ seen_logs = B_TRUE;
+ is_log = B_TRUE;
+ argc--;
+ argv++;
+ /*
+ * A log is not a real grouping device.
+ * We just set is_log and continue.
+ */
+ continue;
+ }
+
+ if (strcmp(type, VDEV_TYPE_L2CACHE) == 0) {
+ if (l2cache != NULL) {
+ (void) fprintf(stderr,
+ gettext("invalid vdev "
+ "specification: 'cache' can be "
+ "specified only once\n"));
+ return (NULL);
+ }
+ is_log = B_FALSE;
+ }
+
+ if (is_log) {
+ if (strcmp(type, VDEV_TYPE_MIRROR) != 0) {
+ (void) fprintf(stderr,
+ gettext("invalid vdev "
+ "specification: unsupported 'log' "
+ "device: %s\n"), type);
+ return (NULL);
+ }
+ nlogs++;
}
for (c = 1; c < argc; c++) {
@@ -746,7 +964,8 @@ construct_spec(int argc, char **argv)
children * sizeof (nvlist_t *));
if (child == NULL)
zpool_no_memory();
- if ((nv = make_leaf_vdev(argv[c])) == NULL)
+ if ((nv = make_leaf_vdev(argv[c], B_FALSE))
+ == NULL)
return (NULL);
child[children - 1] = nv;
}
@@ -765,11 +984,17 @@ construct_spec(int argc, char **argv)
spares = child;
nspares = children;
continue;
+ } else if (strcmp(type, VDEV_TYPE_L2CACHE) == 0) {
+ l2cache = child;
+ nl2cache = children;
+ continue;
} else {
verify(nvlist_alloc(&nv, NV_UNIQUE_NAME,
0) == 0);
verify(nvlist_add_string(nv, ZPOOL_CONFIG_TYPE,
type) == 0);
+ verify(nvlist_add_uint64(nv,
+ ZPOOL_CONFIG_IS_LOG, is_log) == 0);
if (strcmp(type, VDEV_TYPE_RAIDZ) == 0) {
verify(nvlist_add_uint64(nv,
ZPOOL_CONFIG_NPARITY,
@@ -788,8 +1013,10 @@ construct_spec(int argc, char **argv)
* We have a device. Pass off to make_leaf_vdev() to
* construct the appropriate nvlist describing the vdev.
*/
- if ((nv = make_leaf_vdev(argv[0])) == NULL)
+ if ((nv = make_leaf_vdev(argv[0], is_log)) == NULL)
return (NULL);
+ if (is_log)
+ nlogs++;
argc--;
argv++;
}
@@ -801,13 +1028,19 @@ construct_spec(int argc, char **argv)
top[toplevels - 1] = nv;
}
- if (toplevels == 0 && nspares == 0) {
+ if (toplevels == 0 && nspares == 0 && nl2cache == 0) {
(void) fprintf(stderr, gettext("invalid vdev "
"specification: at least one toplevel vdev must be "
"specified\n"));
return (NULL);
}
+ if (seen_logs && nlogs == 0) {
+ (void) fprintf(stderr, gettext("invalid vdev specification: "
+ "log requires at least 1 device\n"));
+ return (NULL);
+ }
+
/*
* Finally, create nvroot and add all top-level vdevs to it.
*/
@@ -819,18 +1052,26 @@ construct_spec(int argc, char **argv)
if (nspares != 0)
verify(nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
spares, nspares) == 0);
+ if (nl2cache != 0)
+ verify(nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE,
+ l2cache, nl2cache) == 0);
for (t = 0; t < toplevels; t++)
nvlist_free(top[t]);
for (t = 0; t < nspares; t++)
nvlist_free(spares[t]);
+ for (t = 0; t < nl2cache; t++)
+ nvlist_free(l2cache[t]);
if (spares)
free(spares);
+ if (l2cache)
+ free(l2cache);
free(top);
return (nvroot);
}
+
/*
* Get and validate the contents of the given vdev specification. This ensures
* that the nvlist returned is well-formed, that all the devices exist, and that
@@ -842,11 +1083,11 @@ construct_spec(int argc, char **argv)
* added, even if they appear in use.
*/
nvlist_t *
-make_root_vdev(nvlist_t *poolconfig, int force, int check_rep,
- boolean_t isreplacing, int argc, char **argv)
+make_root_vdev(zpool_handle_t *zhp, int force, int check_rep,
+ boolean_t isreplacing, boolean_t dryrun, int argc, char **argv)
{
nvlist_t *newroot;
-
+ nvlist_t *poolconfig = NULL;
is_force = force;
/*
@@ -857,6 +1098,9 @@ make_root_vdev(nvlist_t *poolconfig, int force, int check_rep,
if ((newroot = construct_spec(argc, argv)) == NULL)
return (NULL);
+ if (zhp && ((poolconfig = zpool_get_config(zhp, NULL)) == NULL))
+ return (NULL);
+
/*
* Validate each device to make sure that its not shared with another
* subsystem. We do this even if 'force' is set, because there are some
diff --git a/cddl/contrib/opensolaris/cmd/ztest/ztest.c b/cddl/contrib/opensolaris/cmd/ztest/ztest.c
index 5d9f028..b7ca302 100644
--- a/cddl/contrib/opensolaris/cmd/ztest/ztest.c
+++ b/cddl/contrib/opensolaris/cmd/ztest/ztest.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* The objective of this program is to provide a DMU/ZAP/SPA stress test
* that runs entirely in userland, is easy to use, and easy to extend.
@@ -92,6 +90,7 @@
#include <sys/zio_compress.h>
#include <sys/zil.h>
#include <sys/vdev_impl.h>
+#include <sys/vdev_file.h>
#include <sys/spa_impl.h>
#include <sys/dsl_prop.h>
#include <sys/refcount.h>
@@ -128,8 +127,18 @@ static char *zopt_dir = "/tmp";
static uint64_t zopt_time = 300; /* 5 minutes */
static int zopt_maxfaults;
+typedef struct ztest_block_tag {
+ uint64_t bt_objset;
+ uint64_t bt_object;
+ uint64_t bt_offset;
+ uint64_t bt_txg;
+ uint64_t bt_thread;
+ uint64_t bt_seq;
+} ztest_block_tag_t;
+
typedef struct ztest_args {
- char *za_pool;
+ char za_pool[MAXNAMELEN];
+ spa_t *za_spa;
objset_t *za_os;
zilog_t *za_zilog;
thread_t za_thread;
@@ -142,6 +151,13 @@ typedef struct ztest_args {
hrtime_t za_stop;
hrtime_t za_kill;
traverse_handle_t *za_th;
+ /*
+ * Thread-local variables can go here to aid debugging.
+ */
+ ztest_block_tag_t za_rbt;
+ ztest_block_tag_t za_wbt;
+ dmu_object_info_t za_doi;
+ dmu_buf_t *za_dbuf;
} ztest_args_t;
typedef void ztest_func_t(ztest_args_t *);
@@ -160,14 +176,16 @@ ztest_func_t ztest_dmu_objset_create_destroy;
ztest_func_t ztest_dmu_snapshot_create_destroy;
ztest_func_t ztest_spa_create_destroy;
ztest_func_t ztest_fault_inject;
+ztest_func_t ztest_spa_rename;
ztest_func_t ztest_vdev_attach_detach;
ztest_func_t ztest_vdev_LUN_growth;
ztest_func_t ztest_vdev_add_remove;
+ztest_func_t ztest_vdev_aux_add_remove;
ztest_func_t ztest_scrub;
-ztest_func_t ztest_spa_rename;
typedef struct ztest_info {
ztest_func_t *zi_func; /* test function */
+ uint64_t zi_iters; /* iterations per execution */
uint64_t *zi_interval; /* execute every <interval> seconds */
uint64_t zi_calls; /* per-pass count */
uint64_t zi_call_time; /* per-pass time */
@@ -181,22 +199,23 @@ uint64_t zopt_sometimes = 10; /* every 10 seconds */
uint64_t zopt_rarely = 60; /* every 60 seconds */
ztest_info_t ztest_info[] = {
- { ztest_dmu_read_write, &zopt_always },
- { ztest_dmu_write_parallel, &zopt_always },
- { ztest_dmu_object_alloc_free, &zopt_always },
- { ztest_zap, &zopt_always },
- { ztest_zap_parallel, &zopt_always },
- { ztest_traverse, &zopt_often },
- { ztest_dsl_prop_get_set, &zopt_sometimes },
- { ztest_dmu_objset_create_destroy, &zopt_sometimes },
- { ztest_dmu_snapshot_create_destroy, &zopt_rarely },
- { ztest_spa_create_destroy, &zopt_sometimes },
- { ztest_fault_inject, &zopt_sometimes },
- { ztest_spa_rename, &zopt_rarely },
- { ztest_vdev_attach_detach, &zopt_rarely },
- { ztest_vdev_LUN_growth, &zopt_rarely },
- { ztest_vdev_add_remove, &zopt_vdevtime },
- { ztest_scrub, &zopt_vdevtime },
+ { ztest_dmu_read_write, 1, &zopt_always },
+ { ztest_dmu_write_parallel, 30, &zopt_always },
+ { ztest_dmu_object_alloc_free, 1, &zopt_always },
+ { ztest_zap, 30, &zopt_always },
+ { ztest_zap_parallel, 100, &zopt_always },
+ { ztest_traverse, 1, &zopt_often },
+ { ztest_dsl_prop_get_set, 1, &zopt_sometimes },
+ { ztest_dmu_objset_create_destroy, 1, &zopt_sometimes },
+ { ztest_dmu_snapshot_create_destroy, 1, &zopt_sometimes },
+ { ztest_spa_create_destroy, 1, &zopt_sometimes },
+ { ztest_fault_inject, 1, &zopt_sometimes },
+ { ztest_spa_rename, 1, &zopt_rarely },
+ { ztest_vdev_attach_detach, 1, &zopt_rarely },
+ { ztest_vdev_LUN_growth, 1, &zopt_rarely },
+ { ztest_vdev_add_remove, 1, &zopt_vdevtime },
+ { ztest_vdev_aux_add_remove, 1, &zopt_vdevtime },
+ { ztest_scrub, 1, &zopt_vdevtime },
};
#define ZTEST_FUNCS (sizeof (ztest_info) / sizeof (ztest_info_t))
@@ -210,34 +229,27 @@ typedef struct ztest_shared {
mutex_t zs_vdev_lock;
rwlock_t zs_name_lock;
uint64_t zs_vdev_primaries;
+ uint64_t zs_vdev_aux;
uint64_t zs_enospc_count;
hrtime_t zs_start_time;
hrtime_t zs_stop_time;
uint64_t zs_alloc;
uint64_t zs_space;
- uint64_t zs_txg;
ztest_info_t zs_info[ZTEST_FUNCS];
mutex_t zs_sync_lock[ZTEST_SYNC_LOCKS];
uint64_t zs_seq[ZTEST_SYNC_LOCKS];
} ztest_shared_t;
-typedef struct ztest_block_tag {
- uint64_t bt_objset;
- uint64_t bt_object;
- uint64_t bt_offset;
- uint64_t bt_txg;
- uint64_t bt_thread;
- uint64_t bt_seq;
-} ztest_block_tag_t;
-
static char ztest_dev_template[] = "%s/%s.%llua";
+static char ztest_aux_template[] = "%s/%s.%s.%llu";
static ztest_shared_t *ztest_shared;
static int ztest_random_fd;
static int ztest_dump_core = 1;
-extern uint64_t zio_gang_bang;
-extern uint16_t zio_zil_fail_shift;
+static boolean_t ztest_exiting;
+
+extern uint64_t metaslab_gang_bang;
#define ZTEST_DIROBJ 1
#define ZTEST_MICROZAP_OBJ 2
@@ -357,7 +369,7 @@ usage(boolean_t requested)
FILE *fp = requested ? stdout : stderr;
nicenum(zopt_vdev_size, nice_vdev_size);
- nicenum(zio_gang_bang, nice_gang_bang);
+ nicenum(metaslab_gang_bang, nice_gang_bang);
(void) fprintf(fp, "Usage: %s\n"
"\t[-v vdevs (default: %llu)]\n"
@@ -377,26 +389,24 @@ usage(boolean_t requested)
"\t[-E(xisting)] (use existing pool instead of creating new one)\n"
"\t[-T time] total run time (default: %llu sec)\n"
"\t[-P passtime] time per pass (default: %llu sec)\n"
- "\t[-z zil failure rate (default: fail every 2^%llu allocs)]\n"
"\t[-h] (print help)\n"
"",
cmdname,
- (u_longlong_t)zopt_vdevs, /* -v */
- nice_vdev_size, /* -s */
- zopt_ashift, /* -a */
- zopt_mirrors, /* -m */
- zopt_raidz, /* -r */
- zopt_raidz_parity, /* -R */
- zopt_datasets, /* -d */
- zopt_threads, /* -t */
- nice_gang_bang, /* -g */
- zopt_init, /* -i */
- (u_longlong_t)zopt_killrate, /* -k */
- zopt_pool, /* -p */
- zopt_dir, /* -f */
- (u_longlong_t)zopt_time, /* -T */
- (u_longlong_t)zopt_passtime, /* -P */
- (u_longlong_t)zio_zil_fail_shift); /* -z */
+ (u_longlong_t)zopt_vdevs, /* -v */
+ nice_vdev_size, /* -s */
+ zopt_ashift, /* -a */
+ zopt_mirrors, /* -m */
+ zopt_raidz, /* -r */
+ zopt_raidz_parity, /* -R */
+ zopt_datasets, /* -d */
+ zopt_threads, /* -t */
+ nice_gang_bang, /* -g */
+ zopt_init, /* -i */
+ (u_longlong_t)zopt_killrate, /* -k */
+ zopt_pool, /* -p */
+ zopt_dir, /* -f */
+ (u_longlong_t)zopt_time, /* -T */
+ (u_longlong_t)zopt_passtime); /* -P */
exit(requested ? 0 : 1);
}
@@ -431,91 +441,84 @@ process_options(int argc, char **argv)
progname = argv[0];
/* By default, test gang blocks for blocks 32K and greater */
- zio_gang_bang = 32 << 10;
-
- /* Default value, fail every 32nd allocation */
- zio_zil_fail_shift = 5;
+ metaslab_gang_bang = 32 << 10;
while ((opt = getopt(argc, argv,
- "v:s:a:m:r:R:d:t:g:i:k:p:f:VET:P:z:h")) != EOF) {
+ "v:s:a:m:r:R:d:t:g:i:k:p:f:VET:P:h")) != EOF) {
value = 0;
switch (opt) {
- case 'v':
- case 's':
- case 'a':
- case 'm':
- case 'r':
- case 'R':
- case 'd':
- case 't':
- case 'g':
- case 'i':
- case 'k':
- case 'T':
- case 'P':
- case 'z':
+ case 'v':
+ case 's':
+ case 'a':
+ case 'm':
+ case 'r':
+ case 'R':
+ case 'd':
+ case 't':
+ case 'g':
+ case 'i':
+ case 'k':
+ case 'T':
+ case 'P':
value = nicenumtoull(optarg);
}
switch (opt) {
- case 'v':
+ case 'v':
zopt_vdevs = value;
break;
- case 's':
+ case 's':
zopt_vdev_size = MAX(SPA_MINDEVSIZE, value);
break;
- case 'a':
+ case 'a':
zopt_ashift = value;
break;
- case 'm':
+ case 'm':
zopt_mirrors = value;
break;
- case 'r':
+ case 'r':
zopt_raidz = MAX(1, value);
break;
- case 'R':
+ case 'R':
zopt_raidz_parity = MIN(MAX(value, 1), 2);
break;
- case 'd':
+ case 'd':
zopt_datasets = MAX(1, value);
break;
- case 't':
+ case 't':
zopt_threads = MAX(1, value);
break;
- case 'g':
- zio_gang_bang = MAX(SPA_MINBLOCKSIZE << 1, value);
+ case 'g':
+ metaslab_gang_bang = MAX(SPA_MINBLOCKSIZE << 1, value);
break;
- case 'i':
+ case 'i':
zopt_init = value;
break;
- case 'k':
+ case 'k':
zopt_killrate = value;
break;
- case 'p':
+ case 'p':
zopt_pool = strdup(optarg);
break;
- case 'f':
+ case 'f':
zopt_dir = strdup(optarg);
break;
- case 'V':
+ case 'V':
zopt_verbose++;
break;
- case 'E':
+ case 'E':
zopt_init = 0;
break;
- case 'T':
+ case 'T':
zopt_time = value;
break;
- case 'P':
+ case 'P':
zopt_passtime = MAX(1, value);
break;
- case 'z':
- zio_zil_fail_shift = MIN(value, 16);
- break;
- case 'h':
+ case 'h':
usage(B_TRUE);
break;
- case '?':
- default:
+ case '?':
+ default:
usage(B_FALSE);
break;
}
@@ -536,51 +539,58 @@ ztest_get_ashift(void)
}
static nvlist_t *
-make_vdev_file(size_t size)
+make_vdev_file(char *path, char *aux, size_t size, uint64_t ashift)
{
- char dev_name[MAXPATHLEN];
+ char pathbuf[MAXPATHLEN];
uint64_t vdev;
- uint64_t ashift = ztest_get_ashift();
- int fd;
nvlist_t *file;
- if (size == 0) {
- (void) snprintf(dev_name, sizeof (dev_name), "%s",
- "/dev/bogus");
- } else {
- vdev = ztest_shared->zs_vdev_primaries++;
- (void) sprintf(dev_name, ztest_dev_template,
- zopt_dir, zopt_pool, vdev);
+ if (ashift == 0)
+ ashift = ztest_get_ashift();
+
+ if (path == NULL) {
+ path = pathbuf;
+
+ if (aux != NULL) {
+ vdev = ztest_shared->zs_vdev_aux;
+ (void) sprintf(path, ztest_aux_template,
+ zopt_dir, zopt_pool, aux, vdev);
+ } else {
+ vdev = ztest_shared->zs_vdev_primaries++;
+ (void) sprintf(path, ztest_dev_template,
+ zopt_dir, zopt_pool, vdev);
+ }
+ }
- fd = open(dev_name, O_RDWR | O_CREAT | O_TRUNC, 0666);
+ if (size != 0) {
+ int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd == -1)
- fatal(1, "can't open %s", dev_name);
+ fatal(1, "can't open %s", path);
if (ftruncate(fd, size) != 0)
- fatal(1, "can't ftruncate %s", dev_name);
+ fatal(1, "can't ftruncate %s", path);
(void) close(fd);
}
VERIFY(nvlist_alloc(&file, NV_UNIQUE_NAME, 0) == 0);
VERIFY(nvlist_add_string(file, ZPOOL_CONFIG_TYPE, VDEV_TYPE_FILE) == 0);
- VERIFY(nvlist_add_string(file, ZPOOL_CONFIG_PATH, dev_name) == 0);
+ VERIFY(nvlist_add_string(file, ZPOOL_CONFIG_PATH, path) == 0);
VERIFY(nvlist_add_uint64(file, ZPOOL_CONFIG_ASHIFT, ashift) == 0);
return (file);
}
static nvlist_t *
-make_vdev_raidz(size_t size, int r)
+make_vdev_raidz(char *path, char *aux, size_t size, uint64_t ashift, int r)
{
nvlist_t *raidz, **child;
int c;
if (r < 2)
- return (make_vdev_file(size));
-
+ return (make_vdev_file(path, aux, size, ashift));
child = umem_alloc(r * sizeof (nvlist_t *), UMEM_NOFAIL);
for (c = 0; c < r; c++)
- child[c] = make_vdev_file(size);
+ child[c] = make_vdev_file(path, aux, size, ashift);
VERIFY(nvlist_alloc(&raidz, NV_UNIQUE_NAME, 0) == 0);
VERIFY(nvlist_add_string(raidz, ZPOOL_CONFIG_TYPE,
@@ -599,18 +609,19 @@ make_vdev_raidz(size_t size, int r)
}
static nvlist_t *
-make_vdev_mirror(size_t size, int r, int m)
+make_vdev_mirror(char *path, char *aux, size_t size, uint64_t ashift,
+ int r, int m)
{
nvlist_t *mirror, **child;
int c;
if (m < 1)
- return (make_vdev_raidz(size, r));
+ return (make_vdev_raidz(path, aux, size, ashift, r));
child = umem_alloc(m * sizeof (nvlist_t *), UMEM_NOFAIL);
for (c = 0; c < m; c++)
- child[c] = make_vdev_raidz(size, r);
+ child[c] = make_vdev_raidz(path, aux, size, ashift, r);
VERIFY(nvlist_alloc(&mirror, NV_UNIQUE_NAME, 0) == 0);
VERIFY(nvlist_add_string(mirror, ZPOOL_CONFIG_TYPE,
@@ -627,7 +638,8 @@ make_vdev_mirror(size_t size, int r, int m)
}
static nvlist_t *
-make_vdev_root(size_t size, int r, int m, int t)
+make_vdev_root(char *path, char *aux, size_t size, uint64_t ashift,
+ int log, int r, int m, int t)
{
nvlist_t *root, **child;
int c;
@@ -636,12 +648,15 @@ make_vdev_root(size_t size, int r, int m, int t)
child = umem_alloc(t * sizeof (nvlist_t *), UMEM_NOFAIL);
- for (c = 0; c < t; c++)
- child[c] = make_vdev_mirror(size, r, m);
+ for (c = 0; c < t; c++) {
+ child[c] = make_vdev_mirror(path, aux, size, ashift, r, m);
+ VERIFY(nvlist_add_uint64(child[c], ZPOOL_CONFIG_IS_LOG,
+ log) == 0);
+ }
VERIFY(nvlist_alloc(&root, NV_UNIQUE_NAME, 0) == 0);
VERIFY(nvlist_add_string(root, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT) == 0);
- VERIFY(nvlist_add_nvlist_array(root, ZPOOL_CONFIG_CHILDREN,
+ VERIFY(nvlist_add_nvlist_array(root, aux ? aux : ZPOOL_CONFIG_CHILDREN,
child, t) == 0);
for (c = 0; c < t; c++)
@@ -785,8 +800,8 @@ ztest_spa_create_destroy(ztest_args_t *za)
/*
* Attempt to create using a bad file.
*/
- nvroot = make_vdev_root(0, 0, 0, 1);
- error = spa_create("ztest_bad_file", nvroot, NULL);
+ nvroot = make_vdev_root("/dev/bogus", NULL, 0, 0, 0, 0, 0, 1);
+ error = spa_create("ztest_bad_file", nvroot, NULL, NULL, NULL);
nvlist_free(nvroot);
if (error != ENOENT)
fatal(0, "spa_create(bad_file) = %d", error);
@@ -794,8 +809,8 @@ ztest_spa_create_destroy(ztest_args_t *za)
/*
* Attempt to create using a bad mirror.
*/
- nvroot = make_vdev_root(0, 0, 2, 1);
- error = spa_create("ztest_bad_mirror", nvroot, NULL);
+ nvroot = make_vdev_root("/dev/bogus", NULL, 0, 0, 0, 0, 2, 1);
+ error = spa_create("ztest_bad_mirror", nvroot, NULL, NULL, NULL);
nvlist_free(nvroot);
if (error != ENOENT)
fatal(0, "spa_create(bad_mirror) = %d", error);
@@ -805,8 +820,8 @@ ztest_spa_create_destroy(ztest_args_t *za)
* what's in the nvroot; we should fail with EEXIST.
*/
(void) rw_rdlock(&ztest_shared->zs_name_lock);
- nvroot = make_vdev_root(0, 0, 0, 1);
- error = spa_create(za->za_pool, nvroot, NULL);
+ nvroot = make_vdev_root("/dev/bogus", NULL, 0, 0, 0, 0, 0, 1);
+ error = spa_create(za->za_pool, nvroot, NULL, NULL, NULL);
nvlist_free(nvroot);
if (error != EEXIST)
fatal(0, "spa_create(whatever) = %d", error);
@@ -823,30 +838,48 @@ ztest_spa_create_destroy(ztest_args_t *za)
(void) rw_unlock(&ztest_shared->zs_name_lock);
}
+static vdev_t *
+vdev_lookup_by_path(vdev_t *vd, const char *path)
+{
+ vdev_t *mvd;
+
+ if (vd->vdev_path != NULL && strcmp(path, vd->vdev_path) == 0)
+ return (vd);
+
+ for (int c = 0; c < vd->vdev_children; c++)
+ if ((mvd = vdev_lookup_by_path(vd->vdev_child[c], path)) !=
+ NULL)
+ return (mvd);
+
+ return (NULL);
+}
+
/*
* Verify that vdev_add() works as expected.
*/
void
ztest_vdev_add_remove(ztest_args_t *za)
{
- spa_t *spa = dmu_objset_spa(za->za_os);
+ spa_t *spa = za->za_spa;
uint64_t leaves = MAX(zopt_mirrors, 1) * zopt_raidz;
nvlist_t *nvroot;
int error;
- if (zopt_verbose >= 6)
- (void) printf("adding vdev\n");
-
(void) mutex_lock(&ztest_shared->zs_vdev_lock);
- spa_config_enter(spa, RW_READER, FTAG);
+ spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
ztest_shared->zs_vdev_primaries =
spa->spa_root_vdev->vdev_children * leaves;
- spa_config_exit(spa, FTAG);
+ spa_config_exit(spa, SCL_VDEV, FTAG);
+
+ /*
+ * Make 1/4 of the devices be log devices.
+ */
+ nvroot = make_vdev_root(NULL, NULL, zopt_vdev_size, 0,
+ ztest_random(4) == 0, zopt_raidz, zopt_mirrors, 1);
- nvroot = make_vdev_root(zopt_vdev_size, zopt_raidz, zopt_mirrors, 1);
error = spa_vdev_add(spa, nvroot);
nvlist_free(nvroot);
@@ -856,37 +889,86 @@ ztest_vdev_add_remove(ztest_args_t *za)
ztest_record_enospc("spa_vdev_add");
else if (error != 0)
fatal(0, "spa_vdev_add() = %d", error);
-
- if (zopt_verbose >= 6)
- (void) printf("spa_vdev_add = %d, as expected\n", error);
}
-static vdev_t *
-vdev_lookup_by_path(vdev_t *vd, const char *path)
+/*
+ * Verify that adding/removing aux devices (l2arc, hot spare) works as expected.
+ */
+void
+ztest_vdev_aux_add_remove(ztest_args_t *za)
{
- int c;
- vdev_t *mvd;
+ spa_t *spa = za->za_spa;
+ vdev_t *rvd = spa->spa_root_vdev;
+ spa_aux_vdev_t *sav;
+ char *aux;
+ uint64_t guid = 0;
+ int error;
- if (vd->vdev_path != NULL) {
- if (vd->vdev_wholedisk == 1) {
- /*
- * For whole disks, the internal path has 's0', but the
- * path passed in by the user doesn't.
- */
- if (strlen(path) == strlen(vd->vdev_path) - 2 &&
- strncmp(path, vd->vdev_path, strlen(path)) == 0)
- return (vd);
- } else if (strcmp(path, vd->vdev_path) == 0) {
- return (vd);
+ if (ztest_random(2) == 0) {
+ sav = &spa->spa_spares;
+ aux = ZPOOL_CONFIG_SPARES;
+ } else {
+ sav = &spa->spa_l2cache;
+ aux = ZPOOL_CONFIG_L2CACHE;
+ }
+
+ (void) mutex_lock(&ztest_shared->zs_vdev_lock);
+
+ spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
+
+ if (sav->sav_count != 0 && ztest_random(4) == 0) {
+ /*
+ * Pick a random device to remove.
+ */
+ guid = sav->sav_vdevs[ztest_random(sav->sav_count)]->vdev_guid;
+ } else {
+ /*
+ * Find an unused device we can add.
+ */
+ ztest_shared->zs_vdev_aux = 0;
+ for (;;) {
+ char path[MAXPATHLEN];
+ int c;
+ (void) sprintf(path, ztest_aux_template, zopt_dir,
+ zopt_pool, aux, ztest_shared->zs_vdev_aux);
+ for (c = 0; c < sav->sav_count; c++)
+ if (strcmp(sav->sav_vdevs[c]->vdev_path,
+ path) == 0)
+ break;
+ if (c == sav->sav_count &&
+ vdev_lookup_by_path(rvd, path) == NULL)
+ break;
+ ztest_shared->zs_vdev_aux++;
}
}
- for (c = 0; c < vd->vdev_children; c++)
- if ((mvd = vdev_lookup_by_path(vd->vdev_child[c], path)) !=
- NULL)
- return (mvd);
+ spa_config_exit(spa, SCL_VDEV, FTAG);
- return (NULL);
+ if (guid == 0) {
+ /*
+ * Add a new device.
+ */
+ nvlist_t *nvroot = make_vdev_root(NULL, aux,
+ (zopt_vdev_size * 5) / 4, 0, 0, 0, 0, 1);
+ error = spa_vdev_add(spa, nvroot);
+ if (error != 0)
+ fatal(0, "spa_vdev_add(%p) = %d", nvroot, error);
+ nvlist_free(nvroot);
+ } else {
+ /*
+ * Remove an existing device. Sometimes, dirty its
+ * vdev state first to make sure we handle removal
+ * of devices that have pending state changes.
+ */
+ if (ztest_random(2) == 0)
+ (void) vdev_online(spa, guid, B_FALSE, NULL);
+
+ error = spa_vdev_remove(spa, guid, B_FALSE);
+ if (error != 0 && error != EBUSY)
+ fatal(0, "spa_vdev_remove(%llu) = %d", guid, error);
+ }
+
+ (void) mutex_unlock(&ztest_shared->zs_vdev_lock);
}
/*
@@ -895,22 +977,26 @@ vdev_lookup_by_path(vdev_t *vd, const char *path)
void
ztest_vdev_attach_detach(ztest_args_t *za)
{
- spa_t *spa = dmu_objset_spa(za->za_os);
+ spa_t *spa = za->za_spa;
+ spa_aux_vdev_t *sav = &spa->spa_spares;
vdev_t *rvd = spa->spa_root_vdev;
vdev_t *oldvd, *newvd, *pvd;
- nvlist_t *root, *file;
+ nvlist_t *root;
uint64_t leaves = MAX(zopt_mirrors, 1) * zopt_raidz;
uint64_t leaf, top;
uint64_t ashift = ztest_get_ashift();
+ uint64_t oldguid;
size_t oldsize, newsize;
char oldpath[MAXPATHLEN], newpath[MAXPATHLEN];
int replacing;
+ int oldvd_has_siblings = B_FALSE;
+ int newvd_is_spare = B_FALSE;
+ int oldvd_is_log;
int error, expected_error;
- int fd;
(void) mutex_lock(&ztest_shared->zs_vdev_lock);
- spa_config_enter(spa, RW_READER, FTAG);
+ spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
/*
* Decide whether to do an attach or a replace.
@@ -928,39 +1014,70 @@ ztest_vdev_attach_detach(ztest_args_t *za)
leaf = ztest_random(leaves);
/*
- * Generate the path to this leaf. The filename will end with 'a'.
- * We'll alternate replacements with a filename that ends with 'b'.
+ * Locate this vdev.
*/
- (void) snprintf(oldpath, sizeof (oldpath),
- ztest_dev_template, zopt_dir, zopt_pool, top * leaves + leaf);
-
- bcopy(oldpath, newpath, MAXPATHLEN);
+ oldvd = rvd->vdev_child[top];
+ if (zopt_mirrors >= 1)
+ oldvd = oldvd->vdev_child[leaf / zopt_raidz];
+ if (zopt_raidz > 1)
+ oldvd = oldvd->vdev_child[leaf % zopt_raidz];
/*
- * If the 'a' file isn't part of the pool, the 'b' file must be.
+ * If we're already doing an attach or replace, oldvd may be a
+ * mirror vdev -- in which case, pick a random child.
*/
- if (vdev_lookup_by_path(rvd, oldpath) == NULL)
- oldpath[strlen(oldpath) - 1] = 'b';
- else
- newpath[strlen(newpath) - 1] = 'b';
+ while (oldvd->vdev_children != 0) {
+ oldvd_has_siblings = B_TRUE;
+ ASSERT(oldvd->vdev_children == 2);
+ oldvd = oldvd->vdev_child[ztest_random(2)];
+ }
+
+ oldguid = oldvd->vdev_guid;
+ oldsize = vdev_get_rsize(oldvd);
+ oldvd_is_log = oldvd->vdev_top->vdev_islog;
+ (void) strcpy(oldpath, oldvd->vdev_path);
+ pvd = oldvd->vdev_parent;
/*
- * Now oldpath represents something that's already in the pool,
- * and newpath is the thing we'll try to attach.
+ * If oldvd has siblings, then half of the time, detach it.
*/
- oldvd = vdev_lookup_by_path(rvd, oldpath);
- newvd = vdev_lookup_by_path(rvd, newpath);
- ASSERT(oldvd != NULL);
- pvd = oldvd->vdev_parent;
+ if (oldvd_has_siblings && ztest_random(2) == 0) {
+ spa_config_exit(spa, SCL_VDEV, FTAG);
+ error = spa_vdev_detach(spa, oldguid, B_FALSE);
+ if (error != 0 && error != ENODEV && error != EBUSY)
+ fatal(0, "detach (%s) returned %d",
+ oldpath, error);
+ (void) mutex_unlock(&ztest_shared->zs_vdev_lock);
+ return;
+ }
/*
- * Make newsize a little bigger or smaller than oldsize.
- * If it's smaller, the attach should fail.
- * If it's larger, and we're doing a replace,
- * we should get dynamic LUN growth when we're done.
+ * For the new vdev, choose with equal probability between the two
+ * standard paths (ending in either 'a' or 'b') or a random hot spare.
*/
- oldsize = vdev_get_rsize(oldvd);
- newsize = 10 * oldsize / (9 + ztest_random(3));
+ if (sav->sav_count != 0 && ztest_random(3) == 0) {
+ newvd = sav->sav_vdevs[ztest_random(sav->sav_count)];
+ newvd_is_spare = B_TRUE;
+ (void) strcpy(newpath, newvd->vdev_path);
+ } else {
+ (void) snprintf(newpath, sizeof (newpath), ztest_dev_template,
+ zopt_dir, zopt_pool, top * leaves + leaf);
+ if (ztest_random(2) == 0)
+ newpath[strlen(newpath) - 1] = 'b';
+ newvd = vdev_lookup_by_path(rvd, newpath);
+ }
+
+ if (newvd) {
+ newsize = vdev_get_rsize(newvd);
+ } else {
+ /*
+ * Make newsize a little bigger or smaller than oldsize.
+ * If it's smaller, the attach should fail.
+ * If it's larger, and we're doing a replace,
+ * we should get dynamic LUN growth when we're done.
+ */
+ newsize = 10 * oldsize / (9 + ztest_random(3));
+ }
/*
* If pvd is not a mirror or root, the attach should fail with ENOTSUP,
@@ -970,12 +1087,17 @@ ztest_vdev_attach_detach(ztest_args_t *za)
*
* If newvd is too small, it should fail with EOVERFLOW.
*/
- if (newvd != NULL)
- expected_error = EBUSY;
- else if (pvd->vdev_ops != &vdev_mirror_ops &&
- pvd->vdev_ops != &vdev_root_ops &&
- (!replacing || pvd->vdev_ops == &vdev_replacing_ops))
+ if (pvd->vdev_ops != &vdev_mirror_ops &&
+ pvd->vdev_ops != &vdev_root_ops && (!replacing ||
+ pvd->vdev_ops == &vdev_replacing_ops ||
+ pvd->vdev_ops == &vdev_spare_ops))
+ expected_error = ENOTSUP;
+ else if (newvd_is_spare && (!replacing || oldvd_is_log))
expected_error = ENOTSUP;
+ else if (newvd == oldvd)
+ expected_error = replacing ? 0 : EBUSY;
+ else if (vdev_lookup_by_path(rvd, newpath) != NULL)
+ expected_error = EBUSY;
else if (newsize < oldsize)
expected_error = EOVERFLOW;
else if (ashift > oldvd->vdev_top->vdev_ashift)
@@ -983,36 +1105,16 @@ ztest_vdev_attach_detach(ztest_args_t *za)
else
expected_error = 0;
- /*
- * If newvd isn't already part of the pool, create it.
- */
- if (newvd == NULL) {
- fd = open(newpath, O_RDWR | O_CREAT | O_TRUNC, 0666);
- if (fd == -1)
- fatal(1, "can't open %s", newpath);
- if (ftruncate(fd, newsize) != 0)
- fatal(1, "can't ftruncate %s", newpath);
- (void) close(fd);
- }
-
- spa_config_exit(spa, FTAG);
+ spa_config_exit(spa, SCL_VDEV, FTAG);
/*
* Build the nvlist describing newpath.
*/
- VERIFY(nvlist_alloc(&file, NV_UNIQUE_NAME, 0) == 0);
- VERIFY(nvlist_add_string(file, ZPOOL_CONFIG_TYPE, VDEV_TYPE_FILE) == 0);
- VERIFY(nvlist_add_string(file, ZPOOL_CONFIG_PATH, newpath) == 0);
- VERIFY(nvlist_add_uint64(file, ZPOOL_CONFIG_ASHIFT, ashift) == 0);
+ root = make_vdev_root(newpath, NULL, newvd == NULL ? newsize : 0,
+ ashift, 0, 0, 0, 1);
- VERIFY(nvlist_alloc(&root, NV_UNIQUE_NAME, 0) == 0);
- VERIFY(nvlist_add_string(root, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT) == 0);
- VERIFY(nvlist_add_nvlist_array(root, ZPOOL_CONFIG_CHILDREN,
- &file, 1) == 0);
-
- error = spa_vdev_attach(spa, oldvd->vdev_guid, root, replacing);
+ error = spa_vdev_attach(spa, oldguid, root, replacing);
- nvlist_free(file);
nvlist_free(root);
/*
@@ -1027,12 +1129,15 @@ ztest_vdev_attach_detach(ztest_args_t *za)
/*
* If someone grew the LUN, the replacement may be too small.
*/
- if (error == EOVERFLOW)
+ if (error == EOVERFLOW || error == EBUSY)
expected_error = error;
- if (error != expected_error) {
- fatal(0, "attach (%s, %s, %d) returned %d, expected %d",
- oldpath, newpath, replacing, error, expected_error);
+ /* XXX workaround 6690467 */
+ if (error != expected_error && expected_error != EBUSY) {
+ fatal(0, "attach (%s %llu, %s %llu, %d) "
+ "returned %d, expected %d",
+ oldpath, (longlong_t)oldsize, newpath,
+ (longlong_t)newsize, replacing, error, expected_error);
}
(void) mutex_unlock(&ztest_shared->zs_vdev_lock);
@@ -1045,7 +1150,7 @@ ztest_vdev_attach_detach(ztest_args_t *za)
void
ztest_vdev_LUN_growth(ztest_args_t *za)
{
- spa_t *spa = dmu_objset_spa(za->za_os);
+ spa_t *spa = za->za_spa;
char dev_name[MAXPATHLEN];
uint64_t leaves = MAX(zopt_mirrors, 1) * zopt_raidz;
uint64_t vdev;
@@ -1057,9 +1162,9 @@ ztest_vdev_LUN_growth(ztest_args_t *za)
/*
* Pick a random leaf vdev.
*/
- spa_config_enter(spa, RW_READER, FTAG);
+ spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
vdev = ztest_random(spa->spa_root_vdev->vdev_children * leaves);
- spa_config_exit(spa, FTAG);
+ spa_config_exit(spa, SCL_VDEV, FTAG);
(void) sprintf(dev_name, ztest_dev_template, zopt_dir, zopt_pool, vdev);
@@ -1088,14 +1193,14 @@ ztest_vdev_LUN_growth(ztest_args_t *za)
/* ARGSUSED */
static void
-ztest_create_cb(objset_t *os, void *arg, dmu_tx_t *tx)
+ztest_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx)
{
/*
* Create the directory object.
*/
VERIFY(dmu_object_claim(os, ZTEST_DIROBJ,
DMU_OT_UINT64_OTHER, ZTEST_DIROBJ_BLOCKSIZE,
- DMU_OT_UINT64_OTHER, sizeof (ztest_block_tag_t), tx) == 0);
+ DMU_OT_UINT64_OTHER, 5 * sizeof (ztest_block_tag_t), tx) == 0);
VERIFY(zap_create_claim(os, ZTEST_MICROZAP_OBJ,
DMU_OT_ZAP_OTHER, DMU_OT_NONE, 0, tx) == 0);
@@ -1104,26 +1209,26 @@ ztest_create_cb(objset_t *os, void *arg, dmu_tx_t *tx)
DMU_OT_ZAP_OTHER, DMU_OT_NONE, 0, tx) == 0);
}
-/* ARGSUSED */
static int
ztest_destroy_cb(char *name, void *arg)
{
+ ztest_args_t *za = arg;
objset_t *os;
- dmu_object_info_t doi;
+ dmu_object_info_t *doi = &za->za_doi;
int error;
/*
* Verify that the dataset contains a directory object.
*/
error = dmu_objset_open(name, DMU_OST_OTHER,
- DS_MODE_STANDARD | DS_MODE_READONLY, &os);
+ DS_MODE_USER | DS_MODE_READONLY, &os);
ASSERT3U(error, ==, 0);
- error = dmu_object_info(os, ZTEST_DIROBJ, &doi);
+ error = dmu_object_info(os, ZTEST_DIROBJ, doi);
if (error != ENOENT) {
/* We could have crashed in the middle of destroying it */
ASSERT3U(error, ==, 0);
- ASSERT3U(doi.doi_type, ==, DMU_OT_UINT64_OTHER);
- ASSERT3S(doi.doi_physical_blks, >=, 0);
+ ASSERT3U(doi->doi_type, ==, DMU_OT_UINT64_OTHER);
+ ASSERT3S(doi->doi_physical_blks, >=, 0);
}
dmu_objset_close(os);
@@ -1131,7 +1236,11 @@ ztest_destroy_cb(char *name, void *arg)
* Destroy the dataset.
*/
error = dmu_objset_destroy(name);
- ASSERT3U(error, ==, 0);
+ if (error) {
+ (void) dmu_objset_open(name, DMU_OST_OTHER,
+ DS_MODE_USER | DS_MODE_READONLY, &os);
+ fatal(0, "dmu_objset_destroy(os=%p) = %d\n", &os, error);
+ }
return (0);
}
@@ -1171,9 +1280,9 @@ void
ztest_dmu_objset_create_destroy(ztest_args_t *za)
{
int error;
- objset_t *os;
+ objset_t *os, *os2;
char name[100];
- int mode, basemode, expected_error;
+ int basemode, expected_error;
zilog_t *zilog;
uint64_t seq;
uint64_t objects;
@@ -1183,9 +1292,9 @@ ztest_dmu_objset_create_destroy(ztest_args_t *za)
(void) snprintf(name, 100, "%s/%s_temp_%llu", za->za_pool, za->za_pool,
(u_longlong_t)za->za_instance);
- basemode = DS_MODE_LEVEL(za->za_instance);
- if (basemode == DS_MODE_NONE)
- basemode++;
+ basemode = DS_MODE_TYPE(za->za_instance);
+ if (basemode != DS_MODE_USER && basemode != DS_MODE_OWNER)
+ basemode = DS_MODE_USER;
/*
* If this dataset exists from a previous run, process its replay log
@@ -1193,9 +1302,9 @@ ztest_dmu_objset_create_destroy(ztest_args_t *za)
* (invoked from ztest_destroy_cb() below) should just throw it away.
*/
if (ztest_random(2) == 0 &&
- dmu_objset_open(name, DMU_OST_OTHER, DS_MODE_PRIMARY, &os) == 0) {
+ dmu_objset_open(name, DMU_OST_OTHER, DS_MODE_OWNER, &os) == 0) {
zr.zr_os = os;
- zil_replay(os, &zr, &zr.zr_assign, ztest_replay_vector);
+ zil_replay(os, &zr, &zr.zr_assign, ztest_replay_vector, NULL);
dmu_objset_close(os);
}
@@ -1204,7 +1313,7 @@ ztest_dmu_objset_create_destroy(ztest_args_t *za)
* create lying around from a previous run. If so, destroy it
* and all of its snapshots.
*/
- (void) dmu_objset_find(name, ztest_destroy_cb, NULL,
+ (void) dmu_objset_find(name, ztest_destroy_cb, za,
DS_FIND_CHILDREN | DS_FIND_SNAPSHOTS);
/*
@@ -1218,8 +1327,8 @@ ztest_dmu_objset_create_destroy(ztest_args_t *za)
/*
* Verify that we can create a new dataset.
*/
- error = dmu_objset_create(name, DMU_OST_OTHER, NULL, ztest_create_cb,
- NULL);
+ error = dmu_objset_create(name, DMU_OST_OTHER, NULL, 0,
+ ztest_create_cb, NULL);
if (error) {
if (error == ENOSPC) {
ztest_record_enospc("dmu_objset_create");
@@ -1274,26 +1383,29 @@ ztest_dmu_objset_create_destroy(ztest_args_t *za)
/*
* Verify that we cannot create an existing dataset.
*/
- error = dmu_objset_create(name, DMU_OST_OTHER, NULL, NULL, NULL);
+ error = dmu_objset_create(name, DMU_OST_OTHER, NULL, 0, NULL, NULL);
if (error != EEXIST)
fatal(0, "created existing dataset, error = %d", error);
/*
- * Verify that multiple dataset opens are allowed, but only when
+ * Verify that multiple dataset holds are allowed, but only when
* the new access mode is compatible with the base mode.
- * We use a mixture of typed and typeless opens, and when the
- * open succeeds, verify that the discovered type is correct.
- */
- for (mode = DS_MODE_STANDARD; mode < DS_MODE_LEVELS; mode++) {
- objset_t *os2;
- error = dmu_objset_open(name, DMU_OST_OTHER, mode, &os2);
- expected_error = (basemode + mode < DS_MODE_LEVELS) ? 0 : EBUSY;
- if (error != expected_error)
- fatal(0, "dmu_objset_open('%s') = %d, expected %d",
- name, error, expected_error);
- if (error == 0)
+ */
+ if (basemode == DS_MODE_OWNER) {
+ error = dmu_objset_open(name, DMU_OST_OTHER, DS_MODE_USER,
+ &os2);
+ if (error)
+ fatal(0, "dmu_objset_open('%s') = %d", name, error);
+ else
dmu_objset_close(os2);
}
+ error = dmu_objset_open(name, DMU_OST_OTHER, DS_MODE_OWNER, &os2);
+ expected_error = (basemode == DS_MODE_OWNER) ? EBUSY : 0;
+ if (error != expected_error)
+ fatal(0, "dmu_objset_open('%s') = %d, expected %d",
+ name, error, expected_error);
+ if (error == 0)
+ dmu_objset_close(os2);
zil_close(zilog);
dmu_objset_close(os);
@@ -1417,7 +1529,7 @@ ztest_blk_cb(traverse_blk_cache_t *bc, spa_t *spa, void *arg)
void
ztest_traverse(ztest_args_t *za)
{
- spa_t *spa = dmu_objset_spa(za->za_os);
+ spa_t *spa = za->za_spa;
traverse_handle_t *th = za->za_th;
int rc, advance;
uint64_t cbstart, cblimit;
@@ -1489,7 +1601,7 @@ ztest_dmu_object_alloc_free(ztest_args_t *za)
dmu_tx_t *tx;
uint64_t batchobj, object, batchsize, endoff, temp;
int b, c, error, bonuslen;
- dmu_object_info_t doi;
+ dmu_object_info_t *doi = &za->za_doi;
char osname[MAXNAMELEN];
dmu_objset_name(os, osname);
@@ -1500,7 +1612,7 @@ ztest_dmu_object_alloc_free(ztest_args_t *za)
/*
* Create a batch object if necessary, and record it in the directory.
*/
- VERIFY(0 == dmu_read(os, ZTEST_DIROBJ, za->za_diroff,
+ VERIFY3U(0, ==, dmu_read(os, ZTEST_DIROBJ, za->za_diroff,
sizeof (uint64_t), &batchobj));
if (batchobj == 0) {
tx = dmu_tx_create(os);
@@ -1525,7 +1637,7 @@ ztest_dmu_object_alloc_free(ztest_args_t *za)
* Destroy the previous batch of objects.
*/
for (b = 0; b < batchsize; b++) {
- VERIFY(0 == dmu_read(os, batchobj, b * sizeof (uint64_t),
+ VERIFY3U(0, ==, dmu_read(os, batchobj, b * sizeof (uint64_t),
sizeof (uint64_t), &object));
if (object == 0)
continue;
@@ -1534,13 +1646,14 @@ ztest_dmu_object_alloc_free(ztest_args_t *za)
* We expect the nth byte of the bonus buffer to be n.
*/
VERIFY(0 == dmu_bonus_hold(os, object, FTAG, &db));
+ za->za_dbuf = db;
- dmu_object_info_from_db(db, &doi);
- ASSERT(doi.doi_type == DMU_OT_UINT64_OTHER);
- ASSERT(doi.doi_bonus_type == DMU_OT_PLAIN_OTHER);
- ASSERT3S(doi.doi_physical_blks, >=, 0);
+ dmu_object_info_from_db(db, doi);
+ ASSERT(doi->doi_type == DMU_OT_UINT64_OTHER);
+ ASSERT(doi->doi_bonus_type == DMU_OT_PLAIN_OTHER);
+ ASSERT3S(doi->doi_physical_blks, >=, 0);
- bonuslen = db->db_size;
+ bonuslen = doi->doi_bonus_size;
for (c = 0; c < bonuslen; c++) {
if (((uint8_t *)db->db_data)[c] !=
@@ -1554,6 +1667,7 @@ ztest_dmu_object_alloc_free(ztest_args_t *za)
}
dmu_buf_rele(db, FTAG);
+ za->za_dbuf = NULL;
/*
* We expect the word at endoff to be our object number.
@@ -1658,8 +1772,9 @@ ztest_dmu_object_alloc_free(ztest_args_t *za)
/*
* Write to both the bonus buffer and the regular data.
*/
- VERIFY(0 == dmu_bonus_hold(os, object, FTAG, &db));
- ASSERT3U(bonuslen, ==, db->db_size);
+ VERIFY(dmu_bonus_hold(os, object, FTAG, &db) == 0);
+ za->za_dbuf = db;
+ ASSERT3U(bonuslen, <=, db->db_size);
dmu_object_size_from_db(db, &va_blksize, &va_nblocks);
ASSERT3S(va_nblocks, >=, 0);
@@ -1670,10 +1785,11 @@ ztest_dmu_object_alloc_free(ztest_args_t *za)
* See comments above regarding the contents of
* the bonus buffer and the word at endoff.
*/
- for (c = 0; c < db->db_size; c++)
+ for (c = 0; c < bonuslen; c++)
((uint8_t *)db->db_data)[c] = (uint8_t)(c + bonuslen);
dmu_buf_rele(db, FTAG);
+ za->za_dbuf = NULL;
/*
* Write to a large offset to increase indirection.
@@ -1928,226 +2044,240 @@ ztest_dmu_read_write(ztest_args_t *za)
}
void
-ztest_dmu_check_future_leak(objset_t *os, uint64_t txg)
+ztest_dmu_check_future_leak(ztest_args_t *za)
{
+ objset_t *os = za->za_os;
dmu_buf_t *db;
- ztest_block_tag_t rbt;
-
- if (zopt_verbose >= 3) {
- char osname[MAXNAMELEN];
- dmu_objset_name(os, osname);
- (void) printf("checking %s for future leaks in txg %lld...\n",
- osname, (u_longlong_t)txg);
- }
+ ztest_block_tag_t *bt;
+ dmu_object_info_t *doi = &za->za_doi;
/*
* Make sure that, if there is a write record in the bonus buffer
* of the ZTEST_DIROBJ, that the txg for this record is <= the
* last synced txg of the pool.
*/
-
- VERIFY(0 == dmu_bonus_hold(os, ZTEST_DIROBJ, FTAG, &db));
- ASSERT3U(db->db_size, ==, sizeof (rbt));
- bcopy(db->db_data, &rbt, db->db_size);
- if (rbt.bt_objset != 0) {
- ASSERT3U(rbt.bt_objset, ==, dmu_objset_id(os));
- ASSERT3U(rbt.bt_object, ==, ZTEST_DIROBJ);
- ASSERT3U(rbt.bt_offset, ==, -1ULL);
- if (rbt.bt_txg > txg) {
- fatal(0,
- "future leak: got %llx, last synced txg is %llx",
- rbt.bt_txg, txg);
- }
+ VERIFY(dmu_bonus_hold(os, ZTEST_DIROBJ, FTAG, &db) == 0);
+ za->za_dbuf = db;
+ VERIFY(dmu_object_info(os, ZTEST_DIROBJ, doi) == 0);
+ ASSERT3U(doi->doi_bonus_size, >=, sizeof (*bt));
+ ASSERT3U(doi->doi_bonus_size, <=, db->db_size);
+ ASSERT3U(doi->doi_bonus_size % sizeof (*bt), ==, 0);
+ bt = (void *)((char *)db->db_data + doi->doi_bonus_size - sizeof (*bt));
+ if (bt->bt_objset != 0) {
+ ASSERT3U(bt->bt_objset, ==, dmu_objset_id(os));
+ ASSERT3U(bt->bt_object, ==, ZTEST_DIROBJ);
+ ASSERT3U(bt->bt_offset, ==, -1ULL);
+ ASSERT3U(bt->bt_txg, <, spa_first_txg(za->za_spa));
}
dmu_buf_rele(db, FTAG);
+ za->za_dbuf = NULL;
}
void
ztest_dmu_write_parallel(ztest_args_t *za)
{
objset_t *os = za->za_os;
- dmu_tx_t *tx;
+ ztest_block_tag_t *rbt = &za->za_rbt;
+ ztest_block_tag_t *wbt = &za->za_wbt;
+ const size_t btsize = sizeof (ztest_block_tag_t);
dmu_buf_t *db;
- int i, b, error, do_free, bs;
- uint64_t off, txg_how, txg;
+ int b, error;
+ int bs = ZTEST_DIROBJ_BLOCKSIZE;
+ int do_free = 0;
+ uint64_t off, txg, txg_how;
mutex_t *lp;
char osname[MAXNAMELEN];
char iobuf[SPA_MAXBLOCKSIZE];
- ztest_block_tag_t rbt, wbt;
+ blkptr_t blk = { 0 };
+ uint64_t blkoff;
+ zbookmark_t zb;
+ dmu_tx_t *tx = dmu_tx_create(os);
dmu_objset_name(os, osname);
- bs = ZTEST_DIROBJ_BLOCKSIZE;
/*
* Have multiple threads write to large offsets in ZTEST_DIROBJ
* to verify that having multiple threads writing to the same object
* in parallel doesn't cause any trouble.
- * Also do parallel writes to the bonus buffer on occasion.
*/
- for (i = 0; i < 50; i++) {
+ if (ztest_random(4) == 0) {
+ /*
+ * Do the bonus buffer instead of a regular block.
+ * We need a lock to serialize resize vs. others,
+ * so we hash on the objset ID.
+ */
+ b = dmu_objset_id(os) % ZTEST_SYNC_LOCKS;
+ off = -1ULL;
+ dmu_tx_hold_bonus(tx, ZTEST_DIROBJ);
+ } else {
b = ztest_random(ZTEST_SYNC_LOCKS);
- lp = &ztest_shared->zs_sync_lock[b];
-
- do_free = (ztest_random(4) == 0);
-
- off = za->za_diroff_shared + ((uint64_t)b << SPA_MAXBLOCKSHIFT);
-
+ off = za->za_diroff_shared + (b << SPA_MAXBLOCKSHIFT);
if (ztest_random(4) == 0) {
- /*
- * Do the bonus buffer instead of a regular block.
- */
- do_free = 0;
- off = -1ULL;
- }
-
- tx = dmu_tx_create(os);
-
- if (off == -1ULL)
- dmu_tx_hold_bonus(tx, ZTEST_DIROBJ);
- else if (do_free)
+ do_free = 1;
dmu_tx_hold_free(tx, ZTEST_DIROBJ, off, bs);
- else
+ } else {
dmu_tx_hold_write(tx, ZTEST_DIROBJ, off, bs);
+ }
+ }
- txg_how = ztest_random(2) == 0 ? TXG_WAIT : TXG_NOWAIT;
- error = dmu_tx_assign(tx, txg_how);
- if (error) {
- if (error == ERESTART) {
- ASSERT(txg_how == TXG_NOWAIT);
- dmu_tx_wait(tx);
- dmu_tx_abort(tx);
- continue;
- }
- dmu_tx_abort(tx);
+ txg_how = ztest_random(2) == 0 ? TXG_WAIT : TXG_NOWAIT;
+ error = dmu_tx_assign(tx, txg_how);
+ if (error) {
+ if (error == ERESTART) {
+ ASSERT(txg_how == TXG_NOWAIT);
+ dmu_tx_wait(tx);
+ } else {
ztest_record_enospc("dmu write parallel");
- return;
}
- txg = dmu_tx_get_txg(tx);
+ dmu_tx_abort(tx);
+ return;
+ }
+ txg = dmu_tx_get_txg(tx);
- if (do_free) {
- (void) mutex_lock(lp);
- VERIFY(0 == dmu_free_range(os, ZTEST_DIROBJ, off,
- bs, tx));
- (void) mutex_unlock(lp);
- dmu_tx_commit(tx);
- continue;
+ lp = &ztest_shared->zs_sync_lock[b];
+ (void) mutex_lock(lp);
+
+ wbt->bt_objset = dmu_objset_id(os);
+ wbt->bt_object = ZTEST_DIROBJ;
+ wbt->bt_offset = off;
+ wbt->bt_txg = txg;
+ wbt->bt_thread = za->za_instance;
+ wbt->bt_seq = ztest_shared->zs_seq[b]++; /* protected by lp */
+
+ /*
+ * Occasionally, write an all-zero block to test the behavior
+ * of blocks that compress into holes.
+ */
+ if (off != -1ULL && ztest_random(8) == 0)
+ bzero(wbt, btsize);
+
+ if (off == -1ULL) {
+ dmu_object_info_t *doi = &za->za_doi;
+ char *dboff;
+
+ VERIFY(dmu_bonus_hold(os, ZTEST_DIROBJ, FTAG, &db) == 0);
+ za->za_dbuf = db;
+ dmu_object_info_from_db(db, doi);
+ ASSERT3U(doi->doi_bonus_size, <=, db->db_size);
+ ASSERT3U(doi->doi_bonus_size, >=, btsize);
+ ASSERT3U(doi->doi_bonus_size % btsize, ==, 0);
+ dboff = (char *)db->db_data + doi->doi_bonus_size - btsize;
+ bcopy(dboff, rbt, btsize);
+ if (rbt->bt_objset != 0) {
+ ASSERT3U(rbt->bt_objset, ==, wbt->bt_objset);
+ ASSERT3U(rbt->bt_object, ==, wbt->bt_object);
+ ASSERT3U(rbt->bt_offset, ==, wbt->bt_offset);
+ ASSERT3U(rbt->bt_txg, <=, wbt->bt_txg);
}
-
- wbt.bt_objset = dmu_objset_id(os);
- wbt.bt_object = ZTEST_DIROBJ;
- wbt.bt_offset = off;
- wbt.bt_txg = txg;
- wbt.bt_thread = za->za_instance;
-
- if (off == -1ULL) {
- wbt.bt_seq = 0;
- VERIFY(0 == dmu_bonus_hold(os, ZTEST_DIROBJ,
- FTAG, &db));
- ASSERT3U(db->db_size, ==, sizeof (wbt));
- bcopy(db->db_data, &rbt, db->db_size);
- if (rbt.bt_objset != 0) {
- ASSERT3U(rbt.bt_objset, ==, wbt.bt_objset);
- ASSERT3U(rbt.bt_object, ==, wbt.bt_object);
- ASSERT3U(rbt.bt_offset, ==, wbt.bt_offset);
- ASSERT3U(rbt.bt_txg, <=, wbt.bt_txg);
- }
- dmu_buf_will_dirty(db, tx);
- bcopy(&wbt, db->db_data, db->db_size);
- dmu_buf_rele(db, FTAG);
- dmu_tx_commit(tx);
- continue;
+ if (ztest_random(10) == 0) {
+ int newsize = (ztest_random(db->db_size /
+ btsize) + 1) * btsize;
+
+ ASSERT3U(newsize, >=, btsize);
+ ASSERT3U(newsize, <=, db->db_size);
+ VERIFY3U(dmu_set_bonus(db, newsize, tx), ==, 0);
+ dboff = (char *)db->db_data + newsize - btsize;
}
+ dmu_buf_will_dirty(db, tx);
+ bcopy(wbt, dboff, btsize);
+ dmu_buf_rele(db, FTAG);
+ za->za_dbuf = NULL;
+ } else if (do_free) {
+ VERIFY(dmu_free_range(os, ZTEST_DIROBJ, off, bs, tx) == 0);
+ } else {
+ dmu_write(os, ZTEST_DIROBJ, off, btsize, wbt, tx);
+ }
- (void) mutex_lock(lp);
+ (void) mutex_unlock(lp);
- wbt.bt_seq = ztest_shared->zs_seq[b]++;
+ if (ztest_random(1000) == 0)
+ (void) poll(NULL, 0, 1); /* open dn_notxholds window */
- dmu_write(os, ZTEST_DIROBJ, off, sizeof (wbt), &wbt, tx);
+ dmu_tx_commit(tx);
+
+ if (ztest_random(10000) == 0)
+ txg_wait_synced(dmu_objset_pool(os), txg);
+
+ if (off == -1ULL || do_free)
+ return;
+ if (ztest_random(2) != 0)
+ return;
+
+ /*
+ * dmu_sync() the block we just wrote.
+ */
+ (void) mutex_lock(lp);
+
+ blkoff = P2ALIGN_TYPED(off, bs, uint64_t);
+ error = dmu_buf_hold(os, ZTEST_DIROBJ, blkoff, FTAG, &db);
+ za->za_dbuf = db;
+ if (error) {
+ dprintf("dmu_buf_hold(%s, %d, %llx) = %d\n",
+ osname, ZTEST_DIROBJ, blkoff, error);
(void) mutex_unlock(lp);
+ return;
+ }
+ blkoff = off - blkoff;
+ error = dmu_sync(NULL, db, &blk, txg, NULL, NULL);
+ dmu_buf_rele(db, FTAG);
+ za->za_dbuf = NULL;
- if (ztest_random(100) == 0)
- (void) poll(NULL, 0, 1); /* open dn_notxholds window */
+ (void) mutex_unlock(lp);
- dmu_tx_commit(tx);
+ if (error) {
+ dprintf("dmu_sync(%s, %d, %llx) = %d\n",
+ osname, ZTEST_DIROBJ, off, error);
+ return;
+ }
- if (ztest_random(1000) == 0)
- txg_wait_synced(dmu_objset_pool(os), txg);
-
- if (ztest_random(2) == 0) {
- blkptr_t blk = { 0 };
- uint64_t blkoff;
- zbookmark_t zb;
-
- (void) mutex_lock(lp);
- blkoff = P2ALIGN_TYPED(off, bs, uint64_t);
- error = dmu_buf_hold(os,
- ZTEST_DIROBJ, blkoff, FTAG, &db);
- if (error) {
- dprintf("dmu_buf_hold(%s, %d, %llx) = %d\n",
- osname, ZTEST_DIROBJ, blkoff, error);
- (void) mutex_unlock(lp);
- continue;
- }
- blkoff = off - blkoff;
- error = dmu_sync(NULL, db, &blk, txg, NULL, NULL);
- dmu_buf_rele(db, FTAG);
- (void) mutex_unlock(lp);
- if (error) {
- dprintf("dmu_sync(%s, %d, %llx) = %d\n",
- osname, ZTEST_DIROBJ, off, error);
- continue;
- }
+ if (blk.blk_birth == 0) /* concurrent free */
+ return;
- if (blk.blk_birth == 0) { /* concurrent free */
- continue;
- }
- txg_suspend(dmu_objset_pool(os));
+ txg_suspend(dmu_objset_pool(os));
- ASSERT(blk.blk_fill == 1);
- ASSERT3U(BP_GET_TYPE(&blk), ==, DMU_OT_UINT64_OTHER);
- ASSERT3U(BP_GET_LEVEL(&blk), ==, 0);
- ASSERT3U(BP_GET_LSIZE(&blk), ==, bs);
+ ASSERT(blk.blk_fill == 1);
+ ASSERT3U(BP_GET_TYPE(&blk), ==, DMU_OT_UINT64_OTHER);
+ ASSERT3U(BP_GET_LEVEL(&blk), ==, 0);
+ ASSERT3U(BP_GET_LSIZE(&blk), ==, bs);
- /*
- * Read the block that dmu_sync() returned to
- * make sure its contents match what we wrote.
- * We do this while still txg_suspend()ed to ensure
- * that the block can't be reused before we read it.
- */
- zb.zb_objset = dmu_objset_id(os);
- zb.zb_object = ZTEST_DIROBJ;
- zb.zb_level = 0;
- zb.zb_blkid = off / bs;
- error = zio_wait(zio_read(NULL, dmu_objset_spa(os),
- &blk, iobuf, bs, NULL, NULL,
- ZIO_PRIORITY_SYNC_READ, ZIO_FLAG_MUSTSUCCEED, &zb));
- ASSERT(error == 0);
+ /*
+ * Read the block that dmu_sync() returned to make sure its contents
+ * match what we wrote. We do this while still txg_suspend()ed
+ * to ensure that the block can't be reused before we read it.
+ */
+ zb.zb_objset = dmu_objset_id(os);
+ zb.zb_object = ZTEST_DIROBJ;
+ zb.zb_level = 0;
+ zb.zb_blkid = off / bs;
+ error = zio_wait(zio_read(NULL, za->za_spa, &blk, iobuf, bs,
+ NULL, NULL, ZIO_PRIORITY_SYNC_READ, ZIO_FLAG_MUSTSUCCEED, &zb));
+ ASSERT3U(error, ==, 0);
- txg_resume(dmu_objset_pool(os));
+ txg_resume(dmu_objset_pool(os));
- bcopy(&iobuf[blkoff], &rbt, sizeof (rbt));
+ bcopy(&iobuf[blkoff], rbt, btsize);
- if (rbt.bt_objset == 0) /* concurrent free */
- continue;
+ if (rbt->bt_objset == 0) /* concurrent free */
+ return;
- ASSERT3U(rbt.bt_objset, ==, wbt.bt_objset);
- ASSERT3U(rbt.bt_object, ==, wbt.bt_object);
- ASSERT3U(rbt.bt_offset, ==, wbt.bt_offset);
+ if (wbt->bt_objset == 0) /* all-zero overwrite */
+ return;
- /*
- * The semantic of dmu_sync() is that we always
- * push the most recent version of the data,
- * so in the face of concurrent updates we may
- * see a newer version of the block. That's OK.
- */
- ASSERT3U(rbt.bt_txg, >=, wbt.bt_txg);
- if (rbt.bt_thread == wbt.bt_thread)
- ASSERT3U(rbt.bt_seq, ==, wbt.bt_seq);
- else
- ASSERT3U(rbt.bt_seq, >, wbt.bt_seq);
- }
- }
+ ASSERT3U(rbt->bt_objset, ==, wbt->bt_objset);
+ ASSERT3U(rbt->bt_object, ==, wbt->bt_object);
+ ASSERT3U(rbt->bt_offset, ==, wbt->bt_offset);
+
+ /*
+ * The semantic of dmu_sync() is that we always push the most recent
+ * version of the data, so in the face of concurrent updates we may
+ * see a newer version of the block. That's OK.
+ */
+ ASSERT3U(rbt->bt_txg, >=, wbt->bt_txg);
+ if (rbt->bt_thread == wbt->bt_thread)
+ ASSERT3U(rbt->bt_seq, ==, wbt->bt_seq);
+ else
+ ASSERT3U(rbt->bt_seq, >, wbt->bt_seq);
}
/*
@@ -2166,7 +2296,6 @@ ztest_zap(ztest_args_t *za)
uint64_t value[ZTEST_ZAP_MAX_INTS];
uint64_t zl_ints, zl_intsize, prop;
int i, ints;
- int iters = 100;
dmu_tx_t *tx;
char propname[100], txgname[100];
int error;
@@ -2230,122 +2359,113 @@ ztest_zap(ztest_args_t *za)
ints = MAX(ZTEST_ZAP_MIN_INTS, object % ZTEST_ZAP_MAX_INTS);
- while (--iters >= 0) {
- prop = ztest_random(ZTEST_ZAP_MAX_PROPS);
- (void) sprintf(propname, "prop_%llu", (u_longlong_t)prop);
- (void) sprintf(txgname, "txg_%llu", (u_longlong_t)prop);
- bzero(value, sizeof (value));
- last_txg = 0;
-
- /*
- * If these zap entries already exist, validate their contents.
- */
- error = zap_length(os, object, txgname, &zl_intsize, &zl_ints);
- if (error == 0) {
- ASSERT3U(zl_intsize, ==, sizeof (uint64_t));
- ASSERT3U(zl_ints, ==, 1);
-
- error = zap_lookup(os, object, txgname, zl_intsize,
- zl_ints, &last_txg);
+ prop = ztest_random(ZTEST_ZAP_MAX_PROPS);
+ (void) sprintf(propname, "prop_%llu", (u_longlong_t)prop);
+ (void) sprintf(txgname, "txg_%llu", (u_longlong_t)prop);
+ bzero(value, sizeof (value));
+ last_txg = 0;
- ASSERT3U(error, ==, 0);
+ /*
+ * If these zap entries already exist, validate their contents.
+ */
+ error = zap_length(os, object, txgname, &zl_intsize, &zl_ints);
+ if (error == 0) {
+ ASSERT3U(zl_intsize, ==, sizeof (uint64_t));
+ ASSERT3U(zl_ints, ==, 1);
- error = zap_length(os, object, propname, &zl_intsize,
- &zl_ints);
+ VERIFY(zap_lookup(os, object, txgname, zl_intsize,
+ zl_ints, &last_txg) == 0);
- ASSERT3U(error, ==, 0);
- ASSERT3U(zl_intsize, ==, sizeof (uint64_t));
- ASSERT3U(zl_ints, ==, ints);
+ VERIFY(zap_length(os, object, propname, &zl_intsize,
+ &zl_ints) == 0);
- error = zap_lookup(os, object, propname, zl_intsize,
- zl_ints, value);
+ ASSERT3U(zl_intsize, ==, sizeof (uint64_t));
+ ASSERT3U(zl_ints, ==, ints);
- ASSERT3U(error, ==, 0);
+ VERIFY(zap_lookup(os, object, propname, zl_intsize,
+ zl_ints, value) == 0);
- for (i = 0; i < ints; i++) {
- ASSERT3U(value[i], ==, last_txg + object + i);
- }
- } else {
- ASSERT3U(error, ==, ENOENT);
+ for (i = 0; i < ints; i++) {
+ ASSERT3U(value[i], ==, last_txg + object + i);
}
+ } else {
+ ASSERT3U(error, ==, ENOENT);
+ }
- /*
- * Atomically update two entries in our zap object.
- * The first is named txg_%llu, and contains the txg
- * in which the property was last updated. The second
- * is named prop_%llu, and the nth element of its value
- * should be txg + object + n.
- */
- tx = dmu_tx_create(os);
- dmu_tx_hold_zap(tx, object, TRUE, NULL);
- error = dmu_tx_assign(tx, TXG_WAIT);
- if (error) {
- ztest_record_enospc("create zap entry");
- dmu_tx_abort(tx);
- return;
- }
- txg = dmu_tx_get_txg(tx);
+ /*
+ * Atomically update two entries in our zap object.
+ * The first is named txg_%llu, and contains the txg
+ * in which the property was last updated. The second
+ * is named prop_%llu, and the nth element of its value
+ * should be txg + object + n.
+ */
+ tx = dmu_tx_create(os);
+ dmu_tx_hold_zap(tx, object, TRUE, NULL);
+ error = dmu_tx_assign(tx, TXG_WAIT);
+ if (error) {
+ ztest_record_enospc("create zap entry");
+ dmu_tx_abort(tx);
+ return;
+ }
+ txg = dmu_tx_get_txg(tx);
- if (last_txg > txg)
- fatal(0, "zap future leak: old %llu new %llu",
- last_txg, txg);
+ if (last_txg > txg)
+ fatal(0, "zap future leak: old %llu new %llu", last_txg, txg);
- for (i = 0; i < ints; i++)
- value[i] = txg + object + i;
+ for (i = 0; i < ints; i++)
+ value[i] = txg + object + i;
- error = zap_update(os, object, txgname, sizeof (uint64_t),
- 1, &txg, tx);
- if (error)
- fatal(0, "zap_update('%s', %llu, '%s') = %d",
- osname, object, txgname, error);
+ error = zap_update(os, object, txgname, sizeof (uint64_t), 1, &txg, tx);
+ if (error)
+ fatal(0, "zap_update('%s', %llu, '%s') = %d",
+ osname, object, txgname, error);
- error = zap_update(os, object, propname, sizeof (uint64_t),
- ints, value, tx);
- if (error)
- fatal(0, "zap_update('%s', %llu, '%s') = %d",
- osname, object, propname, error);
+ error = zap_update(os, object, propname, sizeof (uint64_t),
+ ints, value, tx);
+ if (error)
+ fatal(0, "zap_update('%s', %llu, '%s') = %d",
+ osname, object, propname, error);
- dmu_tx_commit(tx);
+ dmu_tx_commit(tx);
- /*
- * Remove a random pair of entries.
- */
- prop = ztest_random(ZTEST_ZAP_MAX_PROPS);
- (void) sprintf(propname, "prop_%llu", (u_longlong_t)prop);
- (void) sprintf(txgname, "txg_%llu", (u_longlong_t)prop);
+ /*
+ * Remove a random pair of entries.
+ */
+ prop = ztest_random(ZTEST_ZAP_MAX_PROPS);
+ (void) sprintf(propname, "prop_%llu", (u_longlong_t)prop);
+ (void) sprintf(txgname, "txg_%llu", (u_longlong_t)prop);
- error = zap_length(os, object, txgname, &zl_intsize, &zl_ints);
+ error = zap_length(os, object, txgname, &zl_intsize, &zl_ints);
- if (error == ENOENT)
- continue;
+ if (error == ENOENT)
+ return;
- ASSERT3U(error, ==, 0);
+ ASSERT3U(error, ==, 0);
- tx = dmu_tx_create(os);
- dmu_tx_hold_zap(tx, object, TRUE, NULL);
- error = dmu_tx_assign(tx, TXG_WAIT);
- if (error) {
- ztest_record_enospc("remove zap entry");
- dmu_tx_abort(tx);
- return;
- }
- error = zap_remove(os, object, txgname, tx);
- if (error)
- fatal(0, "zap_remove('%s', %llu, '%s') = %d",
- osname, object, txgname, error);
+ tx = dmu_tx_create(os);
+ dmu_tx_hold_zap(tx, object, TRUE, NULL);
+ error = dmu_tx_assign(tx, TXG_WAIT);
+ if (error) {
+ ztest_record_enospc("remove zap entry");
+ dmu_tx_abort(tx);
+ return;
+ }
+ error = zap_remove(os, object, txgname, tx);
+ if (error)
+ fatal(0, "zap_remove('%s', %llu, '%s') = %d",
+ osname, object, txgname, error);
- error = zap_remove(os, object, propname, tx);
- if (error)
- fatal(0, "zap_remove('%s', %llu, '%s') = %d",
- osname, object, propname, error);
+ error = zap_remove(os, object, propname, tx);
+ if (error)
+ fatal(0, "zap_remove('%s', %llu, '%s') = %d",
+ osname, object, propname, error);
- dmu_tx_commit(tx);
- }
+ dmu_tx_commit(tx);
/*
* Once in a while, destroy the object.
*/
- if (ztest_random(100) != 0)
+ if (ztest_random(1000) != 0)
return;
tx = dmu_tx_create(os);
@@ -2372,111 +2492,107 @@ ztest_zap_parallel(ztest_args_t *za)
{
objset_t *os = za->za_os;
uint64_t txg, object, count, wsize, wc, zl_wsize, zl_wc;
- int iters = 100;
dmu_tx_t *tx;
int i, namelen, error;
char name[20], string_value[20];
void *data;
- while (--iters >= 0) {
- /*
- * Generate a random name of the form 'xxx.....' where each
- * x is a random printable character and the dots are dots.
- * There are 94 such characters, and the name length goes from
- * 6 to 20, so there are 94^3 * 15 = 12,458,760 possible names.
- */
- namelen = ztest_random(sizeof (name) - 5) + 5 + 1;
+ /*
+ * Generate a random name of the form 'xxx.....' where each
+ * x is a random printable character and the dots are dots.
+ * There are 94 such characters, and the name length goes from
+ * 6 to 20, so there are 94^3 * 15 = 12,458,760 possible names.
+ */
+ namelen = ztest_random(sizeof (name) - 5) + 5 + 1;
- for (i = 0; i < 3; i++)
- name[i] = '!' + ztest_random('~' - '!' + 1);
- for (; i < namelen - 1; i++)
- name[i] = '.';
- name[i] = '\0';
+ for (i = 0; i < 3; i++)
+ name[i] = '!' + ztest_random('~' - '!' + 1);
+ for (; i < namelen - 1; i++)
+ name[i] = '.';
+ name[i] = '\0';
- if (ztest_random(2) == 0)
- object = ZTEST_MICROZAP_OBJ;
- else
- object = ZTEST_FATZAP_OBJ;
+ if (ztest_random(2) == 0)
+ object = ZTEST_MICROZAP_OBJ;
+ else
+ object = ZTEST_FATZAP_OBJ;
- if ((namelen & 1) || object == ZTEST_MICROZAP_OBJ) {
- wsize = sizeof (txg);
- wc = 1;
- data = &txg;
- } else {
- wsize = 1;
- wc = namelen;
- data = string_value;
- }
+ if ((namelen & 1) || object == ZTEST_MICROZAP_OBJ) {
+ wsize = sizeof (txg);
+ wc = 1;
+ data = &txg;
+ } else {
+ wsize = 1;
+ wc = namelen;
+ data = string_value;
+ }
- count = -1ULL;
- VERIFY(zap_count(os, object, &count) == 0);
- ASSERT(count != -1ULL);
+ count = -1ULL;
+ VERIFY(zap_count(os, object, &count) == 0);
+ ASSERT(count != -1ULL);
- /*
- * Select an operation: length, lookup, add, update, remove.
- */
- i = ztest_random(5);
-
- if (i >= 2) {
- tx = dmu_tx_create(os);
- dmu_tx_hold_zap(tx, object, TRUE, NULL);
- error = dmu_tx_assign(tx, TXG_WAIT);
- if (error) {
- ztest_record_enospc("zap parallel");
- dmu_tx_abort(tx);
- return;
- }
- txg = dmu_tx_get_txg(tx);
- bcopy(name, string_value, namelen);
- } else {
- tx = NULL;
- txg = 0;
- bzero(string_value, namelen);
- }
+ /*
+ * Select an operation: length, lookup, add, update, remove.
+ */
+ i = ztest_random(5);
- switch (i) {
+ if (i >= 2) {
+ tx = dmu_tx_create(os);
+ dmu_tx_hold_zap(tx, object, TRUE, NULL);
+ error = dmu_tx_assign(tx, TXG_WAIT);
+ if (error) {
+ ztest_record_enospc("zap parallel");
+ dmu_tx_abort(tx);
+ return;
+ }
+ txg = dmu_tx_get_txg(tx);
+ bcopy(name, string_value, namelen);
+ } else {
+ tx = NULL;
+ txg = 0;
+ bzero(string_value, namelen);
+ }
- case 0:
- error = zap_length(os, object, name, &zl_wsize, &zl_wc);
- if (error == 0) {
- ASSERT3U(wsize, ==, zl_wsize);
- ASSERT3U(wc, ==, zl_wc);
- } else {
- ASSERT3U(error, ==, ENOENT);
- }
- break;
+ switch (i) {
- case 1:
- error = zap_lookup(os, object, name, wsize, wc, data);
- if (error == 0) {
- if (data == string_value &&
- bcmp(name, data, namelen) != 0)
- fatal(0, "name '%s' != val '%s' len %d",
- name, data, namelen);
- } else {
- ASSERT3U(error, ==, ENOENT);
- }
- break;
+ case 0:
+ error = zap_length(os, object, name, &zl_wsize, &zl_wc);
+ if (error == 0) {
+ ASSERT3U(wsize, ==, zl_wsize);
+ ASSERT3U(wc, ==, zl_wc);
+ } else {
+ ASSERT3U(error, ==, ENOENT);
+ }
+ break;
- case 2:
- error = zap_add(os, object, name, wsize, wc, data, tx);
- ASSERT(error == 0 || error == EEXIST);
- break;
+ case 1:
+ error = zap_lookup(os, object, name, wsize, wc, data);
+ if (error == 0) {
+ if (data == string_value &&
+ bcmp(name, data, namelen) != 0)
+ fatal(0, "name '%s' != val '%s' len %d",
+ name, data, namelen);
+ } else {
+ ASSERT3U(error, ==, ENOENT);
+ }
+ break;
- case 3:
- VERIFY(zap_update(os, object, name, wsize, wc,
- data, tx) == 0);
- break;
+ case 2:
+ error = zap_add(os, object, name, wsize, wc, data, tx);
+ ASSERT(error == 0 || error == EEXIST);
+ break;
- case 4:
- error = zap_remove(os, object, name, tx);
- ASSERT(error == 0 || error == ENOENT);
- break;
- }
+ case 3:
+ VERIFY(zap_update(os, object, name, wsize, wc, data, tx) == 0);
+ break;
- if (tx != NULL)
- dmu_tx_commit(tx);
+ case 4:
+ error = zap_remove(os, object, name, tx);
+ ASSERT(error == 0 || error == ENOENT);
+ break;
}
+
+ if (tx != NULL)
+ dmu_tx_commit(tx);
}
void
@@ -2532,21 +2648,6 @@ ztest_dsl_prop_get_set(ztest_args_t *za)
(void) rw_unlock(&ztest_shared->zs_name_lock);
}
-static void
-ztest_error_setup(vdev_t *vd, int mode, int mask, uint64_t arg)
-{
- int c;
-
- for (c = 0; c < vd->vdev_children; c++)
- ztest_error_setup(vd->vdev_child[c], mode, mask, arg);
-
- if (vd->vdev_path != NULL) {
- vd->vdev_fault_mode = mode;
- vd->vdev_fault_mask = mask;
- vd->vdev_fault_arg = arg;
- }
-}
-
/*
* Inject random faults into the on-disk data.
*/
@@ -2561,67 +2662,97 @@ ztest_fault_inject(ztest_args_t *za)
char path0[MAXPATHLEN];
char pathrand[MAXPATHLEN];
size_t fsize;
- spa_t *spa = dmu_objset_spa(za->za_os);
+ spa_t *spa = za->za_spa;
int bshift = SPA_MAXBLOCKSHIFT + 2; /* don't scrog all labels */
int iters = 1000;
- vdev_t *vd0;
+ int maxfaults = zopt_maxfaults;
+ vdev_t *vd0 = NULL;
uint64_t guid0 = 0;
- /*
- * We can't inject faults when we have no fault tolerance.
- */
- if (zopt_maxfaults == 0)
- return;
-
- ASSERT(leaves >= 2);
+ ASSERT(leaves >= 1);
/*
- * Pick a random top-level vdev.
+ * We need SCL_STATE here because we're going to look at vd0->vdev_tsd.
*/
- spa_config_enter(spa, RW_READER, FTAG);
- top = ztest_random(spa->spa_root_vdev->vdev_children);
- spa_config_exit(spa, FTAG);
+ spa_config_enter(spa, SCL_STATE, FTAG, RW_READER);
- /*
- * Pick a random leaf.
- */
- leaf = ztest_random(leaves);
+ if (ztest_random(2) == 0) {
+ /*
+ * Inject errors on a normal data device.
+ */
+ top = ztest_random(spa->spa_root_vdev->vdev_children);
+ leaf = ztest_random(leaves);
- /*
- * Generate paths to the first two leaves in this top-level vdev,
- * and to the random leaf we selected. We'll induce transient
- * I/O errors and random online/offline activity on leaf 0,
- * and we'll write random garbage to the randomly chosen leaf.
- */
- (void) snprintf(path0, sizeof (path0),
- ztest_dev_template, zopt_dir, zopt_pool, top * leaves + 0);
- (void) snprintf(pathrand, sizeof (pathrand),
- ztest_dev_template, zopt_dir, zopt_pool, top * leaves + leaf);
+ /*
+ * Generate paths to the first leaf in this top-level vdev,
+ * and to the random leaf we selected. We'll induce transient
+ * write failures and random online/offline activity on leaf 0,
+ * and we'll write random garbage to the randomly chosen leaf.
+ */
+ (void) snprintf(path0, sizeof (path0), ztest_dev_template,
+ zopt_dir, zopt_pool, top * leaves + 0);
+ (void) snprintf(pathrand, sizeof (pathrand), ztest_dev_template,
+ zopt_dir, zopt_pool, top * leaves + leaf);
- dprintf("damaging %s and %s\n", path0, pathrand);
+ vd0 = vdev_lookup_by_path(spa->spa_root_vdev, path0);
+ if (vd0 != NULL && maxfaults != 1) {
+ /*
+ * Make vd0 explicitly claim to be unreadable,
+ * or unwriteable, or reach behind its back
+ * and close the underlying fd. We can do this if
+ * maxfaults == 0 because we'll fail and reexecute,
+ * and we can do it if maxfaults >= 2 because we'll
+ * have enough redundancy. If maxfaults == 1, the
+ * combination of this with injection of random data
+ * corruption below exceeds the pool's fault tolerance.
+ */
+ vdev_file_t *vf = vd0->vdev_tsd;
- spa_config_enter(spa, RW_READER, FTAG);
+ if (vf != NULL && ztest_random(3) == 0) {
+ (void) close(vf->vf_vnode->v_fd);
+ vf->vf_vnode->v_fd = -1;
+ } else if (ztest_random(2) == 0) {
+ vd0->vdev_cant_read = B_TRUE;
+ } else {
+ vd0->vdev_cant_write = B_TRUE;
+ }
+ guid0 = vd0->vdev_guid;
+ }
+ } else {
+ /*
+ * Inject errors on an l2cache device.
+ */
+ spa_aux_vdev_t *sav = &spa->spa_l2cache;
- /*
- * If we can tolerate two or more faults, make vd0 fail randomly.
- */
- vd0 = vdev_lookup_by_path(spa->spa_root_vdev, path0);
- if (vd0 != NULL && zopt_maxfaults >= 2) {
+ if (sav->sav_count == 0) {
+ spa_config_exit(spa, SCL_STATE, FTAG);
+ return;
+ }
+ vd0 = sav->sav_vdevs[ztest_random(sav->sav_count)];
guid0 = vd0->vdev_guid;
- ztest_error_setup(vd0, VDEV_FAULT_COUNT,
- (1U << ZIO_TYPE_READ) | (1U << ZIO_TYPE_WRITE), 100);
+ (void) strcpy(path0, vd0->vdev_path);
+ (void) strcpy(pathrand, vd0->vdev_path);
+
+ leaf = 0;
+ leaves = 1;
+ maxfaults = INT_MAX; /* no limit on cache devices */
}
- spa_config_exit(spa, FTAG);
+ dprintf("damaging %s and %s\n", path0, pathrand);
+
+ spa_config_exit(spa, SCL_STATE, FTAG);
+
+ if (maxfaults == 0)
+ return;
/*
* If we can tolerate two or more faults, randomly online/offline vd0.
*/
- if (zopt_maxfaults >= 2 && guid0 != 0) {
+ if (maxfaults >= 2 && guid0 != 0) {
if (ztest_random(10) < 6)
(void) vdev_offline(spa, guid0, B_TRUE);
else
- (void) vdev_online(spa, guid0);
+ (void) vdev_online(spa, guid0, B_FALSE, NULL);
}
/*
@@ -2660,11 +2791,11 @@ ztest_fault_inject(ztest_args_t *za)
void
ztest_scrub(ztest_args_t *za)
{
- spa_t *spa = dmu_objset_spa(za->za_os);
+ spa_t *spa = za->za_spa;
- (void) spa_scrub(spa, POOL_SCRUB_EVERYTHING, B_FALSE);
+ (void) spa_scrub(spa, POOL_SCRUB_EVERYTHING);
(void) poll(NULL, 0, 1000); /* wait a second, then force a restart */
- (void) spa_scrub(spa, POOL_SCRUB_EVERYTHING, B_FALSE);
+ (void) spa_scrub(spa, POOL_SCRUB_EVERYTHING);
}
/*
@@ -2706,7 +2837,7 @@ ztest_spa_rename(ztest_args_t *za)
if (error != 0)
fatal(0, "spa_open('%s') = %d", newname, error);
- ASSERT(spa == dmu_objset_spa(za->za_os));
+ ASSERT(spa == za->za_spa);
spa_close(spa, FTAG);
/*
@@ -2724,7 +2855,7 @@ ztest_spa_rename(ztest_args_t *za)
if (error != 0)
fatal(0, "spa_open('%s') = %d", oldname, error);
- ASSERT(spa == dmu_objset_spa(za->za_os));
+ ASSERT(spa == za->za_spa);
spa_close(spa, FTAG);
umem_free(newname, strlen(newname) + 1);
@@ -2778,10 +2909,9 @@ static void
ztest_replace_one_disk(spa_t *spa, uint64_t vdev)
{
char dev_name[MAXPATHLEN];
- nvlist_t *file, *root;
+ nvlist_t *root;
int error;
uint64_t guid;
- uint64_t ashift = ztest_get_ashift();
vdev_t *vd;
(void) sprintf(dev_name, ztest_dev_template, zopt_dir, zopt_pool, vdev);
@@ -2789,22 +2919,14 @@ ztest_replace_one_disk(spa_t *spa, uint64_t vdev)
/*
* Build the nvlist describing dev_name.
*/
- VERIFY(nvlist_alloc(&file, NV_UNIQUE_NAME, 0) == 0);
- VERIFY(nvlist_add_string(file, ZPOOL_CONFIG_TYPE, VDEV_TYPE_FILE) == 0);
- VERIFY(nvlist_add_string(file, ZPOOL_CONFIG_PATH, dev_name) == 0);
- VERIFY(nvlist_add_uint64(file, ZPOOL_CONFIG_ASHIFT, ashift) == 0);
-
- VERIFY(nvlist_alloc(&root, NV_UNIQUE_NAME, 0) == 0);
- VERIFY(nvlist_add_string(root, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT) == 0);
- VERIFY(nvlist_add_nvlist_array(root, ZPOOL_CONFIG_CHILDREN,
- &file, 1) == 0);
+ root = make_vdev_root(dev_name, NULL, 0, 0, 0, 0, 0, 1);
- spa_config_enter(spa, RW_READER, FTAG);
+ spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
if ((vd = vdev_lookup_by_path(spa->spa_root_vdev, dev_name)) == NULL)
guid = 0;
else
guid = vd->vdev_guid;
- spa_config_exit(spa, FTAG);
+ spa_config_exit(spa, SCL_VDEV, FTAG);
error = spa_vdev_attach(spa, guid, root, B_TRUE);
if (error != 0 &&
error != EBUSY &&
@@ -2813,7 +2935,6 @@ ztest_replace_one_disk(spa_t *spa, uint64_t vdev)
error != EDOM)
fatal(0, "spa_vdev_attach(in-place) = %d", error);
- nvlist_free(file);
nvlist_free(root);
}
@@ -2824,6 +2945,9 @@ ztest_verify_blocks(char *pool)
char zdb[MAXPATHLEN + MAXNAMELEN + 20];
char zbuf[1024];
char *bin;
+ char *ztest;
+ char *isa;
+ int isalen;
FILE *fp;
if (realpath(progname, zdb) == NULL)
@@ -2831,13 +2955,19 @@ ztest_verify_blocks(char *pool)
/* zdb lives in /usr/sbin, while ztest lives in /usr/bin */
bin = strstr(zdb, "/usr/bin/");
- if (bin == NULL)
- bin = zdb;
+ ztest = strstr(bin, "/ztest");
+ isa = bin + 8;
+ isalen = ztest - isa;
+ isa = strdup(isa);
/* LINTED */
- (void) sprintf(bin, "/usr/sbin/zdb -bc%s%s -U -O %s %s",
+ (void) sprintf(bin,
+ "/usr/sbin%.*s/zdb -bc%s%s -U /tmp/zpool.cache -O %s %s",
+ isalen,
+ isa,
zopt_verbose >= 3 ? "s" : "",
zopt_verbose >= 4 ? "v" : "",
ztest_random(2) == 0 ? "pre" : "post", pool);
+ free(isa);
if (zopt_verbose >= 5)
(void) printf("Executing %s\n", strstr(zdb, "zdb "));
@@ -2909,7 +3039,7 @@ ztest_spa_import_export(char *oldname, char *newname)
/*
* Export it.
*/
- error = spa_export(oldname, &config);
+ error = spa_export(oldname, &config, B_FALSE);
if (error)
fatal(0, "spa_export('%s') = %d", oldname, error);
@@ -2958,35 +3088,41 @@ ztest_spa_import_export(char *oldname, char *newname)
}
static void *
+ztest_resume(void *arg)
+{
+ spa_t *spa = arg;
+
+ while (!ztest_exiting) {
+ (void) poll(NULL, 0, 1000);
+
+ if (!spa_suspended(spa))
+ continue;
+
+ spa_vdev_state_enter(spa);
+ vdev_clear(spa, NULL);
+ (void) spa_vdev_state_exit(spa, NULL, 0);
+
+ zio_resume(spa);
+ }
+ return (NULL);
+}
+
+static void *
ztest_thread(void *arg)
{
ztest_args_t *za = arg;
ztest_shared_t *zs = ztest_shared;
hrtime_t now, functime;
ztest_info_t *zi;
- int f;
+ int f, i;
while ((now = gethrtime()) < za->za_stop) {
/*
* See if it's time to force a crash.
*/
if (now > za->za_kill) {
- dmu_tx_t *tx;
- uint64_t txg;
-
- mutex_enter(&spa_namespace_lock);
- tx = dmu_tx_create(za->za_os);
- VERIFY(0 == dmu_tx_assign(tx, TXG_NOWAIT));
- txg = dmu_tx_get_txg(tx);
- dmu_tx_commit(tx);
- zs->zs_txg = txg;
- if (zopt_verbose >= 3)
- (void) printf(
- "killing process after txg %lld\n",
- (u_longlong_t)txg);
- txg_wait_synced(dmu_objset_pool(za->za_os), txg);
- zs->zs_alloc = spa_get_alloc(dmu_objset_spa(za->za_os));
- zs->zs_space = spa_get_space(dmu_objset_spa(za->za_os));
+ zs->zs_alloc = spa_get_alloc(za->za_spa);
+ zs->zs_space = spa_get_space(za->za_spa);
(void) kill(getpid(), SIGKILL);
}
@@ -3011,9 +3147,8 @@ ztest_thread(void *arg)
ZTEST_DIRSIZE;
za->za_diroff_shared = (1ULL << 63);
- ztest_dmu_write_parallel(za);
-
- zi->zi_func(za);
+ for (i = 0; i < zi->zi_iters; i++)
+ zi->zi_func(za);
functime = gethrtime() - now;
@@ -3047,6 +3182,9 @@ ztest_run(char *pool)
ztest_args_t *za;
spa_t *spa;
char name[100];
+ thread_t resume_tid;
+
+ ztest_exiting = B_FALSE;
(void) _mutex_init(&zs->zs_vdev_lock, USYNC_THREAD, NULL);
(void) rwlock_init(&zs->zs_name_lock, USYNC_THREAD, NULL);
@@ -3071,9 +3209,7 @@ ztest_run(char *pool)
* Kick off a replacement of the disk we just obliterated.
*/
kernel_init(FREAD | FWRITE);
- error = spa_open(pool, &spa, FTAG);
- if (error)
- fatal(0, "spa_open(%s) = %d", pool, error);
+ VERIFY(spa_open(pool, &spa, FTAG) == 0);
ztest_replace_one_disk(spa, 0);
if (zopt_verbose >= 5)
show_pool_stats(spa);
@@ -3106,9 +3242,13 @@ ztest_run(char *pool)
/*
* Open our pool.
*/
- error = spa_open(pool, &spa, FTAG);
- if (error)
- fatal(0, "spa_open() = %d", error);
+ VERIFY(spa_open(pool, &spa, FTAG) == 0);
+
+ /*
+ * Create a thread to periodically resume suspended I/O.
+ */
+ VERIFY(thr_create(0, 0, ztest_resume, spa, THR_BOUND,
+ &resume_tid) == 0);
/*
* Verify that we can safely inquire about about any object,
@@ -3144,71 +3284,62 @@ ztest_run(char *pool)
for (t = 0; t < zopt_threads; t++) {
d = t % zopt_datasets;
+
+ (void) strcpy(za[t].za_pool, pool);
+ za[t].za_os = za[d].za_os;
+ za[t].za_spa = spa;
+ za[t].za_zilog = za[d].za_zilog;
+ za[t].za_instance = t;
+ za[t].za_random = ztest_random(-1ULL);
+ za[t].za_start = za[0].za_start;
+ za[t].za_stop = za[0].za_stop;
+ za[t].za_kill = za[0].za_kill;
+
if (t < zopt_datasets) {
ztest_replay_t zr;
int test_future = FALSE;
(void) rw_rdlock(&ztest_shared->zs_name_lock);
(void) snprintf(name, 100, "%s/%s_%d", pool, pool, d);
- error = dmu_objset_create(name, DMU_OST_OTHER, NULL,
+ error = dmu_objset_create(name, DMU_OST_OTHER, NULL, 0,
ztest_create_cb, NULL);
if (error == EEXIST) {
test_future = TRUE;
+ } else if (error == ENOSPC) {
+ zs->zs_enospc_count++;
+ (void) rw_unlock(&ztest_shared->zs_name_lock);
+ break;
} else if (error != 0) {
- if (error == ENOSPC) {
- zs->zs_enospc_count++;
- (void) rw_unlock(
- &ztest_shared->zs_name_lock);
- break;
- }
fatal(0, "dmu_objset_create(%s) = %d",
name, error);
}
error = dmu_objset_open(name, DMU_OST_OTHER,
- DS_MODE_STANDARD, &za[d].za_os);
+ DS_MODE_USER, &za[d].za_os);
if (error)
fatal(0, "dmu_objset_open('%s') = %d",
name, error);
(void) rw_unlock(&ztest_shared->zs_name_lock);
- if (test_future && ztest_shared->zs_txg > 0)
- ztest_dmu_check_future_leak(za[d].za_os,
- ztest_shared->zs_txg);
+ if (test_future)
+ ztest_dmu_check_future_leak(&za[t]);
zr.zr_os = za[d].za_os;
zil_replay(zr.zr_os, &zr, &zr.zr_assign,
- ztest_replay_vector);
+ ztest_replay_vector, NULL);
za[d].za_zilog = zil_open(za[d].za_os, NULL);
}
- za[t].za_pool = spa_strdup(pool);
- za[t].za_os = za[d].za_os;
- za[t].za_zilog = za[d].za_zilog;
- za[t].za_instance = t;
- za[t].za_random = ztest_random(-1ULL);
- za[t].za_start = za[0].za_start;
- za[t].za_stop = za[0].za_stop;
- za[t].za_kill = za[0].za_kill;
- error = thr_create(0, 0, ztest_thread, &za[t], THR_BOUND,
- &za[t].za_thread);
- if (error)
- fatal(0, "can't create thread %d: error %d",
- t, error);
+ VERIFY(thr_create(0, 0, ztest_thread, &za[t], THR_BOUND,
+ &za[t].za_thread) == 0);
}
- ztest_shared->zs_txg = 0;
while (--t >= 0) {
- error = thr_join(za[t].za_thread, NULL, NULL);
- if (error)
- fatal(0, "thr_join(%d) = %d", t, error);
+ VERIFY(thr_join(za[t].za_thread, NULL, NULL) == 0);
if (za[t].za_th)
traverse_fini(za[t].za_th);
if (t < zopt_datasets) {
zil_close(za[t].za_zilog);
dmu_objset_close(za[t].za_os);
}
- spa_strfree(za[t].za_pool);
}
- umem_free(za, zopt_threads * sizeof (ztest_args_t));
-
if (zopt_verbose >= 3)
show_pool_stats(spa);
@@ -3218,21 +3349,27 @@ ztest_run(char *pool)
zs->zs_space = spa_get_space(spa);
/*
- * Did we have out-of-space errors? If so, destroy a random objset.
+ * If we had out-of-space errors, destroy a random objset.
*/
if (zs->zs_enospc_count != 0) {
(void) rw_rdlock(&ztest_shared->zs_name_lock);
- (void) snprintf(name, 100, "%s/%s_%d", pool, pool,
- (int)ztest_random(zopt_datasets));
+ d = (int)ztest_random(zopt_datasets);
+ (void) snprintf(name, 100, "%s/%s_%d", pool, pool, d);
if (zopt_verbose >= 3)
(void) printf("Destroying %s to free up space\n", name);
- (void) dmu_objset_find(name, ztest_destroy_cb, NULL,
+ (void) dmu_objset_find(name, ztest_destroy_cb, &za[d],
DS_FIND_SNAPSHOTS | DS_FIND_CHILDREN);
(void) rw_unlock(&ztest_shared->zs_name_lock);
}
txg_wait_synced(spa_get_dsl(spa), 0);
+ umem_free(za, zopt_threads * sizeof (ztest_args_t));
+
+ /* Kill the resume thread */
+ ztest_exiting = B_TRUE;
+ VERIFY(thr_join(resume_tid, NULL, NULL) == 0);
+
/*
* Right before closing the pool, kick off a bunch of async I/O;
* spa_close() should wait for it to complete.
@@ -3288,8 +3425,9 @@ ztest_init(char *pool)
*/
(void) spa_destroy(pool);
ztest_shared->zs_vdev_primaries = 0;
- nvroot = make_vdev_root(zopt_vdev_size, zopt_raidz, zopt_mirrors, 1);
- error = spa_create(pool, nvroot, NULL);
+ nvroot = make_vdev_root(NULL, NULL, zopt_vdev_size, 0,
+ 0, zopt_raidz, zopt_mirrors, 1);
+ error = spa_create(pool, nvroot, NULL, NULL, NULL);
nvlist_free(nvroot);
if (error)
@@ -3320,7 +3458,7 @@ main(int argc, char **argv)
(void) setvbuf(stdout, NULL, _IOLBF, 0);
/* Override location of zpool.cache */
- spa_config_dir = "/tmp";
+ spa_config_path = "/tmp/zpool.cache";
ztest_random_fd = open("/dev/urandom", O_RDONLY);
diff --git a/cddl/contrib/opensolaris/head/assert.h b/cddl/contrib/opensolaris/head/assert.h
index b088033..394820a 100644
--- a/cddl/contrib/opensolaris/head/assert.h
+++ b/cddl/contrib/opensolaris/head/assert.h
@@ -39,7 +39,7 @@ extern "C" {
#if defined(__STDC__)
#if __STDC_VERSION__ - 0 >= 199901L
-extern void __assert_c99(const char *, const char *, int, const char *);
+extern void __assert(const char *, const char *, int);
#else
extern void __assert(const char *, const char *, int);
#endif /* __STDC_VERSION__ - 0 >= 199901L */
@@ -70,8 +70,7 @@ extern void _assert();
#if defined(__STDC__)
#if __STDC_VERSION__ - 0 >= 199901L
-#define assert(EX) (void)((EX) || \
- (__assert_c99(#EX, __FILE__, __LINE__, __func__), 0))
+#define assert(EX) (void)((EX) || (__assert(#EX, __FILE__, __LINE__), 0))
#else
#define assert(EX) (void)((EX) || (__assert(#EX, __FILE__, __LINE__), 0))
#endif /* __STDC_VERSION__ - 0 >= 199901L */
diff --git a/cddl/contrib/opensolaris/head/libintl.h b/cddl/contrib/opensolaris/head/libintl.h
index 94b4d03..e649668 100644
--- a/cddl/contrib/opensolaris/head/libintl.h
+++ b/cddl/contrib/opensolaris/head/libintl.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,11 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-/* Libintl is a library of advanced internationalization functions. */
#ifndef _LIBINTL_H
#define _LIBINTL_H
@@ -63,6 +61,9 @@ typedef long wchar_t;
#define TEXTDOMAINMAX 256
+#define __GNU_GETTEXT_SUPPORTED_REVISION(m) \
+ ((((m) == 0) || ((m) == 1)) ? 1 : -1)
+
#ifdef __STDC__
extern char *dcgettext(const char *, const char *, const int);
extern char *dgettext(const char *, const char *);
diff --git a/cddl/contrib/opensolaris/head/synch.h b/cddl/contrib/opensolaris/head/synch.h
index 8d825d5..eab9de8 100644
--- a/cddl/contrib/opensolaris/head/synch.h
+++ b/cddl/contrib/opensolaris/head/synch.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -81,12 +81,12 @@ typedef lwp_cond_t cond_t;
* Because we have to deal with C++, we cannot redefine this one as that one.
*/
typedef struct _rwlock {
- int32_t readers; /* -1 == writer else # of readers */
+ int32_t readers; /* rwstate word */
uint16_t type;
uint16_t magic;
- mutex_t mutex; /* used to indicate ownership */
- cond_t readercv; /* unused */
- cond_t writercv; /* unused */
+ mutex_t mutex; /* used with process-shared rwlocks */
+ cond_t readercv; /* used only to indicate ownership */
+ cond_t writercv; /* used only to indicate ownership */
} rwlock_t;
#ifdef __STDC__
@@ -111,6 +111,7 @@ int cond_signal(cond_t *);
int cond_broadcast(cond_t *);
int mutex_init(mutex_t *, int, void *);
int mutex_destroy(mutex_t *);
+int mutex_consistent(mutex_t *);
int mutex_lock(mutex_t *);
int mutex_trylock(mutex_t *);
int mutex_unlock(mutex_t *);
@@ -152,6 +153,7 @@ int cond_signal();
int cond_broadcast();
int mutex_init();
int mutex_destroy();
+int mutex_consistent();
int mutex_lock();
int mutex_trylock();
int mutex_unlock();
diff --git a/cddl/contrib/opensolaris/head/thread.h b/cddl/contrib/opensolaris/head/thread.h
index b9ed952..b374b8a 100644
--- a/cddl/contrib/opensolaris/head/thread.h
+++ b/cddl/contrib/opensolaris/head/thread.h
@@ -30,6 +30,7 @@
#pragma ident "%Z%%M% %I% %E% SMI"
#include <pthread.h>
+#include <pthread_np.h>
#include <assert.h>
/*
@@ -52,6 +53,7 @@ typedef pthread_rwlock_t rwlock_t;
#define mutex_lock(l) pthread_mutex_lock(l)
#define mutex_trylock(l) pthread_mutex_trylock(l)
#define mutex_unlock(l) pthread_mutex_unlock(l)
+#define mutex_owned(l) pthread_mutex_isowned_np(l)
#define rwlock_init(l,f,a) pthread_rwlock_init(l,NULL)
#define rwlock_destroy(l) pthread_rwlock_destroy(l)
#define rw_rdlock(l) pthread_rwlock_rdlock(l)
diff --git a/cddl/contrib/opensolaris/lib/libnvpair/libnvpair.c b/cddl/contrib/opensolaris/lib/libnvpair/libnvpair.c
index 58a47f1..89e01dd 100644
--- a/cddl/contrib/opensolaris/lib/libnvpair/libnvpair.c
+++ b/cddl/contrib/opensolaris/lib/libnvpair/libnvpair.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,12 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
+#include <inttypes.h>
#include <unistd.h>
#include <strings.h>
#include "libnvpair.h"
@@ -137,6 +137,12 @@ nvlist_print_with_indent(FILE *fp, nvlist_t *nvl, int depth)
(void) fprintf(fp, " 0x%llx", (u_longlong_t)val);
break;
}
+ case DATA_TYPE_DOUBLE: {
+ double val;
+ (void) nvpair_value_double(nvp, &val);
+ (void) fprintf(fp, " 0x%llf", val);
+ break;
+ }
case DATA_TYPE_STRING: {
char *val;
(void) nvpair_value_string(nvp, &val);
@@ -264,3 +270,348 @@ nvlist_print(FILE *fp, nvlist_t *nvl)
{
nvlist_print_with_indent(fp, nvl, 0);
}
+
+/*
+ * Determine if string 'value' matches 'nvp' value. The 'value' string is
+ * converted, depending on the type of 'nvp', prior to match. For numeric
+ * types, a radix independent sscanf conversion of 'value' is used. If 'nvp'
+ * is an array type, 'ai' is the index into the array against which we are
+ * checking for match. If nvp is of DATA_TYPE_STRING*, the caller can pass
+ * in a regex_t compilation of value in 'value_regex' to trigger regular
+ * expression string match instead of simple strcmp().
+ *
+ * Return 1 on match, 0 on no-match, and -1 on error. If the error is
+ * related to value syntax error and 'ep' is non-NULL, *ep will point into
+ * the 'value' string at the location where the error exists.
+ *
+ * NOTE: It may be possible to move the non-regex_t version of this into
+ * common code used by library/kernel/boot.
+ */
+int
+nvpair_value_match_regex(nvpair_t *nvp, int ai,
+ char *value, regex_t *value_regex, char **ep)
+{
+ char *evalue;
+ uint_t a_len;
+ int sr;
+
+ if (ep)
+ *ep = NULL;
+
+ if ((nvp == NULL) || (value == NULL))
+ return (-1); /* error fail match - invalid args */
+
+ /* make sure array and index combination make sense */
+ if ((nvpair_type_is_array(nvp) && (ai < 0)) ||
+ (!nvpair_type_is_array(nvp) && (ai >= 0)))
+ return (-1); /* error fail match - bad index */
+
+ /* non-string values should be single 'chunk' */
+ if ((nvpair_type(nvp) != DATA_TYPE_STRING) &&
+ (nvpair_type(nvp) != DATA_TYPE_STRING_ARRAY)) {
+ value += strspn(value, " \t");
+ evalue = value + strcspn(value, " \t");
+ if (*evalue) {
+ if (ep)
+ *ep = evalue;
+ return (-1); /* error fail match - syntax */
+ }
+ }
+
+ sr = EOF;
+ switch (nvpair_type(nvp)) {
+ case DATA_TYPE_STRING: {
+ char *val;
+
+ /* check string value for match */
+ if (nvpair_value_string(nvp, &val) == 0) {
+ if (value_regex) {
+ if (regexec(value_regex, val,
+ (size_t)0, NULL, 0) == 0)
+ return (1); /* match */
+ } else {
+ if (strcmp(value, val) == 0)
+ return (1); /* match */
+ }
+ }
+ break;
+ }
+ case DATA_TYPE_STRING_ARRAY: {
+ char **val_array;
+
+ /* check indexed string value of array for match */
+ if ((nvpair_value_string_array(nvp, &val_array, &a_len) == 0) &&
+ (ai < a_len)) {
+ if (value_regex) {
+ if (regexec(value_regex, val_array[ai],
+ (size_t)0, NULL, 0) == 0)
+ return (1);
+ } else {
+ if (strcmp(value, val_array[ai]) == 0)
+ return (1);
+ }
+ }
+ break;
+ }
+ case DATA_TYPE_BYTE: {
+ uchar_t val, val_arg;
+
+ /* scanf uchar_t from value and check for match */
+ sr = sscanf(value, "%c", &val_arg);
+ if ((sr == 1) && (nvpair_value_byte(nvp, &val) == 0) &&
+ (val == val_arg))
+ return (1);
+ break;
+ }
+ case DATA_TYPE_BYTE_ARRAY: {
+ uchar_t *val_array, val_arg;
+
+
+ /* check indexed value of array for match */
+ sr = sscanf(value, "%c", &val_arg);
+ if ((sr == 1) &&
+ (nvpair_value_byte_array(nvp, &val_array, &a_len) == 0) &&
+ (ai < a_len) &&
+ (val_array[ai] == val_arg))
+ return (1);
+ break;
+ }
+ case DATA_TYPE_INT8: {
+ int8_t val, val_arg;
+
+ /* scanf int8_t from value and check for match */
+ sr = sscanf(value, "%"SCNi8, &val_arg);
+ if ((sr == 1) &&
+ (nvpair_value_int8(nvp, &val) == 0) &&
+ (val == val_arg))
+ return (1);
+ break;
+ }
+ case DATA_TYPE_INT8_ARRAY: {
+ int8_t *val_array, val_arg;
+
+ /* check indexed value of array for match */
+ sr = sscanf(value, "%"SCNi8, &val_arg);
+ if ((sr == 1) &&
+ (nvpair_value_int8_array(nvp, &val_array, &a_len) == 0) &&
+ (ai < a_len) &&
+ (val_array[ai] == val_arg))
+ return (1);
+ break;
+ }
+ case DATA_TYPE_UINT8: {
+ uint8_t val, val_arg;
+
+ /* scanf uint8_t from value and check for match */
+ sr = sscanf(value, "%"SCNi8, (int8_t *)&val_arg);
+ if ((sr == 1) &&
+ (nvpair_value_uint8(nvp, &val) == 0) &&
+ (val == val_arg))
+ return (1);
+ break;
+ }
+ case DATA_TYPE_UINT8_ARRAY: {
+ uint8_t *val_array, val_arg;
+
+ /* check indexed value of array for match */
+ sr = sscanf(value, "%"SCNi8, (int8_t *)&val_arg);
+ if ((sr == 1) &&
+ (nvpair_value_uint8_array(nvp, &val_array, &a_len) == 0) &&
+ (ai < a_len) &&
+ (val_array[ai] == val_arg))
+ return (1);
+ break;
+ }
+ case DATA_TYPE_INT16: {
+ int16_t val, val_arg;
+
+ /* scanf int16_t from value and check for match */
+ sr = sscanf(value, "%"SCNi16, &val_arg);
+ if ((sr == 1) &&
+ (nvpair_value_int16(nvp, &val) == 0) &&
+ (val == val_arg))
+ return (1);
+ break;
+ }
+ case DATA_TYPE_INT16_ARRAY: {
+ int16_t *val_array, val_arg;
+
+ /* check indexed value of array for match */
+ sr = sscanf(value, "%"SCNi16, &val_arg);
+ if ((sr == 1) &&
+ (nvpair_value_int16_array(nvp, &val_array, &a_len) == 0) &&
+ (ai < a_len) &&
+ (val_array[ai] == val_arg))
+ return (1);
+ break;
+ }
+ case DATA_TYPE_UINT16: {
+ uint16_t val, val_arg;
+
+ /* scanf uint16_t from value and check for match */
+ sr = sscanf(value, "%"SCNi16, (int16_t *)&val_arg);
+ if ((sr == 1) &&
+ (nvpair_value_uint16(nvp, &val) == 0) &&
+ (val == val_arg))
+ return (1);
+ break;
+ }
+ case DATA_TYPE_UINT16_ARRAY: {
+ uint16_t *val_array, val_arg;
+
+ /* check indexed value of array for match */
+ sr = sscanf(value, "%"SCNi16, (int16_t *)&val_arg);
+ if ((sr == 1) &&
+ (nvpair_value_uint16_array(nvp, &val_array, &a_len) == 0) &&
+ (ai < a_len) &&
+ (val_array[ai] == val_arg))
+ return (1);
+ break;
+ }
+ case DATA_TYPE_INT32: {
+ int32_t val, val_arg;
+
+ /* scanf int32_t from value and check for match */
+ sr = sscanf(value, "%"SCNi32, &val_arg);
+ if ((sr == 1) &&
+ (nvpair_value_int32(nvp, &val) == 0) &&
+ (val == val_arg))
+ return (1);
+ break;
+ }
+ case DATA_TYPE_INT32_ARRAY: {
+ int32_t *val_array, val_arg;
+
+ /* check indexed value of array for match */
+ sr = sscanf(value, "%"SCNi32, &val_arg);
+ if ((sr == 1) &&
+ (nvpair_value_int32_array(nvp, &val_array, &a_len) == 0) &&
+ (ai < a_len) &&
+ (val_array[ai] == val_arg))
+ return (1);
+ break;
+ }
+ case DATA_TYPE_UINT32: {
+ uint32_t val, val_arg;
+
+ /* scanf uint32_t from value and check for match */
+ sr = sscanf(value, "%"SCNi32, (int32_t *)&val_arg);
+ if ((sr == 1) &&
+ (nvpair_value_uint32(nvp, &val) == 0) &&
+ (val == val_arg))
+ return (1);
+ break;
+ }
+ case DATA_TYPE_UINT32_ARRAY: {
+ uint32_t *val_array, val_arg;
+
+ /* check indexed value of array for match */
+ sr = sscanf(value, "%"SCNi32, (int32_t *)&val_arg);
+ if ((sr == 1) &&
+ (nvpair_value_uint32_array(nvp, &val_array, &a_len) == 0) &&
+ (ai < a_len) &&
+ (val_array[ai] == val_arg))
+ return (1);
+ break;
+ }
+ case DATA_TYPE_INT64: {
+ int64_t val, val_arg;
+
+ /* scanf int64_t from value and check for match */
+ sr = sscanf(value, "%"SCNi64, &val_arg);
+ if ((sr == 1) &&
+ (nvpair_value_int64(nvp, &val) == 0) &&
+ (val == val_arg))
+ return (1);
+ break;
+ }
+ case DATA_TYPE_INT64_ARRAY: {
+ int64_t *val_array, val_arg;
+
+ /* check indexed value of array for match */
+ sr = sscanf(value, "%"SCNi64, &val_arg);
+ if ((sr == 1) &&
+ (nvpair_value_int64_array(nvp, &val_array, &a_len) == 0) &&
+ (ai < a_len) &&
+ (val_array[ai] == val_arg))
+ return (1);
+ break;
+ }
+ case DATA_TYPE_UINT64: {
+ uint64_t val_arg, val;
+
+ /* scanf uint64_t from value and check for match */
+ sr = sscanf(value, "%"SCNi64, (int64_t *)&val_arg);
+ if ((sr == 1) &&
+ (nvpair_value_uint64(nvp, &val) == 0) &&
+ (val == val_arg))
+ return (1);
+ break;
+ }
+ case DATA_TYPE_UINT64_ARRAY: {
+ uint64_t *val_array, val_arg;
+
+ /* check indexed value of array for match */
+ sr = sscanf(value, "%"SCNi64, (int64_t *)&val_arg);
+ if ((sr == 1) &&
+ (nvpair_value_uint64_array(nvp, &val_array, &a_len) == 0) &&
+ (ai < a_len) &&
+ (val_array[ai] == val_arg))
+ return (1);
+ break;
+ }
+ case DATA_TYPE_BOOLEAN_VALUE: {
+ boolean_t val, val_arg;
+
+ /* scanf boolean_t from value and check for match */
+ sr = sscanf(value, "%"SCNi32, &val_arg);
+ if ((sr == 1) &&
+ (nvpair_value_boolean_value(nvp, &val) == 0) &&
+ (val == val_arg))
+ return (1);
+ break;
+ }
+ case DATA_TYPE_BOOLEAN_ARRAY: {
+ boolean_t *val_array, val_arg;
+
+ /* check indexed value of array for match */
+ sr = sscanf(value, "%"SCNi32, &val_arg);
+ if ((sr == 1) &&
+ (nvpair_value_boolean_array(nvp,
+ &val_array, &a_len) == 0) &&
+ (ai < a_len) &&
+ (val_array[ai] == val_arg))
+ return (1);
+ break;
+ }
+ case DATA_TYPE_HRTIME:
+ case DATA_TYPE_NVLIST:
+ case DATA_TYPE_NVLIST_ARRAY:
+ case DATA_TYPE_BOOLEAN:
+ case DATA_TYPE_DOUBLE:
+ case DATA_TYPE_UNKNOWN:
+ default:
+ /*
+ * unknown/unsupported data type
+ */
+ return (-1); /* error fail match */
+ }
+
+ /*
+ * check to see if sscanf failed conversion, return approximate
+ * pointer to problem
+ */
+ if (sr != 1) {
+ if (ep)
+ *ep = value;
+ return (-1); /* error fail match - syntax */
+ }
+
+ return (0); /* fail match */
+}
+
+int
+nvpair_value_match(nvpair_t *nvp, int ai, char *value, char **ep)
+{
+ return (nvpair_value_match_regex(nvp, ai, value, NULL, ep));
+}
diff --git a/cddl/contrib/opensolaris/lib/libnvpair/libnvpair.h b/cddl/contrib/opensolaris/lib/libnvpair/libnvpair.h
index d1d25ea..e655e0d 100644
--- a/cddl/contrib/opensolaris/lib/libnvpair/libnvpair.h
+++ b/cddl/contrib/opensolaris/lib/libnvpair/libnvpair.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -32,12 +31,15 @@
#include <sys/nvpair.h>
#include <stdlib.h>
#include <stdio.h>
+#include <regex.h>
#ifdef __cplusplus
extern "C" {
#endif
void nvlist_print(FILE *, nvlist_t *);
+int nvpair_value_match(nvpair_t *, int, char *, char **);
+int nvpair_value_match_regex(nvpair_t *, int, char *, regex_t *, char **);
#ifdef __cplusplus
}
diff --git a/cddl/contrib/opensolaris/lib/libuutil/common/libuutil.h b/cddl/contrib/opensolaris/lib/libuutil/common/libuutil.h
index fcd5e79..269687e 100644
--- a/cddl/contrib/opensolaris/lib/libuutil/common/libuutil.h
+++ b/cddl/contrib/opensolaris/lib/libuutil/common/libuutil.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _LIBUUTIL_H
#define _LIBUUTIL_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <solaris.h>
#include <sys/types.h>
#include <stdarg.h>
@@ -149,6 +146,7 @@ extern int uu_open_tmp(const char *dir, uint_t uflags);
/*PRINTFLIKE1*/
extern char *uu_msprintf(const char *format, ...);
extern void *uu_zalloc(size_t);
+extern char *uu_strdup(const char *);
extern void uu_free(void *);
/*
diff --git a/cddl/contrib/opensolaris/lib/libuutil/common/libuutil_common.h b/cddl/contrib/opensolaris/lib/libuutil/common/libuutil_common.h
index fc9cc7c..9ebaaed 100644
--- a/cddl/contrib/opensolaris/lib/libuutil/common/libuutil_common.h
+++ b/cddl/contrib/opensolaris/lib/libuutil/common/libuutil_common.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -21,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -30,16 +29,6 @@
#pragma ident "%Z%%M% %I% %E% SMI"
-#include <solaris.h>
-
-/*
- * We don't bind to the internal libc interfaces if this is a
- * native build.
- */
-#ifndef NATIVE_BUILD
-#include "c_synonyms.h"
-#endif
-
#include <libuutil.h>
#include <libuutil_impl.h>
diff --git a/cddl/contrib/opensolaris/lib/libuutil/common/uu_alloc.c b/cddl/contrib/opensolaris/lib/libuutil/common/uu_alloc.c
index 7cdbf01..05d8622 100644
--- a/cddl/contrib/opensolaris/lib/libuutil/common/uu_alloc.c
+++ b/cddl/contrib/opensolaris/lib/libuutil/common/uu_alloc.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include "libuutil_common.h"
#include <stdarg.h>
@@ -55,6 +52,22 @@ uu_free(void *p)
}
char *
+uu_strdup(const char *str)
+{
+ char *buf = NULL;
+
+ if (str != NULL) {
+ size_t sz;
+
+ sz = strlen(str) + 1;
+ buf = uu_zalloc(sz);
+ if (buf != NULL)
+ (void) memcpy(buf, str, sz);
+ }
+ return (buf);
+}
+
+char *
uu_msprintf(const char *format, ...)
{
va_list args;
diff --git a/cddl/contrib/opensolaris/lib/libuutil/common/uu_avl.c b/cddl/contrib/opensolaris/lib/libuutil/common/uu_avl.c
index 93feea9..308e920 100644
--- a/cddl/contrib/opensolaris/lib/libuutil/common/uu_avl.c
+++ b/cddl/contrib/opensolaris/lib/libuutil/common/uu_avl.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -120,7 +120,8 @@ uu_avl_pool_destroy(uu_avl_pool_t *pp)
UU_PTR_ENCODE(&pp->uap_null_avl)) {
uu_panic("uu_avl_pool_destroy: Pool \"%.*s\" (%p) has "
"outstanding avls, or is corrupt.\n",
- sizeof (pp->uap_name), pp->uap_name, pp);
+ (int)sizeof (pp->uap_name), pp->uap_name,
+ (void *)pp);
}
}
(void) pthread_mutex_lock(&uu_apool_list_lock);
@@ -142,14 +143,14 @@ uu_avl_node_init(void *base, uu_avl_node_t *np, uu_avl_pool_t *pp)
if (offset + sizeof (*np) > pp->uap_objsize) {
uu_panic("uu_avl_node_init(%p, %p, %p (\"%s\")): "
"offset %ld doesn't fit in object (size %ld)\n",
- base, np, pp, pp->uap_name, offset,
- pp->uap_objsize);
+ base, (void *)np, (void *)pp, pp->uap_name,
+ (long)offset, (long)pp->uap_objsize);
}
if (offset != pp->uap_nodeoffset) {
uu_panic("uu_avl_node_init(%p, %p, %p (\"%s\")): "
"offset %ld doesn't match pool's offset (%ld)\n",
- base, np, pp, pp->uap_name, offset,
- pp->uap_objsize);
+ base, (void *)np, (void *)pp, pp->uap_name,
+ (long)offset, (long)pp->uap_objsize);
}
}
@@ -166,12 +167,12 @@ uu_avl_node_fini(void *base, uu_avl_node_t *np, uu_avl_pool_t *pp)
if (na[0] == DEAD_MARKER && na[1] == DEAD_MARKER) {
uu_panic("uu_avl_node_fini(%p, %p, %p (\"%s\")): "
"node already finied\n",
- base, np, pp, pp->uap_name);
+ base, (void *)np, (void *)pp, pp->uap_name);
}
if (na[0] != POOL_TO_MARKER(pp) || na[1] != 0) {
uu_panic("uu_avl_node_fini(%p, %p, %p (\"%s\")): "
"node corrupt, in tree, or in different pool\n",
- base, np, pp, pp->uap_name);
+ base, (void *)np, (void *)pp, pp->uap_name);
}
}
@@ -251,12 +252,13 @@ uu_avl_destroy(uu_avl_t *ap)
if (ap->ua_debug) {
if (avl_numnodes(&ap->ua_tree) != 0) {
- uu_panic("uu_avl_destroy(%p): tree not empty\n", ap);
+ uu_panic("uu_avl_destroy(%p): tree not empty\n",
+ (void *)ap);
}
if (ap->ua_null_walk.uaw_next != &ap->ua_null_walk ||
ap->ua_null_walk.uaw_prev != &ap->ua_null_walk) {
uu_panic("uu_avl_destroy(%p): outstanding walkers\n",
- ap);
+ (void *)ap);
}
}
(void) pthread_mutex_lock(&pp->uap_lock);
@@ -441,7 +443,7 @@ uu_avl_remove(uu_avl_t *ap, void *elem)
(void) _avl_walk_advance(wp, ap);
} else if (wp->uaw_next_result != NULL) {
uu_panic("uu_avl_remove(%p, %p): active non-robust "
- "walker\n", ap, elem);
+ "walker\n", (void *)ap, elem);
}
}
@@ -497,19 +499,19 @@ uu_avl_insert(uu_avl_t *ap, void *elem, uu_avl_index_t idx)
if (na[1] != 0)
uu_panic("uu_avl_insert(%p, %p, %p): node already "
"in tree, or corrupt\n",
- ap, elem, idx);
+ (void *)ap, elem, (void *)idx);
if (na[0] == 0)
uu_panic("uu_avl_insert(%p, %p, %p): node not "
"initialized\n",
- ap, elem, idx);
+ (void *)ap, elem, (void *)idx);
if (na[0] != POOL_TO_MARKER(pp))
uu_panic("uu_avl_insert(%p, %p, %p): node from "
"other pool, or corrupt\n",
- ap, elem, idx);
+ (void *)ap, elem, (void *)idx);
if (!INDEX_VALID(ap, idx))
uu_panic("uu_avl_insert(%p, %p, %p): %s\n",
- ap, elem, idx,
+ (void *)ap, elem, (void *)idx,
INDEX_CHECK(idx)? "outdated index" :
"invalid index");
@@ -526,8 +528,8 @@ uu_avl_nearest_next(uu_avl_t *ap, uu_avl_index_t idx)
{
if (ap->ua_debug && !INDEX_VALID(ap, idx))
uu_panic("uu_avl_nearest_next(%p, %p): %s\n",
- ap, idx, INDEX_CHECK(idx)? "outdated index" :
- "invalid index");
+ (void *)ap, (void *)idx, INDEX_CHECK(idx)?
+ "outdated index" : "invalid index");
return (avl_nearest(&ap->ua_tree, INDEX_DECODE(idx), AVL_AFTER));
}
@@ -536,8 +538,8 @@ uu_avl_nearest_prev(uu_avl_t *ap, uu_avl_index_t idx)
{
if (ap->ua_debug && !INDEX_VALID(ap, idx))
uu_panic("uu_avl_nearest_prev(%p, %p): %s\n",
- ap, idx, INDEX_CHECK(idx)? "outdated index" :
- "invalid index");
+ (void *)ap, (void *)idx, INDEX_CHECK(idx)?
+ "outdated index" : "invalid index");
return (avl_nearest(&ap->ua_tree, INDEX_DECODE(idx), AVL_BEFORE));
}
diff --git a/cddl/contrib/opensolaris/lib/libuutil/common/uu_dprintf.c b/cddl/contrib/opensolaris/lib/libuutil/common/uu_dprintf.c
index 5b990a5..528c3e7 100644
--- a/cddl/contrib/opensolaris/lib/libuutil/common/uu_dprintf.c
+++ b/cddl/contrib/opensolaris/lib/libuutil/common/uu_dprintf.c
@@ -33,7 +33,7 @@
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
-#include <strings.h>
+#include <string.h>
#define FACILITY_FMT "%s (%s): "
diff --git a/cddl/contrib/opensolaris/lib/libuutil/common/uu_list.c b/cddl/contrib/opensolaris/lib/libuutil/common/uu_list.c
index d9dc86f..35c7ba8 100644
--- a/cddl/contrib/opensolaris/lib/libuutil/common/uu_list.c
+++ b/cddl/contrib/opensolaris/lib/libuutil/common/uu_list.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -117,7 +116,8 @@ uu_list_pool_destroy(uu_list_pool_t *pp)
UU_PTR_ENCODE(&pp->ulp_null_list)) {
uu_panic("uu_list_pool_destroy: Pool \"%.*s\" (%p) has "
"outstanding lists, or is corrupt.\n",
- sizeof (pp->ulp_name), pp->ulp_name, pp);
+ (int)sizeof (pp->ulp_name), pp->ulp_name,
+ (void *)pp);
}
}
(void) pthread_mutex_lock(&uu_lpool_list_lock);
@@ -139,14 +139,14 @@ uu_list_node_init(void *base, uu_list_node_t *np_arg, uu_list_pool_t *pp)
if (offset + sizeof (*np) > pp->ulp_objsize) {
uu_panic("uu_list_node_init(%p, %p, %p (\"%s\")): "
"offset %ld doesn't fit in object (size %ld)\n",
- base, np, pp, pp->ulp_name, offset,
- pp->ulp_objsize);
+ base, (void *)np, (void *)pp, pp->ulp_name,
+ (long)offset, (long)pp->ulp_objsize);
}
if (offset != pp->ulp_nodeoffset) {
uu_panic("uu_list_node_init(%p, %p, %p (\"%s\")): "
"offset %ld doesn't match pool's offset (%ld)\n",
- base, np, pp, pp->ulp_name, offset,
- pp->ulp_objsize);
+ base, (void *)np, (void *)pp, pp->ulp_name,
+ (long)offset, (long)pp->ulp_objsize);
}
}
np->uln_next = POOL_TO_MARKER(pp);
@@ -163,13 +163,13 @@ uu_list_node_fini(void *base, uu_list_node_t *np_arg, uu_list_pool_t *pp)
np->uln_prev == NULL) {
uu_panic("uu_list_node_fini(%p, %p, %p (\"%s\")): "
"node already finied\n",
- base, np_arg, pp, pp->ulp_name);
+ base, (void *)np_arg, (void *)pp, pp->ulp_name);
}
if (np->uln_next != POOL_TO_MARKER(pp) ||
np->uln_prev != NULL) {
uu_panic("uu_list_node_fini(%p, %p, %p (\"%s\")): "
"node corrupt or on list\n",
- base, np_arg, pp, pp->ulp_name);
+ base, (void *)np_arg, (void *)pp, pp->ulp_name);
}
}
np->uln_next = NULL;
@@ -190,7 +190,7 @@ uu_list_create(uu_list_pool_t *pp, void *parent, uint32_t flags)
if (pp->ulp_debug)
uu_panic("uu_list_create(%p, ...): requested "
"UU_LIST_SORTED, but pool has no comparison func\n",
- pp);
+ (void *)pp);
uu_set_error(UU_ERROR_NOT_SUPPORTED);
return (NULL);
}
@@ -236,16 +236,16 @@ uu_list_destroy(uu_list_t *lp)
if (lp->ul_null_node.uln_next != &lp->ul_null_node ||
lp->ul_null_node.uln_prev != &lp->ul_null_node) {
uu_panic("uu_list_destroy(%p): list not empty\n",
- lp);
+ (void *)lp);
}
if (lp->ul_numnodes != 0) {
uu_panic("uu_list_destroy(%p): numnodes is nonzero, "
- "but list is empty\n", lp);
+ "but list is empty\n", (void *)lp);
}
if (lp->ul_null_walk.ulw_next != &lp->ul_null_walk ||
lp->ul_null_walk.ulw_prev != &lp->ul_null_walk) {
uu_panic("uu_list_destroy(%p): outstanding walkers\n",
- lp);
+ (void *)lp);
}
}
@@ -266,13 +266,14 @@ list_insert(uu_list_t *lp, uu_list_node_impl_t *np, uu_list_node_impl_t *prev,
if (lp->ul_debug) {
if (next->uln_prev != prev || prev->uln_next != next)
uu_panic("insert(%p): internal error: %p and %p not "
- "neighbors\n", lp, next, prev);
+ "neighbors\n", (void *)lp, (void *)next,
+ (void *)prev);
if (np->uln_next != POOL_TO_MARKER(lp->ul_pool) ||
np->uln_prev != NULL) {
uu_panic("insert(%p): elem %p node %p corrupt, "
"not initialized, or already in a list.\n",
- lp, NODE_TO_ELEM(lp, np), np);
+ (void *)lp, NODE_TO_ELEM(lp, np), (void *)np);
}
/*
* invalidate outstanding uu_list_index_ts.
@@ -299,12 +300,12 @@ uu_list_insert(uu_list_t *lp, void *elem, uu_list_index_t idx)
if (lp->ul_debug) {
if (!INDEX_VALID(lp, idx))
uu_panic("uu_list_insert(%p, %p, %p): %s\n",
- lp, elem, idx,
+ (void *)lp, elem, (void *)idx,
INDEX_CHECK(idx)? "outdated index" :
"invalid index");
if (np->uln_prev == NULL)
uu_panic("uu_list_insert(%p, %p, %p): out-of-date "
- "index\n", lp, elem, idx);
+ "index\n", (void *)lp, elem, (void *)idx);
}
list_insert(lp, ELEM_TO_NODE(lp, elem), np->uln_prev, np);
@@ -354,11 +355,12 @@ uu_list_nearest_next(uu_list_t *lp, uu_list_index_t idx)
if (lp->ul_debug) {
if (!INDEX_VALID(lp, idx))
uu_panic("uu_list_nearest_next(%p, %p): %s\n",
- lp, idx, INDEX_CHECK(idx)? "outdated index" :
+ (void *)lp, (void *)idx,
+ INDEX_CHECK(idx)? "outdated index" :
"invalid index");
if (np->uln_prev == NULL)
uu_panic("uu_list_nearest_next(%p, %p): out-of-date "
- "index\n", lp, idx);
+ "index\n", (void *)lp, (void *)idx);
}
if (np == &lp->ul_null_node)
@@ -378,11 +380,11 @@ uu_list_nearest_prev(uu_list_t *lp, uu_list_index_t idx)
if (lp->ul_debug) {
if (!INDEX_VALID(lp, idx))
uu_panic("uu_list_nearest_prev(%p, %p): %s\n",
- lp, idx, INDEX_CHECK(idx)? "outdated index" :
- "invalid index");
+ (void *)lp, (void *)idx, INDEX_CHECK(idx)?
+ "outdated index" : "invalid index");
if (np->uln_prev == NULL)
uu_panic("uu_list_nearest_prev(%p, %p): out-of-date "
- "index\n", lp, idx);
+ "index\n", (void *)lp, (void *)idx);
}
if ((np = np->uln_prev) == &lp->ul_null_node)
@@ -409,6 +411,11 @@ list_walk_init(uu_list_walk_t *wp, uu_list_t *lp, uint32_t flags)
wp->ulw_next_result = lp->ul_null_node.uln_prev;
if (lp->ul_debug || robust) {
+ /*
+ * Add this walker to the list's list of walkers so
+ * uu_list_remove() can advance us if somebody tries to
+ * remove ulw_next_result.
+ */
wp->ulw_next = next = &lp->ul_null_walk;
wp->ulw_prev = prev = next->ulw_prev;
next->ulw_prev = wp;
@@ -538,7 +545,7 @@ uu_list_remove(uu_list_t *lp, void *elem)
if (lp->ul_debug) {
if (np->uln_prev == NULL)
uu_panic("uu_list_remove(%p, %p): elem not on list\n",
- lp, elem);
+ (void *)lp, elem);
/*
* invalidate outstanding uu_list_index_ts.
*/
@@ -556,7 +563,7 @@ uu_list_remove(uu_list_t *lp, void *elem)
(void) list_walk_advance(wp, lp);
} else if (wp->ulw_next_result != NULL) {
uu_panic("uu_list_remove(%p, %p): active non-robust "
- "walker\n", lp, elem);
+ "walker\n", (void *)lp, elem);
}
}
@@ -578,8 +585,8 @@ uu_list_teardown(uu_list_t *lp, void **cookie)
* XXX: disable list modification until list is empty
*/
if (lp->ul_debug && *cookie != NULL)
- uu_panic("uu_list_teardown(%p, %p): unexpected cookie\n", lp,
- cookie);
+ uu_panic("uu_list_teardown(%p, %p): unexpected cookie\n",
+ (void *)lp, (void *)cookie);
ep = uu_list_first(lp);
if (ep)
@@ -599,12 +606,12 @@ uu_list_insert_before(uu_list_t *lp, void *target, void *elem)
if (np->uln_prev == NULL)
uu_panic("uu_list_insert_before(%p, %p, %p): %p is "
"not currently on a list\n",
- lp, target, elem, target);
+ (void *)lp, target, elem, target);
}
if (lp->ul_sorted) {
if (lp->ul_debug)
uu_panic("uu_list_insert_before(%p, ...): list is "
- "UU_LIST_SORTED\n", lp);
+ "UU_LIST_SORTED\n", (void *)lp);
uu_set_error(UU_ERROR_NOT_SUPPORTED);
return (-1);
}
@@ -625,12 +632,12 @@ uu_list_insert_after(uu_list_t *lp, void *target, void *elem)
if (np->uln_prev == NULL)
uu_panic("uu_list_insert_after(%p, %p, %p): %p is "
"not currently on a list\n",
- lp, target, elem, target);
+ (void *)lp, target, elem, target);
}
if (lp->ul_sorted) {
if (lp->ul_debug)
uu_panic("uu_list_insert_after(%p, ...): list is "
- "UU_LIST_SORTED\n", lp);
+ "UU_LIST_SORTED\n", (void *)lp);
uu_set_error(UU_ERROR_NOT_SUPPORTED);
return (-1);
}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h
index 232324e..71aab1c 100644
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h
+++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h
@@ -20,21 +20,20 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _LIBZFS_H
#define _LIBZFS_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <assert.h>
#include <libnvpair.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/varargs.h>
#include <sys/fs/zfs.h>
+#include <sys/avl.h>
#include <sys/zfs_ioctl.h>
#ifdef __cplusplus
@@ -47,6 +46,7 @@ extern "C" {
#define ZFS_MAXNAMELEN MAXNAMELEN
#define ZPOOL_MAXNAMELEN MAXNAMELEN
#define ZFS_MAXPROPLEN MAXPATHLEN
+#define ZPOOL_MAXPROPLEN MAXPATHLEN
/*
* libzfs errors
@@ -99,10 +99,62 @@ enum {
EZFS_POOL_NOTSUP, /* ops not supported for this type of pool */
EZFS_POOL_INVALARG, /* invalid argument for this pool operation */
EZFS_NAMETOOLONG, /* dataset name is too long */
+ EZFS_OPENFAILED, /* open of device failed */
+ EZFS_NOCAP, /* couldn't get capacity */
+ EZFS_LABELFAILED, /* write of label failed */
+ EZFS_ISCSISVCUNAVAIL, /* iscsi service unavailable */
+ EZFS_BADWHO, /* invalid permission who */
+ EZFS_BADPERM, /* invalid permission */
+ EZFS_BADPERMSET, /* invalid permission set name */
+ EZFS_NODELEGATION, /* delegated administration is disabled */
+ EZFS_PERMRDONLY, /* pemissions are readonly */
+ EZFS_UNSHARESMBFAILED, /* failed to unshare over smb */
+ EZFS_SHARESMBFAILED, /* failed to share over smb */
+ EZFS_BADCACHE, /* bad cache file */
+ EZFS_ISL2CACHE, /* device is for the level 2 ARC */
+ EZFS_VDEVNOTSUP, /* unsupported vdev type */
+ EZFS_NOTSUP, /* ops not supported on this dataset */
+ EZFS_ACTIVE_SPARE, /* pool has active shared spare devices */
EZFS_UNKNOWN
};
/*
+ * The following data structures are all part
+ * of the zfs_allow_t data structure which is
+ * used for printing 'allow' permissions.
+ * It is a linked list of zfs_allow_t's which
+ * then contain avl tree's for user/group/sets/...
+ * and each one of the entries in those trees have
+ * avl tree's for the permissions they belong to and
+ * whether they are local,descendent or local+descendent
+ * permissions. The AVL trees are used primarily for
+ * sorting purposes, but also so that we can quickly find
+ * a given user and or permission.
+ */
+typedef struct zfs_perm_node {
+ avl_node_t z_node;
+ char z_pname[MAXPATHLEN];
+} zfs_perm_node_t;
+
+typedef struct zfs_allow_node {
+ avl_node_t z_node;
+ char z_key[MAXPATHLEN]; /* name, such as joe */
+ avl_tree_t z_localdescend; /* local+descendent perms */
+ avl_tree_t z_local; /* local permissions */
+ avl_tree_t z_descend; /* descendent permissions */
+} zfs_allow_node_t;
+
+typedef struct zfs_allow {
+ struct zfs_allow *z_next;
+ char z_setpoint[MAXPATHLEN];
+ avl_tree_t z_sets;
+ avl_tree_t z_crperms;
+ avl_tree_t z_user;
+ avl_tree_t z_group;
+ avl_tree_t z_everyone;
+} zfs_allow_t;
+
+/*
* Basic handle types
*/
typedef struct zfs_handle zfs_handle_t;
@@ -131,12 +183,9 @@ extern zpool_handle_t *zpool_open(libzfs_handle_t *, const char *);
extern zpool_handle_t *zpool_open_canfail(libzfs_handle_t *, const char *);
extern void zpool_close(zpool_handle_t *);
extern const char *zpool_get_name(zpool_handle_t *);
-extern uint64_t zpool_get_guid(zpool_handle_t *);
-extern uint64_t zpool_get_space_used(zpool_handle_t *);
-extern uint64_t zpool_get_space_total(zpool_handle_t *);
-extern int zpool_get_root(zpool_handle_t *, char *, size_t);
extern int zpool_get_state(zpool_handle_t *);
-extern uint64_t zpool_get_version(zpool_handle_t *);
+extern char *zpool_state_to_name(vdev_state_t, vdev_aux_t);
+extern void zpool_free_handles(libzfs_handle_t *);
/*
* Iterate over all active pools in the system.
@@ -148,7 +197,7 @@ extern int zpool_iter(libzfs_handle_t *, zpool_iter_f, void *);
* Functions to create and destroy pools
*/
extern int zpool_create(libzfs_handle_t *, const char *, nvlist_t *,
- const char *);
+ nvlist_t *, nvlist_t *);
extern int zpool_destroy(zpool_handle_t *);
extern int zpool_add(zpool_handle_t *, nvlist_t *);
@@ -156,22 +205,33 @@ extern int zpool_add(zpool_handle_t *, nvlist_t *);
* Functions to manipulate pool and vdev state
*/
extern int zpool_scrub(zpool_handle_t *, pool_scrub_type_t);
+extern int zpool_clear(zpool_handle_t *, const char *);
-extern int zpool_vdev_online(zpool_handle_t *, const char *);
-extern int zpool_vdev_offline(zpool_handle_t *, const char *, int);
-extern int zpool_vdev_attach(zpool_handle_t *, const char *, const char *,
- nvlist_t *, int);
+extern int zpool_vdev_online(zpool_handle_t *, const char *, int,
+ vdev_state_t *);
+extern int zpool_vdev_offline(zpool_handle_t *, const char *, boolean_t);
+extern int zpool_vdev_attach(zpool_handle_t *, const char *,
+ const char *, nvlist_t *, int);
extern int zpool_vdev_detach(zpool_handle_t *, const char *);
extern int zpool_vdev_remove(zpool_handle_t *, const char *);
-extern int zpool_clear(zpool_handle_t *, const char *);
-extern nvlist_t *zpool_find_vdev(zpool_handle_t *, const char *, boolean_t *);
+
+extern int zpool_vdev_fault(zpool_handle_t *, uint64_t);
+extern int zpool_vdev_degrade(zpool_handle_t *, uint64_t);
+extern int zpool_vdev_clear(zpool_handle_t *, uint64_t);
+
+extern nvlist_t *zpool_find_vdev(zpool_handle_t *, const char *, boolean_t *,
+ boolean_t *, boolean_t *);
+extern int zpool_label_disk(libzfs_handle_t *, zpool_handle_t *, char *);
/*
* Functions to manage pool properties
*/
extern int zpool_set_prop(zpool_handle_t *, const char *, const char *);
-extern int zpool_get_prop(zpool_handle_t *, zfs_prop_t, char *,
- size_t proplen, zfs_source_t *);
+extern int zpool_get_prop(zpool_handle_t *, zpool_prop_t, char *,
+ size_t proplen, zprop_source_t *);
+extern uint64_t zpool_get_prop_int(zpool_handle_t *, zpool_prop_t,
+ zprop_source_t *);
+
extern const char *zpool_prop_to_name(zpool_prop_t);
extern const char *zpool_prop_values(zpool_prop_t);
@@ -194,6 +254,11 @@ typedef enum {
ZPOOL_STATUS_FAILING_DEV, /* device experiencing errors */
ZPOOL_STATUS_VERSION_NEWER, /* newer on-disk version */
ZPOOL_STATUS_HOSTID_MISMATCH, /* last accessed by another system */
+ ZPOOL_STATUS_IO_FAILURE_WAIT, /* failed I/O, failmode 'wait' */
+ ZPOOL_STATUS_IO_FAILURE_CONTINUE, /* failed I/O, failmode 'continue' */
+ ZPOOL_STATUS_FAULTED_DEV_R, /* faulted device with replicas */
+ ZPOOL_STATUS_FAULTED_DEV_NR, /* faulted device with no replicas */
+ ZPOOL_STATUS_BAD_LOG, /* cannot read log chain(s) */
/*
* The following are not faults per se, but still an error possibly
@@ -223,26 +288,39 @@ extern int zpool_get_errlog(zpool_handle_t *, nvlist_t **);
/*
* Import and export functions
*/
-extern int zpool_export(zpool_handle_t *);
+extern int zpool_export(zpool_handle_t *, boolean_t);
extern int zpool_import(libzfs_handle_t *, nvlist_t *, const char *,
- const char *);
+ char *altroot);
+extern int zpool_import_props(libzfs_handle_t *, nvlist_t *, const char *,
+ nvlist_t *, boolean_t);
/*
* Search for pools to import
*/
extern nvlist_t *zpool_find_import(libzfs_handle_t *, int, char **);
+extern nvlist_t *zpool_find_import_cached(libzfs_handle_t *, const char *,
+ char *, uint64_t);
+extern nvlist_t *zpool_find_import_byname(libzfs_handle_t *, int, char **,
+ char *);
+extern nvlist_t *zpool_find_import_byguid(libzfs_handle_t *, int, char **,
+ uint64_t);
+extern nvlist_t *zpool_find_import_activeok(libzfs_handle_t *, int, char **);
/*
* Miscellaneous pool functions
*/
+struct zfs_cmd;
+
extern char *zpool_vdev_name(libzfs_handle_t *, zpool_handle_t *, nvlist_t *);
-extern int zpool_upgrade(zpool_handle_t *);
+extern int zpool_upgrade(zpool_handle_t *, uint64_t);
extern int zpool_get_history(zpool_handle_t *, nvlist_t **);
-extern void zpool_log_history(libzfs_handle_t *, int, char **, const char *,
- boolean_t, boolean_t);
+extern void zpool_set_history_str(const char *subcommand, int argc,
+ char **argv, char *history_str);
+extern int zpool_stage_history(libzfs_handle_t *, const char *);
extern void zpool_obj_to_path(zpool_handle_t *, uint64_t, uint64_t, char *,
size_t len);
-
+extern int zfs_ioctl(libzfs_handle_t *, int, struct zfs_cmd *);
+extern int zpool_get_physpath(zpool_handle_t *, char *);
/*
* Basic handle manipulations. These functions do not create or destroy the
* underlying datasets, only the references to them.
@@ -251,65 +329,84 @@ extern zfs_handle_t *zfs_open(libzfs_handle_t *, const char *, int);
extern void zfs_close(zfs_handle_t *);
extern zfs_type_t zfs_get_type(const zfs_handle_t *);
extern const char *zfs_get_name(const zfs_handle_t *);
+extern zpool_handle_t *zfs_get_pool_handle(const zfs_handle_t *);
/*
* Property management functions. Some functions are shared with the kernel,
* and are found in sys/fs/zfs.h.
*/
+
+/*
+ * zfs dataset property management
+ */
+extern const char *zfs_prop_default_string(zfs_prop_t);
+extern uint64_t zfs_prop_default_numeric(zfs_prop_t);
+extern const char *zfs_prop_column_name(zfs_prop_t);
+extern boolean_t zfs_prop_align_right(zfs_prop_t);
+
+extern nvlist_t *zfs_valid_proplist(libzfs_handle_t *, zfs_type_t,
+ nvlist_t *, uint64_t, zfs_handle_t *, const char *);
+
extern const char *zfs_prop_to_name(zfs_prop_t);
extern int zfs_prop_set(zfs_handle_t *, const char *, const char *);
extern int zfs_prop_get(zfs_handle_t *, zfs_prop_t, char *, size_t,
- zfs_source_t *, char *, size_t, boolean_t);
+ zprop_source_t *, char *, size_t, boolean_t);
extern int zfs_prop_get_numeric(zfs_handle_t *, zfs_prop_t, uint64_t *,
- zfs_source_t *, char *, size_t);
+ zprop_source_t *, char *, size_t);
extern uint64_t zfs_prop_get_int(zfs_handle_t *, zfs_prop_t);
-extern const char *zfs_prop_get_string(zfs_handle_t *, zfs_prop_t);
extern int zfs_prop_inherit(zfs_handle_t *, const char *);
extern const char *zfs_prop_values(zfs_prop_t);
-extern int zfs_prop_valid_for_type(zfs_prop_t, int);
-extern const char *zfs_prop_default_string(zfs_prop_t prop);
-extern uint64_t zfs_prop_default_numeric(zfs_prop_t);
extern int zfs_prop_is_string(zfs_prop_t prop);
-extern const char *zfs_prop_column_name(zfs_prop_t);
-extern boolean_t zfs_prop_align_right(zfs_prop_t);
-extern void nicebool(int value, char *buf, size_t buflen);
+extern nvlist_t *zfs_get_user_props(zfs_handle_t *);
-typedef struct zfs_proplist {
- zfs_prop_t pl_prop;
+typedef struct zprop_list {
+ int pl_prop;
char *pl_user_prop;
- struct zfs_proplist *pl_next;
+ struct zprop_list *pl_next;
boolean_t pl_all;
size_t pl_width;
boolean_t pl_fixed;
-} zfs_proplist_t;
+} zprop_list_t;
-typedef zfs_proplist_t zpool_proplist_t;
-
-extern int zfs_get_proplist(libzfs_handle_t *, char *, zfs_proplist_t **);
-extern int zpool_get_proplist(libzfs_handle_t *, char *, zpool_proplist_t **);
-extern int zfs_expand_proplist(zfs_handle_t *, zfs_proplist_t **);
-extern int zpool_expand_proplist(zpool_handle_t *, zpool_proplist_t **);
-extern void zfs_free_proplist(zfs_proplist_t *);
-extern nvlist_t *zfs_get_user_props(zfs_handle_t *);
+extern int zfs_expand_proplist(zfs_handle_t *, zprop_list_t **);
#define ZFS_MOUNTPOINT_NONE "none"
#define ZFS_MOUNTPOINT_LEGACY "legacy"
/*
- * Functions for printing properties from zfs/zpool
+ * zpool property management
+ */
+extern int zpool_expand_proplist(zpool_handle_t *, zprop_list_t **);
+extern const char *zpool_prop_default_string(zpool_prop_t);
+extern uint64_t zpool_prop_default_numeric(zpool_prop_t);
+extern const char *zpool_prop_column_name(zpool_prop_t);
+extern boolean_t zpool_prop_align_right(zpool_prop_t);
+
+/*
+ * Functions shared by zfs and zpool property management.
*/
-typedef struct libzfs_get_cbdata {
+extern int zprop_iter(zprop_func func, void *cb, boolean_t show_all,
+ boolean_t ordered, zfs_type_t type);
+extern int zprop_get_list(libzfs_handle_t *, char *, zprop_list_t **,
+ zfs_type_t);
+extern void zprop_free_list(zprop_list_t *);
+
+/*
+ * Functions for printing zfs or zpool properties
+ */
+typedef struct zprop_get_cbdata {
int cb_sources;
int cb_columns[4];
int cb_colwidths[5];
boolean_t cb_scripted;
boolean_t cb_literal;
boolean_t cb_first;
- zfs_proplist_t *cb_proplist;
-} libzfs_get_cbdata_t;
+ zprop_list_t *cb_proplist;
+ zfs_type_t cb_type;
+} zprop_get_cbdata_t;
-void libzfs_print_one_property(const char *, libzfs_get_cbdata_t *,
- const char *, const char *, zfs_source_t, const char *);
+void zprop_print_one_property(const char *, zprop_get_cbdata_t *,
+ const char *, const char *, zprop_source_t, const char *);
#define GET_COL_NAME 1
#define GET_COL_PROPERTY 2
@@ -331,26 +428,61 @@ extern int zfs_iter_snapshots(zfs_handle_t *, zfs_iter_f, void *);
*/
extern int zfs_create(libzfs_handle_t *, const char *, zfs_type_t,
nvlist_t *);
+extern int zfs_create_ancestors(libzfs_handle_t *, const char *);
extern int zfs_destroy(zfs_handle_t *);
extern int zfs_destroy_snaps(zfs_handle_t *, char *);
extern int zfs_clone(zfs_handle_t *, const char *, nvlist_t *);
-extern int zfs_snapshot(libzfs_handle_t *, const char *, boolean_t);
-extern int zfs_rollback(zfs_handle_t *, zfs_handle_t *, int);
-extern int zfs_rename(zfs_handle_t *, const char *, int);
-extern int zfs_send(zfs_handle_t *, const char *, int);
-extern int zfs_receive(libzfs_handle_t *, const char *, int, int, int,
- boolean_t, int);
+extern int zfs_snapshot(libzfs_handle_t *, const char *, boolean_t, nvlist_t *);
+extern int zfs_rollback(zfs_handle_t *, zfs_handle_t *, boolean_t);
+extern int zfs_rename(zfs_handle_t *, const char *, boolean_t);
+extern int zfs_send(zfs_handle_t *, const char *, const char *,
+ boolean_t, boolean_t, boolean_t, boolean_t, int);
extern int zfs_promote(zfs_handle_t *);
+typedef struct recvflags {
+ /* print informational messages (ie, -v was specified) */
+ int verbose : 1;
+
+ /* the destination is a prefix, not the exact fs (ie, -d) */
+ int isprefix : 1;
+
+ /* do not actually do the recv, just check if it would work (ie, -n) */
+ int dryrun : 1;
+
+ /* rollback/destroy filesystems as necessary (eg, -F) */
+ int force : 1;
+
+ /* set "canmount=off" on all modified filesystems */
+ int canmountoff : 1;
+
+ /* byteswap flag is used internally; callers need not specify */
+ int byteswap : 1;
+} recvflags_t;
+
+extern int zfs_receive(libzfs_handle_t *, const char *, recvflags_t,
+ int, avl_tree_t *);
+
/*
* Miscellaneous functions.
*/
extern const char *zfs_type_to_name(zfs_type_t);
extern void zfs_refresh_properties(zfs_handle_t *);
extern int zfs_name_valid(const char *, zfs_type_t);
-extern int zfs_disable(zfs_handle_t *);
-extern int zfs_enable(zfs_handle_t *);
extern zfs_handle_t *zfs_path_to_zhandle(libzfs_handle_t *, char *, zfs_type_t);
+extern boolean_t zfs_dataset_exists(libzfs_handle_t *, const char *,
+ zfs_type_t);
+extern int zfs_spa_version(zfs_handle_t *, int *);
+
+/*
+ * dataset permission functions.
+ */
+extern int zfs_perm_set(zfs_handle_t *, nvlist_t *);
+extern int zfs_perm_remove(zfs_handle_t *, nvlist_t *);
+extern int zfs_build_perms(zfs_handle_t *, char *, char *,
+ zfs_deleg_who_type_t, zfs_deleg_inherit_t, nvlist_t **nvlist_t);
+extern int zfs_perm_get(zfs_handle_t *, zfs_allow_t **);
+extern void zfs_free_allows(zfs_allow_t *);
+extern void zfs_deleg_permissions(void);
/*
* Mount support functions.
@@ -369,15 +501,27 @@ extern int zfs_share(zfs_handle_t *);
extern int zfs_unshare(zfs_handle_t *);
/*
- * Protocol-specifc share support functions.
+ * Protocol-specific share support functions.
*/
extern boolean_t zfs_is_shared_nfs(zfs_handle_t *, char **);
+extern boolean_t zfs_is_shared_smb(zfs_handle_t *, char **);
extern int zfs_share_nfs(zfs_handle_t *);
+extern int zfs_share_smb(zfs_handle_t *);
+extern int zfs_shareall(zfs_handle_t *);
extern int zfs_unshare_nfs(zfs_handle_t *, const char *);
+extern int zfs_unshare_smb(zfs_handle_t *, const char *);
extern int zfs_unshareall_nfs(zfs_handle_t *);
+extern int zfs_unshareall_smb(zfs_handle_t *);
+extern int zfs_unshareall_bypath(zfs_handle_t *, const char *);
+extern int zfs_unshareall(zfs_handle_t *);
extern boolean_t zfs_is_shared_iscsi(zfs_handle_t *);
extern int zfs_share_iscsi(zfs_handle_t *);
extern int zfs_unshare_iscsi(zfs_handle_t *);
+#ifdef TODO
+extern int zfs_iscsi_perm_check(libzfs_handle_t *, char *, ucred_t *);
+#endif
+extern int zfs_deleg_share_nfs(libzfs_handle_t *, char *, char *,
+ void *, void *, int, zfs_share_op_t);
/*
* FreeBSD-specific jail support function.
@@ -402,12 +546,6 @@ extern void zfs_nicenum(uint64_t, char *, size_t);
extern int zfs_nicestrtonum(libzfs_handle_t *, const char *, uint64_t *);
/*
- * Pool destroy special. Remove the device information without destroying
- * the underlying dataset.
- */
-extern int zfs_remove_link(zfs_handle_t *);
-
-/*
* Given a device or file, determine if it is part of a pool.
*/
extern int zpool_in_use(libzfs_handle_t *, int, pool_state_t *, char **,
@@ -424,6 +562,9 @@ extern int zpool_read_label(int, nvlist_t **);
extern int zpool_create_zvol_links(zpool_handle_t *);
extern int zpool_remove_zvol_links(zpool_handle_t *);
+/* is this zvol valid for use as a dump device? */
+extern int zvol_check_dump_config(char *);
+
/*
* Enable and disable datasets within a pool by mounting/unmounting and
* sharing/unsharing them.
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_changelist.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_changelist.c
index 5e6de6d..b905bc6 100644
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_changelist.c
+++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_changelist.c
@@ -20,12 +20,12 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Portions Copyright 2007 Ramprakash Jelari
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <libintl.h>
#include <libuutil.h>
#include <stddef.h>
@@ -65,18 +65,21 @@ typedef struct prop_changenode {
int cn_shared;
int cn_mounted;
int cn_zoned;
+ boolean_t cn_needpost; /* is postfix() needed? */
uu_list_node_t cn_listnode;
} prop_changenode_t;
struct prop_changelist {
zfs_prop_t cl_prop;
zfs_prop_t cl_realprop;
+ zfs_prop_t cl_shareprop; /* used with sharenfs/sharesmb */
uu_list_pool_t *cl_pool;
uu_list_t *cl_list;
boolean_t cl_waslegacy;
boolean_t cl_allchildren;
boolean_t cl_alldependents;
- int cl_flags;
+ int cl_mflags; /* Mount flags */
+ int cl_gflags; /* Gather request flags */
boolean_t cl_haszonedchild;
boolean_t cl_sorted;
};
@@ -84,7 +87,8 @@ struct prop_changelist {
/*
* If the property is 'mountpoint', go through and unmount filesystems as
* necessary. We don't do the same for 'sharenfs', because we can just re-share
- * with different options without interrupting service.
+ * with different options without interrupting service. We do handle 'sharesmb'
+ * since there may be old resource names that need to be removed.
*/
int
changelist_prefix(prop_changelist_t *clp)
@@ -92,11 +96,19 @@ changelist_prefix(prop_changelist_t *clp)
prop_changenode_t *cn;
int ret = 0;
- if (clp->cl_prop != ZFS_PROP_MOUNTPOINT)
+ if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
+ clp->cl_prop != ZFS_PROP_SHARESMB)
return (0);
for (cn = uu_list_first(clp->cl_list); cn != NULL;
cn = uu_list_next(clp->cl_list, cn)) {
+
+ /* if a previous loop failed, set the remaining to false */
+ if (ret == -1) {
+ cn->cn_needpost = B_FALSE;
+ continue;
+ }
+
/*
* If we are in the global zone, but this dataset is exported
* to a local zone, do nothing.
@@ -114,8 +126,11 @@ changelist_prefix(prop_changelist_t *clp)
(void) zfs_unshare_iscsi(cn->cn_handle);
if (zvol_remove_link(cn->cn_handle->zfs_hdl,
- cn->cn_handle->zfs_name) != 0)
+ cn->cn_handle->zfs_name) != 0) {
ret = -1;
+ cn->cn_needpost = B_FALSE;
+ (void) zfs_share_iscsi(cn->cn_handle);
+ }
break;
case ZFS_PROP_VOLSIZE:
@@ -126,10 +141,28 @@ changelist_prefix(prop_changelist_t *clp)
(void) zfs_unshare_iscsi(cn->cn_handle);
break;
}
- } else if (zfs_unmount(cn->cn_handle, NULL, clp->cl_flags) != 0)
- ret = -1;
+ } else {
+ /*
+ * Do the property specific processing.
+ */
+ switch (clp->cl_prop) {
+ case ZFS_PROP_MOUNTPOINT:
+ if (zfs_unmount(cn->cn_handle, NULL,
+ clp->cl_mflags) != 0) {
+ ret = -1;
+ cn->cn_needpost = B_FALSE;
+ }
+ break;
+ case ZFS_PROP_SHARESMB:
+ (void) zfs_unshare_smb(cn->cn_handle, NULL);
+ break;
+ }
+ }
}
+ if (ret == -1)
+ (void) changelist_postfix(clp);
+
return (ret);
}
@@ -147,7 +180,8 @@ changelist_postfix(prop_changelist_t *clp)
{
prop_changenode_t *cn;
char shareopts[ZFS_MAXPROPLEN];
- int ret = 0;
+ int errors = 0;
+ libzfs_handle_t *hdl;
/*
* If we're changing the mountpoint, attempt to destroy the underlying
@@ -163,11 +197,28 @@ changelist_postfix(prop_changelist_t *clp)
remove_mountpoint(cn->cn_handle);
/*
+ * It is possible that the changelist_prefix() used libshare
+ * to unshare some entries. Since libshare caches data, an
+ * attempt to reshare during postfix can fail unless libshare
+ * is uninitialized here so that it will reinitialize later.
+ */
+ if (cn->cn_handle != NULL) {
+ hdl = cn->cn_handle->zfs_hdl;
+ assert(hdl != NULL);
+ zfs_uninit_libshare(hdl);
+ }
+
+ /*
* We walk the datasets in reverse, because we want to mount any parent
- * datasets before mounting the children.
+ * datasets before mounting the children. We walk all datasets even if
+ * there are errors.
*/
for (cn = uu_list_last(clp->cl_list); cn != NULL;
cn = uu_list_prev(clp->cl_list, cn)) {
+
+ boolean_t sharenfs;
+ boolean_t sharesmb;
+
/*
* If we are in the global zone, but this dataset is exported
* to a local zone, do nothing.
@@ -175,6 +226,11 @@ changelist_postfix(prop_changelist_t *clp)
if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
continue;
+ /* Only do post-processing if it's required */
+ if (!cn->cn_needpost)
+ continue;
+ cn->cn_needpost = B_FALSE;
+
zfs_refresh_properties(cn->cn_handle);
if (ZFS_IS_VOLUME(cn->cn_handle)) {
@@ -185,7 +241,7 @@ changelist_postfix(prop_changelist_t *clp)
if (clp->cl_realprop == ZFS_PROP_NAME &&
zvol_create_link(cn->cn_handle->zfs_hdl,
cn->cn_handle->zfs_name) != 0) {
- ret = -1;
+ errors++;
} else if (cn->cn_shared ||
clp->cl_prop == ZFS_PROP_SHAREISCSI) {
if (zfs_prop_get(cn->cn_handle,
@@ -193,43 +249,55 @@ changelist_postfix(prop_changelist_t *clp)
sizeof (shareopts), NULL, NULL, 0,
B_FALSE) == 0 &&
strcmp(shareopts, "off") == 0) {
- ret = zfs_unshare_iscsi(cn->cn_handle);
+ errors +=
+ zfs_unshare_iscsi(cn->cn_handle);
} else {
- ret = zfs_share_iscsi(cn->cn_handle);
+ errors +=
+ zfs_share_iscsi(cn->cn_handle);
}
}
continue;
}
- if ((clp->cl_waslegacy || cn->cn_mounted) &&
- !zfs_is_mounted(cn->cn_handle, NULL) &&
+ /*
+ * Remount if previously mounted or mountpoint was legacy,
+ * or sharenfs or sharesmb property is set.
+ */
+ sharenfs = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS,
+ shareopts, sizeof (shareopts), NULL, NULL, 0,
+ B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
+
+ sharesmb = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARESMB,
+ shareopts, sizeof (shareopts), NULL, NULL, 0,
+ B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
+
+ if ((cn->cn_mounted || clp->cl_waslegacy || sharenfs ||
+ sharesmb) && !zfs_is_mounted(cn->cn_handle, NULL) &&
zfs_mount(cn->cn_handle, NULL, 0) != 0)
- ret = -1;
+ errors++;
/*
* We always re-share even if the filesystem is currently
* shared, so that we can adopt any new options.
*/
- if (cn->cn_shared ||
- (clp->cl_prop == ZFS_PROP_SHARENFS && clp->cl_waslegacy)) {
- if (zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS,
- shareopts, sizeof (shareopts), NULL, NULL, 0,
- B_FALSE) == 0 && strcmp(shareopts, "off") == 0) {
- ret = zfs_unshare_nfs(cn->cn_handle, NULL);
- } else {
- ret = zfs_share_nfs(cn->cn_handle);
- }
- }
+ if (sharenfs)
+ errors += zfs_share_nfs(cn->cn_handle);
+ else if (cn->cn_shared || clp->cl_waslegacy)
+ errors += zfs_unshare_nfs(cn->cn_handle, NULL);
+ if (sharesmb)
+ errors += zfs_share_smb(cn->cn_handle);
+ else if (cn->cn_shared || clp->cl_waslegacy)
+ errors += zfs_unshare_smb(cn->cn_handle, NULL);
}
- return (ret);
+ return (errors ? -1 : 0);
}
/*
* Is this "dataset" a child of "parent"?
*/
-static boolean_t
+boolean_t
isa_child_of(const char *dataset, const char *parent)
{
int len;
@@ -280,21 +348,22 @@ changelist_rename(prop_changelist_t *clp, const char *src, const char *dst)
}
/*
- * Given a gathered changelist for the 'sharenfs' property, unshare all the
- * datasets in the list.
+ * Given a gathered changelist for the 'sharenfs' or 'sharesmb' property,
+ * unshare all the datasets in the list.
*/
int
-changelist_unshare(prop_changelist_t *clp)
+changelist_unshare(prop_changelist_t *clp, zfs_share_proto_t *proto)
{
prop_changenode_t *cn;
int ret = 0;
- if (clp->cl_prop != ZFS_PROP_SHARENFS)
+ if (clp->cl_prop != ZFS_PROP_SHARENFS &&
+ clp->cl_prop != ZFS_PROP_SHARESMB)
return (0);
for (cn = uu_list_first(clp->cl_list); cn != NULL;
cn = uu_list_next(clp->cl_list, cn)) {
- if (zfs_unshare_nfs(cn->cn_handle, NULL) != 0)
+ if (zfs_unshare_proto(cn->cn_handle, NULL, proto) != 0)
ret = -1;
}
@@ -316,14 +385,14 @@ changelist_haszonedchild(prop_changelist_t *clp)
* Remove a node from a gathered list.
*/
void
-changelist_remove(zfs_handle_t *zhp, prop_changelist_t *clp)
+changelist_remove(prop_changelist_t *clp, const char *name)
{
prop_changenode_t *cn;
for (cn = uu_list_first(clp->cl_list); cn != NULL;
cn = uu_list_next(clp->cl_list, cn)) {
- if (strcmp(cn->cn_handle->zfs_name, zhp->zfs_name) == 0) {
+ if (strcmp(cn->cn_handle->zfs_name, name) == 0) {
uu_list_remove(clp->cl_list, cn);
zfs_close(cn->cn_handle);
free(cn);
@@ -363,7 +432,8 @@ change_one(zfs_handle_t *zhp, void *data)
char property[ZFS_MAXPROPLEN];
char where[64];
prop_changenode_t *cn;
- zfs_source_t sourcetype;
+ zprop_source_t sourcetype;
+ zprop_source_t share_sourcetype;
/*
* We only want to unmount/unshare those filesystems that may inherit
@@ -383,8 +453,25 @@ change_one(zfs_handle_t *zhp, void *data)
return (0);
}
+ /*
+ * If we are "watching" sharenfs or sharesmb
+ * then check out the companion property which is tracked
+ * in cl_shareprop
+ */
+ if (clp->cl_shareprop != ZPROP_INVAL &&
+ zfs_prop_get(zhp, clp->cl_shareprop, property,
+ sizeof (property), &share_sourcetype, where, sizeof (where),
+ B_FALSE) != 0) {
+ zfs_close(zhp);
+ return (0);
+ }
+
if (clp->cl_alldependents || clp->cl_allchildren ||
- sourcetype == ZFS_SRC_DEFAULT || sourcetype == ZFS_SRC_INHERITED) {
+ sourcetype == ZPROP_SRC_DEFAULT ||
+ sourcetype == ZPROP_SRC_INHERITED ||
+ (clp->cl_shareprop != ZPROP_INVAL &&
+ (share_sourcetype == ZPROP_SRC_DEFAULT ||
+ share_sourcetype == ZPROP_SRC_INHERITED))) {
if ((cn = zfs_alloc(zfs_get_handle(zhp),
sizeof (prop_changenode_t))) == NULL) {
zfs_close(zhp);
@@ -392,9 +479,11 @@ change_one(zfs_handle_t *zhp, void *data)
}
cn->cn_handle = zhp;
- cn->cn_mounted = zfs_is_mounted(zhp, NULL);
+ cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
+ zfs_is_mounted(zhp, NULL);
cn->cn_shared = zfs_is_shared(zhp);
cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
+ cn->cn_needpost = B_TRUE;
/* Indicate if any child is exported to a local zone. */
if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
@@ -467,7 +556,8 @@ compare_mountpoints(const void *a, const void *b, void *unused)
* mark whether it was shared beforehand.
*/
prop_changelist_t *
-changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags)
+changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int gather_flags,
+ int mnt_flags)
{
prop_changelist_t *clp;
prop_changenode_t *cn;
@@ -484,7 +574,8 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags)
* order, regardless of their position in the hierarchy.
*/
if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED ||
- prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS) {
+ prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS ||
+ prop == ZFS_PROP_SHARESMB) {
compare = compare_mountpoints;
clp->cl_sorted = B_TRUE;
}
@@ -502,7 +593,8 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags)
clp->cl_list = uu_list_create(clp->cl_pool, NULL,
clp->cl_sorted ? UU_LIST_SORTED : 0);
- clp->cl_flags = flags;
+ clp->cl_gflags = gather_flags;
+ clp->cl_mflags = mnt_flags;
if (clp->cl_list == NULL) {
assert(uu_error() == UU_ERROR_NO_MEMORY);
@@ -529,6 +621,8 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags)
clp->cl_prop = ZFS_PROP_MOUNTPOINT;
} else if (prop == ZFS_PROP_VOLSIZE) {
clp->cl_prop = ZFS_PROP_MOUNTPOINT;
+ } else if (prop == ZFS_PROP_VERSION) {
+ clp->cl_prop = ZFS_PROP_MOUNTPOINT;
} else {
clp->cl_prop = prop;
}
@@ -536,9 +630,19 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags)
if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
clp->cl_prop != ZFS_PROP_SHARENFS &&
+ clp->cl_prop != ZFS_PROP_SHARESMB &&
clp->cl_prop != ZFS_PROP_SHAREISCSI)
return (clp);
+ /*
+ * If watching SHARENFS or SHARESMB then
+ * also watch its companion property.
+ */
+ if (clp->cl_prop == ZFS_PROP_SHARENFS)
+ clp->cl_shareprop = ZFS_PROP_SHARESMB;
+ else if (clp->cl_prop == ZFS_PROP_SHARESMB)
+ clp->cl_shareprop = ZFS_PROP_SHARENFS;
+
if (clp->cl_alldependents) {
if (zfs_iter_dependents(zhp, B_TRUE, change_one, clp) != 0) {
changelist_free(clp);
@@ -554,7 +658,7 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags)
* and can't tell the difference.
*/
if ((temp = zfs_open(zhp->zfs_hdl, zfs_get_name(zhp),
- ZFS_TYPE_ANY)) == NULL) {
+ ZFS_TYPE_DATASET)) == NULL) {
changelist_free(clp);
return (NULL);
}
@@ -571,9 +675,11 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags)
}
cn->cn_handle = temp;
- cn->cn_mounted = zfs_is_mounted(temp, NULL);
+ cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
+ zfs_is_mounted(temp, NULL);
cn->cn_shared = zfs_is_shared(temp);
cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
+ cn->cn_needpost = B_TRUE;
uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool);
if (clp->cl_sorted) {
@@ -586,14 +692,22 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags)
}
/*
- * If the property was previously 'legacy' or 'none', record this fact,
- * as the behavior of changelist_postfix() will be different.
+ * If the mountpoint property was previously 'legacy', or 'none',
+ * record it as the behavior of changelist_postfix() will be different.
*/
- if (zfs_prop_get(zhp, prop, property, sizeof (property),
+ if ((clp->cl_prop == ZFS_PROP_MOUNTPOINT) &&
+ (zfs_prop_get(zhp, prop, property, sizeof (property),
NULL, NULL, 0, B_FALSE) == 0 &&
- (strcmp(property, "legacy") == 0 || strcmp(property, "none") == 0 ||
- strcmp(property, "off") == 0))
- clp->cl_waslegacy = B_TRUE;
+ (strcmp(property, "legacy") == 0 ||
+ strcmp(property, "none") == 0))) {
+ /*
+ * do not automatically mount ex-legacy datasets if
+ * we specifically set canmount to noauto
+ */
+ if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) !=
+ ZFS_CANMOUNT_NOAUTO)
+ clp->cl_waslegacy = B_TRUE;
+ }
return (clp);
}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
index 4fc441a..58ce6c8 100644
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
+++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
@@ -20,12 +20,10 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <assert.h>
#include <ctype.h>
#include <errno.h>
@@ -35,20 +33,26 @@
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
+#include <stddef.h>
#include <zone.h>
#include <fcntl.h>
#include <sys/mntent.h>
#include <sys/mnttab.h>
#include <sys/mount.h>
+#include <sys/avl.h>
+#include <priv.h>
+#include <pwd.h>
+#include <grp.h>
+#include <stddef.h>
#include <sys/spa.h>
-#include <sys/zio.h>
#include <sys/zap.h>
#include <libzfs.h>
#include "zfs_namecheck.h"
#include "zfs_prop.h"
#include "libzfs_impl.h"
+#include "zfs_deleg.h"
static int zvol_create_link_common(libzfs_handle_t *, const char *, int);
@@ -121,7 +125,8 @@ path_to_str(const char *path, int types)
* 'buf' detailing exactly why the name was not valid.
*/
static int
-zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type)
+zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
+ boolean_t modifying)
{
namecheck_err_t why;
char what;
@@ -194,43 +199,118 @@ zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type)
return (0);
}
+ if (modifying && strchr(path, '%') != NULL) {
+ if (hdl != NULL)
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "invalid character %c in name"), '%');
+ return (0);
+ }
+
return (-1);
}
int
zfs_name_valid(const char *name, zfs_type_t type)
{
- return (zfs_validate_name(NULL, name, type));
+ if (type == ZFS_TYPE_POOL)
+ return (zpool_name_valid(NULL, B_FALSE, name));
+ return (zfs_validate_name(NULL, name, type, B_FALSE));
}
/*
* This function takes the raw DSL properties, and filters out the user-defined
* properties into a separate nvlist.
*/
-static int
-process_user_props(zfs_handle_t *zhp)
+static nvlist_t *
+process_user_props(zfs_handle_t *zhp, nvlist_t *props)
{
libzfs_handle_t *hdl = zhp->zfs_hdl;
nvpair_t *elem;
nvlist_t *propval;
+ nvlist_t *nvl;
- nvlist_free(zhp->zfs_user_props);
-
- if (nvlist_alloc(&zhp->zfs_user_props, NV_UNIQUE_NAME, 0) != 0)
- return (no_memory(hdl));
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
+ (void) no_memory(hdl);
+ return (NULL);
+ }
elem = NULL;
- while ((elem = nvlist_next_nvpair(zhp->zfs_props, elem)) != NULL) {
+ while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
if (!zfs_prop_user(nvpair_name(elem)))
continue;
verify(nvpair_value_nvlist(elem, &propval) == 0);
- if (nvlist_add_nvlist(zhp->zfs_user_props,
- nvpair_name(elem), propval) != 0)
- return (no_memory(hdl));
+ if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) {
+ nvlist_free(nvl);
+ (void) no_memory(hdl);
+ return (NULL);
+ }
}
- return (0);
+ return (nvl);
+}
+
+static zpool_handle_t *
+zpool_add_handle(zfs_handle_t *zhp, const char *pool_name)
+{
+ libzfs_handle_t *hdl = zhp->zfs_hdl;
+ zpool_handle_t *zph;
+
+ if ((zph = zpool_open_canfail(hdl, pool_name)) != NULL) {
+ if (hdl->libzfs_pool_handles != NULL)
+ zph->zpool_next = hdl->libzfs_pool_handles;
+ hdl->libzfs_pool_handles = zph;
+ }
+ return (zph);
+}
+
+static zpool_handle_t *
+zpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len)
+{
+ libzfs_handle_t *hdl = zhp->zfs_hdl;
+ zpool_handle_t *zph = hdl->libzfs_pool_handles;
+
+ while ((zph != NULL) &&
+ (strncmp(pool_name, zpool_get_name(zph), len) != 0))
+ zph = zph->zpool_next;
+ return (zph);
+}
+
+/*
+ * Returns a handle to the pool that contains the provided dataset.
+ * If a handle to that pool already exists then that handle is returned.
+ * Otherwise, a new handle is created and added to the list of handles.
+ */
+static zpool_handle_t *
+zpool_handle(zfs_handle_t *zhp)
+{
+ char *pool_name;
+ int len;
+ zpool_handle_t *zph;
+
+ len = strcspn(zhp->zfs_name, "/@") + 1;
+ pool_name = zfs_alloc(zhp->zfs_hdl, len);
+ (void) strlcpy(pool_name, zhp->zfs_name, len);
+
+ zph = zpool_find_handle(zhp, pool_name, len);
+ if (zph == NULL)
+ zph = zpool_add_handle(zhp, pool_name);
+
+ free(pool_name);
+ return (zph);
+}
+
+void
+zpool_free_handles(libzfs_handle_t *hdl)
+{
+ zpool_handle_t *next, *zph = hdl->libzfs_pool_handles;
+
+ while (zph != NULL) {
+ next = zph->zpool_next;
+ zpool_close(zph);
+ zph = next;
+ }
+ hdl->libzfs_pool_handles = NULL;
}
/*
@@ -241,6 +321,7 @@ get_stats(zfs_handle_t *zhp)
{
zfs_cmd_t zc = { 0 };
libzfs_handle_t *hdl = zhp->zfs_hdl;
+ nvlist_t *allprops, *userprops;
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
@@ -261,22 +342,23 @@ get_stats(zfs_handle_t *zhp)
zhp->zfs_dmustats = zc.zc_objset_stats; /* structure assignment */
- (void) strlcpy(zhp->zfs_root, zc.zc_value, sizeof (zhp->zfs_root));
-
- if (zhp->zfs_props) {
- nvlist_free(zhp->zfs_props);
- zhp->zfs_props = NULL;
- }
-
- if (zcmd_read_dst_nvlist(hdl, &zc, &zhp->zfs_props) != 0) {
+ if (zcmd_read_dst_nvlist(hdl, &zc, &allprops) != 0) {
zcmd_free_nvlists(&zc);
return (-1);
}
zcmd_free_nvlists(&zc);
- if (process_user_props(zhp) != 0)
+ if ((userprops = process_user_props(zhp, allprops)) == NULL) {
+ nvlist_free(allprops);
return (-1);
+ }
+
+ nvlist_free(zhp->zfs_props);
+ nvlist_free(zhp->zfs_user_props);
+
+ zhp->zfs_props = allprops;
+ zhp->zfs_user_props = userprops;
return (0);
}
@@ -298,16 +380,25 @@ zfs_handle_t *
make_dataset_handle(libzfs_handle_t *hdl, const char *path)
{
zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
+ char *logstr;
if (zhp == NULL)
return (NULL);
zhp->zfs_hdl = hdl;
+ /*
+ * Preserve history log string.
+ * any changes performed here will be
+ * logged as an internal event.
+ */
+ logstr = zhp->zfs_hdl->libzfs_log_str;
+ zhp->zfs_hdl->libzfs_log_str = NULL;
top:
(void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
if (get_stats(zhp) != 0) {
+ zhp->zfs_hdl->libzfs_log_str = logstr;
free(zhp);
return (NULL);
}
@@ -339,18 +430,19 @@ top:
zc.zc_objset_type = DMU_OST_ZFS;
}
- /* If we can successfully roll it back, reget the stats */
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_ROLLBACK, &zc) == 0)
- goto top;
/*
- * If we can sucessfully destroy it, pretend that it
+ * If we can successfully destroy it, pretend that it
* never existed.
*/
if (ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc) == 0) {
+ zhp->zfs_hdl->libzfs_log_str = logstr;
free(zhp);
errno = ENOENT;
return (NULL);
}
+ /* If we can successfully roll it back, reget the stats */
+ if (ioctl(hdl->libzfs_fd, ZFS_IOC_ROLLBACK, &zc) == 0)
+ goto top;
}
/*
@@ -373,6 +465,8 @@ top:
else
abort(); /* we should never see any other types */
+ zhp->zfs_hdl->libzfs_log_str = logstr;
+ zhp->zpool_hdl = zpool_handle(zhp);
return (zhp);
}
@@ -393,7 +487,7 @@ zfs_open(libzfs_handle_t *hdl, const char *path, int types)
/*
* Validate the name before we even try to open it.
*/
- if (!zfs_validate_name(hdl, path, ZFS_TYPE_ANY)) {
+ if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"invalid dataset name"));
(void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
@@ -431,350 +525,92 @@ zfs_close(zfs_handle_t *zhp)
free(zhp);
}
-/*
- * Given a numeric suffix, convert the value into a number of bits that the
- * resulting value must be shifted.
- */
-static int
-str2shift(libzfs_handle_t *hdl, const char *buf)
-{
- const char *ends = "BKMGTPEZ";
- int i;
-
- if (buf[0] == '\0')
- return (0);
- for (i = 0; i < strlen(ends); i++) {
- if (toupper(buf[0]) == ends[i])
- break;
- }
- if (i == strlen(ends)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid numeric suffix '%s'"), buf);
- return (-1);
- }
-
- /*
- * We want to allow trailing 'b' characters for 'GB' or 'Mb'. But don't
- * allow 'BB' - that's just weird.
- */
- if (buf[1] == '\0' || (toupper(buf[1]) == 'B' && buf[2] == '\0' &&
- toupper(buf[0]) != 'B'))
- return (10*i);
-
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid numeric suffix '%s'"), buf);
- return (-1);
-}
-
-/*
- * Convert a string of the form '100G' into a real number. Used when setting
- * properties or creating a volume. 'buf' is used to place an extended error
- * message for the caller to use.
- */
-static int
-nicestrtonum(libzfs_handle_t *hdl, const char *value, uint64_t *num)
+int
+zfs_spa_version(zfs_handle_t *zhp, int *spa_version)
{
- char *end;
- int shift;
-
- *num = 0;
-
- /* Check to see if this looks like a number. */
- if ((value[0] < '0' || value[0] > '9') && value[0] != '.') {
- if (hdl)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "bad numeric value '%s'"), value);
- return (-1);
- }
-
- /* Rely on stroll() to process the numeric portion. */
- errno = 0;
- *num = strtoll(value, &end, 10);
+ zpool_handle_t *zpool_handle = zhp->zpool_hdl;
- /*
- * Check for ERANGE, which indicates that the value is too large to fit
- * in a 64-bit value.
- */
- if (errno == ERANGE) {
- if (hdl)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "numeric value is too large"));
+ if (zpool_handle == NULL)
return (-1);
- }
-
- /*
- * If we have a decimal value, then do the computation with floating
- * point arithmetic. Otherwise, use standard arithmetic.
- */
- if (*end == '.') {
- double fval = strtod(value, &end);
-
- if ((shift = str2shift(hdl, end)) == -1)
- return (-1);
-
- fval *= pow(2, shift);
-
- if (fval > UINT64_MAX) {
- if (hdl)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "numeric value is too large"));
- return (-1);
- }
-
- *num = (uint64_t)fval;
- } else {
- if ((shift = str2shift(hdl, end)) == -1)
- return (-1);
-
- /* Check for overflow */
- if (shift >= 64 || (*num << shift) >> shift != *num) {
- if (hdl)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "numeric value is too large"));
- return (-1);
- }
-
- *num <<= shift;
- }
+ *spa_version = zpool_get_prop_int(zpool_handle,
+ ZPOOL_PROP_VERSION, NULL);
return (0);
}
-int
-zfs_nicestrtonum(libzfs_handle_t *hdl, const char *str, uint64_t *val)
-{
- return (nicestrtonum(hdl, str, val));
-}
-
/*
- * The prop_parse_*() functions are designed to allow flexibility in callers
- * when setting properties. At the DSL layer, all properties are either 64-bit
- * numbers or strings. We want the user to be able to ignore this fact and
- * specify properties as native values (boolean, for example) or as strings (to
- * simplify command line utilities). This also handles converting index types
- * (compression, checksum, etc) from strings to their on-disk index.
+ * The choice of reservation property depends on the SPA version.
*/
-
static int
-prop_parse_boolean(libzfs_handle_t *hdl, nvpair_t *elem, uint64_t *val)
+zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop)
{
- uint64_t ret;
+ int spa_version;
- switch (nvpair_type(elem)) {
- case DATA_TYPE_STRING:
- {
- char *value;
- verify(nvpair_value_string(elem, &value) == 0);
-
- if (strcmp(value, "on") == 0) {
- ret = 1;
- } else if (strcmp(value, "off") == 0) {
- ret = 0;
- } else {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property '%s' must be 'on' or 'off'"),
- nvpair_name(elem));
- return (-1);
- }
- break;
- }
-
- case DATA_TYPE_UINT64:
- {
- verify(nvpair_value_uint64(elem, &ret) == 0);
- if (ret > 1) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be a boolean value"),
- nvpair_name(elem));
- return (-1);
- }
- break;
- }
-
- case DATA_TYPE_BOOLEAN_VALUE:
- {
- boolean_t value;
- verify(nvpair_value_boolean_value(elem, &value) == 0);
- ret = value;
- break;
- }
-
- default:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be a boolean value"),
- nvpair_name(elem));
+ if (zfs_spa_version(zhp, &spa_version) < 0)
return (-1);
- }
-
- *val = ret;
- return (0);
-}
-
-static int
-prop_parse_number(libzfs_handle_t *hdl, nvpair_t *elem, zfs_prop_t prop,
- uint64_t *val)
-{
- uint64_t ret;
- boolean_t isnone = B_FALSE;
- switch (nvpair_type(elem)) {
- case DATA_TYPE_STRING:
- {
- char *value;
- (void) nvpair_value_string(elem, &value);
- if (strcmp(value, "none") == 0) {
- isnone = B_TRUE;
- ret = 0;
- } else if (nicestrtonum(hdl, value, &ret) != 0) {
- return (-1);
- }
- break;
- }
-
- case DATA_TYPE_UINT64:
- (void) nvpair_value_uint64(elem, &ret);
- break;
-
- default:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be a number"),
- nvpair_name(elem));
- return (-1);
- }
-
- /*
- * Quota special: force 'none' and don't allow 0.
- */
- if (ret == 0 && !isnone && prop == ZFS_PROP_QUOTA) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "use 'none' to disable quota"));
- return (-1);
- }
-
- *val = ret;
- return (0);
-}
-
-static int
-prop_parse_index(libzfs_handle_t *hdl, nvpair_t *elem, zfs_prop_t prop,
- uint64_t *val)
-{
- char *propname = nvpair_name(elem);
- char *value;
-
- if (nvpair_type(elem) != DATA_TYPE_STRING) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be a string"), propname);
- return (-1);
- }
-
- (void) nvpair_value_string(elem, &value);
-
- if (zfs_prop_string_to_index(prop, value, val) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be one of '%s'"), propname,
- zfs_prop_values(prop));
- return (-1);
- }
+ if (spa_version >= SPA_VERSION_REFRESERVATION)
+ *resv_prop = ZFS_PROP_REFRESERVATION;
+ else
+ *resv_prop = ZFS_PROP_RESERVATION;
return (0);
}
/*
- * Check if the bootfs name has the same pool name as it is set to.
- * Assuming bootfs is a valid dataset name.
- */
-static boolean_t
-bootfs_poolname_valid(char *pool, char *bootfs)
-{
- char ch, *pname;
-
- /* get the pool name from the bootfs name */
- pname = bootfs;
- while (*bootfs && !isspace(*bootfs) && *bootfs != '/')
- bootfs++;
-
- ch = *bootfs;
- *bootfs = 0;
-
- if (strcmp(pool, pname) == 0) {
- *bootfs = ch;
- return (B_TRUE);
- }
-
- *bootfs = ch;
- return (B_FALSE);
-}
-
-/*
* Given an nvlist of properties to set, validates that they are correct, and
* parses any numeric properties (index, boolean, etc) if they are specified as
* strings.
*/
nvlist_t *
-zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, char *pool_name,
- nvlist_t *nvl, uint64_t zoned, zfs_handle_t *zhp, const char *errbuf)
+zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
+ uint64_t zoned, zfs_handle_t *zhp, const char *errbuf)
{
nvpair_t *elem;
- const char *propname;
- zfs_prop_t prop;
uint64_t intval;
char *strval;
+ zfs_prop_t prop;
nvlist_t *ret;
- int isuser;
+ int chosen_normal = -1;
+ int chosen_utf = -1;
if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) {
(void) no_memory(hdl);
return (NULL);
}
- if (type == ZFS_TYPE_SNAPSHOT) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "snapshot properties cannot be modified"));
- (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
- goto error;
- }
-
elem = NULL;
while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
- propname = nvpair_name(elem);
+ const char *propname = nvpair_name(elem);
/*
* Make sure this property is valid and applies to this type.
*/
- if ((prop = zfs_name_to_prop_common(propname, type))
- == ZFS_PROP_INVAL) {
- isuser = zfs_prop_user(propname);
- if (!isuser || (isuser && (type & ZFS_TYPE_POOL))) {
+ if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) {
+ if (!zfs_prop_user(propname)) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid property '%s'"),
- propname);
+ "invalid property '%s'"), propname);
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
goto error;
- } else {
- /*
- * If this is a user property, make sure it's a
- * string, and that it's less than
- * ZAP_MAXNAMELEN.
- */
- if (nvpair_type(elem) != DATA_TYPE_STRING) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be a string"),
- propname);
- (void) zfs_error(hdl, EZFS_BADPROP,
- errbuf);
- goto error;
- }
+ }
- if (strlen(nvpair_name(elem)) >=
- ZAP_MAXNAMELEN) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property name '%s' is too long"),
- propname);
- (void) zfs_error(hdl, EZFS_BADPROP,
- errbuf);
- goto error;
- }
+ /*
+ * If this is a user property, make sure it's a
+ * string, and that it's less than ZAP_MAXNAMELEN.
+ */
+ if (nvpair_type(elem) != DATA_TYPE_STRING) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "'%s' must be a string"), propname);
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+
+ if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "property name '%s' is too long"),
+ propname);
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
}
(void) nvpair_value_string(elem, &strval);
@@ -785,10 +621,12 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, char *pool_name,
continue;
}
- /*
- * Normalize the name, to get rid of shorthand abbrevations.
- */
- propname = zfs_prop_to_name(prop);
+ if (type == ZFS_TYPE_SNAPSHOT) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "this property can not be modified for snapshots"));
+ (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
+ goto error;
+ }
if (!zfs_prop_valid_for_type(prop, type)) {
zfs_error_aux(hdl,
@@ -799,7 +637,7 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, char *pool_name,
}
if (zfs_prop_readonly(prop) &&
- (prop != ZFS_PROP_VOLBLOCKSIZE || zhp != NULL)) {
+ (!zfs_prop_setonce(prop) || zhp != NULL)) {
zfs_error_aux(hdl,
dgettext(TEXT_DOMAIN, "'%s' is readonly"),
propname);
@@ -807,70 +645,31 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, char *pool_name,
goto error;
}
+ if (zprop_parse_value(hdl, elem, prop, type, ret,
+ &strval, &intval, errbuf) != 0)
+ goto error;
+
/*
- * Convert any properties to the internal DSL value types.
+ * Perform some additional checks for specific properties.
*/
- strval = NULL;
- switch (zfs_prop_get_type(prop)) {
- case prop_type_boolean:
- if (prop_parse_boolean(hdl, elem, &intval) != 0) {
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- break;
+ switch (prop) {
+ case ZFS_PROP_VERSION:
+ {
+ int version;
- case prop_type_string:
- if (nvpair_type(elem) != DATA_TYPE_STRING) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be a string"),
- propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- (void) nvpair_value_string(elem, &strval);
- if (strlen(strval) >= ZFS_MAXPROPLEN) {
+ if (zhp == NULL)
+ break;
+ version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
+ if (intval < version) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' is too long"), propname);
+ "Can not downgrade; already at version %u"),
+ version);
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
goto error;
}
break;
-
- case prop_type_number:
- if (prop_parse_number(hdl, elem, prop, &intval) != 0) {
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- break;
-
- case prop_type_index:
- if (prop_parse_index(hdl, elem, prop, &intval) != 0) {
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- break;
-
- default:
- abort();
}
- /*
- * Add the result to our return set of properties.
- */
- if (strval) {
- if (nvlist_add_string(ret, propname, strval) != 0) {
- (void) no_memory(hdl);
- goto error;
- }
- } else if (nvlist_add_uint64(ret, propname, intval) != 0) {
- (void) no_memory(hdl);
- goto error;
- }
-
- /*
- * Perform some additional checks for specific properties.
- */
- switch (prop) {
case ZFS_PROP_RECORDSIZE:
case ZFS_PROP_VOLBLOCKSIZE:
/* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */
@@ -900,32 +699,52 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, char *pool_name,
break;
case ZFS_PROP_MOUNTPOINT:
+ {
+ namecheck_err_t why;
+
if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 ||
strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0)
break;
- if (strval[0] != '/') {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be an absolute path, "
- "'none', or 'legacy'"), propname);
+ if (mountpoint_namecheck(strval, &why)) {
+ switch (why) {
+ case NAME_ERR_LEADING_SLASH:
+ zfs_error_aux(hdl,
+ dgettext(TEXT_DOMAIN,
+ "'%s' must be an absolute path, "
+ "'none', or 'legacy'"), propname);
+ break;
+ case NAME_ERR_TOOLONG:
+ zfs_error_aux(hdl,
+ dgettext(TEXT_DOMAIN,
+ "component of '%s' is too long"),
+ propname);
+ break;
+ }
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
goto error;
}
+ }
+
/*FALLTHRU*/
+ case ZFS_PROP_SHARESMB:
case ZFS_PROP_SHARENFS:
/*
- * For the mountpoint and sharenfs properties, check if
- * it can be set in a global/non-global zone based on
+ * For the mountpoint and sharenfs or sharesmb
+ * properties, check if it can be set in a
+ * global/non-global zone based on
* the zoned property value:
*
* global zone non-global zone
* --------------------------------------------------
* zoned=on mountpoint (no) mountpoint (yes)
* sharenfs (no) sharenfs (no)
+ * sharesmb (no) sharesmb (no)
*
* zoned=off mountpoint (yes) N/A
* sharenfs (yes)
+ * sharesmb (yes)
*/
if (zoned) {
if (getzoneid() == GLOBAL_ZONEID) {
@@ -936,7 +755,8 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, char *pool_name,
(void) zfs_error(hdl, EZFS_ZONED,
errbuf);
goto error;
- } else if (prop == ZFS_PROP_SHARENFS) {
+ } else if (prop == ZFS_PROP_SHARENFS ||
+ prop == ZFS_PROP_SHARESMB) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"'%s' cannot be set in "
"a non-global zone"), propname);
@@ -956,22 +776,73 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, char *pool_name,
goto error;
}
- break;
-
- case ZFS_PROP_BOOTFS:
/*
- * bootfs property value has to be a dataset name and
- * the dataset has to be in the same pool as it sets to.
+ * At this point, it is legitimate to set the
+ * property. Now we want to make sure that the
+ * property value is valid if it is sharenfs.
*/
- if (strval[0] != '\0' && (!zfs_name_valid(strval,
- ZFS_TYPE_FILESYSTEM) || !bootfs_poolname_valid(
- pool_name, strval))) {
+ if ((prop == ZFS_PROP_SHARENFS ||
+ prop == ZFS_PROP_SHARESMB) &&
+ strcmp(strval, "on") != 0 &&
+ strcmp(strval, "off") != 0) {
+ zfs_share_proto_t proto;
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "'%s' "
- "is an invalid name"), strval);
- (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
- goto error;
+ if (prop == ZFS_PROP_SHARESMB)
+ proto = PROTO_SMB;
+ else
+ proto = PROTO_NFS;
+
+ /*
+ * Must be an valid sharing protocol
+ * option string so init the libshare
+ * in order to enable the parser and
+ * then parse the options. We use the
+ * control API since we don't care about
+ * the current configuration and don't
+ * want the overhead of loading it
+ * until we actually do something.
+ */
+
+ if (zfs_init_libshare(hdl,
+ SA_INIT_CONTROL_API) != SA_OK) {
+ /*
+ * An error occurred so we can't do
+ * anything
+ */
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "'%s' cannot be set: problem "
+ "in share initialization"),
+ propname);
+ (void) zfs_error(hdl, EZFS_BADPROP,
+ errbuf);
+ goto error;
+ }
+
+ if (zfs_parse_options(strval, proto) != SA_OK) {
+ /*
+ * There was an error in parsing so
+ * deal with it by issuing an error
+ * message and leaving after
+ * uninitializing the the libshare
+ * interface.
+ */
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "'%s' cannot be set to invalid "
+ "options"), propname);
+ (void) zfs_error(hdl, EZFS_BADPROP,
+ errbuf);
+ zfs_uninit_libshare(hdl);
+ goto error;
+ }
+ zfs_uninit_libshare(hdl);
}
+
+ break;
+ case ZFS_PROP_UTF8ONLY:
+ chosen_utf = (int)intval;
+ break;
+ case ZFS_PROP_NORMALIZE:
+ chosen_normal = (int)intval;
break;
}
@@ -988,6 +859,7 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, char *pool_name,
switch (prop) {
case ZFS_PROP_RESERVATION:
+ case ZFS_PROP_REFRESERVATION:
if (intval > volsize) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"'%s' is greater than current "
@@ -1025,6 +897,27 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, char *pool_name,
}
/*
+ * If normalization was chosen, but no UTF8 choice was made,
+ * enforce rejection of non-UTF8 names.
+ *
+ * If normalization was chosen, but rejecting non-UTF8 names
+ * was explicitly not chosen, it is an error.
+ */
+ if (chosen_normal > 0 && chosen_utf < 0) {
+ if (nvlist_add_uint64(ret,
+ zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) {
+ (void) no_memory(hdl);
+ goto error;
+ }
+ } else if (chosen_normal > 0 && chosen_utf == 0) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "'%s' must be set 'on' if normalization chosen"),
+ zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+
+ /*
* If this is an existing volume, and someone is setting the volsize,
* make sure that it matches the reservation, or add it if necessary.
*/
@@ -1033,23 +926,24 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, char *pool_name,
&intval) == 0) {
uint64_t old_volsize = zfs_prop_get_int(zhp,
ZFS_PROP_VOLSIZE);
- uint64_t old_reservation = zfs_prop_get_int(zhp,
- ZFS_PROP_RESERVATION);
+ uint64_t old_reservation;
uint64_t new_reservation;
+ zfs_prop_t resv_prop;
+
+ if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
+ goto error;
+ old_reservation = zfs_prop_get_int(zhp, resv_prop);
if (old_volsize == old_reservation &&
- nvlist_lookup_uint64(ret,
- zfs_prop_to_name(ZFS_PROP_RESERVATION),
+ nvlist_lookup_uint64(ret, zfs_prop_to_name(resv_prop),
&new_reservation) != 0) {
if (nvlist_add_uint64(ret,
- zfs_prop_to_name(ZFS_PROP_RESERVATION),
- intval) != 0) {
+ zfs_prop_to_name(resv_prop), intval) != 0) {
(void) no_memory(hdl);
goto error;
}
}
}
-
return (ret);
error:
@@ -1057,6 +951,808 @@ error:
return (NULL);
}
+static int
+zfs_get_perm_who(const char *who, zfs_deleg_who_type_t *who_type,
+ uint64_t *ret_who)
+{
+ struct passwd *pwd;
+ struct group *grp;
+ uid_t id;
+
+ if (*who_type == ZFS_DELEG_EVERYONE || *who_type == ZFS_DELEG_CREATE ||
+ *who_type == ZFS_DELEG_NAMED_SET) {
+ *ret_who = -1;
+ return (0);
+ }
+ if (who == NULL && !(*who_type == ZFS_DELEG_EVERYONE))
+ return (EZFS_BADWHO);
+
+ if (*who_type == ZFS_DELEG_WHO_UNKNOWN &&
+ strcmp(who, "everyone") == 0) {
+ *ret_who = -1;
+ *who_type = ZFS_DELEG_EVERYONE;
+ return (0);
+ }
+
+ pwd = getpwnam(who);
+ grp = getgrnam(who);
+
+ if ((*who_type == ZFS_DELEG_USER) && pwd) {
+ *ret_who = pwd->pw_uid;
+ } else if ((*who_type == ZFS_DELEG_GROUP) && grp) {
+ *ret_who = grp->gr_gid;
+ } else if (pwd) {
+ *ret_who = pwd->pw_uid;
+ *who_type = ZFS_DELEG_USER;
+ } else if (grp) {
+ *ret_who = grp->gr_gid;
+ *who_type = ZFS_DELEG_GROUP;
+ } else {
+ char *end;
+
+ id = strtol(who, &end, 10);
+ if (errno != 0 || *end != '\0') {
+ return (EZFS_BADWHO);
+ } else {
+ *ret_who = id;
+ if (*who_type == ZFS_DELEG_WHO_UNKNOWN)
+ *who_type = ZFS_DELEG_USER;
+ }
+ }
+
+ return (0);
+}
+
+static void
+zfs_perms_add_to_nvlist(nvlist_t *who_nvp, char *name, nvlist_t *perms_nvp)
+{
+ if (perms_nvp != NULL) {
+ verify(nvlist_add_nvlist(who_nvp,
+ name, perms_nvp) == 0);
+ } else {
+ verify(nvlist_add_boolean(who_nvp, name) == 0);
+ }
+}
+
+static void
+helper(zfs_deleg_who_type_t who_type, uint64_t whoid, char *whostr,
+ zfs_deleg_inherit_t inherit, nvlist_t *who_nvp, nvlist_t *perms_nvp,
+ nvlist_t *sets_nvp)
+{
+ boolean_t do_perms, do_sets;
+ char name[ZFS_MAX_DELEG_NAME];
+
+ do_perms = (nvlist_next_nvpair(perms_nvp, NULL) != NULL);
+ do_sets = (nvlist_next_nvpair(sets_nvp, NULL) != NULL);
+
+ if (!do_perms && !do_sets)
+ do_perms = do_sets = B_TRUE;
+
+ if (do_perms) {
+ zfs_deleg_whokey(name, who_type, inherit,
+ (who_type == ZFS_DELEG_NAMED_SET) ?
+ whostr : (void *)&whoid);
+ zfs_perms_add_to_nvlist(who_nvp, name, perms_nvp);
+ }
+ if (do_sets) {
+ zfs_deleg_whokey(name, toupper(who_type), inherit,
+ (who_type == ZFS_DELEG_NAMED_SET) ?
+ whostr : (void *)&whoid);
+ zfs_perms_add_to_nvlist(who_nvp, name, sets_nvp);
+ }
+}
+
+static void
+zfs_perms_add_who_nvlist(nvlist_t *who_nvp, uint64_t whoid, void *whostr,
+ nvlist_t *perms_nvp, nvlist_t *sets_nvp,
+ zfs_deleg_who_type_t who_type, zfs_deleg_inherit_t inherit)
+{
+ if (who_type == ZFS_DELEG_NAMED_SET || who_type == ZFS_DELEG_CREATE) {
+ helper(who_type, whoid, whostr, 0,
+ who_nvp, perms_nvp, sets_nvp);
+ } else {
+ if (inherit & ZFS_DELEG_PERM_LOCAL) {
+ helper(who_type, whoid, whostr, ZFS_DELEG_LOCAL,
+ who_nvp, perms_nvp, sets_nvp);
+ }
+ if (inherit & ZFS_DELEG_PERM_DESCENDENT) {
+ helper(who_type, whoid, whostr, ZFS_DELEG_DESCENDENT,
+ who_nvp, perms_nvp, sets_nvp);
+ }
+ }
+}
+
+/*
+ * Construct nvlist to pass down to kernel for setting/removing permissions.
+ *
+ * The nvlist is constructed as a series of nvpairs with an optional embedded
+ * nvlist of permissions to remove or set. The topmost nvpairs are the actual
+ * base attribute named stored in the dsl.
+ * Arguments:
+ *
+ * whostr: is a comma separated list of users, groups, or a single set name.
+ * whostr may be null for everyone or create perms.
+ * who_type: is the type of entry in whostr. Typically this will be
+ * ZFS_DELEG_WHO_UNKNOWN.
+ * perms: common separated list of permissions. May be null if user
+ * is requested to remove permissions by who.
+ * inherit: Specifies the inheritance of the permissions. Will be either
+ * ZFS_DELEG_PERM_LOCAL and/or ZFS_DELEG_PERM_DESCENDENT.
+ * nvp The constructed nvlist to pass to zfs_perm_set().
+ * The output nvp will look something like this.
+ * ul$1234 -> {create ; destroy }
+ * Ul$1234 -> { @myset }
+ * s-$@myset - { snapshot; checksum; compression }
+ */
+int
+zfs_build_perms(zfs_handle_t *zhp, char *whostr, char *perms,
+ zfs_deleg_who_type_t who_type, zfs_deleg_inherit_t inherit, nvlist_t **nvp)
+{
+ nvlist_t *who_nvp;
+ nvlist_t *perms_nvp = NULL;
+ nvlist_t *sets_nvp = NULL;
+ char errbuf[1024];
+ char *who_tok, *perm;
+ int error;
+
+ *nvp = NULL;
+
+ if (perms) {
+ if ((error = nvlist_alloc(&perms_nvp,
+ NV_UNIQUE_NAME, 0)) != 0) {
+ return (1);
+ }
+ if ((error = nvlist_alloc(&sets_nvp,
+ NV_UNIQUE_NAME, 0)) != 0) {
+ nvlist_free(perms_nvp);
+ return (1);
+ }
+ }
+
+ if ((error = nvlist_alloc(&who_nvp, NV_UNIQUE_NAME, 0)) != 0) {
+ if (perms_nvp)
+ nvlist_free(perms_nvp);
+ if (sets_nvp)
+ nvlist_free(sets_nvp);
+ return (1);
+ }
+
+ if (who_type == ZFS_DELEG_NAMED_SET) {
+ namecheck_err_t why;
+ char what;
+
+ if ((error = permset_namecheck(whostr, &why, &what)) != 0) {
+ nvlist_free(who_nvp);
+ if (perms_nvp)
+ nvlist_free(perms_nvp);
+ if (sets_nvp)
+ nvlist_free(sets_nvp);
+
+ switch (why) {
+ case NAME_ERR_NO_AT:
+ zfs_error_aux(zhp->zfs_hdl,
+ dgettext(TEXT_DOMAIN,
+ "set definition must begin with an '@' "
+ "character"));
+ }
+ return (zfs_error(zhp->zfs_hdl,
+ EZFS_BADPERMSET, whostr));
+ }
+ }
+
+ /*
+ * Build up nvlist(s) of permissions. Two nvlists are maintained.
+ * The first nvlist perms_nvp will have normal permissions and the
+ * other sets_nvp will have only permssion set names in it.
+ */
+ for (perm = strtok(perms, ","); perm; perm = strtok(NULL, ",")) {
+ const char *perm_canonical = zfs_deleg_canonicalize_perm(perm);
+
+ if (perm_canonical) {
+ verify(nvlist_add_boolean(perms_nvp,
+ perm_canonical) == 0);
+ } else if (perm[0] == '@') {
+ verify(nvlist_add_boolean(sets_nvp, perm) == 0);
+ } else {
+ nvlist_free(who_nvp);
+ nvlist_free(perms_nvp);
+ nvlist_free(sets_nvp);
+ return (zfs_error(zhp->zfs_hdl, EZFS_BADPERM, perm));
+ }
+ }
+
+ if (whostr && who_type != ZFS_DELEG_CREATE) {
+ who_tok = strtok(whostr, ",");
+ if (who_tok == NULL) {
+ nvlist_free(who_nvp);
+ if (perms_nvp)
+ nvlist_free(perms_nvp);
+ if (sets_nvp)
+ nvlist_free(sets_nvp);
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "Who string is NULL"),
+ whostr);
+ return (zfs_error(zhp->zfs_hdl, EZFS_BADWHO, errbuf));
+ }
+ }
+
+ /*
+ * Now create the nvlist(s)
+ */
+ do {
+ uint64_t who_id;
+
+ error = zfs_get_perm_who(who_tok, &who_type,
+ &who_id);
+ if (error) {
+ nvlist_free(who_nvp);
+ if (perms_nvp)
+ nvlist_free(perms_nvp);
+ if (sets_nvp)
+ nvlist_free(sets_nvp);
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN,
+ "Unable to determine uid/gid for "
+ "%s "), who_tok);
+ return (zfs_error(zhp->zfs_hdl, EZFS_BADWHO, errbuf));
+ }
+
+ /*
+ * add entries for both local and descendent when required
+ */
+ zfs_perms_add_who_nvlist(who_nvp, who_id, who_tok,
+ perms_nvp, sets_nvp, who_type, inherit);
+
+ } while (who_tok = strtok(NULL, ","));
+ *nvp = who_nvp;
+ return (0);
+}
+
+static int
+zfs_perm_set_common(zfs_handle_t *zhp, nvlist_t *nvp, boolean_t unset)
+{
+ zfs_cmd_t zc = { 0 };
+ int error;
+ char errbuf[1024];
+
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "Cannot update 'allows' for '%s'"),
+ zhp->zfs_name);
+
+ if (zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, nvp))
+ return (-1);
+
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+ zc.zc_perm_action = unset;
+
+ error = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SET_FSACL, &zc);
+ if (error && errno == ENOTSUP) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ gettext("Pool must be upgraded to use 'allow/unallow'"));
+ zcmd_free_nvlists(&zc);
+ return (zfs_error(zhp->zfs_hdl, EZFS_BADVERSION, errbuf));
+ } else if (error) {
+ return (zfs_standard_error(zhp->zfs_hdl, errno, errbuf));
+ }
+ zcmd_free_nvlists(&zc);
+
+ return (error);
+}
+
+int
+zfs_perm_set(zfs_handle_t *zhp, nvlist_t *nvp)
+{
+ return (zfs_perm_set_common(zhp, nvp, B_FALSE));
+}
+
+int
+zfs_perm_remove(zfs_handle_t *zhp, nvlist_t *perms)
+{
+ return (zfs_perm_set_common(zhp, perms, B_TRUE));
+}
+
+static int
+perm_compare(const void *arg1, const void *arg2)
+{
+ const zfs_perm_node_t *node1 = arg1;
+ const zfs_perm_node_t *node2 = arg2;
+ int ret;
+
+ ret = strcmp(node1->z_pname, node2->z_pname);
+
+ if (ret > 0)
+ return (1);
+ if (ret < 0)
+ return (-1);
+ else
+ return (0);
+}
+
+static void
+zfs_destroy_perm_tree(avl_tree_t *tree)
+{
+ zfs_perm_node_t *permnode;
+ void *cookie = NULL;
+
+ while ((permnode = avl_destroy_nodes(tree, &cookie)) != NULL)
+ free(permnode);
+ avl_destroy(tree);
+}
+
+static void
+zfs_destroy_tree(avl_tree_t *tree)
+{
+ zfs_allow_node_t *allownode;
+ void *cookie = NULL;
+
+ while ((allownode = avl_destroy_nodes(tree, &cookie)) != NULL) {
+ zfs_destroy_perm_tree(&allownode->z_localdescend);
+ zfs_destroy_perm_tree(&allownode->z_local);
+ zfs_destroy_perm_tree(&allownode->z_descend);
+ free(allownode);
+ }
+ avl_destroy(tree);
+}
+
+void
+zfs_free_allows(zfs_allow_t *allow)
+{
+ zfs_allow_t *allownext;
+ zfs_allow_t *freeallow;
+
+ allownext = allow;
+ while (allownext) {
+ zfs_destroy_tree(&allownext->z_sets);
+ zfs_destroy_tree(&allownext->z_crperms);
+ zfs_destroy_tree(&allownext->z_user);
+ zfs_destroy_tree(&allownext->z_group);
+ zfs_destroy_tree(&allownext->z_everyone);
+ freeallow = allownext;
+ allownext = allownext->z_next;
+ free(freeallow);
+ }
+}
+
+static zfs_allow_t *
+zfs_alloc_perm_tree(zfs_handle_t *zhp, zfs_allow_t *prev, char *setpoint)
+{
+ zfs_allow_t *ptree;
+
+ if ((ptree = zfs_alloc(zhp->zfs_hdl,
+ sizeof (zfs_allow_t))) == NULL) {
+ return (NULL);
+ }
+
+ (void) strlcpy(ptree->z_setpoint, setpoint, sizeof (ptree->z_setpoint));
+ avl_create(&ptree->z_sets,
+ perm_compare, sizeof (zfs_allow_node_t),
+ offsetof(zfs_allow_node_t, z_node));
+ avl_create(&ptree->z_crperms,
+ perm_compare, sizeof (zfs_allow_node_t),
+ offsetof(zfs_allow_node_t, z_node));
+ avl_create(&ptree->z_user,
+ perm_compare, sizeof (zfs_allow_node_t),
+ offsetof(zfs_allow_node_t, z_node));
+ avl_create(&ptree->z_group,
+ perm_compare, sizeof (zfs_allow_node_t),
+ offsetof(zfs_allow_node_t, z_node));
+ avl_create(&ptree->z_everyone,
+ perm_compare, sizeof (zfs_allow_node_t),
+ offsetof(zfs_allow_node_t, z_node));
+
+ if (prev)
+ prev->z_next = ptree;
+ ptree->z_next = NULL;
+ return (ptree);
+}
+
+/*
+ * Add permissions to the appropriate AVL permission tree.
+ * The appropriate tree may not be the requested tree.
+ * For example if ld indicates a local permission, but
+ * same permission also exists as a descendent permission
+ * then the permission will be removed from the descendent
+ * tree and add the the local+descendent tree.
+ */
+static int
+zfs_coalesce_perm(zfs_handle_t *zhp, zfs_allow_node_t *allownode,
+ char *perm, char ld)
+{
+ zfs_perm_node_t pnode, *permnode, *permnode2;
+ zfs_perm_node_t *newnode;
+ avl_index_t where, where2;
+ avl_tree_t *tree, *altree;
+
+ (void) strlcpy(pnode.z_pname, perm, sizeof (pnode.z_pname));
+
+ if (ld == ZFS_DELEG_NA) {
+ tree = &allownode->z_localdescend;
+ altree = &allownode->z_descend;
+ } else if (ld == ZFS_DELEG_LOCAL) {
+ tree = &allownode->z_local;
+ altree = &allownode->z_descend;
+ } else {
+ tree = &allownode->z_descend;
+ altree = &allownode->z_local;
+ }
+ permnode = avl_find(tree, &pnode, &where);
+ permnode2 = avl_find(altree, &pnode, &where2);
+
+ if (permnode2) {
+ avl_remove(altree, permnode2);
+ free(permnode2);
+ if (permnode == NULL) {
+ tree = &allownode->z_localdescend;
+ }
+ }
+
+ /*
+ * Now insert new permission in either requested location
+ * local/descendent or into ld when perm will exist in both.
+ */
+ if (permnode == NULL) {
+ if ((newnode = zfs_alloc(zhp->zfs_hdl,
+ sizeof (zfs_perm_node_t))) == NULL) {
+ return (-1);
+ }
+ *newnode = pnode;
+ avl_add(tree, newnode);
+ }
+ return (0);
+}
+
+/*
+ * Uggh, this is going to be a bit complicated.
+ * we have an nvlist coming out of the kernel that
+ * will indicate where the permission is set and then
+ * it will contain allow of the various "who's", and what
+ * their permissions are. To further complicate this
+ * we will then have to coalesce the local,descendent
+ * and local+descendent permissions where appropriate.
+ * The kernel only knows about a permission as being local
+ * or descendent, but not both.
+ *
+ * In order to make this easier for zfs_main to deal with
+ * a series of AVL trees will be used to maintain
+ * all of this, primarily for sorting purposes as well
+ * as the ability to quickly locate a specific entry.
+ *
+ * What we end up with are tree's for sets, create perms,
+ * user, groups and everyone. With each of those trees
+ * we have subtrees for local, descendent and local+descendent
+ * permissions.
+ */
+int
+zfs_perm_get(zfs_handle_t *zhp, zfs_allow_t **zfs_perms)
+{
+ zfs_cmd_t zc = { 0 };
+ int error;
+ nvlist_t *nvlist;
+ nvlist_t *permnv, *sourcenv;
+ nvpair_t *who_pair, *source_pair;
+ nvpair_t *perm_pair;
+ char errbuf[1024];
+ zfs_allow_t *zallowp, *newallowp;
+ char ld;
+ char *nvpname;
+ uid_t uid;
+ gid_t gid;
+ avl_tree_t *tree;
+ avl_index_t where;
+
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+
+ if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
+ return (-1);
+
+ while (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_GET_FSACL, &zc) != 0) {
+ if (errno == ENOMEM) {
+ if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, &zc) != 0) {
+ zcmd_free_nvlists(&zc);
+ return (-1);
+ }
+ } else if (errno == ENOTSUP) {
+ zcmd_free_nvlists(&zc);
+ (void) snprintf(errbuf, sizeof (errbuf),
+ gettext("Pool must be upgraded to use 'allow'"));
+ return (zfs_error(zhp->zfs_hdl,
+ EZFS_BADVERSION, errbuf));
+ } else {
+ zcmd_free_nvlists(&zc);
+ return (-1);
+ }
+ }
+
+ if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &nvlist) != 0) {
+ zcmd_free_nvlists(&zc);
+ return (-1);
+ }
+
+ zcmd_free_nvlists(&zc);
+
+ source_pair = nvlist_next_nvpair(nvlist, NULL);
+
+ if (source_pair == NULL) {
+ *zfs_perms = NULL;
+ return (0);
+ }
+
+ *zfs_perms = zfs_alloc_perm_tree(zhp, NULL, nvpair_name(source_pair));
+ if (*zfs_perms == NULL) {
+ return (0);
+ }
+
+ zallowp = *zfs_perms;
+
+ for (;;) {
+ struct passwd *pwd;
+ struct group *grp;
+ zfs_allow_node_t *allownode;
+ zfs_allow_node_t findallownode;
+ zfs_allow_node_t *newallownode;
+
+ (void) strlcpy(zallowp->z_setpoint,
+ nvpair_name(source_pair),
+ sizeof (zallowp->z_setpoint));
+
+ if ((error = nvpair_value_nvlist(source_pair, &sourcenv)) != 0)
+ goto abort;
+
+ /*
+ * Make sure nvlist is composed correctly
+ */
+ if (zfs_deleg_verify_nvlist(sourcenv)) {
+ goto abort;
+ }
+
+ who_pair = nvlist_next_nvpair(sourcenv, NULL);
+ if (who_pair == NULL) {
+ goto abort;
+ }
+
+ do {
+ error = nvpair_value_nvlist(who_pair, &permnv);
+ if (error) {
+ goto abort;
+ }
+
+ /*
+ * First build up the key to use
+ * for looking up in the various
+ * who trees.
+ */
+ ld = nvpair_name(who_pair)[1];
+ nvpname = nvpair_name(who_pair);
+ switch (nvpair_name(who_pair)[0]) {
+ case ZFS_DELEG_USER:
+ case ZFS_DELEG_USER_SETS:
+ tree = &zallowp->z_user;
+ uid = atol(&nvpname[3]);
+ pwd = getpwuid(uid);
+ (void) snprintf(findallownode.z_key,
+ sizeof (findallownode.z_key), "user %s",
+ (pwd) ? pwd->pw_name :
+ &nvpair_name(who_pair)[3]);
+ break;
+ case ZFS_DELEG_GROUP:
+ case ZFS_DELEG_GROUP_SETS:
+ tree = &zallowp->z_group;
+ gid = atol(&nvpname[3]);
+ grp = getgrgid(gid);
+ (void) snprintf(findallownode.z_key,
+ sizeof (findallownode.z_key), "group %s",
+ (grp) ? grp->gr_name :
+ &nvpair_name(who_pair)[3]);
+ break;
+ case ZFS_DELEG_CREATE:
+ case ZFS_DELEG_CREATE_SETS:
+ tree = &zallowp->z_crperms;
+ (void) strlcpy(findallownode.z_key, "",
+ sizeof (findallownode.z_key));
+ break;
+ case ZFS_DELEG_EVERYONE:
+ case ZFS_DELEG_EVERYONE_SETS:
+ (void) snprintf(findallownode.z_key,
+ sizeof (findallownode.z_key), "everyone");
+ tree = &zallowp->z_everyone;
+ break;
+ case ZFS_DELEG_NAMED_SET:
+ case ZFS_DELEG_NAMED_SET_SETS:
+ (void) snprintf(findallownode.z_key,
+ sizeof (findallownode.z_key), "%s",
+ &nvpair_name(who_pair)[3]);
+ tree = &zallowp->z_sets;
+ break;
+ }
+
+ /*
+ * Place who in tree
+ */
+ allownode = avl_find(tree, &findallownode, &where);
+ if (allownode == NULL) {
+ if ((newallownode = zfs_alloc(zhp->zfs_hdl,
+ sizeof (zfs_allow_node_t))) == NULL) {
+ goto abort;
+ }
+ avl_create(&newallownode->z_localdescend,
+ perm_compare,
+ sizeof (zfs_perm_node_t),
+ offsetof(zfs_perm_node_t, z_node));
+ avl_create(&newallownode->z_local,
+ perm_compare,
+ sizeof (zfs_perm_node_t),
+ offsetof(zfs_perm_node_t, z_node));
+ avl_create(&newallownode->z_descend,
+ perm_compare,
+ sizeof (zfs_perm_node_t),
+ offsetof(zfs_perm_node_t, z_node));
+ (void) strlcpy(newallownode->z_key,
+ findallownode.z_key,
+ sizeof (findallownode.z_key));
+ avl_insert(tree, newallownode, where);
+ allownode = newallownode;
+ }
+
+ /*
+ * Now iterate over the permissions and
+ * place them in the appropriate local,
+ * descendent or local+descendent tree.
+ *
+ * The permissions are added to the tree
+ * via zfs_coalesce_perm().
+ */
+ perm_pair = nvlist_next_nvpair(permnv, NULL);
+ if (perm_pair == NULL)
+ goto abort;
+ do {
+ if (zfs_coalesce_perm(zhp, allownode,
+ nvpair_name(perm_pair), ld) != 0)
+ goto abort;
+ } while (perm_pair = nvlist_next_nvpair(permnv,
+ perm_pair));
+ } while (who_pair = nvlist_next_nvpair(sourcenv, who_pair));
+
+ source_pair = nvlist_next_nvpair(nvlist, source_pair);
+ if (source_pair == NULL)
+ break;
+
+ /*
+ * allocate another node from the link list of
+ * zfs_allow_t structures
+ */
+ newallowp = zfs_alloc_perm_tree(zhp, zallowp,
+ nvpair_name(source_pair));
+ if (newallowp == NULL) {
+ goto abort;
+ }
+ zallowp = newallowp;
+ }
+ nvlist_free(nvlist);
+ return (0);
+abort:
+ zfs_free_allows(*zfs_perms);
+ nvlist_free(nvlist);
+ return (-1);
+}
+
+static char *
+zfs_deleg_perm_note(zfs_deleg_note_t note)
+{
+ /*
+ * Don't put newlines on end of lines
+ */
+ switch (note) {
+ case ZFS_DELEG_NOTE_CREATE:
+ return (dgettext(TEXT_DOMAIN,
+ "Must also have the 'mount' ability"));
+ case ZFS_DELEG_NOTE_DESTROY:
+ return (dgettext(TEXT_DOMAIN,
+ "Must also have the 'mount' ability"));
+ case ZFS_DELEG_NOTE_SNAPSHOT:
+ return (dgettext(TEXT_DOMAIN,
+ "Must also have the 'mount' ability"));
+ case ZFS_DELEG_NOTE_ROLLBACK:
+ return (dgettext(TEXT_DOMAIN,
+ "Must also have the 'mount' ability"));
+ case ZFS_DELEG_NOTE_CLONE:
+ return (dgettext(TEXT_DOMAIN, "Must also have the 'create' "
+ "ability and 'mount'\n"
+ "\t\t\t\tability in the origin file system"));
+ case ZFS_DELEG_NOTE_PROMOTE:
+ return (dgettext(TEXT_DOMAIN, "Must also have the 'mount'\n"
+ "\t\t\t\tand 'promote' ability in the origin file system"));
+ case ZFS_DELEG_NOTE_RENAME:
+ return (dgettext(TEXT_DOMAIN, "Must also have the 'mount' "
+ "and 'create' \n\t\t\t\tability in the new parent"));
+ case ZFS_DELEG_NOTE_RECEIVE:
+ return (dgettext(TEXT_DOMAIN, "Must also have the 'mount'"
+ " and 'create' ability"));
+ case ZFS_DELEG_NOTE_USERPROP:
+ return (dgettext(TEXT_DOMAIN,
+ "Allows changing any user property"));
+ case ZFS_DELEG_NOTE_ALLOW:
+ return (dgettext(TEXT_DOMAIN,
+ "Must also have the permission that is being\n"
+ "\t\t\t\tallowed"));
+ case ZFS_DELEG_NOTE_MOUNT:
+ return (dgettext(TEXT_DOMAIN,
+ "Allows mount/umount of ZFS datasets"));
+ case ZFS_DELEG_NOTE_SHARE:
+ return (dgettext(TEXT_DOMAIN,
+ "Allows sharing file systems over NFS or SMB\n"
+ "\t\t\t\tprotocols"));
+ case ZFS_DELEG_NOTE_NONE:
+ default:
+ return (dgettext(TEXT_DOMAIN, ""));
+ }
+}
+
+typedef enum {
+ ZFS_DELEG_SUBCOMMAND,
+ ZFS_DELEG_PROP,
+ ZFS_DELEG_OTHER
+} zfs_deleg_perm_type_t;
+
+/*
+ * is the permission a subcommand or other?
+ */
+zfs_deleg_perm_type_t
+zfs_deleg_perm_type(const char *perm)
+{
+ if (strcmp(perm, "userprop") == 0)
+ return (ZFS_DELEG_OTHER);
+ else
+ return (ZFS_DELEG_SUBCOMMAND);
+}
+
+static char *
+zfs_deleg_perm_type_str(zfs_deleg_perm_type_t type)
+{
+ switch (type) {
+ case ZFS_DELEG_SUBCOMMAND:
+ return (dgettext(TEXT_DOMAIN, "subcommand"));
+ case ZFS_DELEG_PROP:
+ return (dgettext(TEXT_DOMAIN, "property"));
+ case ZFS_DELEG_OTHER:
+ return (dgettext(TEXT_DOMAIN, "other"));
+ }
+ return ("");
+}
+
+/*ARGSUSED*/
+static int
+zfs_deleg_prop_cb(int prop, void *cb)
+{
+ if (zfs_prop_delegatable(prop))
+ (void) fprintf(stderr, "%-15s %-15s\n", zfs_prop_to_name(prop),
+ zfs_deleg_perm_type_str(ZFS_DELEG_PROP));
+
+ return (ZPROP_CONT);
+}
+
+void
+zfs_deleg_permissions(void)
+{
+ int i;
+
+ (void) fprintf(stderr, "\n%-15s %-15s\t%s\n\n", "NAME",
+ "TYPE", "NOTES");
+
+ /*
+ * First print out the subcommands
+ */
+ for (i = 0; zfs_deleg_perm_tab[i].z_perm != NULL; i++) {
+ (void) fprintf(stderr, "%-15s %-15s\t%s\n",
+ zfs_deleg_perm_tab[i].z_perm,
+ zfs_deleg_perm_type_str(
+ zfs_deleg_perm_type(zfs_deleg_perm_tab[i].z_perm)),
+ zfs_deleg_perm_note(zfs_deleg_perm_tab[i].z_note));
+ }
+
+ (void) zprop_iter(zfs_deleg_prop_cb, NULL, B_FALSE, B_TRUE,
+ ZFS_TYPE_DATASET|ZFS_TYPE_VOLUME);
+}
+
/*
* Given a property name and value, set the property for the given dataset.
*/
@@ -1070,6 +1766,8 @@ zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
libzfs_handle_t *hdl = zhp->zfs_hdl;
nvlist_t *nvl = NULL, *realprops;
zfs_prop_t prop;
+ boolean_t do_prefix;
+ uint64_t idx;
(void) snprintf(errbuf, sizeof (errbuf),
dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
@@ -1081,9 +1779,10 @@ zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
goto error;
}
- if ((realprops = zfs_validate_properties(hdl, zhp->zfs_type, NULL, nvl,
+ if ((realprops = zfs_valid_proplist(hdl, zhp->zfs_type, nvl,
zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL)
goto error;
+
nvlist_free(nvl);
nvl = realprops;
@@ -1102,7 +1801,7 @@ zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
goto error;
}
- if ((cl = changelist_gather(zhp, prop, 0)) == NULL)
+ if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL)
goto error;
if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
@@ -1113,7 +1812,15 @@ zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
goto error;
}
- if ((ret = changelist_prefix(cl)) != 0)
+ /*
+ * If the dataset's canmount property is being set to noauto,
+ * then we want to prevent unmounting & remounting it.
+ */
+ do_prefix = !((prop == ZFS_PROP_CANMOUNT) &&
+ (zprop_string_to_index(prop, propval, &idx,
+ ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO));
+
+ if (do_prefix && (ret = changelist_prefix(cl)) != 0)
goto error;
/*
@@ -1121,11 +1828,10 @@ zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
*/
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- if (zcmd_write_src_nvlist(hdl, &zc, nvl, NULL) != 0)
+ if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0)
goto error;
- ret = ioctl(hdl->libzfs_fd, ZFS_IOC_SET_PROP, &zc);
-
+ ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
if (ret != 0) {
switch (errno) {
@@ -1137,6 +1843,7 @@ zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
*/
switch (prop) {
case ZFS_PROP_QUOTA:
+ case ZFS_PROP_REFQUOTA:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"size is less than current used or "
"reserved space"));
@@ -1144,6 +1851,7 @@ zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
break;
case ZFS_PROP_RESERVATION:
+ case ZFS_PROP_REFRESERVATION:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"size is greater than available space"));
(void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
@@ -1168,10 +1876,22 @@ zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
case ENOTSUP:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded to allow gzip compression"));
+ "pool and or dataset must be upgraded to set this "
+ "property or value"));
(void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
break;
+ case ERANGE:
+ if (prop == ZFS_PROP_COMPRESSION) {
+ (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "property setting is not allowed on "
+ "bootable datasets"));
+ (void) zfs_error(hdl, EZFS_NOTSUP, errbuf);
+ } else {
+ (void) zfs_standard_error(hdl, errno, errbuf);
+ }
+ break;
+
case EOVERFLOW:
/*
* This platform can't address a volume this big.
@@ -1187,11 +1907,14 @@ zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
(void) zfs_standard_error(hdl, errno, errbuf);
}
} else {
+ if (do_prefix)
+ ret = changelist_postfix(cl);
+
/*
* Refresh the statistics so the new property value
* is reflected.
*/
- if ((ret = changelist_postfix(cl)) == 0)
+ if (ret == 0)
(void) get_stats(zhp);
}
@@ -1219,7 +1942,7 @@ zfs_prop_inherit(zfs_handle_t *zhp, const char *propname)
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"cannot inherit %s for '%s'"), propname, zhp->zfs_name);
- if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL) {
+ if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) {
/*
* For user properties, the amount of work we have to do is very
* small, so just do it here.
@@ -1233,8 +1956,7 @@ zfs_prop_inherit(zfs_handle_t *zhp, const char *propname)
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
- if (ioctl(zhp->zfs_hdl->libzfs_fd,
- ZFS_IOC_SET_PROP, &zc) != 0)
+ if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0)
return (zfs_standard_error(hdl, errno, errbuf));
return (0);
@@ -1272,7 +1994,7 @@ zfs_prop_inherit(zfs_handle_t *zhp, const char *propname)
/*
* Determine datasets which will be affected by this change, if any.
*/
- if ((cl = changelist_gather(zhp, prop, 0)) == NULL)
+ if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL)
return (-1);
if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
@@ -1286,8 +2008,7 @@ zfs_prop_inherit(zfs_handle_t *zhp, const char *propname)
if ((ret = changelist_prefix(cl)) != 0)
goto error;
- if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd,
- ZFS_IOC_SET_PROP, &zc)) != 0) {
+ if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) {
return (zfs_standard_error(hdl, errno, errbuf));
} else {
@@ -1305,15 +2026,6 @@ error:
return (ret);
}
-void
-nicebool(int value, char *buf, size_t buflen)
-{
- if (value)
- (void) strlcpy(buf, "on", buflen);
- else
- (void) strlcpy(buf, "off", buflen);
-}
-
/*
* True DSL properties are stored in an nvlist. The following two functions
* extract them appropriately.
@@ -1327,8 +2039,8 @@ getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
*source = NULL;
if (nvlist_lookup_nvlist(zhp->zfs_props,
zfs_prop_to_name(prop), &nv) == 0) {
- verify(nvlist_lookup_uint64(nv, ZFS_PROP_VALUE, &value) == 0);
- (void) nvlist_lookup_string(nv, ZFS_PROP_SOURCE, source);
+ verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0);
+ (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
} else {
value = zfs_prop_default_numeric(prop);
*source = "";
@@ -1346,8 +2058,8 @@ getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
*source = NULL;
if (nvlist_lookup_nvlist(zhp->zfs_props,
zfs_prop_to_name(prop), &nv) == 0) {
- verify(nvlist_lookup_string(nv, ZFS_PROP_VALUE, &value) == 0);
- (void) nvlist_lookup_string(nv, ZFS_PROP_SOURCE, source);
+ verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0);
+ (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
} else {
if ((value = (char *)zfs_prop_default_string(prop)) == NULL)
value = "";
@@ -1367,9 +2079,11 @@ getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
* the source "temporary".
*/
static int
-get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zfs_source_t *src,
+get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
char **source, uint64_t *val)
{
+ zfs_cmd_t zc = { 0 };
+ nvlist_t *zplprops = NULL;
struct mnttab mnt;
char *mntopt_on = NULL;
char *mntopt_off = NULL;
@@ -1406,6 +2120,11 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zfs_source_t *src,
mntopt_on = MNTOPT_XATTR;
mntopt_off = MNTOPT_NOXATTR;
break;
+
+ case ZFS_PROP_NBMAND:
+ mntopt_on = MNTOPT_NBMAND;
+ mntopt_off = MNTOPT_NONBMAND;
+ break;
}
/*
@@ -1444,43 +2163,32 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zfs_source_t *src,
case ZFS_PROP_READONLY:
case ZFS_PROP_SETUID:
case ZFS_PROP_XATTR:
+ case ZFS_PROP_NBMAND:
*val = getprop_uint64(zhp, prop, source);
if (hasmntopt(&mnt, mntopt_on) && !*val) {
*val = B_TRUE;
if (src)
- *src = ZFS_SRC_TEMPORARY;
+ *src = ZPROP_SRC_TEMPORARY;
} else if (hasmntopt(&mnt, mntopt_off) && *val) {
*val = B_FALSE;
if (src)
- *src = ZFS_SRC_TEMPORARY;
+ *src = ZPROP_SRC_TEMPORARY;
}
break;
- case ZFS_PROP_RECORDSIZE:
- case ZFS_PROP_COMPRESSION:
- case ZFS_PROP_ZONED:
- case ZFS_PROP_CREATION:
- case ZFS_PROP_COMPRESSRATIO:
- case ZFS_PROP_REFERENCED:
- case ZFS_PROP_USED:
- case ZFS_PROP_CREATETXG:
- case ZFS_PROP_AVAILABLE:
- case ZFS_PROP_VOLSIZE:
- case ZFS_PROP_VOLBLOCKSIZE:
- *val = getprop_uint64(zhp, prop, source);
- break;
-
case ZFS_PROP_CANMOUNT:
*val = getprop_uint64(zhp, prop, source);
- if (*val == 0)
+ if (*val != ZFS_CANMOUNT_ON)
*source = zhp->zfs_name;
else
*source = ""; /* default */
break;
case ZFS_PROP_QUOTA:
+ case ZFS_PROP_REFQUOTA:
case ZFS_PROP_RESERVATION:
+ case ZFS_PROP_REFRESERVATION:
*val = getprop_uint64(zhp, prop, source);
if (*val == 0)
*source = ""; /* default */
@@ -1496,11 +2204,60 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zfs_source_t *src,
*val = zhp->zfs_dmustats.dds_num_clones;
break;
+ case ZFS_PROP_VERSION:
+ case ZFS_PROP_NORMALIZE:
+ case ZFS_PROP_UTF8ONLY:
+ case ZFS_PROP_CASE:
+ if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) ||
+ zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
+ return (-1);
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+ if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) {
+ zcmd_free_nvlists(&zc);
+ zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
+ "unable to get %s property"),
+ zfs_prop_to_name(prop));
+ return (zfs_error(zhp->zfs_hdl, EZFS_BADVERSION,
+ dgettext(TEXT_DOMAIN, "internal error")));
+ }
+ if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 ||
+ nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop),
+ val) != 0) {
+ zcmd_free_nvlists(&zc);
+ zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
+ "unable to get %s property"),
+ zfs_prop_to_name(prop));
+ return (zfs_error(zhp->zfs_hdl, EZFS_NOMEM,
+ dgettext(TEXT_DOMAIN, "internal error")));
+ }
+ if (zplprops)
+ nvlist_free(zplprops);
+ zcmd_free_nvlists(&zc);
+ break;
+
default:
- zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
- "cannot get non-numeric property"));
- return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP,
- dgettext(TEXT_DOMAIN, "internal error")));
+ switch (zfs_prop_get_type(prop)) {
+ case PROP_TYPE_NUMBER:
+ case PROP_TYPE_INDEX:
+ *val = getprop_uint64(zhp, prop, source);
+ /*
+ * If we tried to use a defalut value for a
+ * readonly property, it means that it was not
+ * present; return an error.
+ */
+ if (zfs_prop_readonly(prop) &&
+ *source && (*source)[0] == '\0') {
+ return (-1);
+ }
+ break;
+
+ case PROP_TYPE_STRING:
+ default:
+ zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
+ "cannot get non-numeric property"));
+ return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP,
+ dgettext(TEXT_DOMAIN, "internal error")));
+ }
}
return (0);
@@ -1510,22 +2267,22 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zfs_source_t *src,
* Calculate the source type, given the raw source string.
*/
static void
-get_source(zfs_handle_t *zhp, zfs_source_t *srctype, char *source,
+get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source,
char *statbuf, size_t statlen)
{
- if (statbuf == NULL || *srctype == ZFS_SRC_TEMPORARY)
+ if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY)
return;
if (source == NULL) {
- *srctype = ZFS_SRC_NONE;
+ *srctype = ZPROP_SRC_NONE;
} else if (source[0] == '\0') {
- *srctype = ZFS_SRC_DEFAULT;
+ *srctype = ZPROP_SRC_DEFAULT;
} else {
if (strcmp(source, zhp->zfs_name) == 0) {
- *srctype = ZFS_SRC_LOCAL;
+ *srctype = ZPROP_SRC_LOCAL;
} else {
(void) strlcpy(statbuf, source, statlen);
- *srctype = ZFS_SRC_INHERITED;
+ *srctype = ZPROP_SRC_INHERITED;
}
}
@@ -1540,12 +2297,11 @@ get_source(zfs_handle_t *zhp, zfs_source_t *srctype, char *source,
*/
int
zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
- zfs_source_t *src, char *statbuf, size_t statlen, boolean_t literal)
+ zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal)
{
char *source = NULL;
uint64_t val;
char *str;
- const char *root;
const char *strval;
/*
@@ -1555,66 +2311,9 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
return (-1);
if (src)
- *src = ZFS_SRC_NONE;
+ *src = ZPROP_SRC_NONE;
switch (prop) {
- case ZFS_PROP_ATIME:
- case ZFS_PROP_READONLY:
- case ZFS_PROP_SETUID:
- case ZFS_PROP_ZONED:
- case ZFS_PROP_DEVICES:
- case ZFS_PROP_EXEC:
- case ZFS_PROP_CANMOUNT:
- case ZFS_PROP_XATTR:
- /*
- * Basic boolean values are built on top of
- * get_numeric_property().
- */
- if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
- return (-1);
- nicebool(val, propbuf, proplen);
-
- break;
-
- case ZFS_PROP_AVAILABLE:
- case ZFS_PROP_RECORDSIZE:
- case ZFS_PROP_CREATETXG:
- case ZFS_PROP_REFERENCED:
- case ZFS_PROP_USED:
- case ZFS_PROP_VOLSIZE:
- case ZFS_PROP_VOLBLOCKSIZE:
- case ZFS_PROP_NUMCLONES:
- /*
- * Basic numeric values are built on top of
- * get_numeric_property().
- */
- if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
- return (-1);
- if (literal)
- (void) snprintf(propbuf, proplen, "%llu",
- (u_longlong_t)val);
- else
- zfs_nicenum(val, propbuf, proplen);
- break;
-
- case ZFS_PROP_COMPRESSION:
- case ZFS_PROP_CHECKSUM:
- case ZFS_PROP_SNAPDIR:
-#ifdef ZFS_NO_ACL
- case ZFS_PROP_ACLMODE:
- case ZFS_PROP_ACLINHERIT:
- case ZFS_PROP_COPIES:
- val = getprop_uint64(zhp, prop, &source);
- verify(zfs_prop_index_to_string(prop, val, &strval) == 0);
- (void) strlcpy(propbuf, strval, proplen);
- break;
-#else /* ZFS_NO_ACL */
- case ZFS_PROP_ACLMODE:
- case ZFS_PROP_ACLINHERIT:
- (void) strlcpy(propbuf, "<unsupported>", proplen);
- break;
-#endif /* ZFS_NO_ACL */
-
case ZFS_PROP_CREATION:
/*
* 'creation' is a time_t stored in the statistics. We convert
@@ -1638,25 +2337,42 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
* Getting the precise mountpoint can be tricky.
*
* - for 'none' or 'legacy', return those values.
- * - for default mountpoints, construct it as /zfs/<dataset>
* - for inherited mountpoints, we want to take everything
* after our ancestor and append it to the inherited value.
*
* If the pool has an alternate root, we want to prepend that
* root to any values we return.
*/
- root = zhp->zfs_root;
+
str = getprop_string(zhp, prop, &source);
- if (str[0] == '\0') {
- (void) snprintf(propbuf, proplen, "%s/zfs/%s",
- root, zhp->zfs_name);
- } else if (str[0] == '/') {
+ if (str[0] == '/') {
+ char buf[MAXPATHLEN];
+ char *root = buf;
const char *relpath = zhp->zfs_name + strlen(source);
if (relpath[0] == '/')
relpath++;
- if (str[1] == '\0')
+
+ if ((zpool_get_prop(zhp->zpool_hdl,
+ ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL)) ||
+ (strcmp(root, "-") == 0))
+ root[0] = '\0';
+ /*
+ * Special case an alternate root of '/'. This will
+ * avoid having multiple leading slashes in the
+ * mountpoint path.
+ */
+ if (strcmp(root, "/") == 0)
+ root++;
+
+ /*
+ * If the mountpoint is '/' then skip over this
+ * if we are obtaining either an alternate root or
+ * an inherited mountpoint.
+ */
+ if (str[1] == '\0' && (root[0] != '\0' ||
+ relpath[0] != '\0'))
str++;
if (relpath[0] == '\0')
@@ -1673,13 +2389,6 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
break;
- case ZFS_PROP_SHARENFS:
- case ZFS_PROP_SHAREISCSI:
- case ZFS_PROP_ISCSIOPTIONS:
- (void) strlcpy(propbuf, getprop_string(zhp, prop, &source),
- proplen);
- break;
-
case ZFS_PROP_ORIGIN:
(void) strlcpy(propbuf, getprop_string(zhp, prop, &source),
proplen);
@@ -1692,7 +2401,10 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
break;
case ZFS_PROP_QUOTA:
+ case ZFS_PROP_REFQUOTA:
case ZFS_PROP_RESERVATION:
+ case ZFS_PROP_REFRESERVATION:
+
if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
return (-1);
@@ -1766,7 +2478,35 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
break;
default:
- abort();
+ switch (zfs_prop_get_type(prop)) {
+ case PROP_TYPE_NUMBER:
+ if (get_numeric_property(zhp, prop, src,
+ &source, &val) != 0)
+ return (-1);
+ if (literal)
+ (void) snprintf(propbuf, proplen, "%llu",
+ (u_longlong_t)val);
+ else
+ zfs_nicenum(val, propbuf, proplen);
+ break;
+
+ case PROP_TYPE_STRING:
+ (void) strlcpy(propbuf,
+ getprop_string(zhp, prop, &source), proplen);
+ break;
+
+ case PROP_TYPE_INDEX:
+ if (get_numeric_property(zhp, prop, src,
+ &source, &val) != 0)
+ return (-1);
+ if (zfs_prop_index_to_string(prop, val, &strval) != 0)
+ return (-1);
+ (void) strlcpy(propbuf, strval, proplen);
+ break;
+
+ default:
+ abort();
+ }
}
get_source(zhp, src, source, statbuf, statlen);
@@ -1783,33 +2523,42 @@ uint64_t
zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop)
{
char *source;
- zfs_source_t sourcetype = ZFS_SRC_NONE;
uint64_t val;
- (void) get_numeric_property(zhp, prop, &sourcetype, &source, &val);
+ (void) get_numeric_property(zhp, prop, NULL, &source, &val);
return (val);
}
+int
+zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val)
+{
+ char buf[64];
+
+ zfs_nicenum(val, buf, sizeof (buf));
+ return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf));
+}
+
/*
* Similar to zfs_prop_get(), but returns the value as an integer.
*/
int
zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value,
- zfs_source_t *src, char *statbuf, size_t statlen)
+ zprop_source_t *src, char *statbuf, size_t statlen)
{
char *source;
/*
* Check to see if this property applies to our object
*/
- if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
+ if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) {
return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE,
dgettext(TEXT_DOMAIN, "cannot get property '%s'"),
zfs_prop_to_name(prop)));
+ }
if (src)
- *src = ZFS_SRC_NONE;
+ *src = ZPROP_SRC_NONE;
if (get_numeric_property(zhp, prop, src, &source, value) != 0)
return (-1);
@@ -1847,6 +2596,9 @@ zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
zfs_handle_t *nzhp;
int ret;
+ if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
+ return (0);
+
for ((void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0;
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name))) {
@@ -1890,6 +2642,9 @@ zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
zfs_handle_t *nzhp;
int ret;
+ if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
+ return (0);
+
for ((void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT,
&zc) == 0;
@@ -1948,12 +2703,16 @@ parent_name(const char *path, char *buf, size_t buflen)
}
/*
- * Checks to make sure that the given path has a parent, and that it exists. We
- * also fetch the 'zoned' property, which is used to validate property settings
- * when creating new datasets.
+ * If accept_ancestor is false, then check to make sure that the given path has
+ * a parent, and that it exists. If accept_ancestor is true, then find the
+ * closest existing ancestor for the given path. In prefixlen return the
+ * length of already existing prefix of the given path. We also fetch the
+ * 'zoned' property, which is used to validate property settings when creating
+ * new datasets.
*/
static int
-check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned)
+check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned,
+ boolean_t accept_ancestor, int *prefixlen)
{
zfs_cmd_t zc = { 0 };
char parent[ZFS_MAXNAMELEN];
@@ -1984,16 +2743,22 @@ check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned)
}
/* check to see if the parent dataset exists */
- if ((zhp = make_dataset_handle(hdl, parent)) == NULL) {
- switch (errno) {
- case ENOENT:
+ while ((zhp = make_dataset_handle(hdl, parent)) == NULL) {
+ if (errno == ENOENT && accept_ancestor) {
+ /*
+ * Go deeper to find an ancestor, give up on top level.
+ */
+ if (parent_name(parent, parent, sizeof (parent)) != 0) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "no such pool '%s'"), zc.zc_name);
+ return (zfs_error(hdl, EZFS_NOENT, errbuf));
+ }
+ } else if (errno == ENOENT) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"parent does not exist"));
return (zfs_error(hdl, EZFS_NOENT, errbuf));
-
- default:
+ } else
return (zfs_standard_error(hdl, errno, errbuf));
- }
}
*zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
@@ -2014,6 +2779,136 @@ check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned)
}
zfs_close(zhp);
+ if (prefixlen != NULL)
+ *prefixlen = strlen(parent);
+ return (0);
+}
+
+/*
+ * Finds whether the dataset of the given type(s) exists.
+ */
+boolean_t
+zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types)
+{
+ zfs_handle_t *zhp;
+
+ if (!zfs_validate_name(hdl, path, types, B_FALSE))
+ return (B_FALSE);
+
+ /*
+ * Try to get stats for the dataset, which will tell us if it exists.
+ */
+ if ((zhp = make_dataset_handle(hdl, path)) != NULL) {
+ int ds_type = zhp->zfs_type;
+
+ zfs_close(zhp);
+ if (types & ds_type)
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
+/*
+ * Given a path to 'target', create all the ancestors between
+ * the prefixlen portion of the path, and the target itself.
+ * Fail if the initial prefixlen-ancestor does not already exist.
+ */
+int
+create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
+{
+ zfs_handle_t *h;
+ char *cp;
+ const char *opname;
+
+ /* make sure prefix exists */
+ cp = target + prefixlen;
+ if (*cp != '/') {
+ assert(strchr(cp, '/') == NULL);
+ h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
+ } else {
+ *cp = '\0';
+ h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
+ *cp = '/';
+ }
+ if (h == NULL)
+ return (-1);
+ zfs_close(h);
+
+ /*
+ * Attempt to create, mount, and share any ancestor filesystems,
+ * up to the prefixlen-long one.
+ */
+ for (cp = target + prefixlen + 1;
+ cp = strchr(cp, '/'); *cp = '/', cp++) {
+ char *logstr;
+
+ *cp = '\0';
+
+ h = make_dataset_handle(hdl, target);
+ if (h) {
+ /* it already exists, nothing to do here */
+ zfs_close(h);
+ continue;
+ }
+
+ logstr = hdl->libzfs_log_str;
+ hdl->libzfs_log_str = NULL;
+ if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM,
+ NULL) != 0) {
+ hdl->libzfs_log_str = logstr;
+ opname = dgettext(TEXT_DOMAIN, "create");
+ goto ancestorerr;
+ }
+
+ hdl->libzfs_log_str = logstr;
+ h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
+ if (h == NULL) {
+ opname = dgettext(TEXT_DOMAIN, "open");
+ goto ancestorerr;
+ }
+
+ if (zfs_mount(h, NULL, 0) != 0) {
+ opname = dgettext(TEXT_DOMAIN, "mount");
+ goto ancestorerr;
+ }
+
+ if (zfs_share(h) != 0) {
+ opname = dgettext(TEXT_DOMAIN, "share");
+ goto ancestorerr;
+ }
+
+ zfs_close(h);
+ }
+
+ return (0);
+
+ancestorerr:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "failed to %s ancestor '%s'"), opname, target);
+ return (-1);
+}
+
+/*
+ * Creates non-existing ancestors of the given path.
+ */
+int
+zfs_create_ancestors(libzfs_handle_t *hdl, const char *path)
+{
+ int prefix;
+ uint64_t zoned;
+ char *path_copy;
+ int rc;
+
+ if (check_parents(hdl, path, &zoned, B_TRUE, &prefix) != 0)
+ return (-1);
+
+ if ((path_copy = strdup(path)) != NULL) {
+ rc = create_parents(hdl, path_copy, prefix);
+ free(path_copy);
+ }
+ if (path_copy == NULL || rc != 0)
+ return (-1);
+
return (0);
}
@@ -2035,11 +2930,11 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
"cannot create '%s'"), path);
/* validate the path, taking care to note the extended error message */
- if (!zfs_validate_name(hdl, path, type))
+ if (!zfs_validate_name(hdl, path, type, B_TRUE))
return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
/* validate parents exist */
- if (check_parents(hdl, path, &zoned) != 0)
+ if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0)
return (-1);
/*
@@ -2050,7 +2945,7 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
* first try to see if the dataset exists.
*/
(void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name));
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0) {
+ if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"dataset already exists"));
return (zfs_error(hdl, EZFS_EXISTS, errbuf));
@@ -2061,7 +2956,7 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
else
zc.zc_objset_type = DMU_OST_ZFS;
- if (props && (props = zfs_validate_properties(hdl, type, NULL, props,
+ if (props && (props = zfs_valid_proplist(hdl, type, props,
zoned, NULL, errbuf)) == 0)
return (-1);
@@ -2111,13 +3006,12 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
}
}
- if (props &&
- zcmd_write_src_nvlist(hdl, &zc, props, NULL) != 0)
+ if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0)
return (-1);
nvlist_free(props);
/* create the dataset */
- ret = ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE, &zc);
+ ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc);
if (ret == 0 && type == ZFS_TYPE_VOLUME) {
ret = zvol_create_link(hdl, path);
@@ -2158,6 +3052,11 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
return (zfs_error(hdl, EZFS_BADPROP, errbuf));
+ case ENOTSUP:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "pool must be upgraded to set this "
+ "property or value"));
+ return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
#ifdef _ILP32
case EOVERFLOW:
/*
@@ -2189,10 +3088,13 @@ zfs_destroy(zfs_handle_t *zhp)
if (ZFS_IS_VOLUME(zhp)) {
/*
- * Unconditionally unshare this zvol ignoring failure as it
- * indicates only that the volume wasn't shared initially.
+ * If user doesn't have permissions to unshare volume, then
+ * abort the request. This would only happen for a
+ * non-privileged user.
*/
- (void) zfs_unshare_iscsi(zhp);
+ if (zfs_unshare_iscsi(zhp) != 0) {
+ return (-1);
+ }
if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0)
return (-1);
@@ -2202,7 +3104,7 @@ zfs_destroy(zfs_handle_t *zhp)
zc.zc_objset_type = DMU_OST_ZFS;
}
- if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc) != 0) {
+ if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0) {
return (zfs_standard_error_fmt(zhp->zfs_hdl, errno,
dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
zhp->zfs_name));
@@ -2276,7 +3178,7 @@ zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname)
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
- ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DESTROY_SNAPS, &zc);
+ ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc);
if (ret != 0) {
char errbuf[1024];
@@ -2318,11 +3220,11 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
"cannot create '%s'"), target);
/* validate the target name */
- if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM))
+ if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE))
return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
/* validate parents exist */
- if (check_parents(hdl, target, &zoned) != 0)
+ if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0)
return (-1);
(void) parent_name(target, parent, sizeof (parent));
@@ -2337,11 +3239,11 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
}
if (props) {
- if ((props = zfs_validate_properties(hdl, type, NULL, props,
- zoned, zhp, errbuf)) == NULL)
+ if ((props = zfs_valid_proplist(hdl, type, props, zoned,
+ zhp, errbuf)) == NULL)
return (-1);
- if (zcmd_write_src_nvlist(hdl, &zc, props, NULL) != 0) {
+ if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
nvlist_free(props);
return (-1);
}
@@ -2351,7 +3253,7 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
(void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name));
(void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value));
- ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_CREATE, &zc);
+ ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc);
zcmd_free_nvlists(&zc);
@@ -2470,7 +3372,7 @@ zfs_promote(zfs_handle_t *zhp)
return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
}
- (void) strlcpy(parent, zhp->zfs_dmustats.dds_clone_of, sizeof (parent));
+ (void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent));
if (parent[0] == '\0') {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"not a cloned filesystem"));
@@ -2480,14 +3382,14 @@ zfs_promote(zfs_handle_t *zhp)
*cp = '\0';
/* Walk the snapshots we will be moving */
- pzhp = zfs_open(hdl, zhp->zfs_dmustats.dds_clone_of, ZFS_TYPE_SNAPSHOT);
+ pzhp = zfs_open(hdl, zhp->zfs_dmustats.dds_origin, ZFS_TYPE_SNAPSHOT);
if (pzhp == NULL)
return (-1);
pd.cb_pivot_txg = zfs_prop_get_int(pzhp, ZFS_PROP_CREATETXG);
zfs_close(pzhp);
pd.cb_target = zhp->zfs_name;
pd.cb_errbuf = errbuf;
- pzhp = zfs_open(hdl, parent, ZFS_TYPE_ANY);
+ pzhp = zfs_open(hdl, parent, ZFS_TYPE_DATASET);
if (pzhp == NULL)
return (-1);
(void) zfs_prop_get(pzhp, ZFS_PROP_MOUNTPOINT, pd.cb_mountpoint,
@@ -2499,10 +3401,10 @@ zfs_promote(zfs_handle_t *zhp)
}
/* issue the ioctl */
- (void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_clone_of,
+ (void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin,
sizeof (zc.zc_value));
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- ret = ioctl(hdl->libzfs_fd, ZFS_IOC_PROMOTE, &zc);
+ ret = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc);
if (ret != 0) {
int save_errno = errno;
@@ -2570,10 +3472,11 @@ zfs_create_link_cb(zfs_handle_t *zhp, void *arg)
* Takes a snapshot of the given dataset.
*/
int
-zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive)
+zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
+ nvlist_t *props)
{
const char *delim;
- char *parent;
+ char parent[ZFS_MAXNAMELEN];
zfs_handle_t *zhp;
zfs_cmd_t zc = { 0 };
int ret;
@@ -2583,33 +3486,52 @@ zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive)
"cannot snapshot '%s'"), path);
/* validate the target name */
- if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT))
+ if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE))
return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
+ if (props) {
+ if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
+ props, B_FALSE, NULL, errbuf)) == NULL)
+ return (-1);
+
+ if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
+ nvlist_free(props);
+ return (-1);
+ }
+
+ nvlist_free(props);
+ }
+
/* make sure the parent exists and is of the appropriate type */
delim = strchr(path, '@');
- if ((parent = zfs_alloc(hdl, delim - path + 1)) == NULL)
- return (-1);
(void) strncpy(parent, path, delim - path);
parent[delim - path] = '\0';
if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM |
ZFS_TYPE_VOLUME)) == NULL) {
- free(parent);
+ zcmd_free_nvlists(&zc);
return (-1);
}
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
(void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value));
+ if (ZFS_IS_VOLUME(zhp))
+ zc.zc_objset_type = DMU_OST_ZVOL;
+ else
+ zc.zc_objset_type = DMU_OST_ZFS;
zc.zc_cookie = recursive;
- ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SNAPSHOT, &zc);
+ ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc);
+
+ zcmd_free_nvlists(&zc);
/*
* if it was recursive, the one that actually failed will be in
* zc.zc_name.
*/
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value);
+ if (ret != 0)
+ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+ "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value);
+
if (ret == 0 && recursive) {
struct createdata cd;
@@ -2620,408 +3542,24 @@ zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive)
if (ret == 0 && zhp->zfs_type == ZFS_TYPE_VOLUME) {
ret = zvol_create_link(zhp->zfs_hdl, path);
if (ret != 0) {
- (void) ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DESTROY,
- &zc);
+ (void) zfs_standard_error(hdl, errno,
+ dgettext(TEXT_DOMAIN,
+ "Volume successfully snapshotted, but device links "
+ "were not created"));
+ zfs_close(zhp);
+ return (-1);
}
}
if (ret != 0)
(void) zfs_standard_error(hdl, errno, errbuf);
- free(parent);
zfs_close(zhp);
return (ret);
}
/*
- * Dumps a backup of the given snapshot (incremental from fromsnap if it's not
- * NULL) to the file descriptor specified by outfd.
- */
-int
-zfs_send(zfs_handle_t *zhp, const char *fromsnap, int outfd)
-{
- zfs_cmd_t zc = { 0 };
- char errbuf[1024];
- libzfs_handle_t *hdl = zhp->zfs_hdl;
-
- assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- if (fromsnap)
- (void) strlcpy(zc.zc_value, fromsnap, sizeof (zc.zc_name));
- zc.zc_cookie = outfd;
-
- if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SENDBACKUP, &zc) != 0) {
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot send '%s'"), zhp->zfs_name);
-
- switch (errno) {
-
- case EXDEV:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "not an earlier snapshot from the same fs"));
- return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
-
- case EDQUOT:
- case EFBIG:
- case EIO:
- case ENOLINK:
- case ENOSPC:
- case ENXIO:
- case EPIPE:
- case ERANGE:
- case EFAULT:
- case EROFS:
- zfs_error_aux(hdl, strerror(errno));
- return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
-
- default:
- return (zfs_standard_error(hdl, errno, errbuf));
- }
- }
-
- return (0);
-}
-
-/*
- * Create ancestors of 'target', but not target itself, and not
- * ancestors whose names are shorter than prefixlen. Die if
- * prefixlen-ancestor does not exist.
- */
-static int
-create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
-{
- zfs_handle_t *h;
- char *cp;
-
- /* make sure prefix exists */
- cp = strchr(target + prefixlen, '/');
- *cp = '\0';
- h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
- *cp = '/';
- if (h == NULL)
- return (-1);
- zfs_close(h);
-
- /*
- * Attempt to create, mount, and share any ancestor filesystems,
- * up to the prefixlen-long one.
- */
- for (cp = target + prefixlen + 1;
- cp = strchr(cp, '/'); *cp = '/', cp++) {
- const char *opname;
-
- *cp = '\0';
-
- h = make_dataset_handle(hdl, target);
- if (h) {
- /* it already exists, nothing to do here */
- zfs_close(h);
- continue;
- }
-
- opname = dgettext(TEXT_DOMAIN, "create");
- if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM,
- NULL) != 0)
- goto ancestorerr;
-
- opname = dgettext(TEXT_DOMAIN, "open");
- h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
- if (h == NULL)
- goto ancestorerr;
-
- opname = dgettext(TEXT_DOMAIN, "mount");
- if (zfs_mount(h, NULL, 0) != 0)
- goto ancestorerr;
-
- opname = dgettext(TEXT_DOMAIN, "share");
- if (zfs_share(h) != 0)
- goto ancestorerr;
-
- zfs_close(h);
-
- continue;
-ancestorerr:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "failed to %s ancestor '%s'"), opname, target);
- return (-1);
- }
-
- return (0);
-}
-
-/*
- * Restores a backup of tosnap from the file descriptor specified by infd.
- */
-int
-zfs_receive(libzfs_handle_t *hdl, const char *tosnap, int isprefix,
- int verbose, int dryrun, boolean_t force, int infd)
-{
- zfs_cmd_t zc = { 0 };
- time_t begin_time;
- int ioctl_err, err, bytes, size, choplen;
- char *cp;
- dmu_replay_record_t drr;
- struct drr_begin *drrb = &zc.zc_begin_record;
- char errbuf[1024];
- prop_changelist_t *clp;
- char chopprefix[ZFS_MAXNAMELEN];
-
- begin_time = time(NULL);
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot receive"));
-
- /* read in the BEGIN record */
- cp = (char *)&drr;
- bytes = 0;
- do {
- size = read(infd, cp, sizeof (drr) - bytes);
- cp += size;
- bytes += size;
- } while (size > 0);
-
- if (size < 0 || bytes != sizeof (drr)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
- "stream (failed to read first record)"));
- return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
- }
-
- zc.zc_begin_record = drr.drr_u.drr_begin;
-
- if (drrb->drr_magic != DMU_BACKUP_MAGIC &&
- drrb->drr_magic != BSWAP_64(DMU_BACKUP_MAGIC)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
- "stream (bad magic number)"));
- return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
- }
-
- if (drrb->drr_version != DMU_BACKUP_VERSION &&
- drrb->drr_version != BSWAP_64(DMU_BACKUP_VERSION)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "only version "
- "0x%llx is supported (stream is version 0x%llx)"),
- DMU_BACKUP_VERSION, drrb->drr_version);
- return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
- }
-
- if (strchr(drr.drr_u.drr_begin.drr_toname, '@') == NULL) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
- "stream (bad snapshot name)"));
- return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
- }
- /*
- * Determine how much of the snapshot name stored in the stream
- * we are going to tack on to the name they specified on the
- * command line, and how much we are going to chop off.
- *
- * If they specified a snapshot, chop the entire name stored in
- * the stream.
- */
- (void) strcpy(chopprefix, drr.drr_u.drr_begin.drr_toname);
- if (isprefix) {
- /*
- * They specified a fs with -d, we want to tack on
- * everything but the pool name stored in the stream
- */
- if (strchr(tosnap, '@')) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
- "argument - snapshot not allowed with -d"));
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
- }
- cp = strchr(chopprefix, '/');
- if (cp == NULL)
- cp = strchr(chopprefix, '@');
- *cp = '\0';
- } else if (strchr(tosnap, '@') == NULL) {
- /*
- * If they specified a filesystem without -d, we want to
- * tack on everything after the fs specified in the
- * first name from the stream.
- */
- cp = strchr(chopprefix, '@');
- *cp = '\0';
- }
- choplen = strlen(chopprefix);
-
- /*
- * Determine name of destination snapshot, store in zc_value.
- */
- (void) strcpy(zc.zc_value, tosnap);
- (void) strncat(zc.zc_value, drr.drr_u.drr_begin.drr_toname+choplen,
- sizeof (zc.zc_value));
- if (!zfs_validate_name(hdl, zc.zc_value, ZFS_TYPE_SNAPSHOT))
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
-
- (void) strcpy(zc.zc_name, zc.zc_value);
- if (drrb->drr_fromguid) {
- /* incremental backup stream */
- zfs_handle_t *h;
-
- /* do the recvbackup ioctl to the containing fs */
- *strchr(zc.zc_name, '@') = '\0';
-
- /* make sure destination fs exists */
- h = zfs_open(hdl, zc.zc_name,
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
- if (h == NULL)
- return (-1);
- if (!dryrun) {
- /*
- * We need to unmount all the dependents of the dataset
- * and the dataset itself. If it's a volume
- * then remove device link.
- */
- if (h->zfs_type == ZFS_TYPE_FILESYSTEM) {
- clp = changelist_gather(h, ZFS_PROP_NAME, 0);
- if (clp == NULL)
- return (-1);
- if (changelist_prefix(clp) != 0) {
- changelist_free(clp);
- return (-1);
- }
- } else {
- (void) zvol_remove_link(hdl, h->zfs_name);
- }
- }
- zfs_close(h);
- } else {
- /* full backup stream */
-
- /* Make sure destination fs does not exist */
- *strchr(zc.zc_name, '@') = '\0';
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "destination '%s' exists"), zc.zc_name);
- return (zfs_error(hdl, EZFS_EXISTS, errbuf));
- }
-
- if (strchr(zc.zc_name, '/') == NULL) {
- /*
- * they're trying to do a recv into a
- * nonexistant topmost filesystem.
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "destination does not exist"), zc.zc_name);
- return (zfs_error(hdl, EZFS_EXISTS, errbuf));
- }
-
- /* Do the recvbackup ioctl to the fs's parent. */
- *strrchr(zc.zc_name, '/') = '\0';
-
- if (isprefix && (err = create_parents(hdl,
- zc.zc_value, strlen(tosnap))) != 0) {
- return (zfs_error(hdl, EZFS_BADRESTORE, errbuf));
- }
-
- }
-
- zc.zc_cookie = infd;
- zc.zc_guid = force;
- if (verbose) {
- (void) printf("%s %s stream of %s into %s\n",
- dryrun ? "would receive" : "receiving",
- drrb->drr_fromguid ? "incremental" : "full",
- drr.drr_u.drr_begin.drr_toname,
- zc.zc_value);
- (void) fflush(stdout);
- }
- if (dryrun)
- return (0);
- err = ioctl_err = ioctl(hdl->libzfs_fd, ZFS_IOC_RECVBACKUP, &zc);
- if (ioctl_err != 0) {
- switch (errno) {
- case ENODEV:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "most recent snapshot does not match incremental "
- "source"));
- (void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
- break;
- case ETXTBSY:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "destination has been modified since most recent "
- "snapshot"));
- (void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
- break;
- case EEXIST:
- if (drrb->drr_fromguid == 0) {
- /* it's the containing fs that exists */
- cp = strchr(zc.zc_value, '@');
- *cp = '\0';
- }
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "destination already exists"));
- (void) zfs_error_fmt(hdl, EZFS_EXISTS,
- dgettext(TEXT_DOMAIN, "cannot restore to %s"),
- zc.zc_value);
- break;
- case EINVAL:
- (void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
- break;
- case ECKSUM:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid stream (checksum mismatch)"));
- (void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
- break;
- default:
- (void) zfs_standard_error(hdl, errno, errbuf);
- }
- }
-
- /*
- * Mount or recreate the /dev links for the target filesystem
- * (if created, or if we tore them down to do an incremental
- * restore), and the /dev links for the new snapshot (if
- * created). Also mount any children of the target filesystem
- * if we did an incremental receive.
- */
- cp = strchr(zc.zc_value, '@');
- if (cp && (ioctl_err == 0 || drrb->drr_fromguid)) {
- zfs_handle_t *h;
-
- *cp = '\0';
- h = zfs_open(hdl, zc.zc_value,
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
- *cp = '@';
- if (h) {
- if (h->zfs_type == ZFS_TYPE_VOLUME) {
- err = zvol_create_link(hdl, h->zfs_name);
- if (err == 0 && ioctl_err == 0)
- err = zvol_create_link(hdl,
- zc.zc_value);
- } else {
- if (drrb->drr_fromguid) {
- err = changelist_postfix(clp);
- changelist_free(clp);
- } else {
- err = zfs_mount(h, NULL, 0);
- }
- }
- zfs_close(h);
- }
- }
-
- if (err || ioctl_err)
- return (-1);
-
- if (verbose) {
- char buf1[64];
- char buf2[64];
- uint64_t bytes = zc.zc_cookie;
- time_t delta = time(NULL) - begin_time;
- if (delta == 0)
- delta = 1;
- zfs_nicenum(bytes, buf1, sizeof (buf1));
- zfs_nicenum(bytes/delta, buf2, sizeof (buf1));
-
- (void) printf("received %sb stream in %lu seconds (%sb/sec)\n",
- buf1, delta, buf2);
- }
-
- return (0);
-}
-
-/*
* Destroy any more recent snapshots. We invoke this callback on any dependents
* of the snapshot first. If the 'cb_dependent' member is non-zero, then this
* is a dependent and we should just destroy it without checking the transaction
@@ -3030,9 +3568,9 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, int isprefix,
typedef struct rollback_data {
const char *cb_target; /* the snapshot */
uint64_t cb_create; /* creation time reference */
- prop_changelist_t *cb_clp; /* changelist pointer */
- int cb_error;
+ boolean_t cb_error;
boolean_t cb_dependent;
+ boolean_t cb_force;
} rollback_data_t;
static int
@@ -3045,23 +3583,35 @@ rollback_destroy(zfs_handle_t *zhp, void *data)
zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
cbp->cb_create) {
+ char *logstr;
cbp->cb_dependent = B_TRUE;
- if (zfs_iter_dependents(zhp, B_FALSE, rollback_destroy,
- cbp) != 0)
- cbp->cb_error = 1;
+ cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE,
+ rollback_destroy, cbp);
cbp->cb_dependent = B_FALSE;
- if (zfs_destroy(zhp) != 0)
- cbp->cb_error = 1;
- else
- changelist_remove(zhp, cbp->cb_clp);
+ logstr = zhp->zfs_hdl->libzfs_log_str;
+ zhp->zfs_hdl->libzfs_log_str = NULL;
+ cbp->cb_error |= zfs_destroy(zhp);
+ zhp->zfs_hdl->libzfs_log_str = logstr;
}
} else {
+ /* We must destroy this clone; first unmount it */
+ prop_changelist_t *clp;
+
+ clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
+ cbp->cb_force ? MS_FORCE: 0);
+ if (clp == NULL || changelist_prefix(clp) != 0) {
+ cbp->cb_error = B_TRUE;
+ zfs_close(zhp);
+ return (0);
+ }
if (zfs_destroy(zhp) != 0)
- cbp->cb_error = 1;
+ cbp->cb_error = B_TRUE;
else
- changelist_remove(zhp, cbp->cb_clp);
+ changelist_remove(clp, zhp->zfs_name);
+ (void) changelist_postfix(clp);
+ changelist_free(clp);
}
zfs_close(zhp);
@@ -3069,48 +3619,6 @@ rollback_destroy(zfs_handle_t *zhp, void *data)
}
/*
- * Rollback the dataset to its latest snapshot.
- */
-static int
-do_rollback(zfs_handle_t *zhp)
-{
- int ret;
- zfs_cmd_t zc = { 0 };
-
- assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM ||
- zhp->zfs_type == ZFS_TYPE_VOLUME);
-
- if (zhp->zfs_type == ZFS_TYPE_VOLUME &&
- zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0)
- return (-1);
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-
- if (ZFS_IS_VOLUME(zhp))
- zc.zc_objset_type = DMU_OST_ZVOL;
- else
- zc.zc_objset_type = DMU_OST_ZFS;
-
- /*
- * We rely on the consumer to verify that there are no newer snapshots
- * for the given dataset. Given these constraints, we can simply pass
- * the name on to the ioctl() call. There is still an unlikely race
- * condition where the user has taken a snapshot since we verified that
- * this was the most recent.
- */
- if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_ROLLBACK,
- &zc)) != 0) {
- (void) zfs_standard_error_fmt(zhp->zfs_hdl, errno,
- dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
- zhp->zfs_name);
- } else if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
- ret = zvol_create_link(zhp->zfs_hdl, zhp->zfs_name);
- }
-
- return (ret);
-}
-
-/*
* Given a dataset, rollback to a specific snapshot, discarding any
* data changes since then and making it the active dataset.
*
@@ -3118,56 +3626,87 @@ do_rollback(zfs_handle_t *zhp)
* their dependents.
*/
int
-zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, int flag)
+zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
{
- int ret;
rollback_data_t cb = { 0 };
- prop_changelist_t *clp;
-
- /*
- * Unmount all dependendents of the dataset and the dataset itself.
- * The list we need to gather is the same as for doing rename
- */
- clp = changelist_gather(zhp, ZFS_PROP_NAME, flag ? MS_FORCE: 0);
- if (clp == NULL)
- return (-1);
+ int err;
+ zfs_cmd_t zc = { 0 };
+ boolean_t restore_resv = 0;
+ uint64_t old_volsize, new_volsize;
+ zfs_prop_t resv_prop;
- if ((ret = changelist_prefix(clp)) != 0)
- goto out;
+ assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM ||
+ zhp->zfs_type == ZFS_TYPE_VOLUME);
/*
* Destroy all recent snapshots and its dependends.
*/
+ cb.cb_force = force;
cb.cb_target = snap->zfs_name;
cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
- cb.cb_clp = clp;
(void) zfs_iter_children(zhp, rollback_destroy, &cb);
- if ((ret = cb.cb_error) != 0) {
- (void) changelist_postfix(clp);
- goto out;
- }
+ if (cb.cb_error)
+ return (-1);
/*
* Now that we have verified that the snapshot is the latest,
* rollback to the given snapshot.
*/
- ret = do_rollback(zhp);
- if (ret != 0) {
- (void) changelist_postfix(clp);
- goto out;
+ if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
+ if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0)
+ return (-1);
+ if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
+ return (-1);
+ old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
+ restore_resv =
+ (old_volsize == zfs_prop_get_int(zhp, resv_prop));
}
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+
+ if (ZFS_IS_VOLUME(zhp))
+ zc.zc_objset_type = DMU_OST_ZVOL;
+ else
+ zc.zc_objset_type = DMU_OST_ZFS;
+
/*
- * We only want to re-mount the filesystem if it was mounted in the
- * first place.
+ * We rely on zfs_iter_children() to verify that there are no
+ * newer snapshots for the given dataset. Therefore, we can
+ * simply pass the name on to the ioctl() call. There is still
+ * an unlikely race condition where the user has taken a
+ * snapshot since we verified that this was the most recent.
+ *
*/
- ret = changelist_postfix(clp);
+ if ((err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) {
+ (void) zfs_standard_error_fmt(zhp->zfs_hdl, errno,
+ dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
+ zhp->zfs_name);
+ return (err);
+ }
-out:
- changelist_free(clp);
- return (ret);
+ /*
+ * For volumes, if the pre-rollback volsize matched the pre-
+ * rollback reservation and the volsize has changed then set
+ * the reservation property to the post-rollback volsize.
+ * Make a new handle since the rollback closed the dataset.
+ */
+ if ((zhp->zfs_type == ZFS_TYPE_VOLUME) &&
+ (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) {
+ if (err = zvol_create_link(zhp->zfs_hdl, zhp->zfs_name)) {
+ zfs_close(zhp);
+ return (err);
+ }
+ if (restore_resv) {
+ new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
+ if (old_volsize != new_volsize)
+ err = zfs_prop_set_int(zhp, resv_prop,
+ new_volsize);
+ }
+ zfs_close(zhp);
+ }
+ return (err);
}
/*
@@ -3210,7 +3749,7 @@ zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
* Renames the given dataset.
*/
int
-zfs_rename(zfs_handle_t *zhp, const char *target, int recursive)
+zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive)
{
int ret;
zfs_cmd_t zc = { 0 };
@@ -3262,7 +3801,7 @@ zfs_rename(zfs_handle_t *zhp, const char *target, int recursive)
errbuf));
}
}
- if (!zfs_validate_name(hdl, target, zhp->zfs_type))
+ if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
} else {
if (recursive) {
@@ -3271,12 +3810,12 @@ zfs_rename(zfs_handle_t *zhp, const char *target, int recursive)
return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
}
- if (!zfs_validate_name(hdl, target, zhp->zfs_type))
+ if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
uint64_t unused;
/* validate parents */
- if (check_parents(hdl, target, &unused) != 0)
+ if (check_parents(hdl, target, &unused, B_FALSE, NULL) != 0)
return (-1);
(void) parent_name(target, parent, sizeof (parent));
@@ -3313,17 +3852,22 @@ zfs_rename(zfs_handle_t *zhp, const char *target, int recursive)
if (recursive) {
struct destroydata dd;
- parentname = strdup(zhp->zfs_name);
+ parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name);
+ if (parentname == NULL) {
+ ret = -1;
+ goto error;
+ }
delim = strchr(parentname, '@');
*delim = '\0';
- zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_ANY);
+ zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET);
if (zhrp == NULL) {
- return (-1);
+ ret = -1;
+ goto error;
}
dd.snapname = delim + 1;
dd.gotone = B_FALSE;
- dd.closezhp = B_FALSE;
+ dd.closezhp = B_TRUE;
/* We remove any zvol links prior to renaming them */
ret = zfs_iter_filesystems(zhrp, zfs_remove_link_cb, &dd);
@@ -3331,7 +3875,7 @@ zfs_rename(zfs_handle_t *zhp, const char *target, int recursive)
goto error;
}
} else {
- if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0)) == NULL)
+ if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0)) == NULL)
return (-1);
if (changelist_haszonedchild(cl)) {
@@ -3356,19 +3900,19 @@ zfs_rename(zfs_handle_t *zhp, const char *target, int recursive)
zc.zc_cookie = recursive;
- if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_RENAME, &zc)) != 0) {
+ if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) {
/*
* if it was recursive, the one that actually failed will
* be in zc.zc_name
*/
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot rename to '%s'"), zc.zc_name);
+ "cannot rename '%s'"), zc.zc_name);
if (recursive && errno == EEXIST) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"a child dataset already has a snapshot "
"with the new name"));
- (void) zfs_error(hdl, EZFS_CROSSTARGET, errbuf);
+ (void) zfs_error(hdl, EZFS_EXISTS, errbuf);
} else {
(void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf);
}
@@ -3432,6 +3976,8 @@ zvol_create_link_common(libzfs_handle_t *hdl, const char *dataset, int ifexists)
zfs_cmd_t zc = { 0 };
#if 0
di_devlink_handle_t dhdl;
+ priv_set_t *priv_effective;
+ int privileged;
#endif
(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
@@ -3470,17 +4016,51 @@ zvol_create_link_common(libzfs_handle_t *hdl, const char *dataset, int ifexists)
#if 0
/*
- * Call devfsadm and wait for the links to magically appear.
+ * If privileged call devfsadm and wait for the links to
+ * magically appear.
+ * Otherwise, print out an informational message.
*/
- if ((dhdl = di_devlink_init(ZFS_DRIVER, DI_MAKE_LINK)) == NULL) {
- zfs_error_aux(hdl, strerror(errno));
- (void) zfs_error_fmt(hdl, EZFS_DEVLINKS,
- dgettext(TEXT_DOMAIN, "cannot create device links "
- "for '%s'"), dataset);
- (void) ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc);
- return (-1);
+
+ priv_effective = priv_allocset();
+ (void) getppriv(PRIV_EFFECTIVE, priv_effective);
+ privileged = (priv_isfullset(priv_effective) == B_TRUE);
+ priv_freeset(priv_effective);
+
+ if (privileged) {
+ if ((dhdl = di_devlink_init(ZFS_DRIVER,
+ DI_MAKE_LINK)) == NULL) {
+ zfs_error_aux(hdl, strerror(errno));
+ (void) zfs_error_fmt(hdl, errno,
+ dgettext(TEXT_DOMAIN, "cannot create device links "
+ "for '%s'"), dataset);
+ (void) ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc);
+ return (-1);
+ } else {
+ (void) di_devlink_fini(&dhdl);
+ }
} else {
- (void) di_devlink_fini(&dhdl);
+ char pathname[MAXPATHLEN];
+ struct stat64 statbuf;
+ int i;
+
+#define MAX_WAIT 10
+
+ /*
+ * This is the poor mans way of waiting for the link
+ * to show up. If after 10 seconds we still don't
+ * have it, then print out a message.
+ */
+ (void) snprintf(pathname, sizeof (pathname), "/dev/zvol/dsk/%s",
+ dataset);
+
+ for (i = 0; i != MAX_WAIT; i++) {
+ if (stat64(pathname, &statbuf) == 0)
+ break;
+ (void) sleep(1);
+ }
+ if (i == MAX_WAIT)
+ (void) printf(gettext("%s may not be immediately "
+ "available\n"), pathname);
}
#endif
@@ -3524,200 +4104,6 @@ zfs_get_user_props(zfs_handle_t *zhp)
}
/*
- * Given a comma-separated list of properties, contruct a property list
- * containing both user-defined and native properties. This function will
- * return a NULL list if 'all' is specified, which can later be expanded on a
- * per-dataset basis by zfs_expand_proplist().
- */
-int
-zfs_get_proplist_common(libzfs_handle_t *hdl, char *fields,
- zfs_proplist_t **listp, zfs_type_t type)
-{
- size_t len;
- char *s, *p;
- char c;
- zfs_prop_t prop;
- zfs_proplist_t *entry;
- zfs_proplist_t **last;
-
- *listp = NULL;
- last = listp;
-
- /*
- * If 'all' is specified, return a NULL list.
- */
- if (strcmp(fields, "all") == 0)
- return (0);
-
- /*
- * If no fields were specified, return an error.
- */
- if (fields[0] == '\0') {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "no properties specified"));
- return (zfs_error(hdl, EZFS_BADPROP, dgettext(TEXT_DOMAIN,
- "bad property list")));
- }
-
- /*
- * It would be nice to use getsubopt() here, but the inclusion of column
- * aliases makes this more effort than it's worth.
- */
- s = fields;
- while (*s != '\0') {
- if ((p = strchr(s, ',')) == NULL) {
- len = strlen(s);
- p = s + len;
- } else {
- len = p - s;
- }
-
- /*
- * Check for empty options.
- */
- if (len == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "empty property name"));
- return (zfs_error(hdl, EZFS_BADPROP,
- dgettext(TEXT_DOMAIN, "bad property list")));
- }
-
- /*
- * Check all regular property names.
- */
- c = s[len];
- s[len] = '\0';
- prop = zfs_name_to_prop_common(s, type);
-
- if (prop != ZFS_PROP_INVAL &&
- !zfs_prop_valid_for_type(prop, type))
- prop = ZFS_PROP_INVAL;
-
- /*
- * When no property table entry can be found, return failure if
- * this is a pool property or if this isn't a user-defined
- * dataset property,
- */
- if (prop == ZFS_PROP_INVAL &&
- (type & ZFS_TYPE_POOL || !zfs_prop_user(s))) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid property '%s'"), s);
- return (zfs_error(hdl, EZFS_BADPROP,
- dgettext(TEXT_DOMAIN, "bad property list")));
- }
-
- if ((entry = zfs_alloc(hdl, sizeof (zfs_proplist_t))) == NULL)
- return (-1);
-
- entry->pl_prop = prop;
- if (prop == ZFS_PROP_INVAL) {
- if ((entry->pl_user_prop =
- zfs_strdup(hdl, s)) == NULL) {
- free(entry);
- return (-1);
- }
- entry->pl_width = strlen(s);
- } else {
- entry->pl_width = zfs_prop_width(prop,
- &entry->pl_fixed);
- }
-
- *last = entry;
- last = &entry->pl_next;
-
- s = p;
- if (c == ',')
- s++;
- }
-
- return (0);
-}
-
-int
-zfs_get_proplist(libzfs_handle_t *hdl, char *fields, zfs_proplist_t **listp)
-{
- return (zfs_get_proplist_common(hdl, fields, listp, ZFS_TYPE_ANY));
-}
-
-void
-zfs_free_proplist(zfs_proplist_t *pl)
-{
- zfs_proplist_t *next;
-
- while (pl != NULL) {
- next = pl->pl_next;
- free(pl->pl_user_prop);
- free(pl);
- pl = next;
- }
-}
-
-typedef struct expand_data {
- zfs_proplist_t **last;
- libzfs_handle_t *hdl;
-} expand_data_t;
-
-static zfs_prop_t
-zfs_expand_proplist_cb(zfs_prop_t prop, void *cb)
-{
- zfs_proplist_t *entry;
- expand_data_t *edp = cb;
-
- if ((entry = zfs_alloc(edp->hdl, sizeof (zfs_proplist_t))) == NULL)
- return (ZFS_PROP_INVAL);
-
- entry->pl_prop = prop;
- entry->pl_width = zfs_prop_width(prop, &entry->pl_fixed);
- entry->pl_all = B_TRUE;
-
- *(edp->last) = entry;
- edp->last = &entry->pl_next;
-
- return (ZFS_PROP_CONT);
-}
-
-int
-zfs_expand_proplist_common(libzfs_handle_t *hdl, zfs_proplist_t **plp,
- zfs_type_t type)
-{
- zfs_proplist_t *entry;
- zfs_proplist_t **last;
- expand_data_t exp;
-
- if (*plp == NULL) {
- /*
- * If this is the very first time we've been called for an 'all'
- * specification, expand the list to include all native
- * properties.
- */
- last = plp;
-
- exp.last = last;
- exp.hdl = hdl;
-
- if (zfs_prop_iter_common(zfs_expand_proplist_cb, &exp, type,
- B_FALSE) == ZFS_PROP_INVAL)
- return (-1);
-
- /*
- * Add 'name' to the beginning of the list, which is handled
- * specially.
- */
- if ((entry = zfs_alloc(hdl,
- sizeof (zfs_proplist_t))) == NULL)
- return (-1);
-
- entry->pl_prop = ZFS_PROP_NAME;
- entry->pl_width = zfs_prop_width(ZFS_PROP_NAME,
- &entry->pl_fixed);
- entry->pl_all = B_TRUE;
- entry->pl_next = *plp;
- *plp = entry;
- }
- return (0);
-}
-
-/*
* This function is used by 'zfs list' to determine the exact set of columns to
* display, and their maximum widths. This does two main things:
*
@@ -3729,17 +4115,17 @@ zfs_expand_proplist_common(libzfs_handle_t *hdl, zfs_proplist_t **plp,
* so that we can size the column appropriately.
*/
int
-zfs_expand_proplist(zfs_handle_t *zhp, zfs_proplist_t **plp)
+zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp)
{
libzfs_handle_t *hdl = zhp->zfs_hdl;
- zfs_proplist_t *entry;
- zfs_proplist_t **last, **start;
+ zprop_list_t *entry;
+ zprop_list_t **last, **start;
nvlist_t *userprops, *propval;
nvpair_t *elem;
char *strval;
char buf[ZFS_MAXPROPLEN];
- if (zfs_expand_proplist_common(hdl, plp, ZFS_TYPE_ANY) != 0)
+ if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0)
return (-1);
userprops = zfs_get_user_props(zhp);
@@ -3753,7 +4139,7 @@ zfs_expand_proplist(zfs_handle_t *zhp, zfs_proplist_t **plp)
*/
start = plp;
while (*start != NULL) {
- if ((*start)->pl_prop == ZFS_PROP_INVAL)
+ if ((*start)->pl_prop == ZPROP_INVAL)
break;
start = &(*start)->pl_next;
}
@@ -3772,14 +4158,14 @@ zfs_expand_proplist(zfs_handle_t *zhp, zfs_proplist_t **plp)
if (*last == NULL) {
if ((entry = zfs_alloc(hdl,
- sizeof (zfs_proplist_t))) == NULL ||
+ sizeof (zprop_list_t))) == NULL ||
((entry->pl_user_prop = zfs_strdup(hdl,
nvpair_name(elem)))) == NULL) {
free(entry);
return (-1);
}
- entry->pl_prop = ZFS_PROP_INVAL;
+ entry->pl_prop = ZPROP_INVAL;
entry->pl_width = strlen(nvpair_name(elem));
entry->pl_all = B_TRUE;
*last = entry;
@@ -3794,7 +4180,7 @@ zfs_expand_proplist(zfs_handle_t *zhp, zfs_proplist_t **plp)
if (entry->pl_fixed)
continue;
- if (entry->pl_prop != ZFS_PROP_INVAL) {
+ if (entry->pl_prop != ZPROP_INVAL) {
if (zfs_prop_get(zhp, entry->pl_prop,
buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) {
if (strlen(buf) > entry->pl_width)
@@ -3803,7 +4189,7 @@ zfs_expand_proplist(zfs_handle_t *zhp, zfs_proplist_t **plp)
} else if (nvlist_lookup_nvlist(userprops,
entry->pl_user_prop, &propval) == 0) {
verify(nvlist_lookup_string(propval,
- ZFS_PROP_VALUE, &strval) == 0);
+ ZPROP_VALUE, &strval) == 0);
if (strlen(strval) > entry->pl_width)
entry->pl_width = strlen(strval);
}
@@ -3812,6 +4198,72 @@ zfs_expand_proplist(zfs_handle_t *zhp, zfs_proplist_t **plp)
return (0);
}
+#ifdef TODO
+int
+zfs_iscsi_perm_check(libzfs_handle_t *hdl, char *dataset, ucred_t *cred)
+{
+ zfs_cmd_t zc = { 0 };
+ nvlist_t *nvp;
+ gid_t gid;
+ uid_t uid;
+ const gid_t *groups;
+ int group_cnt;
+ int error;
+
+ if (nvlist_alloc(&nvp, NV_UNIQUE_NAME, 0) != 0)
+ return (no_memory(hdl));
+
+ uid = ucred_geteuid(cred);
+ gid = ucred_getegid(cred);
+ group_cnt = ucred_getgroups(cred, &groups);
+
+ if (uid == (uid_t)-1 || gid == (uid_t)-1 || group_cnt == (uid_t)-1)
+ return (1);
+
+ if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_UID, uid) != 0) {
+ nvlist_free(nvp);
+ return (1);
+ }
+
+ if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_GID, gid) != 0) {
+ nvlist_free(nvp);
+ return (1);
+ }
+
+ if (nvlist_add_uint32_array(nvp,
+ ZFS_DELEG_PERM_GROUPS, (uint32_t *)groups, group_cnt) != 0) {
+ nvlist_free(nvp);
+ return (1);
+ }
+ (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
+
+ if (zcmd_write_src_nvlist(hdl, &zc, nvp))
+ return (-1);
+
+ error = ioctl(hdl->libzfs_fd, ZFS_IOC_ISCSI_PERM_CHECK, &zc);
+ nvlist_free(nvp);
+ return (error);
+}
+#endif
+
+int
+zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path,
+ void *export, void *sharetab, int sharemax, zfs_share_op_t operation)
+{
+ zfs_cmd_t zc = { 0 };
+ int error;
+
+ (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
+ (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));
+ zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab;
+ zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export;
+ zc.zc_share.z_sharetype = operation;
+ zc.zc_share.z_sharemax = sharemax;
+
+ error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc);
+ return (error);
+}
+
/*
* Attach/detach the given filesystem to/from the given jail.
*/
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_graph.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_graph.c
index c283016..e7cbf23 100644
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_graph.c
+++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_graph.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -126,6 +126,8 @@ typedef struct zfs_graph {
zfs_vertex_t **zg_hash;
size_t zg_size;
size_t zg_nvertex;
+ const char *zg_root;
+ int zg_clone_count;
} zfs_graph_t;
/*
@@ -255,7 +257,7 @@ zfs_vertex_sort_edges(zfs_vertex_t *zvp)
* datasets in the pool.
*/
static zfs_graph_t *
-zfs_graph_create(libzfs_handle_t *hdl, size_t size)
+zfs_graph_create(libzfs_handle_t *hdl, const char *dataset, size_t size)
{
zfs_graph_t *zgp = zfs_alloc(hdl, sizeof (zfs_graph_t));
@@ -269,6 +271,9 @@ zfs_graph_create(libzfs_handle_t *hdl, size_t size)
return (NULL);
}
+ zgp->zg_root = dataset;
+ zgp->zg_clone_count = 0;
+
return (zgp);
}
@@ -367,17 +372,16 @@ zfs_graph_add(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *source,
}
/*
- * Iterate over all children of the given dataset, adding any vertices as
- * necessary. Returns 0 if no cloned snapshots were seen, -1 if there was an
- * error, or 1 otherwise. This is a simple recursive algorithm - the ZFS
- * namespace typically is very flat. We manually invoke the necessary ioctl()
- * calls to avoid the overhead and additional semantics of zfs_open().
+ * Iterate over all children of the given dataset, adding any vertices
+ * as necessary. Returns -1 if there was an error, or 0 otherwise.
+ * This is a simple recursive algorithm - the ZFS namespace typically
+ * is very flat. We manually invoke the necessary ioctl() calls to
+ * avoid the overhead and additional semantics of zfs_open().
*/
static int
iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
{
zfs_cmd_t zc = { 0 };
- int ret = 0, err;
zfs_vertex_t *zvp;
/*
@@ -390,18 +394,8 @@ iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
return (0);
/*
- * We check the clone parent here instead of within the loop, so that if
- * the root dataset has been promoted from a clone, we find its parent
- * appropriately.
+ * Iterate over all children
*/
- (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0 &&
- zc.zc_objset_stats.dds_clone_of[0] != '\0') {
- if (zfs_graph_add(hdl, zgp, zc.zc_objset_stats.dds_clone_of,
- zc.zc_name, zc.zc_objset_stats.dds_creation_txg) != 0)
- return (-1);
- }
-
for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
ioctl(hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0;
(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) {
@@ -417,9 +411,23 @@ iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
* dataset and clone statistics. If this fails, the dataset has
* since been removed, and we're pretty much screwed anyway.
*/
+ zc.zc_objset_stats.dds_origin[0] = '\0';
if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
continue;
+ if (zc.zc_objset_stats.dds_origin[0] != '\0') {
+ if (zfs_graph_add(hdl, zgp,
+ zc.zc_objset_stats.dds_origin, zc.zc_name,
+ zc.zc_objset_stats.dds_creation_txg) != 0)
+ return (-1);
+ /*
+ * Count origins only if they are contained in the graph
+ */
+ if (isa_child_of(zc.zc_objset_stats.dds_origin,
+ zgp->zg_root))
+ zgp->zg_clone_count--;
+ }
+
/*
* Add an edge between the parent and the child.
*/
@@ -428,19 +436,10 @@ iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
return (-1);
/*
- * Iterate over all children
+ * Recursively visit child
*/
- err = iterate_children(hdl, zgp, zc.zc_name);
- if (err == -1)
+ if (iterate_children(hdl, zgp, zc.zc_name))
return (-1);
- else if (err == 1)
- ret = 1;
-
- /*
- * Indicate if we found a dataset with a non-zero clone count.
- */
- if (zc.zc_objset_stats.dds_num_clones != 0)
- ret = 1;
}
/*
@@ -467,67 +466,84 @@ iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
zc.zc_objset_stats.dds_creation_txg) != 0)
return (-1);
- /*
- * Indicate if we found a dataset with a non-zero clone count.
- */
- if (zc.zc_objset_stats.dds_num_clones != 0)
- ret = 1;
+ zgp->zg_clone_count += zc.zc_objset_stats.dds_num_clones;
}
zvp->zv_visited = VISIT_SEEN;
- return (ret);
+ return (0);
}
/*
- * Construct a complete graph of all necessary vertices. First, we iterate over
- * only our object's children. If we don't find any cloned snapshots, then we
- * simple return that. Otherwise, we have to start at the pool root and iterate
- * over all datasets.
+ * Returns false if there are no snapshots with dependent clones in this
+ * subtree or if all of those clones are also in this subtree. Returns
+ * true if there is an error or there are external dependents.
+ */
+static boolean_t
+external_dependents(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
+{
+ zfs_cmd_t zc = { 0 };
+
+ /*
+ * Check whether this dataset is a clone or has clones since
+ * iterate_children() only checks the children.
+ */
+ (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
+ if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
+ return (B_TRUE);
+
+ if (zc.zc_objset_stats.dds_origin[0] != '\0') {
+ if (zfs_graph_add(hdl, zgp,
+ zc.zc_objset_stats.dds_origin, zc.zc_name,
+ zc.zc_objset_stats.dds_creation_txg) != 0)
+ return (B_TRUE);
+ if (isa_child_of(zc.zc_objset_stats.dds_origin, dataset))
+ zgp->zg_clone_count--;
+ }
+
+ if ((zc.zc_objset_stats.dds_num_clones) ||
+ iterate_children(hdl, zgp, dataset))
+ return (B_TRUE);
+
+ return (zgp->zg_clone_count != 0);
+}
+
+/*
+ * Construct a complete graph of all necessary vertices. First, iterate over
+ * only our object's children. If no cloned snapshots are found, or all of
+ * the cloned snapshots are in this subtree then return a graph of the subtree.
+ * Otherwise, start at the root of the pool and iterate over all datasets.
*/
static zfs_graph_t *
construct_graph(libzfs_handle_t *hdl, const char *dataset)
{
- zfs_graph_t *zgp = zfs_graph_create(hdl, ZFS_GRAPH_SIZE);
- zfs_cmd_t zc = { 0 };
+ zfs_graph_t *zgp = zfs_graph_create(hdl, dataset, ZFS_GRAPH_SIZE);
int ret = 0;
if (zgp == NULL)
return (zgp);
- /*
- * We need to explicitly check whether this dataset has clones or not,
- * since iterate_children() only checks the children.
- */
- (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
- (void) ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc);
-
- if (zc.zc_objset_stats.dds_num_clones != 0 ||
- (ret = iterate_children(hdl, zgp, dataset)) != 0) {
+ if ((strchr(dataset, '/') == NULL) ||
+ (external_dependents(hdl, zgp, dataset))) {
/*
* Determine pool name and try again.
*/
- char *pool, *slash;
-
- if ((slash = strchr(dataset, '/')) != NULL ||
- (slash = strchr(dataset, '@')) != NULL) {
- pool = zfs_alloc(hdl, slash - dataset + 1);
- if (pool == NULL) {
- zfs_graph_destroy(zgp);
- return (NULL);
- }
- (void) strncpy(pool, dataset, slash - dataset);
- pool[slash - dataset] = '\0';
-
- if (iterate_children(hdl, zgp, pool) == -1 ||
- zfs_graph_add(hdl, zgp, pool, NULL, 0) != 0) {
- free(pool);
- zfs_graph_destroy(zgp);
- return (NULL);
- }
+ int len = strcspn(dataset, "/@") + 1;
+ char *pool = zfs_alloc(hdl, len);
+
+ if (pool == NULL) {
+ zfs_graph_destroy(zgp);
+ return (NULL);
+ }
+ (void) strlcpy(pool, dataset, len);
+ if (iterate_children(hdl, zgp, pool) == -1 ||
+ zfs_graph_add(hdl, zgp, pool, NULL, 0) != 0) {
free(pool);
+ zfs_graph_destroy(zgp);
+ return (NULL);
}
+ free(pool);
}
if (ret == -1 || zfs_graph_add(hdl, zgp, dataset, NULL, 0) != 0) {
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h
index 9581331..afe71f3 100644
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h
+++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h
@@ -1,5 +1,5 @@
/*
- * CDDL HEADER START
+ * CDDL HEADER SART
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
@@ -20,21 +20,21 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _LIBFS_IMPL_H
#define _LIBFS_IMPL_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/dmu.h>
#include <sys/fs/zfs.h>
#include <sys/zfs_ioctl.h>
#include <sys/zfs_acl.h>
+#include <sys/spa.h>
#include <sys/nvpair.h>
+#include <libshare.h>
#include <libuutil.h>
#include <libzfs.h>
@@ -42,22 +42,33 @@
extern "C" {
#endif
+#ifdef VERIFY
+#undef VERIFY
+#endif
+#define VERIFY verify
+
struct libzfs_handle {
int libzfs_error;
int libzfs_fd;
FILE *libzfs_mnttab;
FILE *libzfs_sharetab;
+ zpool_handle_t *libzfs_pool_handles;
uu_avl_pool_t *libzfs_ns_avlpool;
uu_avl_t *libzfs_ns_avl;
uint64_t libzfs_ns_gen;
int libzfs_desc_active;
char libzfs_action[1024];
char libzfs_desc[1024];
+ char *libzfs_log_str;
int libzfs_printerr;
+ void *libzfs_sharehdl; /* libshare handle */
+ uint_t libzfs_shareflags;
};
+#define ZFSSHARE_MISS 0x01 /* Didn't find entry in cache */
struct zfs_handle {
libzfs_handle_t *zfs_hdl;
+ zpool_handle_t *zpool_hdl;
char zfs_name[ZFS_MAXNAMELEN];
zfs_type_t zfs_type; /* type including snapshot */
zfs_type_t zfs_head_type; /* type excluding snapshot */
@@ -66,7 +77,6 @@ struct zfs_handle {
nvlist_t *zfs_user_props;
boolean_t zfs_mntcheck;
char *zfs_mntopts;
- char zfs_root[MAXPATHLEN];
};
/*
@@ -77,14 +87,33 @@ struct zfs_handle {
struct zpool_handle {
libzfs_handle_t *zpool_hdl;
+ zpool_handle_t *zpool_next;
char zpool_name[ZPOOL_MAXNAMELEN];
int zpool_state;
size_t zpool_config_size;
nvlist_t *zpool_config;
nvlist_t *zpool_old_config;
nvlist_t *zpool_props;
+ diskaddr_t zpool_start_block;
};
+typedef enum {
+ PROTO_NFS = 0,
+ PROTO_SMB = 1,
+ PROTO_END = 2
+} zfs_share_proto_t;
+
+/*
+ * The following can be used as a bitmask and any new values
+ * added must preserve that capability.
+ */
+typedef enum {
+ SHARED_NOT_SHARED = 0x0,
+ SHARED_ISCSI = 0x1,
+ SHARED_NFS = 0x2,
+ SHARED_SMB = 0x4
+} zfs_share_type_t;
+
int zfs_error(libzfs_handle_t *, int, const char *);
int zfs_error_fmt(libzfs_handle_t *, int, const char *, ...);
void zfs_error_aux(libzfs_handle_t *, const char *, ...);
@@ -101,20 +130,24 @@ int zpool_standard_error_fmt(libzfs_handle_t *, int, const char *, ...);
int get_dependents(libzfs_handle_t *, boolean_t, const char *, char ***,
size_t *);
-int zfs_expand_proplist_common(libzfs_handle_t *, zfs_proplist_t **,
- zfs_type_t);
-int zfs_get_proplist_common(libzfs_handle_t *, char *, zfs_proplist_t **,
- zfs_type_t);
-zfs_prop_t zfs_prop_iter_common(zfs_prop_f, void *, zfs_type_t, boolean_t);
-zfs_prop_t zfs_name_to_prop_common(const char *, zfs_type_t);
-nvlist_t *zfs_validate_properties(libzfs_handle_t *, zfs_type_t, char *,
- nvlist_t *, uint64_t, zfs_handle_t *zhp, const char *errbuf);
+int zprop_parse_value(libzfs_handle_t *, nvpair_t *, int, zfs_type_t,
+ nvlist_t *, char **, uint64_t *, const char *);
+int zprop_expand_list(libzfs_handle_t *hdl, zprop_list_t **plp,
+ zfs_type_t type);
+
+/*
+ * Use this changelist_gather() flag to force attempting mounts
+ * on each change node regardless of whether or not it is currently
+ * mounted.
+ */
+#define CL_GATHER_MOUNT_ALWAYS 1
typedef struct prop_changelist prop_changelist_t;
int zcmd_alloc_dst_nvlist(libzfs_handle_t *, zfs_cmd_t *, size_t);
-int zcmd_write_src_nvlist(libzfs_handle_t *, zfs_cmd_t *, nvlist_t *, size_t *);
+int zcmd_write_src_nvlist(libzfs_handle_t *, zfs_cmd_t *, nvlist_t *);
+int zcmd_write_conf_nvlist(libzfs_handle_t *, zfs_cmd_t *, nvlist_t *);
int zcmd_expand_dst_nvlist(libzfs_handle_t *, zfs_cmd_t *);
int zcmd_read_dst_nvlist(libzfs_handle_t *, zfs_cmd_t *, nvlist_t **);
void zcmd_free_nvlists(zfs_cmd_t *);
@@ -122,13 +155,15 @@ void zcmd_free_nvlists(zfs_cmd_t *);
int changelist_prefix(prop_changelist_t *);
int changelist_postfix(prop_changelist_t *);
void changelist_rename(prop_changelist_t *, const char *, const char *);
-void changelist_remove(zfs_handle_t *, prop_changelist_t *);
+void changelist_remove(prop_changelist_t *, const char *);
void changelist_free(prop_changelist_t *);
-prop_changelist_t *changelist_gather(zfs_handle_t *, zfs_prop_t, int);
-int changelist_unshare(prop_changelist_t *);
+prop_changelist_t *changelist_gather(zfs_handle_t *, zfs_prop_t, int, int);
+int changelist_unshare(prop_changelist_t *, zfs_share_proto_t *);
int changelist_haszonedchild(prop_changelist_t *);
void remove_mountpoint(zfs_handle_t *);
+int create_parents(libzfs_handle_t *, char *, int);
+boolean_t isa_child_of(const char *dataset, const char *parent);
zfs_handle_t *make_dataset_handle(libzfs_handle_t *, const char *);
@@ -137,10 +172,23 @@ int zpool_open_silent(libzfs_handle_t *, const char *, zpool_handle_t **);
int zvol_create_link(libzfs_handle_t *, const char *);
int zvol_remove_link(libzfs_handle_t *, const char *);
int zpool_iter_zvol(zpool_handle_t *, int (*)(const char *, void *), void *);
+boolean_t zpool_name_valid(libzfs_handle_t *, boolean_t, const char *);
void namespace_clear(libzfs_handle_t *);
+/*
+ * libshare (sharemgr) interfaces used internally.
+ */
+
+extern int zfs_init_libshare(libzfs_handle_t *, int);
+extern void zfs_uninit_libshare(libzfs_handle_t *);
+extern int zfs_parse_options(char *, zfs_share_proto_t);
+
+extern int zfs_unshare_proto(zfs_handle_t *zhp,
+ const char *, zfs_share_proto_t *);
+
#ifdef __FreeBSD__
+
/*
* This is FreeBSD version of ioctl, because Solaris' ioctl() updates
* zc_nvlist_dst_size even if an error is returned, on FreeBSD if an
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c
index 1c77045..166c831 100644
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c
+++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -213,11 +213,13 @@ add_config(libzfs_handle_t *hdl, pool_list_t *pl, const char *path,
name_entry_t *ne;
/*
- * If this is a hot spare not currently in use, add it to the list of
- * names to translate, but don't do anything else.
+ * If this is a hot spare not currently in use or level 2 cache
+ * device, add it to the list of names to translate, but don't do
+ * anything else.
*/
if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE,
- &state) == 0 && state == POOL_STATE_SPARE &&
+ &state) == 0 &&
+ (state == POOL_STATE_SPARE || state == POOL_STATE_L2CACHE) &&
nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, &vdev_guid) == 0) {
if ((ne = zfs_alloc(hdl, sizeof (name_entry_t))) == NULL)
return (-1);
@@ -361,6 +363,46 @@ pool_active(libzfs_handle_t *hdl, const char *name, uint64_t guid,
return (0);
}
+static nvlist_t *
+refresh_config(libzfs_handle_t *hdl, nvlist_t *config)
+{
+ nvlist_t *nvl;
+ zfs_cmd_t zc = { 0 };
+ int err;
+
+ if (zcmd_write_conf_nvlist(hdl, &zc, config) != 0)
+ return (NULL);
+
+ if (zcmd_alloc_dst_nvlist(hdl, &zc,
+ zc.zc_nvlist_conf_size * 2) != 0) {
+ zcmd_free_nvlists(&zc);
+ return (NULL);
+ }
+
+ while ((err = ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_TRYIMPORT,
+ &zc)) != 0 && errno == ENOMEM) {
+ if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
+ zcmd_free_nvlists(&zc);
+ return (NULL);
+ }
+ }
+
+ if (err) {
+ (void) zpool_standard_error(hdl, errno,
+ dgettext(TEXT_DOMAIN, "cannot discover pools"));
+ zcmd_free_nvlists(&zc);
+ return (NULL);
+ }
+
+ if (zcmd_read_dst_nvlist(hdl, &zc, &nvl) != 0) {
+ zcmd_free_nvlists(&zc);
+ return (NULL);
+ }
+
+ zcmd_free_nvlists(&zc);
+ return (nvl);
+}
+
/*
* Convert our list of pools into the definitive set of configurations. We
* start by picking the best config for each toplevel vdev. Once that's done,
@@ -369,26 +411,25 @@ pool_active(libzfs_handle_t *hdl, const char *name, uint64_t guid,
* return to the user.
*/
static nvlist_t *
-get_configs(libzfs_handle_t *hdl, pool_list_t *pl)
+get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok)
{
pool_entry_t *pe;
vdev_entry_t *ve;
config_entry_t *ce;
nvlist_t *ret = NULL, *config = NULL, *tmp, *nvtop, *nvroot;
- nvlist_t **spares;
- uint_t i, nspares;
+ nvlist_t **spares, **l2cache;
+ uint_t i, nspares, nl2cache;
boolean_t config_seen;
uint64_t best_txg;
char *name, *hostname;
- zfs_cmd_t zc = { 0 };
uint64_t version, guid;
- size_t len;
- int err;
uint_t children = 0;
nvlist_t **child = NULL;
uint_t c;
boolean_t isactive;
uint64_t hostid;
+ nvlist_t *nvl;
+ boolean_t found_one = B_FALSE;
if (nvlist_alloc(&ret, 0, 0) != 0)
goto nomem;
@@ -571,6 +612,13 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl)
nvlist_free(nvroot);
/*
+ * zdb uses this path to report on active pools that were
+ * imported or created using -R.
+ */
+ if (active_ok)
+ goto add_pool;
+
+ /*
* Determine if this pool is currently active, in which case we
* can't actually import it.
*/
@@ -588,41 +636,11 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl)
continue;
}
- /*
- * Try to do the import in order to get vdev state.
- */
- if (zcmd_write_src_nvlist(hdl, &zc, config, &len) != 0)
+ if ((nvl = refresh_config(hdl, config)) == NULL)
goto error;
nvlist_free(config);
- config = NULL;
-
- if (zcmd_alloc_dst_nvlist(hdl, &zc, len * 2) != 0) {
- zcmd_free_nvlists(&zc);
- goto error;
- }
-
- while ((err = ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_TRYIMPORT,
- &zc)) != 0 && errno == ENOMEM) {
- if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
- zcmd_free_nvlists(&zc);
- goto error;
- }
- }
-
- if (err) {
- (void) zpool_standard_error(hdl, errno,
- dgettext(TEXT_DOMAIN, "cannot discover pools"));
- zcmd_free_nvlists(&zc);
- goto error;
- }
-
- if (zcmd_read_dst_nvlist(hdl, &zc, &config) != 0) {
- zcmd_free_nvlists(&zc);
- goto error;
- }
-
- zcmd_free_nvlists(&zc);
+ config = nvl;
/*
* Go through and update the paths for spares, now that we have
@@ -639,6 +657,17 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl)
}
/*
+ * Update the paths for l2cache devices.
+ */
+ if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE,
+ &l2cache, &nl2cache) == 0) {
+ for (i = 0; i < nl2cache; i++) {
+ if (fix_paths(l2cache[i], pl->names) != 0)
+ goto nomem;
+ }
+ }
+
+ /*
* Restore the original information read from the actual label.
*/
(void) nvlist_remove(config, ZPOOL_CONFIG_HOSTID,
@@ -652,6 +681,7 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl)
hostname) == 0);
}
+add_pool:
/*
* Add this pool to the list of configs.
*/
@@ -660,10 +690,16 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl)
if (nvlist_add_nvlist(ret, name, config) != 0)
goto nomem;
+ found_one = B_TRUE;
nvlist_free(config);
config = NULL;
}
+ if (!found_one) {
+ nvlist_free(ret);
+ ret = NULL;
+ }
+
return (ret);
nomem:
@@ -682,8 +718,9 @@ error:
* Return the offset of the given label.
*/
static uint64_t
-label_offset(size_t size, int l)
+label_offset(uint64_t size, int l)
{
+ ASSERT(P2PHASE_TYPED(size, sizeof (vdev_label_t), uint64_t) == 0);
return (l * sizeof (vdev_label_t) + (l < VDEV_LABELS / 2 ?
0 : size - VDEV_LABELS * sizeof (vdev_label_t)));
}
@@ -698,19 +735,20 @@ zpool_read_label(int fd, nvlist_t **config)
struct stat64 statbuf;
int l;
vdev_label_t *label;
- uint64_t state, txg;
+ uint64_t state, txg, size;
*config = NULL;
if (fstat64(fd, &statbuf) == -1)
return (0);
+ size = P2ALIGN_TYPED(statbuf.st_size, sizeof (vdev_label_t), uint64_t);
if ((label = malloc(sizeof (vdev_label_t))) == NULL)
return (-1);
for (l = 0; l < VDEV_LABELS; l++) {
- if (pread(fd, label, sizeof (vdev_label_t),
- label_offset(statbuf.st_size, l)) != sizeof (vdev_label_t))
+ if (pread64(fd, label, sizeof (vdev_label_t),
+ label_offset(size, l)) != sizeof (vdev_label_t))
continue;
if (nvlist_unpack(label->vl_vdev_phys.vp_nvlist,
@@ -718,12 +756,12 @@ zpool_read_label(int fd, nvlist_t **config)
continue;
if (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_STATE,
- &state) != 0 || state > POOL_STATE_SPARE) {
+ &state) != 0 || state > POOL_STATE_L2CACHE) {
nvlist_free(*config);
continue;
}
- if (state != POOL_STATE_SPARE &&
+ if (state != POOL_STATE_SPARE && state != POOL_STATE_L2CACHE &&
(nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_TXG,
&txg) != 0 || txg == 0)) {
nvlist_free(*config);
@@ -739,31 +777,20 @@ zpool_read_label(int fd, nvlist_t **config)
return (0);
}
-/*
- * Given a list of directories to search, find all pools stored on disk. This
- * includes partial pools which are not available to import. If no args are
- * given (argc is 0), then the default directory (/dev) is searched.
- */
-nvlist_t *
-zpool_find_import(libzfs_handle_t *hdl, int argc, char **argv)
+static int
+geom_find_import(libzfs_handle_t *hdl, pool_list_t *pools)
{
- int i;
char path[MAXPATHLEN];
- nvlist_t *ret = NULL, *config;
- int fd;
- pool_list_t pools = { 0 };
- pool_entry_t *pe, *penext;
- vdev_entry_t *ve, *venext;
- config_entry_t *ce, *cenext;
- name_entry_t *ne, *nenext;
struct gmesh mesh;
struct gclass *mp;
struct ggeom *gp;
struct gprovider *pp;
+ nvlist_t *config;
+ int fd, ret = 0;
/*
* Go through and read the label configuration information from every
- * possible device, organizing the information according to pool GUID
+ * GEOM provider, organizing the information according to pool GUID
* and toplevel GUID.
*/
@@ -773,32 +800,183 @@ zpool_find_import(libzfs_handle_t *hdl, int argc, char **argv)
LIST_FOREACH(mp, &mesh.lg_class, lg_class) {
LIST_FOREACH(gp, &mp->lg_geom, lg_geom) {
LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
+ if ((fd = g_open(pp->lg_name, 0)) < 0)
+ continue;
(void) snprintf(path, sizeof (path), "%s%s",
_PATH_DEV, pp->lg_name);
- if ((fd = open64(path, O_RDONLY)) < 0)
- continue;
-
if ((zpool_read_label(fd, &config)) != 0) {
+ (void) g_close(fd);
(void) no_memory(hdl);
goto error;
}
- (void) close(fd);
+ (void) g_close(fd);
if (config == NULL)
continue;
- if (add_config(hdl, &pools, path, config) != 0)
+ if (add_config(hdl, pools, path, config) != 0) {
+ ret = -1;
goto error;
+ }
}
}
}
-
+error:
geom_deletetree(&mesh);
+ return (ret);
+}
+
+/*
+ * Given a list of directories to search, find all pools stored on disk. This
+ * includes partial pools which are not available to import. If no args are
+ * given (argc is 0), then the default directory (/dev/dsk) is searched.
+ * poolname or guid (but not both) are provided by the caller when trying
+ * to import a specific pool.
+ */
+static nvlist_t *
+zpool_find_import_impl(libzfs_handle_t *hdl, int argc, char **argv,
+ boolean_t active_ok, char *poolname, uint64_t guid)
+{
+ int i;
+ DIR *dirp = NULL;
+ struct dirent64 *dp;
+ char path[MAXPATHLEN];
+ char *end;
+ size_t pathleft;
+ struct stat64 statbuf;
+ nvlist_t *ret = NULL, *config;
+ static char *default_dir = "/dev/dsk";
+ int fd;
+ pool_list_t pools = { 0 };
+ pool_entry_t *pe, *penext;
+ vdev_entry_t *ve, *venext;
+ config_entry_t *ce, *cenext;
+ name_entry_t *ne, *nenext;
+
+ verify(poolname == NULL || guid == 0);
+
+ if (argc == 0) {
+ argc = 1;
+ argv = &default_dir;
+ }
+
+ /*
+ * Go through and read the label configuration information from every
+ * possible device, organizing the information according to pool GUID
+ * and toplevel GUID.
+ */
+ for (i = 0; i < argc; i++) {
+ char *rdsk;
+ int dfd;
+
+ /* use realpath to normalize the path */
+ if (realpath(argv[i], path) == 0) {
+ (void) zfs_error_fmt(hdl, EZFS_BADPATH,
+ dgettext(TEXT_DOMAIN, "cannot open '%s'"),
+ argv[i]);
+ goto error;
+ }
+ end = &path[strlen(path)];
+ *end++ = '/';
+ *end = 0;
+ pathleft = &path[sizeof (path)] - end;
- ret = get_configs(hdl, &pools);
+ if (strcmp(argv[i], default_dir) == 0) {
+ geom_find_import(hdl, &pools);
+ continue;
+ }
+
+ /*
+ * Using raw devices instead of block devices when we're
+ * reading the labels skips a bunch of slow operations during
+ * close(2) processing, so we replace /dev/dsk with /dev/rdsk.
+ */
+ if (strcmp(path, "/dev/dsk/") == 0)
+ rdsk = "/dev/rdsk/";
+ else
+ rdsk = path;
+
+ if ((dirp = opendir(rdsk)) == NULL) {
+ zfs_error_aux(hdl, strerror(errno));
+ (void) zfs_error_fmt(hdl, EZFS_BADPATH,
+ dgettext(TEXT_DOMAIN, "cannot open '%s'"),
+ rdsk);
+ goto error;
+ }
+
+ /*
+ * This is not MT-safe, but we have no MT consumers of libzfs
+ */
+ while ((dp = readdir64(dirp)) != NULL) {
+ const char *name = dp->d_name;
+ if (name[0] == '.' &&
+ (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
+ continue;
+
+ (void) snprintf(path, sizeof (path), "%s/%s", rdsk,
+ dp->d_name);
+
+ if ((fd = open64(path, O_RDONLY)) < 0)
+ continue;
+
+ /*
+ * Ignore failed stats. We only want regular
+ * files, character devs and block devs.
+ */
+ if (fstat64(fd, &statbuf) != 0 ||
+ (!S_ISREG(statbuf.st_mode) &&
+ !S_ISCHR(statbuf.st_mode) &&
+ !S_ISBLK(statbuf.st_mode))) {
+ (void) close(fd);
+ continue;
+ }
+
+ if ((zpool_read_label(fd, &config)) != 0) {
+ (void) close(fd);
+ (void) no_memory(hdl);
+ goto error;
+ }
+
+ (void) close(fd);
+
+ if (config != NULL) {
+ boolean_t matched = B_TRUE;
+
+ if (poolname != NULL) {
+ char *pname;
+
+ matched = nvlist_lookup_string(config,
+ ZPOOL_CONFIG_POOL_NAME,
+ &pname) == 0 &&
+ strcmp(poolname, pname) == 0;
+ } else if (guid != 0) {
+ uint64_t this_guid;
+
+ matched = nvlist_lookup_uint64(config,
+ ZPOOL_CONFIG_POOL_GUID,
+ &this_guid) == 0 &&
+ guid == this_guid;
+ }
+ if (!matched) {
+ nvlist_free(config);
+ config = NULL;
+ continue;
+ }
+ /* use the non-raw path for the config */
+ (void) strlcpy(end, name, pathleft);
+ if (add_config(hdl, &pools, path, config) != 0)
+ goto error;
+ }
+ }
+
+ (void) closedir(dirp);
+ dirp = NULL;
+ }
+
+ ret = get_configs(hdl, &pools, active_ok);
error:
for (pe = pools.pools; pe != NULL; pe = penext) {
@@ -823,9 +1001,158 @@ error:
free(ne);
}
+ if (dirp)
+ (void) closedir(dirp);
+
return (ret);
}
+nvlist_t *
+zpool_find_import(libzfs_handle_t *hdl, int argc, char **argv)
+{
+ return (zpool_find_import_impl(hdl, argc, argv, B_FALSE, NULL, 0));
+}
+
+nvlist_t *
+zpool_find_import_byname(libzfs_handle_t *hdl, int argc, char **argv,
+ char *pool)
+{
+ return (zpool_find_import_impl(hdl, argc, argv, B_FALSE, pool, 0));
+}
+
+nvlist_t *
+zpool_find_import_byguid(libzfs_handle_t *hdl, int argc, char **argv,
+ uint64_t guid)
+{
+ return (zpool_find_import_impl(hdl, argc, argv, B_FALSE, NULL, guid));
+}
+
+nvlist_t *
+zpool_find_import_activeok(libzfs_handle_t *hdl, int argc, char **argv)
+{
+ return (zpool_find_import_impl(hdl, argc, argv, B_TRUE, NULL, 0));
+}
+
+/*
+ * Given a cache file, return the contents as a list of importable pools.
+ * poolname or guid (but not both) are provided by the caller when trying
+ * to import a specific pool.
+ */
+nvlist_t *
+zpool_find_import_cached(libzfs_handle_t *hdl, const char *cachefile,
+ char *poolname, uint64_t guid)
+{
+ char *buf;
+ int fd;
+ struct stat64 statbuf;
+ nvlist_t *raw, *src, *dst;
+ nvlist_t *pools;
+ nvpair_t *elem;
+ char *name;
+ uint64_t this_guid;
+ boolean_t active;
+
+ verify(poolname == NULL || guid == 0);
+
+ if ((fd = open(cachefile, O_RDONLY)) < 0) {
+ zfs_error_aux(hdl, "%s", strerror(errno));
+ (void) zfs_error(hdl, EZFS_BADCACHE,
+ dgettext(TEXT_DOMAIN, "failed to open cache file"));
+ return (NULL);
+ }
+
+ if (fstat64(fd, &statbuf) != 0) {
+ zfs_error_aux(hdl, "%s", strerror(errno));
+ (void) close(fd);
+ (void) zfs_error(hdl, EZFS_BADCACHE,
+ dgettext(TEXT_DOMAIN, "failed to get size of cache file"));
+ return (NULL);
+ }
+
+ if ((buf = zfs_alloc(hdl, statbuf.st_size)) == NULL) {
+ (void) close(fd);
+ return (NULL);
+ }
+
+ if (read(fd, buf, statbuf.st_size) != statbuf.st_size) {
+ (void) close(fd);
+ free(buf);
+ (void) zfs_error(hdl, EZFS_BADCACHE,
+ dgettext(TEXT_DOMAIN,
+ "failed to read cache file contents"));
+ return (NULL);
+ }
+
+ (void) close(fd);
+
+ if (nvlist_unpack(buf, statbuf.st_size, &raw, 0) != 0) {
+ free(buf);
+ (void) zfs_error(hdl, EZFS_BADCACHE,
+ dgettext(TEXT_DOMAIN,
+ "invalid or corrupt cache file contents"));
+ return (NULL);
+ }
+
+ free(buf);
+
+ /*
+ * Go through and get the current state of the pools and refresh their
+ * state.
+ */
+ if (nvlist_alloc(&pools, 0, 0) != 0) {
+ (void) no_memory(hdl);
+ nvlist_free(raw);
+ return (NULL);
+ }
+
+ elem = NULL;
+ while ((elem = nvlist_next_nvpair(raw, elem)) != NULL) {
+ verify(nvpair_value_nvlist(elem, &src) == 0);
+
+ verify(nvlist_lookup_string(src, ZPOOL_CONFIG_POOL_NAME,
+ &name) == 0);
+ if (poolname != NULL && strcmp(poolname, name) != 0)
+ continue;
+
+ verify(nvlist_lookup_uint64(src, ZPOOL_CONFIG_POOL_GUID,
+ &this_guid) == 0);
+ if (guid != 0) {
+ verify(nvlist_lookup_uint64(src, ZPOOL_CONFIG_POOL_GUID,
+ &this_guid) == 0);
+ if (guid != this_guid)
+ continue;
+ }
+
+ if (pool_active(hdl, name, this_guid, &active) != 0) {
+ nvlist_free(raw);
+ nvlist_free(pools);
+ return (NULL);
+ }
+
+ if (active)
+ continue;
+
+ if ((dst = refresh_config(hdl, src)) == NULL) {
+ nvlist_free(raw);
+ nvlist_free(pools);
+ return (NULL);
+ }
+
+ if (nvlist_add_nvlist(pools, nvpair_name(elem), dst) != 0) {
+ (void) no_memory(hdl);
+ nvlist_free(dst);
+ nvlist_free(raw);
+ nvlist_free(pools);
+ return (NULL);
+ }
+ nvlist_free(dst);
+ }
+
+ nvlist_free(raw);
+ return (pools);
+}
+
+
boolean_t
find_guid(nvlist_t *nv, uint64_t guid)
{
@@ -847,27 +1174,28 @@ find_guid(nvlist_t *nv, uint64_t guid)
return (B_FALSE);
}
-typedef struct spare_cbdata {
+typedef struct aux_cbdata {
+ const char *cb_type;
uint64_t cb_guid;
zpool_handle_t *cb_zhp;
-} spare_cbdata_t;
+} aux_cbdata_t;
static int
-find_spare(zpool_handle_t *zhp, void *data)
+find_aux(zpool_handle_t *zhp, void *data)
{
- spare_cbdata_t *cbp = data;
- nvlist_t **spares;
- uint_t i, nspares;
+ aux_cbdata_t *cbp = data;
+ nvlist_t **list;
+ uint_t i, count;
uint64_t guid;
nvlist_t *nvroot;
verify(nvlist_lookup_nvlist(zhp->zpool_config, ZPOOL_CONFIG_VDEV_TREE,
&nvroot) == 0);
- if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
- &spares, &nspares) == 0) {
- for (i = 0; i < nspares; i++) {
- verify(nvlist_lookup_uint64(spares[i],
+ if (nvlist_lookup_nvlist_array(nvroot, cbp->cb_type,
+ &list, &count) == 0) {
+ for (i = 0; i < count; i++) {
+ verify(nvlist_lookup_uint64(list[i],
ZPOOL_CONFIG_GUID, &guid) == 0);
if (guid == cbp->cb_guid) {
cbp->cb_zhp = zhp;
@@ -896,7 +1224,7 @@ zpool_in_use(libzfs_handle_t *hdl, int fd, pool_state_t *state, char **namestr,
zpool_handle_t *zhp;
nvlist_t *pool_config;
uint64_t stateval, isspare;
- spare_cbdata_t cb = { 0 };
+ aux_cbdata_t cb = { 0 };
boolean_t isactive;
*inuse = B_FALSE;
@@ -914,7 +1242,7 @@ zpool_in_use(libzfs_handle_t *hdl, int fd, pool_state_t *state, char **namestr,
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID,
&vdev_guid) == 0);
- if (stateval != POOL_STATE_SPARE) {
+ if (stateval != POOL_STATE_SPARE && stateval != POOL_STATE_L2CACHE) {
verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
&name) == 0);
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
@@ -993,7 +1321,24 @@ zpool_in_use(libzfs_handle_t *hdl, int fd, pool_state_t *state, char **namestr,
*/
cb.cb_zhp = NULL;
cb.cb_guid = vdev_guid;
- if (zpool_iter(hdl, find_spare, &cb) == 1) {
+ cb.cb_type = ZPOOL_CONFIG_SPARES;
+ if (zpool_iter(hdl, find_aux, &cb) == 1) {
+ name = (char *)zpool_get_name(cb.cb_zhp);
+ ret = TRUE;
+ } else {
+ ret = FALSE;
+ }
+ break;
+
+ case POOL_STATE_L2CACHE:
+
+ /*
+ * Check if any pool is currently using this l2cache device.
+ */
+ cb.cb_zhp = NULL;
+ cb.cb_guid = vdev_guid;
+ cb.cb_type = ZPOOL_CONFIG_L2CACHE;
+ if (zpool_iter(hdl, find_aux, &cb) == 1) {
name = (char *)zpool_get_name(cb.cb_zhp);
ret = TRUE;
} else {
@@ -1008,6 +1353,8 @@ zpool_in_use(libzfs_handle_t *hdl, int fd, pool_state_t *state, char **namestr,
if (ret) {
if ((*namestr = zfs_strdup(hdl, name)) == NULL) {
+ if (cb.cb_zhp)
+ zpool_close(cb.cb_zhp);
nvlist_free(config);
return (-1);
}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_mount.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_mount.c
index b4bc945..84a8a57 100644
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_mount.c
+++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_mount.c
@@ -20,12 +20,10 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* Routines to manage ZFS mounts. We separate all the nasty routines that have
* to deal with the OS. The following functions are the main entry points --
@@ -45,11 +43,17 @@
* zfs_unshare()
*
* zfs_is_shared_nfs()
- * zfs_share_nfs()
- * zfs_unshare_nfs()
- * zfs_unshareall_nfs()
+ * zfs_is_shared_smb()
* zfs_is_shared_iscsi()
+ * zfs_share_proto()
+ * zfs_shareall();
* zfs_share_iscsi()
+ * zfs_unshare_nfs()
+ * zfs_unshare_smb()
+ * zfs_unshareall_nfs()
+ * zfs_unshareall_smb()
+ * zfs_unshareall()
+ * zfs_unshareall_bypath()
* zfs_unshare_iscsi()
*
* The following functions are available for pool consumers, and will
@@ -78,9 +82,49 @@
#include "libzfs_impl.h"
+#include <libshare.h>
+
+#define MAXISALEN 257 /* based on sysinfo(2) man page */
+
+static int zfs_share_proto(zfs_handle_t *, zfs_share_proto_t *);
+zfs_share_type_t zfs_is_shared_proto(zfs_handle_t *, char **,
+ zfs_share_proto_t);
+
static int (*iscsitgt_zfs_share)(const char *);
static int (*iscsitgt_zfs_unshare)(const char *);
static int (*iscsitgt_zfs_is_shared)(const char *);
+static int (*iscsitgt_svc_online)();
+
+/*
+ * The share protocols table must be in the same order as the zfs_share_prot_t
+ * enum in libzfs_impl.h
+ */
+typedef struct {
+ zfs_prop_t p_prop;
+ char *p_name;
+ int p_share_err;
+ int p_unshare_err;
+} proto_table_t;
+
+proto_table_t proto_table[PROTO_END] = {
+ {ZFS_PROP_SHARENFS, "nfs", EZFS_SHARENFSFAILED, EZFS_UNSHARENFSFAILED},
+ {ZFS_PROP_SHARESMB, "smb", EZFS_SHARESMBFAILED, EZFS_UNSHARESMBFAILED},
+};
+
+zfs_share_proto_t nfs_only[] = {
+ PROTO_NFS,
+ PROTO_END
+};
+
+zfs_share_proto_t smb_only[] = {
+ PROTO_SMB,
+ PROTO_END
+};
+zfs_share_proto_t share_all_proto[] = {
+ PROTO_NFS,
+ PROTO_SMB,
+ PROTO_END
+};
#pragma init(zfs_iscsi_init)
static void
@@ -95,37 +139,65 @@ zfs_iscsi_init(void)
(iscsitgt_zfs_unshare = (int (*)(const char *))dlsym(libiscsitgt,
"iscsitgt_zfs_unshare")) == NULL ||
(iscsitgt_zfs_is_shared = (int (*)(const char *))dlsym(libiscsitgt,
- "iscsitgt_zfs_is_shared")) == NULL) {
+ "iscsitgt_zfs_is_shared")) == NULL ||
+ (iscsitgt_svc_online = (int (*)(const char *))dlsym(libiscsitgt,
+ "iscsitgt_svc_online")) == NULL) {
iscsitgt_zfs_share = NULL;
iscsitgt_zfs_unshare = NULL;
iscsitgt_zfs_is_shared = NULL;
+ iscsitgt_svc_online = NULL;
}
}
/*
- * Search the sharetab for the given mountpoint, returning true if it is found.
+ * Search the sharetab for the given mountpoint and protocol, returning
+ * a zfs_share_type_t value.
*/
-static boolean_t
-is_shared(libzfs_handle_t *hdl, const char *mountpoint)
+static zfs_share_type_t
+is_shared(libzfs_handle_t *hdl, const char *mountpoint, zfs_share_proto_t proto)
{
char buf[MAXPATHLEN], *tab;
+ char *ptr;
if (hdl->libzfs_sharetab == NULL)
- return (0);
+ return (SHARED_NOT_SHARED);
(void) fseek(hdl->libzfs_sharetab, 0, SEEK_SET);
while (fgets(buf, sizeof (buf), hdl->libzfs_sharetab) != NULL) {
/* the mountpoint is the first entry on each line */
- if ((tab = strchr(buf, '\t')) != NULL) {
+ if ((tab = strchr(buf, '\t')) == NULL)
+ continue;
+
+ *tab = '\0';
+ if (strcmp(buf, mountpoint) == 0) {
+ /*
+ * the protocol field is the third field
+ * skip over second field
+ */
+ ptr = ++tab;
+ if ((tab = strchr(ptr, '\t')) == NULL)
+ continue;
+ ptr = ++tab;
+ if ((tab = strchr(ptr, '\t')) == NULL)
+ continue;
*tab = '\0';
- if (strcmp(buf, mountpoint) == 0)
- return (B_TRUE);
+ if (strcmp(ptr,
+ proto_table[proto].p_name) == 0) {
+ switch (proto) {
+ case PROTO_NFS:
+ return (SHARED_NFS);
+ case PROTO_SMB:
+ return (SHARED_SMB);
+ default:
+ return (0);
+ }
+ }
}
}
- return (B_FALSE);
+ return (SHARED_NOT_SHARED);
}
#if 0
@@ -198,10 +270,10 @@ zfs_is_mounted(zfs_handle_t *zhp, char **where)
*/
static boolean_t
zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen,
- zfs_source_t *source)
+ zprop_source_t *source)
{
char sourceloc[ZFS_MAXNAMELEN];
- zfs_source_t sourcetype;
+ zprop_source_t sourcetype;
if (!zfs_prop_valid_for_type(ZFS_PROP_MOUNTPOINT, zhp->zfs_type))
return (B_FALSE);
@@ -213,7 +285,7 @@ zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen,
strcmp(buf, ZFS_MOUNTPOINT_LEGACY) == 0)
return (B_FALSE);
- if (!zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT))
+ if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_OFF)
return (B_FALSE);
if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) &&
@@ -281,11 +353,15 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
* from mount(), and they're well-understood. We pick a few
* common ones to improve upon.
*/
- if (errno == EBUSY)
+ if (errno == EBUSY) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"mountpoint or dataset is busy"));
- else
+ } else if (errno == EPERM) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "Insufficient privileges"));
+ } else {
zfs_error_aux(hdl, strerror(errno));
+ }
return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED,
dgettext(TEXT_DOMAIN, "cannot mount '%s'"),
@@ -318,6 +394,7 @@ int
zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags)
{
struct mnttab search = { 0 }, entry;
+ char *mntpt = NULL;
/* check to see if need to unmount the filesystem */
search.mnt_special = zhp->zfs_name;
@@ -326,15 +403,29 @@ zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags)
if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) {
+ /*
+ * mountpoint may have come from a call to
+ * getmnt/getmntany if it isn't NULL. If it is NULL,
+ * we know it comes from getmntany which can then get
+ * overwritten later. We strdup it to play it safe.
+ */
if (mountpoint == NULL)
- mountpoint = entry.mnt_mountp;
+ mntpt = zfs_strdup(zhp->zfs_hdl, entry.mnt_mountp);
+ else
+ mntpt = zfs_strdup(zhp->zfs_hdl, mountpoint);
/*
* Unshare and unmount the filesystem
*/
- if (zfs_unshare_nfs(zhp, mountpoint) != 0 ||
- unmount_one(zhp->zfs_hdl, mountpoint, flags) != 0)
+ if (zfs_unshare_proto(zhp, mntpt, share_all_proto) != 0)
+ return (-1);
+
+ if (unmount_one(zhp->zfs_hdl, mntpt, flags) != 0) {
+ free(mntpt);
+ (void) zfs_shareall(zhp);
return (-1);
+ }
+ free(mntpt);
}
return (0);
@@ -351,7 +442,7 @@ zfs_unmountall(zfs_handle_t *zhp, int flags)
prop_changelist_t *clp;
int ret;
- clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, flags);
+ clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, 0, flags);
if (clp == NULL)
return (-1);
@@ -364,10 +455,17 @@ zfs_unmountall(zfs_handle_t *zhp, int flags)
boolean_t
zfs_is_shared(zfs_handle_t *zhp)
{
+ zfs_share_type_t rc = 0;
+ zfs_share_proto_t *curr_proto;
+
if (ZFS_IS_VOLUME(zhp))
return (zfs_is_shared_iscsi(zhp));
- return (zfs_is_shared_nfs(zhp, NULL));
+ for (curr_proto = share_all_proto; *curr_proto != PROTO_END;
+ curr_proto++)
+ rc |= zfs_is_shared_proto(zhp, NULL, *curr_proto);
+
+ return (rc ? B_TRUE : B_FALSE);
}
int
@@ -376,7 +474,7 @@ zfs_share(zfs_handle_t *zhp)
if (ZFS_IS_VOLUME(zhp))
return (zfs_share_iscsi(zhp));
- return (zfs_share_nfs(zhp));
+ return (zfs_share_proto(zhp, share_all_proto));
}
int
@@ -385,139 +483,399 @@ zfs_unshare(zfs_handle_t *zhp)
if (ZFS_IS_VOLUME(zhp))
return (zfs_unshare_iscsi(zhp));
- return (zfs_unshare_nfs(zhp, NULL));
+ return (zfs_unshareall(zhp));
}
/*
* Check to see if the filesystem is currently shared.
*/
-boolean_t
-zfs_is_shared_nfs(zfs_handle_t *zhp, char **where)
+zfs_share_type_t
+zfs_is_shared_proto(zfs_handle_t *zhp, char **where, zfs_share_proto_t proto)
{
char *mountpoint;
+ zfs_share_type_t rc;
if (!zfs_is_mounted(zhp, &mountpoint))
- return (B_FALSE);
+ return (SHARED_NOT_SHARED);
- if (is_shared(zhp->zfs_hdl, mountpoint)) {
+ if (rc = is_shared(zhp->zfs_hdl, mountpoint, proto)) {
if (where != NULL)
*where = mountpoint;
else
free(mountpoint);
- return (B_TRUE);
+ return (rc);
} else {
free(mountpoint);
- return (B_FALSE);
+ return (SHARED_NOT_SHARED);
+ }
+}
+
+boolean_t
+zfs_is_shared_nfs(zfs_handle_t *zhp, char **where)
+{
+ return (zfs_is_shared_proto(zhp, where,
+ PROTO_NFS) != SHARED_NOT_SHARED);
+}
+
+boolean_t
+zfs_is_shared_smb(zfs_handle_t *zhp, char **where)
+{
+ return (zfs_is_shared_proto(zhp, where,
+ PROTO_SMB) != SHARED_NOT_SHARED);
+}
+
+/*
+ * Make sure things will work if libshare isn't installed by using
+ * wrapper functions that check to see that the pointers to functions
+ * initialized in _zfs_init_libshare() are actually present.
+ */
+
+#if 0
+static sa_handle_t (*_sa_init)(int);
+static void (*_sa_fini)(sa_handle_t);
+static sa_share_t (*_sa_find_share)(sa_handle_t, char *);
+static int (*_sa_enable_share)(sa_share_t, char *);
+static int (*_sa_disable_share)(sa_share_t, char *);
+static char *(*_sa_errorstr)(int);
+static int (*_sa_parse_legacy_options)(sa_group_t, char *, char *);
+static boolean_t (*_sa_needs_refresh)(sa_handle_t *);
+static libzfs_handle_t *(*_sa_get_zfs_handle)(sa_handle_t);
+static int (*_sa_zfs_process_share)(sa_handle_t, sa_group_t, sa_share_t,
+ char *, char *, zprop_source_t, char *, char *, char *);
+static void (*_sa_update_sharetab_ts)(sa_handle_t);
+#endif
+
+/*
+ * _zfs_init_libshare()
+ *
+ * Find the libshare.so.1 entry points that we use here and save the
+ * values to be used later. This is triggered by the runtime loader.
+ * Make sure the correct ISA version is loaded.
+ */
+
+#pragma init(_zfs_init_libshare)
+static void
+_zfs_init_libshare(void)
+{
+#if 0
+ void *libshare;
+ char path[MAXPATHLEN];
+ char isa[MAXISALEN];
+
+#if defined(_LP64)
+ if (sysinfo(SI_ARCHITECTURE_64, isa, MAXISALEN) == -1)
+ isa[0] = '\0';
+#else
+ isa[0] = '\0';
+#endif
+ (void) snprintf(path, MAXPATHLEN,
+ "/usr/lib/%s/libshare.so.1", isa);
+
+ if ((libshare = dlopen(path, RTLD_LAZY | RTLD_GLOBAL)) != NULL) {
+ _sa_init = (sa_handle_t (*)(int))dlsym(libshare, "sa_init");
+ _sa_fini = (void (*)(sa_handle_t))dlsym(libshare, "sa_fini");
+ _sa_find_share = (sa_share_t (*)(sa_handle_t, char *))
+ dlsym(libshare, "sa_find_share");
+ _sa_enable_share = (int (*)(sa_share_t, char *))dlsym(libshare,
+ "sa_enable_share");
+ _sa_disable_share = (int (*)(sa_share_t, char *))dlsym(libshare,
+ "sa_disable_share");
+ _sa_errorstr = (char *(*)(int))dlsym(libshare, "sa_errorstr");
+ _sa_parse_legacy_options = (int (*)(sa_group_t, char *, char *))
+ dlsym(libshare, "sa_parse_legacy_options");
+ _sa_needs_refresh = (boolean_t (*)(sa_handle_t *))
+ dlsym(libshare, "sa_needs_refresh");
+ _sa_get_zfs_handle = (libzfs_handle_t *(*)(sa_handle_t))
+ dlsym(libshare, "sa_get_zfs_handle");
+ _sa_zfs_process_share = (int (*)(sa_handle_t, sa_group_t,
+ sa_share_t, char *, char *, zprop_source_t, char *,
+ char *, char *))dlsym(libshare, "sa_zfs_process_share");
+ _sa_update_sharetab_ts = (void (*)(sa_handle_t))
+ dlsym(libshare, "sa_update_sharetab_ts");
+ if (_sa_init == NULL || _sa_fini == NULL ||
+ _sa_find_share == NULL || _sa_enable_share == NULL ||
+ _sa_disable_share == NULL || _sa_errorstr == NULL ||
+ _sa_parse_legacy_options == NULL ||
+ _sa_needs_refresh == NULL || _sa_get_zfs_handle == NULL ||
+ _sa_zfs_process_share == NULL ||
+ _sa_update_sharetab_ts == NULL) {
+ _sa_init = NULL;
+ _sa_fini = NULL;
+ _sa_disable_share = NULL;
+ _sa_enable_share = NULL;
+ _sa_errorstr = NULL;
+ _sa_parse_legacy_options = NULL;
+ (void) dlclose(libshare);
+ _sa_needs_refresh = NULL;
+ _sa_get_zfs_handle = NULL;
+ _sa_zfs_process_share = NULL;
+ _sa_update_sharetab_ts = NULL;
+ }
}
+#endif
}
/*
- * Share the given filesystem according to the options in 'sharenfs'. We rely
- * on share(1M) to the dirty work for us.
+ * zfs_init_libshare(zhandle, service)
+ *
+ * Initialize the libshare API if it hasn't already been initialized.
+ * In all cases it returns 0 if it succeeded and an error if not. The
+ * service value is which part(s) of the API to initialize and is a
+ * direct map to the libshare sa_init(service) interface.
*/
int
-zfs_share_nfs(zfs_handle_t *zhp)
+zfs_init_libshare(libzfs_handle_t *zhandle, int service)
{
- char mountpoint[ZFS_MAXPROPLEN];
- char shareopts[ZFS_MAXPROPLEN];
- char buf[MAXPATHLEN];
- FILE *fp;
- libzfs_handle_t *hdl = zhp->zfs_hdl;
+ int ret = SA_OK;
- if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL))
- return (0);
+#if 0
+ if (_sa_init == NULL)
+ ret = SA_CONFIG_ERR;
- /*
- * Return success if there are no share options.
- */
- if (zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts),
- NULL, NULL, 0, B_FALSE) != 0 ||
- strcmp(shareopts, "off") == 0)
- return (0);
+ if (ret == SA_OK && zhandle->libzfs_shareflags & ZFSSHARE_MISS) {
+ /*
+ * We had a cache miss. Most likely it is a new ZFS
+ * dataset that was just created. We want to make sure
+ * so check timestamps to see if a different process
+ * has updated any of the configuration. If there was
+ * some non-ZFS change, we need to re-initialize the
+ * internal cache.
+ */
+ zhandle->libzfs_shareflags &= ~ZFSSHARE_MISS;
+ if (_sa_needs_refresh != NULL &&
+ _sa_needs_refresh(zhandle->libzfs_sharehdl)) {
+ zfs_uninit_libshare(zhandle);
+ zhandle->libzfs_sharehdl = _sa_init(service);
+ }
+ }
- /*
- * If the 'zoned' property is set, then zfs_is_mountable() will have
- * already bailed out if we are in the global zone. But local
- * zones cannot be NFS servers, so we ignore it for local zones as well.
- */
- if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED))
- return (0);
+ if (ret == SA_OK && zhandle && zhandle->libzfs_sharehdl == NULL)
+ zhandle->libzfs_sharehdl = _sa_init(service);
-#ifdef __FreeBSD__
- {
- int error;
+ if (ret == SA_OK && zhandle->libzfs_sharehdl == NULL)
+ ret = SA_NO_MEMORY;
+#endif
- if (strcmp(shareopts, "on") == 0)
- error = fsshare(ZFS_EXPORTS_PATH, mountpoint, "");
- else
- error = fsshare(ZFS_EXPORTS_PATH, mountpoint, shareopts);
- if (error != 0) {
- zfs_error_aux(hdl, "%s", strerror(error));
- (void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED,
- dgettext(TEXT_DOMAIN, "cannot share '%s'"),
- zfs_get_name(zhp));
- return (-1);
+ return (ret);
+}
+
+/*
+ * zfs_uninit_libshare(zhandle)
+ *
+ * Uninitialize the libshare API if it hasn't already been
+ * uninitialized. It is OK to call multiple times.
+ */
+void
+zfs_uninit_libshare(libzfs_handle_t *zhandle)
+{
+ if (zhandle != NULL && zhandle->libzfs_sharehdl != NULL) {
+#if 0
+ if (_sa_fini != NULL)
+ _sa_fini(zhandle->libzfs_sharehdl);
+#endif
+ zhandle->libzfs_sharehdl = NULL;
}
+}
+
+/*
+ * zfs_parse_options(options, proto)
+ *
+ * Call the legacy parse interface to get the protocol specific
+ * options using the NULL arg to indicate that this is a "parse" only.
+ */
+int
+zfs_parse_options(char *options, zfs_share_proto_t proto)
+{
+#if 0
+ if (_sa_parse_legacy_options != NULL) {
+ return (_sa_parse_legacy_options(NULL, options,
+ proto_table[proto].p_name));
}
+ return (SA_CONFIG_ERR);
#else
- /*
- * Invoke the share(1M) command. We always do this, even if it's
- * currently shared, as the options may have changed.
- */
- if (strcmp(shareopts, "on") == 0)
- (void) snprintf(buf, sizeof (buf), "/usr/sbin/share "
- "-F nfs \"%s\" 2>&1", mountpoint);
- else
- (void) snprintf(buf, sizeof (buf), "/usr/sbin/share "
- "-F nfs -o \"%s\" \"%s\" 2>&1", shareopts,
- mountpoint);
+ return (SA_OK);
+#endif
+}
- if ((fp = popen(buf, "r")) == NULL)
- return (zfs_error_fmt(hdl, EZFS_SHARENFSFAILED,
- dgettext(TEXT_DOMAIN, "cannot share '%s'"),
- zfs_get_name(zhp)));
+#if 0
+/*
+ * zfs_sa_find_share(handle, path)
+ *
+ * wrapper around sa_find_share to find a share path in the
+ * configuration.
+ */
+static sa_share_t
+zfs_sa_find_share(sa_handle_t handle, char *path)
+{
+ if (_sa_find_share != NULL)
+ return (_sa_find_share(handle, path));
+ return (NULL);
+}
- /*
- * share(1M) should only produce output if there is some kind
- * of error. All output begins with "share_nfs: ", so we trim
- * this off to get to the real error.
- */
- if (fgets(buf, sizeof (buf), fp) != NULL) {
- char *colon = strchr(buf, ':');
+/*
+ * zfs_sa_enable_share(share, proto)
+ *
+ * Wrapper for sa_enable_share which enables a share for a specified
+ * protocol.
+ */
+static int
+zfs_sa_enable_share(sa_share_t share, char *proto)
+{
+ if (_sa_enable_share != NULL)
+ return (_sa_enable_share(share, proto));
+ return (SA_CONFIG_ERR);
+}
- while (buf[strlen(buf) - 1] == '\n')
- buf[strlen(buf) - 1] = '\0';
+/*
+ * zfs_sa_disable_share(share, proto)
+ *
+ * Wrapper for sa_enable_share which disables a share for a specified
+ * protocol.
+ */
+static int
+zfs_sa_disable_share(sa_share_t share, char *proto)
+{
+ if (_sa_disable_share != NULL)
+ return (_sa_disable_share(share, proto));
+ return (SA_CONFIG_ERR);
+}
+#endif
- if (colon != NULL)
- zfs_error_aux(hdl, colon + 2);
+/*
+ * Share the given filesystem according to the options in the specified
+ * protocol specific properties (sharenfs, sharesmb). We rely
+ * on "libshare" to the dirty work for us.
+ */
+static int
+zfs_share_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto)
+{
+ char mountpoint[ZFS_MAXPROPLEN];
+ char shareopts[ZFS_MAXPROPLEN];
+ char sourcestr[ZFS_MAXPROPLEN];
+ libzfs_handle_t *hdl = zhp->zfs_hdl;
+ zfs_share_proto_t *curr_proto;
+ zprop_source_t sourcetype;
+ int error, ret;
- (void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED,
- dgettext(TEXT_DOMAIN, "cannot share '%s'"),
- zfs_get_name(zhp));
+ if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL))
+ return (0);
- verify(pclose(fp) != 0);
- return (-1);
- }
+ for (curr_proto = proto; *curr_proto != PROTO_END; curr_proto++) {
+ /*
+ * Return success if there are no share options.
+ */
+ if (zfs_prop_get(zhp, proto_table[*curr_proto].p_prop,
+ shareopts, sizeof (shareopts), &sourcetype, sourcestr,
+ ZFS_MAXPROPLEN, B_FALSE) != 0 ||
+ strcmp(shareopts, "off") == 0)
+ continue;
- verify(pclose(fp) == 0);
-#endif
+ /*
+ * If the 'zoned' property is set, then zfs_is_mountable()
+ * will have already bailed out if we are in the global zone.
+ * But local zones cannot be NFS servers, so we ignore it for
+ * local zones as well.
+ */
+ if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED))
+ continue;
+ if (*curr_proto != PROTO_NFS) {
+ fprintf(stderr, "Unsupported share protocol: %d.\n",
+ *curr_proto);
+ continue;
+ }
+
+#if 0
+ share = zfs_sa_find_share(hdl->libzfs_sharehdl, mountpoint);
+ if (share == NULL) {
+ /*
+ * This may be a new file system that was just
+ * created so isn't in the internal cache
+ * (second time through). Rather than
+ * reloading the entire configuration, we can
+ * assume ZFS has done the checking and it is
+ * safe to add this to the internal
+ * configuration.
+ */
+ if (_sa_zfs_process_share(hdl->libzfs_sharehdl,
+ NULL, NULL, mountpoint,
+ proto_table[*curr_proto].p_name, sourcetype,
+ shareopts, sourcestr, zhp->zfs_name) != SA_OK) {
+ (void) zfs_error_fmt(hdl,
+ proto_table[*curr_proto].p_share_err,
+ dgettext(TEXT_DOMAIN, "cannot share '%s'"),
+ zfs_get_name(zhp));
+ return (-1);
+ }
+ hdl->libzfs_shareflags |= ZFSSHARE_MISS;
+ share = zfs_sa_find_share(hdl->libzfs_sharehdl,
+ mountpoint);
+ }
+ if (share != NULL) {
+ int err;
+ err = zfs_sa_enable_share(share,
+ proto_table[*curr_proto].p_name);
+ if (err != SA_OK) {
+ (void) zfs_error_fmt(hdl,
+ proto_table[*curr_proto].p_share_err,
+ dgettext(TEXT_DOMAIN, "cannot share '%s'"),
+ zfs_get_name(zhp));
+ return (-1);
+ }
+ } else
+#else
+ if (strcmp(shareopts, "on") == 0)
+ error = fsshare(ZFS_EXPORTS_PATH, mountpoint, "");
+ else
+ error = fsshare(ZFS_EXPORTS_PATH, mountpoint, shareopts);
+ if (error != 0)
+#endif
+ {
+ (void) zfs_error_fmt(hdl,
+ proto_table[*curr_proto].p_share_err,
+ dgettext(TEXT_DOMAIN, "cannot share '%s'"),
+ zfs_get_name(zhp));
+ return (-1);
+ }
+ }
return (0);
}
+
+int
+zfs_share_nfs(zfs_handle_t *zhp)
+{
+ return (zfs_share_proto(zhp, nfs_only));
+}
+
+int
+zfs_share_smb(zfs_handle_t *zhp)
+{
+ return (zfs_share_proto(zhp, smb_only));
+}
+
+int
+zfs_shareall(zfs_handle_t *zhp)
+{
+ return (zfs_share_proto(zhp, share_all_proto));
+}
+
/*
* Unshare a filesystem by mountpoint.
*/
static int
-unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint)
+unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint,
+ zfs_share_proto_t proto)
{
char buf[MAXPATHLEN];
FILE *fp;
-
-#ifdef __FreeBSD__
- {
int error;
+ if (proto != PROTO_NFS) {
+ fprintf(stderr, "No SMB support in FreeBSD yet.\n");
+ return (EOPNOTSUPP);
+ }
+
error = fsunshare(ZFS_EXPORTS_PATH, mountpoint);
if (error != 0) {
zfs_error_aux(hdl, "%s", strerror(error));
@@ -525,40 +883,6 @@ unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint)
dgettext(TEXT_DOMAIN,
"cannot unshare '%s'"), name));
}
- }
-#else
- (void) snprintf(buf, sizeof (buf),
- "/usr/sbin/unshare \"%s\" 2>&1",
- mountpoint);
-
- if ((fp = popen(buf, "r")) == NULL)
- return (zfs_error_fmt(hdl, EZFS_UNSHARENFSFAILED,
- dgettext(TEXT_DOMAIN,
- "cannot unshare '%s'"), name));
-
- /*
- * unshare(1M) should only produce output if there is
- * some kind of error. All output begins with "unshare
- * nfs: ", so we trim this off to get to the real error.
- */
- if (fgets(buf, sizeof (buf), fp) != NULL) {
- char *colon = strchr(buf, ':');
-
- while (buf[strlen(buf) - 1] == '\n')
- buf[strlen(buf) - 1] = '\0';
-
- if (colon != NULL)
- zfs_error_aux(hdl, colon + 2);
-
- verify(pclose(fp) != 0);
-
- return (zfs_error_fmt(hdl, EZFS_UNSHARENFSFAILED,
- dgettext(TEXT_DOMAIN,
- "cannot unshare '%s'"), name));
- }
-
- verify(pclose(fp) == 0);
-#endif
return (0);
}
@@ -567,47 +891,99 @@ unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint)
* Unshare the given filesystem.
*/
int
-zfs_unshare_nfs(zfs_handle_t *zhp, const char *mountpoint)
+zfs_unshare_proto(zfs_handle_t *zhp, const char *mountpoint,
+ zfs_share_proto_t *proto)
{
struct mnttab search = { 0 }, entry;
+ char *mntpt = NULL;
/* check to see if need to unmount the filesystem */
search.mnt_special = (char *)zfs_get_name(zhp);
search.mnt_fstype = MNTTYPE_ZFS;
rewind(zhp->zfs_hdl->libzfs_mnttab);
+ if (mountpoint != NULL)
+ mntpt = zfs_strdup(zhp->zfs_hdl, mountpoint);
+
if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) {
+ zfs_share_proto_t *curr_proto;
if (mountpoint == NULL)
- mountpoint = entry.mnt_mountp;
+ mntpt = zfs_strdup(zhp->zfs_hdl, entry.mnt_mountp);
- if (is_shared(zhp->zfs_hdl, mountpoint) &&
- unshare_one(zhp->zfs_hdl, zhp->zfs_name, mountpoint) != 0)
- return (-1);
+ for (curr_proto = proto; *curr_proto != PROTO_END;
+ curr_proto++) {
+
+ if (is_shared(zhp->zfs_hdl, mntpt, *curr_proto) &&
+ unshare_one(zhp->zfs_hdl, zhp->zfs_name,
+ mntpt, *curr_proto) != 0) {
+ if (mntpt != NULL)
+ free(mntpt);
+ return (-1);
+ }
+ }
}
+ if (mntpt != NULL)
+ free(mntpt);
return (0);
}
+int
+zfs_unshare_nfs(zfs_handle_t *zhp, const char *mountpoint)
+{
+ return (zfs_unshare_proto(zhp, mountpoint, nfs_only));
+}
+
+int
+zfs_unshare_smb(zfs_handle_t *zhp, const char *mountpoint)
+{
+ return (zfs_unshare_proto(zhp, mountpoint, smb_only));
+}
+
/*
- * Same as zfs_unmountall(), but for NFS unshares.
+ * Same as zfs_unmountall(), but for NFS and SMB unshares.
*/
int
-zfs_unshareall_nfs(zfs_handle_t *zhp)
+zfs_unshareall_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto)
{
prop_changelist_t *clp;
int ret;
- clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0);
+ clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0, 0);
if (clp == NULL)
return (-1);
- ret = changelist_unshare(clp);
+ ret = changelist_unshare(clp, proto);
changelist_free(clp);
return (ret);
}
+int
+zfs_unshareall_nfs(zfs_handle_t *zhp)
+{
+ return (zfs_unshareall_proto(zhp, nfs_only));
+}
+
+int
+zfs_unshareall_smb(zfs_handle_t *zhp)
+{
+ return (zfs_unshareall_proto(zhp, smb_only));
+}
+
+int
+zfs_unshareall(zfs_handle_t *zhp)
+{
+ return (zfs_unshareall_proto(zhp, share_all_proto));
+}
+
+int
+zfs_unshareall_bypath(zfs_handle_t *zhp, const char *mountpoint)
+{
+ return (zfs_unshare_proto(zhp, mountpoint, share_all_proto));
+}
+
/*
* Remove the mountpoint associated with the current dataset, if necessary.
* We only remove the underlying directory if:
@@ -623,14 +999,14 @@ void
remove_mountpoint(zfs_handle_t *zhp)
{
char mountpoint[ZFS_MAXPROPLEN];
- zfs_source_t source;
+ zprop_source_t source;
if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint),
&source))
return;
- if (source == ZFS_SRC_DEFAULT ||
- source == ZFS_SRC_INHERITED) {
+ if (source == ZPROP_SRC_DEFAULT ||
+ source == ZPROP_SRC_INHERITED) {
/*
* Try to remove the directory, silently ignoring any errors.
* The filesystem may have since been removed or moved around,
@@ -644,8 +1020,15 @@ remove_mountpoint(zfs_handle_t *zhp)
boolean_t
zfs_is_shared_iscsi(zfs_handle_t *zhp)
{
- return (iscsitgt_zfs_is_shared != NULL &&
- iscsitgt_zfs_is_shared(zhp->zfs_name) != 0);
+
+ /*
+ * If iscsi deamon isn't running then we aren't shared
+ */
+ if (iscsitgt_svc_online && iscsitgt_svc_online() == 1)
+ return (B_FALSE);
+ else
+ return (iscsitgt_zfs_is_shared != NULL &&
+ iscsitgt_zfs_is_shared(zhp->zfs_name) != 0);
}
int
@@ -665,9 +1048,20 @@ zfs_share_iscsi(zfs_handle_t *zhp)
/* We don't support iSCSI on FreeBSD yet. */
#ifdef TODO
- if (iscsitgt_zfs_share == NULL || iscsitgt_zfs_share(dataset) != 0)
- return (zfs_error_fmt(hdl, EZFS_SHAREISCSIFAILED,
+ if (iscsitgt_zfs_share == NULL || iscsitgt_zfs_share(dataset) != 0) {
+ int error = EZFS_SHAREISCSIFAILED;
+
+ /*
+ * If service isn't availabele and EPERM was
+ * returned then use special error.
+ */
+ if (iscsitgt_svc_online && errno == EPERM &&
+ (iscsitgt_svc_online() != 0))
+ error = EZFS_ISCSISVCUNAVAIL;
+
+ return (zfs_error_fmt(hdl, error,
dgettext(TEXT_DOMAIN, "cannot share '%s'"), dataset));
+ }
#endif
return (0);
@@ -684,7 +1078,7 @@ zfs_unshare_iscsi(zfs_handle_t *zhp)
/*
* Return if the volume is not shared
*/
- if (!zfs_is_shared_iscsi(zhp))
+ if (zfs_is_shared_iscsi(zhp) != SHARED_ISCSI)
return (0);
/*
@@ -692,9 +1086,13 @@ zfs_unshare_iscsi(zfs_handle_t *zhp)
* we should return success in that case.
*/
if (iscsitgt_zfs_unshare == NULL ||
- (iscsitgt_zfs_unshare(dataset) != 0 && errno != ENODEV))
+ (iscsitgt_zfs_unshare(dataset) != 0 && errno != ENODEV)) {
+ if (errno == EPERM)
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "Insufficient privileges to unshare iscsi"));
return (zfs_error_fmt(hdl, EZFS_UNSHAREISCSIFAILED,
dgettext(TEXT_DOMAIN, "cannot unshare '%s'"), dataset));
+ }
#endif
return (0);
@@ -716,6 +1114,11 @@ mount_cb(zfs_handle_t *zhp, void *data)
return (0);
}
+ if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_NOAUTO) {
+ zfs_close(zhp);
+ return (0);
+ }
+
if (cbp->cb_alloc == cbp->cb_used) {
void *ptr;
@@ -730,7 +1133,7 @@ mount_cb(zfs_handle_t *zhp, void *data)
cbp->cb_datasets[cbp->cb_used++] = zhp;
- return (zfs_iter_children(zhp, mount_cb, cbp));
+ return (zfs_iter_filesystems(zhp, mount_cb, cbp));
}
static int
@@ -776,21 +1179,22 @@ zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags)
libzfs_handle_t *hdl = zhp->zpool_hdl;
zfs_handle_t *zfsp;
int i, ret = -1;
+ int *good;
/*
- * Gather all datasets within the pool.
+ * Gather all non-snap datasets within the pool.
*/
if ((cb.cb_datasets = zfs_alloc(hdl, 4 * sizeof (void *))) == NULL)
return (-1);
cb.cb_alloc = 4;
- if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_ANY)) == NULL)
+ if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_DATASET)) == NULL)
goto out;
cb.cb_datasets[0] = zfsp;
cb.cb_used = 1;
- if (zfs_iter_children(zfsp, mount_cb, &cb) != 0)
+ if (zfs_iter_filesystems(zfsp, mount_cb, &cb) != 0)
goto out;
/*
@@ -799,15 +1203,32 @@ zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags)
qsort(cb.cb_datasets, cb.cb_used, sizeof (void *), dataset_cmp);
/*
- * And mount all the datasets.
+ * And mount all the datasets, keeping track of which ones
+ * succeeded or failed. By using zfs_alloc(), the good pointer
+ * will always be non-NULL.
*/
+ good = zfs_alloc(zhp->zpool_hdl, cb.cb_used * sizeof (int));
ret = 0;
for (i = 0; i < cb.cb_used; i++) {
- if (zfs_mount(cb.cb_datasets[i], mntopts, flags) != 0 ||
- zfs_share(cb.cb_datasets[i]) != 0)
+ if (zfs_mount(cb.cb_datasets[i], mntopts, flags) != 0)
+ ret = -1;
+ else
+ good[i] = 1;
+ }
+
+ /*
+ * Then share all the ones that need to be shared. This needs
+ * to be a separate pass in order to avoid excessive reloading
+ * of the configuration. Good should never be NULL since
+ * zfs_alloc is supposed to exit if memory isn't available.
+ */
+ for (i = 0; i < cb.cb_used; i++) {
+ if (good[i] && zfs_share(cb.cb_datasets[i]) != 0)
ret = -1;
}
+ free(good);
+
out:
for (i = 0; i < cb.cb_used; i++)
zfs_close(cb.cb_datasets[i]);
@@ -830,7 +1251,8 @@ zvol_cb(const char *dataset, void *data)
(zhp = zfs_open(hdl, dataset, ZFS_TYPE_VOLUME)) == NULL)
return (0);
- (void) zfs_unshare_iscsi(zhp);
+ if (zfs_unshare_iscsi(zhp) != 0)
+ return (-1);
zfs_close(zhp);
@@ -953,9 +1375,14 @@ zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force)
* Walk through and first unshare everything.
*/
for (i = 0; i < used; i++) {
- if (is_shared(hdl, mountpoints[i]) &&
- unshare_one(hdl, mountpoints[i], mountpoints[i]) != 0)
- goto out;
+ zfs_share_proto_t *curr_proto;
+ for (curr_proto = share_all_proto; *curr_proto != PROTO_END;
+ curr_proto++) {
+ if (is_shared(hdl, mountpoints[i], *curr_proto) &&
+ unshare_one(hdl, mountpoints[i],
+ mountpoints[i], *curr_proto) != 0)
+ goto out;
+ }
}
/*
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c
index 8580837..c3cb547 100644
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c
+++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c
@@ -20,12 +20,10 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
@@ -39,6 +37,7 @@
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
+#include <zone.h>
#include <sys/zfs_ioctl.h>
#include <sys/zio.h>
#include <strings.h>
@@ -48,11 +47,554 @@
#include "zfs_prop.h"
#include "libzfs_impl.h"
+static int read_efi_label(nvlist_t *config, diskaddr_t *sb);
+
+/*
+ * ====================================================================
+ * zpool property functions
+ * ====================================================================
+ */
+
+static int
+zpool_get_all_props(zpool_handle_t *zhp)
+{
+ zfs_cmd_t zc = { 0 };
+ libzfs_handle_t *hdl = zhp->zpool_hdl;
+
+ (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+
+ if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0)
+ return (-1);
+
+ while (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_GET_PROPS, &zc) != 0) {
+ if (errno == ENOMEM) {
+ if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
+ zcmd_free_nvlists(&zc);
+ return (-1);
+ }
+ } else {
+ zcmd_free_nvlists(&zc);
+ return (-1);
+ }
+ }
+
+ if (zcmd_read_dst_nvlist(hdl, &zc, &zhp->zpool_props) != 0) {
+ zcmd_free_nvlists(&zc);
+ return (-1);
+ }
+
+ zcmd_free_nvlists(&zc);
+
+ return (0);
+}
+
+static int
+zpool_props_refresh(zpool_handle_t *zhp)
+{
+ nvlist_t *old_props;
+
+ old_props = zhp->zpool_props;
+
+ if (zpool_get_all_props(zhp) != 0)
+ return (-1);
+
+ nvlist_free(old_props);
+ return (0);
+}
+
+static char *
+zpool_get_prop_string(zpool_handle_t *zhp, zpool_prop_t prop,
+ zprop_source_t *src)
+{
+ nvlist_t *nv, *nvl;
+ uint64_t ival;
+ char *value;
+ zprop_source_t source;
+
+ nvl = zhp->zpool_props;
+ if (nvlist_lookup_nvlist(nvl, zpool_prop_to_name(prop), &nv) == 0) {
+ verify(nvlist_lookup_uint64(nv, ZPROP_SOURCE, &ival) == 0);
+ source = ival;
+ verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0);
+ } else {
+ source = ZPROP_SRC_DEFAULT;
+ if ((value = (char *)zpool_prop_default_string(prop)) == NULL)
+ value = "-";
+ }
+
+ if (src)
+ *src = source;
+
+ return (value);
+}
+
+uint64_t
+zpool_get_prop_int(zpool_handle_t *zhp, zpool_prop_t prop, zprop_source_t *src)
+{
+ nvlist_t *nv, *nvl;
+ uint64_t value;
+ zprop_source_t source;
+
+ if (zhp->zpool_props == NULL && zpool_get_all_props(zhp)) {
+ /*
+ * zpool_get_all_props() has most likely failed because
+ * the pool is faulted, but if all we need is the top level
+ * vdev's guid then get it from the zhp config nvlist.
+ */
+ if ((prop == ZPOOL_PROP_GUID) &&
+ (nvlist_lookup_nvlist(zhp->zpool_config,
+ ZPOOL_CONFIG_VDEV_TREE, &nv) == 0) &&
+ (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &value)
+ == 0)) {
+ return (value);
+ }
+ return (zpool_prop_default_numeric(prop));
+ }
+
+ nvl = zhp->zpool_props;
+ if (nvlist_lookup_nvlist(nvl, zpool_prop_to_name(prop), &nv) == 0) {
+ verify(nvlist_lookup_uint64(nv, ZPROP_SOURCE, &value) == 0);
+ source = value;
+ verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0);
+ } else {
+ source = ZPROP_SRC_DEFAULT;
+ value = zpool_prop_default_numeric(prop);
+ }
+
+ if (src)
+ *src = source;
+
+ return (value);
+}
+
+/*
+ * Map VDEV STATE to printed strings.
+ */
+char *
+zpool_state_to_name(vdev_state_t state, vdev_aux_t aux)
+{
+ switch (state) {
+ case VDEV_STATE_CLOSED:
+ case VDEV_STATE_OFFLINE:
+ return (gettext("OFFLINE"));
+ case VDEV_STATE_REMOVED:
+ return (gettext("REMOVED"));
+ case VDEV_STATE_CANT_OPEN:
+ if (aux == VDEV_AUX_CORRUPT_DATA || aux == VDEV_AUX_BAD_LOG)
+ return (gettext("FAULTED"));
+ else
+ return (gettext("UNAVAIL"));
+ case VDEV_STATE_FAULTED:
+ return (gettext("FAULTED"));
+ case VDEV_STATE_DEGRADED:
+ return (gettext("DEGRADED"));
+ case VDEV_STATE_HEALTHY:
+ return (gettext("ONLINE"));
+ }
+
+ return (gettext("UNKNOWN"));
+}
+
+/*
+ * Get a zpool property value for 'prop' and return the value in
+ * a pre-allocated buffer.
+ */
+int
+zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len,
+ zprop_source_t *srctype)
+{
+ uint64_t intval;
+ const char *strval;
+ zprop_source_t src = ZPROP_SRC_NONE;
+ nvlist_t *nvroot;
+ vdev_stat_t *vs;
+ uint_t vsc;
+
+ if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
+ if (prop == ZPOOL_PROP_NAME)
+ (void) strlcpy(buf, zpool_get_name(zhp), len);
+ else if (prop == ZPOOL_PROP_HEALTH)
+ (void) strlcpy(buf, "FAULTED", len);
+ else
+ (void) strlcpy(buf, "-", len);
+ return (0);
+ }
+
+ if (zhp->zpool_props == NULL && zpool_get_all_props(zhp) &&
+ prop != ZPOOL_PROP_NAME)
+ return (-1);
+
+ switch (zpool_prop_get_type(prop)) {
+ case PROP_TYPE_STRING:
+ (void) strlcpy(buf, zpool_get_prop_string(zhp, prop, &src),
+ len);
+ break;
+
+ case PROP_TYPE_NUMBER:
+ intval = zpool_get_prop_int(zhp, prop, &src);
+
+ switch (prop) {
+ case ZPOOL_PROP_SIZE:
+ case ZPOOL_PROP_USED:
+ case ZPOOL_PROP_AVAILABLE:
+ (void) zfs_nicenum(intval, buf, len);
+ break;
+
+ case ZPOOL_PROP_CAPACITY:
+ (void) snprintf(buf, len, "%llu%%",
+ (u_longlong_t)intval);
+ break;
+
+ case ZPOOL_PROP_HEALTH:
+ verify(nvlist_lookup_nvlist(zpool_get_config(zhp, NULL),
+ ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
+ verify(nvlist_lookup_uint64_array(nvroot,
+ ZPOOL_CONFIG_STATS, (uint64_t **)&vs, &vsc) == 0);
+
+ (void) strlcpy(buf, zpool_state_to_name(intval,
+ vs->vs_aux), len);
+ break;
+ default:
+ (void) snprintf(buf, len, "%llu", intval);
+ }
+ break;
+
+ case PROP_TYPE_INDEX:
+ intval = zpool_get_prop_int(zhp, prop, &src);
+ if (zpool_prop_index_to_string(prop, intval, &strval)
+ != 0)
+ return (-1);
+ (void) strlcpy(buf, strval, len);
+ break;
+
+ default:
+ abort();
+ }
+
+ if (srctype)
+ *srctype = src;
+
+ return (0);
+}
+
+/*
+ * Check if the bootfs name has the same pool name as it is set to.
+ * Assuming bootfs is a valid dataset name.
+ */
+static boolean_t
+bootfs_name_valid(const char *pool, char *bootfs)
+{
+ int len = strlen(pool);
+
+ if (!zfs_name_valid(bootfs, ZFS_TYPE_FILESYSTEM|ZFS_TYPE_SNAPSHOT))
+ return (B_FALSE);
+
+ if (strncmp(pool, bootfs, len) == 0 &&
+ (bootfs[len] == '/' || bootfs[len] == '\0'))
+ return (B_TRUE);
+
+ return (B_FALSE);
+}
+
+#if defined(sun)
+/*
+ * Inspect the configuration to determine if any of the devices contain
+ * an EFI label.
+ */
+static boolean_t
+pool_uses_efi(nvlist_t *config)
+{
+ nvlist_t **child;
+ uint_t c, children;
+
+ if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN,
+ &child, &children) != 0)
+ return (read_efi_label(config, NULL) >= 0);
+
+ for (c = 0; c < children; c++) {
+ if (pool_uses_efi(child[c]))
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+#endif
+
+/*
+ * Given an nvlist of zpool properties to be set, validate that they are
+ * correct, and parse any numeric properties (index, boolean, etc) if they are
+ * specified as strings.
+ */
+static nvlist_t *
+zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
+ nvlist_t *props, uint64_t version, boolean_t create_or_import, char *errbuf)
+{
+ nvpair_t *elem;
+ nvlist_t *retprops;
+ zpool_prop_t prop;
+ char *strval;
+ uint64_t intval;
+ char *slash;
+ struct stat64 statbuf;
+ zpool_handle_t *zhp;
+ nvlist_t *nvroot;
+
+ if (nvlist_alloc(&retprops, NV_UNIQUE_NAME, 0) != 0) {
+ (void) no_memory(hdl);
+ return (NULL);
+ }
+
+ elem = NULL;
+ while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
+ const char *propname = nvpair_name(elem);
+
+ /*
+ * Make sure this property is valid and applies to this type.
+ */
+ if ((prop = zpool_name_to_prop(propname)) == ZPROP_INVAL) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "invalid property '%s'"), propname);
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+
+ if (zpool_prop_readonly(prop)) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "'%s' "
+ "is readonly"), propname);
+ (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
+ goto error;
+ }
+
+ if (zprop_parse_value(hdl, elem, prop, ZFS_TYPE_POOL, retprops,
+ &strval, &intval, errbuf) != 0)
+ goto error;
+
+ /*
+ * Perform additional checking for specific properties.
+ */
+ switch (prop) {
+ case ZPOOL_PROP_VERSION:
+ if (intval < version || intval > SPA_VERSION) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "property '%s' number %d is invalid."),
+ propname, intval);
+ (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
+ goto error;
+ }
+ break;
+
+ case ZPOOL_PROP_BOOTFS:
+ if (create_or_import) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "property '%s' cannot be set at creation "
+ "or import time"), propname);
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+
+ if (version < SPA_VERSION_BOOTFS) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "pool must be upgraded to support "
+ "'%s' property"), propname);
+ (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
+ goto error;
+ }
+
+ /*
+ * bootfs property value has to be a dataset name and
+ * the dataset has to be in the same pool as it sets to.
+ */
+ if (strval[0] != '\0' && !bootfs_name_valid(poolname,
+ strval)) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "'%s' "
+ "is an invalid name"), strval);
+ (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
+ goto error;
+ }
+
+ if ((zhp = zpool_open_canfail(hdl, poolname)) == NULL) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "could not open pool '%s'"), poolname);
+ (void) zfs_error(hdl, EZFS_OPENFAILED, errbuf);
+ goto error;
+ }
+ verify(nvlist_lookup_nvlist(zpool_get_config(zhp, NULL),
+ ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
+
+#if defined(sun)
+ /*
+ * bootfs property cannot be set on a disk which has
+ * been EFI labeled.
+ */
+ if (pool_uses_efi(nvroot)) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "property '%s' not supported on "
+ "EFI labeled devices"), propname);
+ (void) zfs_error(hdl, EZFS_POOL_NOTSUP, errbuf);
+ zpool_close(zhp);
+ goto error;
+ }
+#endif
+ zpool_close(zhp);
+ break;
+
+ case ZPOOL_PROP_ALTROOT:
+ if (!create_or_import) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "property '%s' can only be set during pool "
+ "creation or import"), propname);
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+
+ if (strval[0] != '/') {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "bad alternate root '%s'"), strval);
+ (void) zfs_error(hdl, EZFS_BADPATH, errbuf);
+ goto error;
+ }
+ break;
+
+ case ZPOOL_PROP_CACHEFILE:
+ if (strval[0] == '\0')
+ break;
+
+ if (strcmp(strval, "none") == 0)
+ break;
+
+ if (strval[0] != '/') {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "property '%s' must be empty, an "
+ "absolute path, or 'none'"), propname);
+ (void) zfs_error(hdl, EZFS_BADPATH, errbuf);
+ goto error;
+ }
+
+ slash = strrchr(strval, '/');
+
+ if (slash[1] == '\0' || strcmp(slash, "/.") == 0 ||
+ strcmp(slash, "/..") == 0) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "'%s' is not a valid file"), strval);
+ (void) zfs_error(hdl, EZFS_BADPATH, errbuf);
+ goto error;
+ }
+
+ *slash = '\0';
+
+ if (strval[0] != '\0' &&
+ (stat64(strval, &statbuf) != 0 ||
+ !S_ISDIR(statbuf.st_mode))) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "'%s' is not a valid directory"),
+ strval);
+ (void) zfs_error(hdl, EZFS_BADPATH, errbuf);
+ goto error;
+ }
+
+ *slash = '/';
+ break;
+ }
+ }
+
+ return (retprops);
+error:
+ nvlist_free(retprops);
+ return (NULL);
+}
+
+/*
+ * Set zpool property : propname=propval.
+ */
+int
+zpool_set_prop(zpool_handle_t *zhp, const char *propname, const char *propval)
+{
+ zfs_cmd_t zc = { 0 };
+ int ret = -1;
+ char errbuf[1024];
+ nvlist_t *nvl = NULL;
+ nvlist_t *realprops;
+ uint64_t version;
+
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
+ zhp->zpool_name);
+
+ if (zhp->zpool_props == NULL && zpool_get_all_props(zhp))
+ return (zfs_error(zhp->zpool_hdl, EZFS_POOLPROPS, errbuf));
+
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
+ return (no_memory(zhp->zpool_hdl));
+
+ if (nvlist_add_string(nvl, propname, propval) != 0) {
+ nvlist_free(nvl);
+ return (no_memory(zhp->zpool_hdl));
+ }
+
+ version = zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL);
+ if ((realprops = zpool_valid_proplist(zhp->zpool_hdl,
+ zhp->zpool_name, nvl, version, B_FALSE, errbuf)) == NULL) {
+ nvlist_free(nvl);
+ return (-1);
+ }
+
+ nvlist_free(nvl);
+ nvl = realprops;
+
+ /*
+ * Execute the corresponding ioctl() to set this property.
+ */
+ (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+
+ if (zcmd_write_src_nvlist(zhp->zpool_hdl, &zc, nvl) != 0) {
+ nvlist_free(nvl);
+ return (-1);
+ }
+
+ ret = zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_SET_PROPS, &zc);
+
+ zcmd_free_nvlists(&zc);
+ nvlist_free(nvl);
+
+ if (ret)
+ (void) zpool_standard_error(zhp->zpool_hdl, errno, errbuf);
+ else
+ (void) zpool_props_refresh(zhp);
+
+ return (ret);
+}
+
+int
+zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp)
+{
+ libzfs_handle_t *hdl = zhp->zpool_hdl;
+ zprop_list_t *entry;
+ char buf[ZFS_MAXPROPLEN];
+
+ if (zprop_expand_list(hdl, plp, ZFS_TYPE_POOL) != 0)
+ return (-1);
+
+ for (entry = *plp; entry != NULL; entry = entry->pl_next) {
+
+ if (entry->pl_fixed)
+ continue;
+
+ if (entry->pl_prop != ZPROP_INVAL &&
+ zpool_get_prop(zhp, entry->pl_prop, buf, sizeof (buf),
+ NULL) == 0) {
+ if (strlen(buf) > entry->pl_width)
+ entry->pl_width = strlen(buf);
+ }
+ }
+
+ return (0);
+}
+
+
/*
* Validate the given pool name, optionally putting an extended error message in
* 'buf'.
*/
-static boolean_t
+boolean_t
zpool_name_valid(libzfs_handle_t *hdl, boolean_t isopen, const char *pool)
{
namecheck_err_t why;
@@ -70,9 +612,11 @@ zpool_name_valid(libzfs_handle_t *hdl, boolean_t isopen, const char *pool)
if (ret == 0 && !isopen &&
(strncmp(pool, "mirror", 6) == 0 ||
strncmp(pool, "raidz", 5) == 0 ||
- strncmp(pool, "spare", 5) == 0)) {
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN, "name is reserved"));
+ strncmp(pool, "spare", 5) == 0 ||
+ strcmp(pool, "log") == 0)) {
+ if (hdl != NULL)
+ zfs_error_aux(hdl,
+ dgettext(TEXT_DOMAIN, "name is reserved"));
return (B_FALSE);
}
@@ -134,39 +678,6 @@ zpool_name_valid(libzfs_handle_t *hdl, boolean_t isopen, const char *pool)
return (B_TRUE);
}
-static int
-zpool_get_all_props(zpool_handle_t *zhp)
-{
- zfs_cmd_t zc = { 0 };
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
-
- if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0)
- return (-1);
-
- while (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_GET_PROPS, &zc) != 0) {
- if (errno == ENOMEM) {
- if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- } else {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- }
-
- if (zcmd_read_dst_nvlist(hdl, &zc, &zhp->zpool_props) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
-
- zcmd_free_nvlists(&zc);
-
- return (0);
-}
-
/*
* Open a handle to the given pool, even if the pool is currently in the FAULTED
* state.
@@ -199,11 +710,9 @@ zpool_open_canfail(libzfs_handle_t *hdl, const char *pool)
}
if (missing) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "no such pool"));
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "no such pool"));
(void) zfs_error_fmt(hdl, EZFS_NOENT,
- dgettext(TEXT_DOMAIN, "cannot open '%s'"),
- pool);
+ dgettext(TEXT_DOMAIN, "cannot open '%s'"), pool);
zpool_close(zhp);
return (NULL);
}
@@ -288,86 +797,6 @@ zpool_get_name(zpool_handle_t *zhp)
return (zhp->zpool_name);
}
-/*
- * Return the GUID of the pool.
- */
-uint64_t
-zpool_get_guid(zpool_handle_t *zhp)
-{
- uint64_t guid;
-
- verify(nvlist_lookup_uint64(zhp->zpool_config, ZPOOL_CONFIG_POOL_GUID,
- &guid) == 0);
- return (guid);
-}
-
-/*
- * Return the version of the pool.
- */
-uint64_t
-zpool_get_version(zpool_handle_t *zhp)
-{
- uint64_t version;
-
- verify(nvlist_lookup_uint64(zhp->zpool_config, ZPOOL_CONFIG_VERSION,
- &version) == 0);
-
- return (version);
-}
-
-/*
- * Return the amount of space currently consumed by the pool.
- */
-uint64_t
-zpool_get_space_used(zpool_handle_t *zhp)
-{
- nvlist_t *nvroot;
- vdev_stat_t *vs;
- uint_t vsc;
-
- verify(nvlist_lookup_nvlist(zhp->zpool_config, ZPOOL_CONFIG_VDEV_TREE,
- &nvroot) == 0);
- verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS,
- (uint64_t **)&vs, &vsc) == 0);
-
- return (vs->vs_alloc);
-}
-
-/*
- * Return the total space in the pool.
- */
-uint64_t
-zpool_get_space_total(zpool_handle_t *zhp)
-{
- nvlist_t *nvroot;
- vdev_stat_t *vs;
- uint_t vsc;
-
- verify(nvlist_lookup_nvlist(zhp->zpool_config, ZPOOL_CONFIG_VDEV_TREE,
- &nvroot) == 0);
- verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS,
- (uint64_t **)&vs, &vsc) == 0);
-
- return (vs->vs_space);
-}
-
-/*
- * Return the alternate root for this pool, if any.
- */
-int
-zpool_get_root(zpool_handle_t *zhp, char *buf, size_t buflen)
-{
- zfs_cmd_t zc = { 0 };
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 ||
- zc.zc_value[0] == '\0')
- return (-1);
-
- (void) strlcpy(buf, zc.zc_value, buflen);
-
- return (0);
-}
/*
* Return the state of the pool (ACTIVE or UNAVAILABLE)
@@ -385,10 +814,14 @@ zpool_get_state(zpool_handle_t *zhp)
*/
int
zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot,
- const char *altroot)
+ nvlist_t *props, nvlist_t *fsprops)
{
zfs_cmd_t zc = { 0 };
+ nvlist_t *zc_fsprops = NULL;
+ nvlist_t *zc_props = NULL;
char msg[1024];
+ char *altroot;
+ int ret = -1;
(void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
"cannot create '%s'"), pool);
@@ -396,20 +829,48 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot,
if (!zpool_name_valid(hdl, B_FALSE, pool))
return (zfs_error(hdl, EZFS_INVALIDNAME, msg));
- if (altroot != NULL && altroot[0] != '/')
- return (zfs_error_fmt(hdl, EZFS_BADPATH,
- dgettext(TEXT_DOMAIN, "bad alternate root '%s'"), altroot));
-
- if (zcmd_write_src_nvlist(hdl, &zc, nvroot, NULL) != 0)
+ if (zcmd_write_conf_nvlist(hdl, &zc, nvroot) != 0)
return (-1);
+ if (props) {
+ if ((zc_props = zpool_valid_proplist(hdl, pool, props,
+ SPA_VERSION_1, B_TRUE, msg)) == NULL) {
+ goto create_failed;
+ }
+ }
+
+ if (fsprops) {
+ uint64_t zoned;
+ char *zonestr;
+
+ zoned = ((nvlist_lookup_string(fsprops,
+ zfs_prop_to_name(ZFS_PROP_ZONED), &zonestr) == 0) &&
+ strcmp(zonestr, "on") == 0);
+
+ if ((zc_fsprops = zfs_valid_proplist(hdl,
+ ZFS_TYPE_FILESYSTEM, fsprops, zoned, NULL, msg)) == NULL) {
+ goto create_failed;
+ }
+ if (!zc_props &&
+ (nvlist_alloc(&zc_props, NV_UNIQUE_NAME, 0) != 0)) {
+ goto create_failed;
+ }
+ if (nvlist_add_nvlist(zc_props,
+ ZPOOL_ROOTFS_PROPS, zc_fsprops) != 0) {
+ goto create_failed;
+ }
+ }
+
+ if (zc_props && zcmd_write_src_nvlist(hdl, &zc, zc_props) != 0)
+ goto create_failed;
+
(void) strlcpy(zc.zc_name, pool, sizeof (zc.zc_name));
- if (altroot != NULL)
- (void) strlcpy(zc.zc_value, altroot, sizeof (zc.zc_value));
+ if ((ret = zfs_ioctl(hdl, ZFS_IOC_POOL_CREATE, &zc)) != 0) {
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_CREATE, &zc) != 0) {
zcmd_free_nvlists(&zc);
+ nvlist_free(zc_props);
+ nvlist_free(zc_fsprops);
switch (errno) {
case EBUSY:
@@ -446,28 +907,36 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot,
"one or more devices is out of space"));
return (zfs_error(hdl, EZFS_BADDEV, msg));
+ case ENOTBLK:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "cache device must be a disk or disk slice"));
+ return (zfs_error(hdl, EZFS_BADDEV, msg));
+
default:
return (zpool_standard_error(hdl, errno, msg));
}
}
- zcmd_free_nvlists(&zc);
-
/*
* If this is an alternate root pool, then we automatically set the
* mountpoint of the root dataset to be '/'.
*/
- if (altroot != NULL) {
+ if (nvlist_lookup_string(props, zpool_prop_to_name(ZPOOL_PROP_ALTROOT),
+ &altroot) == 0) {
zfs_handle_t *zhp;
- verify((zhp = zfs_open(hdl, pool, ZFS_TYPE_ANY)) != NULL);
+ verify((zhp = zfs_open(hdl, pool, ZFS_TYPE_DATASET)) != NULL);
verify(zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
"/") == 0);
zfs_close(zhp);
}
- return (0);
+create_failed:
+ zcmd_free_nvlists(&zc);
+ nvlist_free(zc_props);
+ nvlist_free(zc_fsprops);
+ return (ret);
}
/*
@@ -492,7 +961,7 @@ zpool_destroy(zpool_handle_t *zhp)
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_POOL_DESTROY, &zc) != 0) {
+ if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_DESTROY, &zc) != 0) {
(void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
"cannot destroy '%s'"), zhp->zpool_name);
@@ -528,13 +997,14 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
int ret;
libzfs_handle_t *hdl = zhp->zpool_hdl;
char msg[1024];
- nvlist_t **spares;
- uint_t nspares;
+ nvlist_t **spares, **l2cache;
+ uint_t nspares, nl2cache;
(void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
"cannot add to '%s'"), zhp->zpool_name);
- if (zpool_get_version(zhp) < ZFS_VERSION_SPARES &&
+ if (zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL) <
+ SPA_VERSION_SPARES &&
nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
&spares, &nspares) == 0) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "pool must be "
@@ -542,11 +1012,20 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
return (zfs_error(hdl, EZFS_BADVERSION, msg));
}
- if (zcmd_write_src_nvlist(hdl, &zc, nvroot, NULL) != 0)
+ if (zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL) <
+ SPA_VERSION_L2CACHE &&
+ nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE,
+ &l2cache, &nl2cache) == 0) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "pool must be "
+ "upgraded to add cache devices"));
+ return (zfs_error(hdl, EZFS_BADVERSION, msg));
+ }
+
+ if (zcmd_write_conf_nvlist(hdl, &zc, nvroot) != 0)
return (-1);
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_ADD, &zc) != 0) {
+ if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_VDEV_ADD, &zc) != 0) {
switch (errno) {
case EBUSY:
/*
@@ -581,16 +1060,23 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
case ENOTSUP:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded to add raidz2 vdevs"));
+ "pool must be upgraded to add these vdevs"));
(void) zfs_error(hdl, EZFS_BADVERSION, msg);
break;
case EDOM:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "root pool can not have concatenated devices"));
+ "root pool can not have multiple vdevs"
+ " or separate logs"));
(void) zfs_error(hdl, EZFS_POOL_NOTSUP, msg);
break;
+ case ENOTBLK:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "cache device must be a disk or disk slice"));
+ (void) zfs_error(hdl, EZFS_BADDEV, msg);
+ break;
+
default:
(void) zpool_standard_error(hdl, errno, msg);
}
@@ -610,40 +1096,97 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
* mounted datasets in the pool.
*/
int
-zpool_export(zpool_handle_t *zhp)
+zpool_export(zpool_handle_t *zhp, boolean_t force)
{
zfs_cmd_t zc = { 0 };
+ char msg[1024];
if (zpool_remove_zvol_links(zhp) != 0)
return (-1);
+ (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
+ "cannot export '%s'"), zhp->zpool_name);
+
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+ zc.zc_cookie = force;
+
+ if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_EXPORT, &zc) != 0) {
+ switch (errno) {
+ case EXDEV:
+ zfs_error_aux(zhp->zpool_hdl, dgettext(TEXT_DOMAIN,
+ "use '-f' to override the following errors:\n"
+ "'%s' has an active shared spare which could be"
+ " used by other pools once '%s' is exported."),
+ zhp->zpool_name, zhp->zpool_name);
+ return (zfs_error(zhp->zpool_hdl, EZFS_ACTIVE_SPARE,
+ msg));
+ default:
+ return (zpool_standard_error_fmt(zhp->zpool_hdl, errno,
+ msg));
+ }
+ }
- if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_POOL_EXPORT, &zc) != 0)
- return (zpool_standard_error_fmt(zhp->zpool_hdl, errno,
- dgettext(TEXT_DOMAIN, "cannot export '%s'"),
- zhp->zpool_name));
return (0);
}
/*
- * Import the given pool using the known configuration. The configuration
- * should have come from zpool_find_import(). The 'newname' and 'altroot'
- * parameters control whether the pool is imported with a different name or with
- * an alternate root, respectively.
+ * zpool_import() is a contracted interface. Should be kept the same
+ * if possible.
+ *
+ * Applications should use zpool_import_props() to import a pool with
+ * new properties value to be set.
*/
int
zpool_import(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
- const char *altroot)
+ char *altroot)
+{
+ nvlist_t *props = NULL;
+ int ret;
+
+ if (altroot != NULL) {
+ if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) {
+ return (zfs_error_fmt(hdl, EZFS_NOMEM,
+ dgettext(TEXT_DOMAIN, "cannot import '%s'"),
+ newname));
+ }
+
+ if (nvlist_add_string(props,
+ zpool_prop_to_name(ZPOOL_PROP_ALTROOT), altroot) != 0) {
+ nvlist_free(props);
+ return (zfs_error_fmt(hdl, EZFS_NOMEM,
+ dgettext(TEXT_DOMAIN, "cannot import '%s'"),
+ newname));
+ }
+ }
+
+ ret = zpool_import_props(hdl, config, newname, props, B_FALSE);
+ if (props)
+ nvlist_free(props);
+ return (ret);
+}
+
+/*
+ * Import the given pool using the known configuration and a list of
+ * properties to be set. The configuration should have come from
+ * zpool_find_import(). The 'newname' parameters control whether the pool
+ * is imported with a different name.
+ */
+int
+zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
+ nvlist_t *props, boolean_t importfaulted)
{
zfs_cmd_t zc = { 0 };
char *thename;
char *origname;
int ret;
+ char errbuf[1024];
verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
&origname) == 0);
+ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+ "cannot import pool '%s'"), origname);
+
if (newname != NULL) {
if (!zpool_name_valid(hdl, B_FALSE, newname))
return (zfs_error_fmt(hdl, EZFS_INVALIDNAME,
@@ -654,26 +1197,34 @@ zpool_import(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
thename = origname;
}
- if (altroot != NULL && altroot[0] != '/')
- return (zfs_error_fmt(hdl, EZFS_BADPATH,
- dgettext(TEXT_DOMAIN, "bad alternate root '%s'"),
- altroot));
+ if (props) {
+ uint64_t version;
- (void) strlcpy(zc.zc_name, thename, sizeof (zc.zc_name));
+ verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
+ &version) == 0);
- if (altroot != NULL)
- (void) strlcpy(zc.zc_value, altroot, sizeof (zc.zc_value));
- else
- zc.zc_value[0] = '\0';
+ if ((props = zpool_valid_proplist(hdl, origname,
+ props, version, B_TRUE, errbuf)) == NULL) {
+ return (-1);
+ } else if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
+ nvlist_free(props);
+ return (-1);
+ }
+ }
+
+ (void) strlcpy(zc.zc_name, thename, sizeof (zc.zc_name));
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
&zc.zc_guid) == 0);
- if (zcmd_write_src_nvlist(hdl, &zc, config, NULL) != 0)
+ if (zcmd_write_conf_nvlist(hdl, &zc, config) != 0) {
+ nvlist_free(props);
return (-1);
+ }
+ zc.zc_cookie = (uint64_t)importfaulted;
ret = 0;
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_IMPORT, &zc) != 0) {
+ if (zfs_ioctl(hdl, ZFS_IOC_POOL_IMPORT, &zc) != 0) {
char desc[1024];
if (newname == NULL)
(void) snprintf(desc, sizeof (desc),
@@ -703,6 +1254,7 @@ zpool_import(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
ret = -1;
} else {
zpool_handle_t *zhp;
+
/*
* This should never fail, but play it safe anyway.
*/
@@ -712,9 +1264,12 @@ zpool_import(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
ret = zpool_create_zvol_links(zhp);
zpool_close(zhp);
}
+
}
zcmd_free_nvlists(&zc);
+ nvlist_free(props);
+
return (ret);
}
@@ -731,7 +1286,7 @@ zpool_scrub(zpool_handle_t *zhp, pool_scrub_type_t type)
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
zc.zc_cookie = type;
- if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_POOL_SCRUB, &zc) == 0)
+ if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_SCRUB, &zc) == 0)
return (0);
(void) snprintf(msg, sizeof (msg),
@@ -749,7 +1304,7 @@ zpool_scrub(zpool_handle_t *zhp, pool_scrub_type_t type)
*/
static nvlist_t *
vdev_to_nvlist_iter(nvlist_t *nv, const char *search, uint64_t guid,
- boolean_t *avail_spare)
+ boolean_t *avail_spare, boolean_t *l2cache, boolean_t *log)
{
uint_t c, children;
nvlist_t **child;
@@ -757,6 +1312,7 @@ vdev_to_nvlist_iter(nvlist_t *nv, const char *search, uint64_t guid,
char *path;
uint64_t wholedisk = 0;
nvlist_t *ret;
+ uint64_t is_log;
verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &theguid) == 0);
@@ -789,27 +1345,53 @@ vdev_to_nvlist_iter(nvlist_t *nv, const char *search, uint64_t guid,
&child, &children) != 0)
return (NULL);
- for (c = 0; c < children; c++)
+ for (c = 0; c < children; c++) {
if ((ret = vdev_to_nvlist_iter(child[c], search, guid,
- avail_spare)) != NULL)
+ avail_spare, l2cache, NULL)) != NULL) {
+ /*
+ * The 'is_log' value is only set for the toplevel
+ * vdev, not the leaf vdevs. So we always lookup the
+ * log device from the root of the vdev tree (where
+ * 'log' is non-NULL).
+ */
+ if (log != NULL &&
+ nvlist_lookup_uint64(child[c],
+ ZPOOL_CONFIG_IS_LOG, &is_log) == 0 &&
+ is_log) {
+ *log = B_TRUE;
+ }
return (ret);
+ }
+ }
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES,
&child, &children) == 0) {
for (c = 0; c < children; c++) {
if ((ret = vdev_to_nvlist_iter(child[c], search, guid,
- avail_spare)) != NULL) {
+ avail_spare, l2cache, NULL)) != NULL) {
*avail_spare = B_TRUE;
return (ret);
}
}
}
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
+ &child, &children) == 0) {
+ for (c = 0; c < children; c++) {
+ if ((ret = vdev_to_nvlist_iter(child[c], search, guid,
+ avail_spare, l2cache, NULL)) != NULL) {
+ *l2cache = B_TRUE;
+ return (ret);
+ }
+ }
+ }
+
return (NULL);
}
nvlist_t *
-zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare)
+zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare,
+ boolean_t *l2cache, boolean_t *log)
{
char buf[MAXPATHLEN];
const char *search;
@@ -831,29 +1413,124 @@ zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare)
&nvroot) == 0);
*avail_spare = B_FALSE;
- return (vdev_to_nvlist_iter(nvroot, search, guid, avail_spare));
+ *l2cache = B_FALSE;
+ if (log != NULL)
+ *log = B_FALSE;
+ return (vdev_to_nvlist_iter(nvroot, search, guid, avail_spare,
+ l2cache, log));
+}
+
+static int
+vdev_online(nvlist_t *nv)
+{
+ uint64_t ival;
+
+ if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_OFFLINE, &ival) == 0 ||
+ nvlist_lookup_uint64(nv, ZPOOL_CONFIG_FAULTED, &ival) == 0 ||
+ nvlist_lookup_uint64(nv, ZPOOL_CONFIG_REMOVED, &ival) == 0)
+ return (0);
+
+ return (1);
+}
+
+/*
+ * Get phys_path for a root pool
+ * Return 0 on success; non-zeron on failure.
+ */
+int
+zpool_get_physpath(zpool_handle_t *zhp, char *physpath)
+{
+ char bootfs[ZPOOL_MAXNAMELEN];
+ nvlist_t *vdev_root;
+ nvlist_t **child;
+ uint_t count;
+ int i;
+
+ /*
+ * Make sure this is a root pool, as phys_path doesn't mean
+ * anything to a non-root pool.
+ */
+ if (zpool_get_prop(zhp, ZPOOL_PROP_BOOTFS, bootfs,
+ sizeof (bootfs), NULL) != 0)
+ return (-1);
+
+ verify(nvlist_lookup_nvlist(zhp->zpool_config,
+ ZPOOL_CONFIG_VDEV_TREE, &vdev_root) == 0);
+
+ if (nvlist_lookup_nvlist_array(vdev_root, ZPOOL_CONFIG_CHILDREN,
+ &child, &count) != 0)
+ return (-2);
+
+ for (i = 0; i < count; i++) {
+ nvlist_t **child2;
+ uint_t count2;
+ char *type;
+ char *tmppath;
+ int j;
+
+ if (nvlist_lookup_string(child[i], ZPOOL_CONFIG_TYPE, &type)
+ != 0)
+ return (-3);
+
+ if (strcmp(type, VDEV_TYPE_DISK) == 0) {
+ if (!vdev_online(child[i]))
+ return (-8);
+ verify(nvlist_lookup_string(child[i],
+ ZPOOL_CONFIG_PHYS_PATH, &tmppath) == 0);
+ (void) strncpy(physpath, tmppath, strlen(tmppath));
+ } else if (strcmp(type, VDEV_TYPE_MIRROR) == 0) {
+ if (nvlist_lookup_nvlist_array(child[i],
+ ZPOOL_CONFIG_CHILDREN, &child2, &count2) != 0)
+ return (-4);
+
+ for (j = 0; j < count2; j++) {
+ if (!vdev_online(child2[j]))
+ return (-8);
+ if (nvlist_lookup_string(child2[j],
+ ZPOOL_CONFIG_PHYS_PATH, &tmppath) != 0)
+ return (-5);
+
+ if ((strlen(physpath) + strlen(tmppath)) >
+ MAXNAMELEN)
+ return (-6);
+
+ if (strlen(physpath) == 0) {
+ (void) strncpy(physpath, tmppath,
+ strlen(tmppath));
+ } else {
+ (void) strcat(physpath, " ");
+ (void) strcat(physpath, tmppath);
+ }
+ }
+ } else {
+ return (-7);
+ }
+ }
+
+ return (0);
}
/*
- * Returns TRUE if the given guid corresponds to a spare (INUSE or not).
+ * Returns TRUE if the given guid corresponds to the given type.
+ * This is used to check for hot spares (INUSE or not), and level 2 cache
+ * devices.
*/
static boolean_t
-is_spare(zpool_handle_t *zhp, uint64_t guid)
+is_guid_type(zpool_handle_t *zhp, uint64_t guid, const char *type)
{
- uint64_t spare_guid;
+ uint64_t target_guid;
nvlist_t *nvroot;
- nvlist_t **spares;
- uint_t nspares;
+ nvlist_t **list;
+ uint_t count;
int i;
verify(nvlist_lookup_nvlist(zhp->zpool_config, ZPOOL_CONFIG_VDEV_TREE,
&nvroot) == 0);
- if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
- &spares, &nspares) == 0) {
- for (i = 0; i < nspares; i++) {
- verify(nvlist_lookup_uint64(spares[i],
- ZPOOL_CONFIG_GUID, &spare_guid) == 0);
- if (guid == spare_guid)
+ if (nvlist_lookup_nvlist_array(nvroot, type, &list, &count) == 0) {
+ for (i = 0; i < count; i++) {
+ verify(nvlist_lookup_uint64(list[i], ZPOOL_CONFIG_GUID,
+ &target_guid) == 0);
+ if (guid == target_guid)
return (B_TRUE);
}
}
@@ -862,62 +1539,106 @@ is_spare(zpool_handle_t *zhp, uint64_t guid)
}
/*
- * Bring the specified vdev online
+ * Bring the specified vdev online. The 'flags' parameter is a set of the
+ * ZFS_ONLINE_* flags.
*/
int
-zpool_vdev_online(zpool_handle_t *zhp, const char *path)
+zpool_vdev_online(zpool_handle_t *zhp, const char *path, int flags,
+ vdev_state_t *newstate)
{
zfs_cmd_t zc = { 0 };
char msg[1024];
nvlist_t *tgt;
- boolean_t avail_spare;
+ boolean_t avail_spare, l2cache;
libzfs_handle_t *hdl = zhp->zpool_hdl;
(void) snprintf(msg, sizeof (msg),
dgettext(TEXT_DOMAIN, "cannot online %s"), path);
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if ((tgt = zpool_find_vdev(zhp, path, &avail_spare)) == NULL)
+ if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache,
+ NULL)) == NULL)
return (zfs_error(hdl, EZFS_NODEVICE, msg));
verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0);
- if (avail_spare || is_spare(zhp, zc.zc_guid) == B_TRUE)
+ if (avail_spare ||
+ is_guid_type(zhp, zc.zc_guid, ZPOOL_CONFIG_SPARES) == B_TRUE)
return (zfs_error(hdl, EZFS_ISSPARE, msg));
- if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_ONLINE, &zc) == 0)
- return (0);
+ zc.zc_cookie = VDEV_STATE_ONLINE;
+ zc.zc_obj = flags;
- return (zpool_standard_error(hdl, errno, msg));
+ if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_VDEV_SET_STATE, &zc) != 0)
+ return (zpool_standard_error(hdl, errno, msg));
+
+ *newstate = zc.zc_cookie;
+ return (0);
}
/*
* Take the specified vdev offline
*/
int
-zpool_vdev_offline(zpool_handle_t *zhp, const char *path, int istmp)
+zpool_vdev_offline(zpool_handle_t *zhp, const char *path, boolean_t istmp)
{
zfs_cmd_t zc = { 0 };
char msg[1024];
nvlist_t *tgt;
- boolean_t avail_spare;
+ boolean_t avail_spare, l2cache;
libzfs_handle_t *hdl = zhp->zpool_hdl;
(void) snprintf(msg, sizeof (msg),
dgettext(TEXT_DOMAIN, "cannot offline %s"), path);
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if ((tgt = zpool_find_vdev(zhp, path, &avail_spare)) == NULL)
+ if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache,
+ NULL)) == NULL)
return (zfs_error(hdl, EZFS_NODEVICE, msg));
verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0);
- if (avail_spare || is_spare(zhp, zc.zc_guid) == B_TRUE)
+ if (avail_spare ||
+ is_guid_type(zhp, zc.zc_guid, ZPOOL_CONFIG_SPARES) == B_TRUE)
return (zfs_error(hdl, EZFS_ISSPARE, msg));
- zc.zc_cookie = istmp;
+ zc.zc_cookie = VDEV_STATE_OFFLINE;
+ zc.zc_obj = istmp ? ZFS_OFFLINE_TEMPORARY : 0;
+
+ if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_VDEV_SET_STATE, &zc) == 0)
+ return (0);
+
+ switch (errno) {
+ case EBUSY:
+
+ /*
+ * There are no other replicas of this device.
+ */
+ return (zfs_error(hdl, EZFS_NOREPLICAS, msg));
+
+ default:
+ return (zpool_standard_error(hdl, errno, msg));
+ }
+}
+
+/*
+ * Mark the given vdev faulted.
+ */
+int
+zpool_vdev_fault(zpool_handle_t *zhp, uint64_t guid)
+{
+ zfs_cmd_t zc = { 0 };
+ char msg[1024];
+ libzfs_handle_t *hdl = zhp->zpool_hdl;
+
+ (void) snprintf(msg, sizeof (msg),
+ dgettext(TEXT_DOMAIN, "cannot fault %llu"), guid);
+
+ (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+ zc.zc_guid = guid;
+ zc.zc_cookie = VDEV_STATE_FAULTED;
- if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_OFFLINE, &zc) == 0)
+ if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_SET_STATE, &zc) == 0)
return (0);
switch (errno) {
@@ -931,6 +1652,30 @@ zpool_vdev_offline(zpool_handle_t *zhp, const char *path, int istmp)
default:
return (zpool_standard_error(hdl, errno, msg));
}
+
+}
+
+/*
+ * Mark the given vdev degraded.
+ */
+int
+zpool_vdev_degrade(zpool_handle_t *zhp, uint64_t guid)
+{
+ zfs_cmd_t zc = { 0 };
+ char msg[1024];
+ libzfs_handle_t *hdl = zhp->zpool_hdl;
+
+ (void) snprintf(msg, sizeof (msg),
+ dgettext(TEXT_DOMAIN, "cannot degrade %llu"), guid);
+
+ (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+ zc.zc_guid = guid;
+ zc.zc_cookie = VDEV_STATE_DEGRADED;
+
+ if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_SET_STATE, &zc) == 0)
+ return (0);
+
+ return (zpool_standard_error(hdl, errno, msg));
}
/*
@@ -963,7 +1708,7 @@ is_replacing_spare(nvlist_t *search, nvlist_t *tgt, int which)
/*
* Attach new_disk (fully described by nvroot) to old_disk.
- * If 'replacing' is specified, tne new disk will replace the old one.
+ * If 'replacing' is specified, the new disk will replace the old one.
*/
int
zpool_vdev_attach(zpool_handle_t *zhp,
@@ -973,9 +1718,9 @@ zpool_vdev_attach(zpool_handle_t *zhp,
char msg[1024];
int ret;
nvlist_t *tgt;
- boolean_t avail_spare;
+ boolean_t avail_spare, l2cache, islog;
uint64_t val;
- char *path;
+ char *path, *newname;
nvlist_t **child;
uint_t children;
nvlist_t *config_root;
@@ -989,12 +1734,16 @@ zpool_vdev_attach(zpool_handle_t *zhp,
"cannot attach %s to %s"), new_disk, old_disk);
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if ((tgt = zpool_find_vdev(zhp, old_disk, &avail_spare)) == 0)
+ if ((tgt = zpool_find_vdev(zhp, old_disk, &avail_spare, &l2cache,
+ &islog)) == 0)
return (zfs_error(hdl, EZFS_NODEVICE, msg));
if (avail_spare)
return (zfs_error(hdl, EZFS_ISSPARE, msg));
+ if (l2cache)
+ return (zfs_error(hdl, EZFS_ISL2CACHE, msg));
+
verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0);
zc.zc_cookie = replacing;
@@ -1008,17 +1757,21 @@ zpool_vdev_attach(zpool_handle_t *zhp,
verify(nvlist_lookup_nvlist(zpool_get_config(zhp, NULL),
ZPOOL_CONFIG_VDEV_TREE, &config_root) == 0);
+ if ((newname = zpool_vdev_name(NULL, NULL, child[0])) == NULL)
+ return (-1);
+
/*
* If the target is a hot spare that has been swapped in, we can only
* replace it with another hot spare.
*/
if (replacing &&
nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_IS_SPARE, &val) == 0 &&
- nvlist_lookup_string(child[0], ZPOOL_CONFIG_PATH, &path) == 0 &&
- (zpool_find_vdev(zhp, path, &avail_spare) == NULL ||
- !avail_spare) && is_replacing_spare(config_root, tgt, 1)) {
+ (zpool_find_vdev(zhp, newname, &avail_spare, &l2cache,
+ NULL) == NULL || !avail_spare) &&
+ is_replacing_spare(config_root, tgt, 1)) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"can only be replaced by another hot spare"));
+ free(newname);
return (zfs_error(hdl, EZFS_BADTARGET, msg));
}
@@ -1028,17 +1781,21 @@ zpool_vdev_attach(zpool_handle_t *zhp,
*/
if (replacing &&
nvlist_lookup_string(child[0], ZPOOL_CONFIG_PATH, &path) == 0 &&
- zpool_find_vdev(zhp, path, &avail_spare) != NULL && avail_spare &&
+ zpool_find_vdev(zhp, newname, &avail_spare,
+ &l2cache, NULL) != NULL && avail_spare &&
is_replacing_spare(config_root, tgt, 0)) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"device has already been replaced with a spare"));
+ free(newname);
return (zfs_error(hdl, EZFS_BADTARGET, msg));
}
- if (zcmd_write_src_nvlist(hdl, &zc, nvroot, NULL) != 0)
+ free(newname);
+
+ if (zcmd_write_conf_nvlist(hdl, &zc, nvroot) != 0)
return (-1);
- ret = ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_ATTACH, &zc);
+ ret = zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_VDEV_ATTACH, &zc);
zcmd_free_nvlists(&zc);
@@ -1050,13 +1807,18 @@ zpool_vdev_attach(zpool_handle_t *zhp,
/*
* Can't attach to or replace this type of vdev.
*/
- if (replacing)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "cannot replace a replacing device"));
- else
+ if (replacing) {
+ if (islog)
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "cannot replace a log with a spare"));
+ else
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "cannot replace a replacing device"));
+ } else {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"can only attach to mirrors and top-level "
"disks"));
+ }
(void) zfs_error(hdl, EZFS_BADTARGET, msg);
break;
@@ -1116,22 +1878,26 @@ zpool_vdev_detach(zpool_handle_t *zhp, const char *path)
zfs_cmd_t zc = { 0 };
char msg[1024];
nvlist_t *tgt;
- boolean_t avail_spare;
+ boolean_t avail_spare, l2cache;
libzfs_handle_t *hdl = zhp->zpool_hdl;
(void) snprintf(msg, sizeof (msg),
dgettext(TEXT_DOMAIN, "cannot detach %s"), path);
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if ((tgt = zpool_find_vdev(zhp, path, &avail_spare)) == 0)
+ if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache,
+ NULL)) == 0)
return (zfs_error(hdl, EZFS_NODEVICE, msg));
if (avail_spare)
return (zfs_error(hdl, EZFS_ISSPARE, msg));
+ if (l2cache)
+ return (zfs_error(hdl, EZFS_ISL2CACHE, msg));
+
verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0);
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_VDEV_DETACH, &zc) == 0)
+ if (zfs_ioctl(hdl, ZFS_IOC_VDEV_DETACH, &zc) == 0)
return (0);
switch (errno) {
@@ -1160,7 +1926,8 @@ zpool_vdev_detach(zpool_handle_t *zhp, const char *path)
}
/*
- * Remove the given device. Currently, this is supported only for hot spares.
+ * Remove the given device. Currently, this is supported only for hot spares
+ * and level 2 cache devices.
*/
int
zpool_vdev_remove(zpool_handle_t *zhp, const char *path)
@@ -1168,25 +1935,27 @@ zpool_vdev_remove(zpool_handle_t *zhp, const char *path)
zfs_cmd_t zc = { 0 };
char msg[1024];
nvlist_t *tgt;
- boolean_t avail_spare;
+ boolean_t avail_spare, l2cache;
libzfs_handle_t *hdl = zhp->zpool_hdl;
(void) snprintf(msg, sizeof (msg),
dgettext(TEXT_DOMAIN, "cannot remove %s"), path);
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if ((tgt = zpool_find_vdev(zhp, path, &avail_spare)) == 0)
+ if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache,
+ NULL)) == 0)
return (zfs_error(hdl, EZFS_NODEVICE, msg));
- if (!avail_spare) {
+ if (!avail_spare && !l2cache) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "only inactive hot spares can be removed"));
+ "only inactive hot spares or cache devices "
+ "can be removed"));
return (zfs_error(hdl, EZFS_NODEVICE, msg));
}
verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0);
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_VDEV_REMOVE, &zc) == 0)
+ if (zfs_ioctl(hdl, ZFS_IOC_VDEV_REMOVE, &zc) == 0)
return (0);
return (zpool_standard_error(hdl, errno, msg));
@@ -1201,7 +1970,7 @@ zpool_clear(zpool_handle_t *zhp, const char *path)
zfs_cmd_t zc = { 0 };
char msg[1024];
nvlist_t *tgt;
- boolean_t avail_spare;
+ boolean_t avail_spare, l2cache;
libzfs_handle_t *hdl = zhp->zpool_hdl;
if (path)
@@ -1215,9 +1984,14 @@ zpool_clear(zpool_handle_t *zhp, const char *path)
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
if (path) {
- if ((tgt = zpool_find_vdev(zhp, path, &avail_spare)) == 0)
+ if ((tgt = zpool_find_vdev(zhp, path, &avail_spare,
+ &l2cache, NULL)) == 0)
return (zfs_error(hdl, EZFS_NODEVICE, msg));
+ /*
+ * Don't allow error clearing for hot spares. Do allow
+ * error clearing for l2cache devices.
+ */
if (avail_spare)
return (zfs_error(hdl, EZFS_ISSPARE, msg));
@@ -1225,6 +1999,29 @@ zpool_clear(zpool_handle_t *zhp, const char *path)
&zc.zc_guid) == 0);
}
+ if (zfs_ioctl(hdl, ZFS_IOC_CLEAR, &zc) == 0)
+ return (0);
+
+ return (zpool_standard_error(hdl, errno, msg));
+}
+
+/*
+ * Similar to zpool_clear(), but takes a GUID (used by fmd).
+ */
+int
+zpool_vdev_clear(zpool_handle_t *zhp, uint64_t guid)
+{
+ zfs_cmd_t zc = { 0 };
+ char msg[1024];
+ libzfs_handle_t *hdl = zhp->zpool_hdl;
+
+ (void) snprintf(msg, sizeof (msg),
+ dgettext(TEXT_DOMAIN, "cannot clear errors for %llx"),
+ guid);
+
+ (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+ zc.zc_guid = guid;
+
if (ioctl(hdl->libzfs_fd, ZFS_IOC_CLEAR, &zc) == 0)
return (0);
@@ -1342,12 +2139,15 @@ typedef struct zvol_cb {
static int
do_zvol_create(zfs_handle_t *zhp, void *data)
{
- int ret;
+ int ret = 0;
- if (ZFS_IS_VOLUME(zhp))
+ if (ZFS_IS_VOLUME(zhp)) {
(void) zvol_create_link(zhp->zfs_hdl, zhp->zfs_name);
+ ret = zfs_iter_snapshots(zhp, do_zvol_create, NULL);
+ }
- ret = zfs_iter_children(zhp, do_zvol_create, NULL);
+ if (ret == 0)
+ ret = zfs_iter_filesystems(zhp, do_zvol_create, NULL);
zfs_close(zhp);
@@ -1370,7 +2170,7 @@ zpool_create_zvol_links(zpool_handle_t *zhp)
zhp->zpool_name)) == NULL)
return (0);
- ret = zfs_iter_children(zfp, do_zvol_create, NULL);
+ ret = zfs_iter_filesystems(zfp, do_zvol_create, NULL);
zfs_close(zfp);
return (ret);
@@ -1492,6 +2292,8 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv)
char *path, *devid;
uint64_t value;
char buf[64];
+ vdev_stat_t *vs;
+ uint_t vsc;
if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT,
&value) == 0) {
@@ -1502,7 +2304,16 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv)
path = buf;
} else if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0) {
- if (zhp != NULL &&
+ /*
+ * If the device is dead (faulted, offline, etc) then don't
+ * bother opening it. Otherwise we may be forcing the user to
+ * open a misbehaving device, which can have undesirable
+ * effects.
+ */
+ if ((nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_STATS,
+ (uint64_t **)&vs, &vsc) != 0 ||
+ vs->vs_state >= VDEV_STATE_DEGRADED) &&
+ zhp != NULL &&
nvlist_lookup_string(nv, ZPOOL_CONFIG_DEVID, &devid) == 0) {
/*
* Determine if the current path is correct.
@@ -1585,6 +2396,8 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t **nverrlistp)
*/
verify(nvlist_lookup_uint64(zhp->zpool_config, ZPOOL_CONFIG_ERRCOUNT,
&count) == 0);
+ if (count == 0)
+ return (0);
if ((zc.zc_nvlist_dst = (uintptr_t)zfs_alloc(zhp->zpool_hdl,
count * sizeof (zbookmark_t))) == (uintptr_t)NULL)
return (-1);
@@ -1665,62 +2478,56 @@ nomem:
* Upgrade a ZFS pool to the latest on-disk version.
*/
int
-zpool_upgrade(zpool_handle_t *zhp)
+zpool_upgrade(zpool_handle_t *zhp, uint64_t new_version)
{
zfs_cmd_t zc = { 0 };
libzfs_handle_t *hdl = zhp->zpool_hdl;
(void) strcpy(zc.zc_name, zhp->zpool_name);
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_UPGRADE, &zc) != 0)
+ zc.zc_cookie = new_version;
+
+ if (zfs_ioctl(hdl, ZFS_IOC_POOL_UPGRADE, &zc) != 0)
return (zpool_standard_error_fmt(hdl, errno,
dgettext(TEXT_DOMAIN, "cannot upgrade '%s'"),
zhp->zpool_name));
-
return (0);
}
-/*
- * Log command history.
- *
- * 'pool' is B_TRUE if we are logging a command for 'zpool'; B_FALSE
- * otherwise ('zfs'). 'pool_create' is B_TRUE if we are logging the creation
- * of the pool; B_FALSE otherwise. 'path' is the pathanme containing the
- * poolname. 'argc' and 'argv' are used to construct the command string.
- */
void
-zpool_log_history(libzfs_handle_t *hdl, int argc, char **argv, const char *path,
- boolean_t pool, boolean_t pool_create)
+zpool_set_history_str(const char *subcommand, int argc, char **argv,
+ char *history_str)
{
- char cmd_buf[HIS_MAX_RECORD_LEN];
- char *dspath;
- zfs_cmd_t zc = { 0 };
int i;
- /* construct the command string */
- (void) strcpy(cmd_buf, pool ? "zpool" : "zfs");
- for (i = 0; i < argc; i++) {
- if (strlen(cmd_buf) + 1 + strlen(argv[i]) > HIS_MAX_RECORD_LEN)
+ (void) strlcpy(history_str, subcommand, HIS_MAX_RECORD_LEN);
+ for (i = 1; i < argc; i++) {
+ if (strlen(history_str) + 1 + strlen(argv[i]) >
+ HIS_MAX_RECORD_LEN)
break;
- (void) strcat(cmd_buf, " ");
- (void) strcat(cmd_buf, argv[i]);
+ (void) strlcat(history_str, " ", HIS_MAX_RECORD_LEN);
+ (void) strlcat(history_str, argv[i], HIS_MAX_RECORD_LEN);
}
+}
- /* figure out the poolname */
- dspath = strpbrk(path, "/@");
- if (dspath == NULL) {
- (void) strcpy(zc.zc_name, path);
- } else {
- (void) strncpy(zc.zc_name, path, dspath - path);
- zc.zc_name[dspath-path] = '\0';
- }
+/*
+ * Stage command history for logging.
+ */
+int
+zpool_stage_history(libzfs_handle_t *hdl, const char *history_str)
+{
+ if (history_str == NULL)
+ return (EINVAL);
+
+ if (strlen(history_str) > HIS_MAX_RECORD_LEN)
+ return (EINVAL);
- zc.zc_history = (uint64_t)(uintptr_t)cmd_buf;
- zc.zc_history_len = strlen(cmd_buf);
+ if (hdl->libzfs_log_str != NULL)
+ free(hdl->libzfs_log_str);
- /* overloading zc_history_offset */
- zc.zc_history_offset = pool_create;
+ if ((hdl->libzfs_log_str = strdup(history_str)) == NULL)
+ return (no_memory(hdl));
- (void) ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_LOG_HISTORY, &zc);
+ return (0);
}
/*
@@ -1906,150 +2713,296 @@ zpool_obj_to_path(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj,
free(mntpnt);
}
-int
-zpool_set_prop(zpool_handle_t *zhp, const char *propname, const char *propval)
+#define RDISK_ROOT "/dev/rdsk"
+#define BACKUP_SLICE "s2"
+/*
+ * Don't start the slice at the default block of 34; many storage
+ * devices will use a stripe width of 128k, so start there instead.
+ */
+#define NEW_START_BLOCK 256
+
+#if defined(sun)
+/*
+ * Read the EFI label from the config, if a label does not exist then
+ * pass back the error to the caller. If the caller has passed a non-NULL
+ * diskaddr argument then we set it to the starting address of the EFI
+ * partition.
+ */
+static int
+read_efi_label(nvlist_t *config, diskaddr_t *sb)
{
- zfs_cmd_t zc = { 0 };
- int ret = -1;
- char errbuf[1024];
- nvlist_t *nvl = NULL;
- nvlist_t *realprops;
+ char *path;
+ int fd;
+ char diskname[MAXPATHLEN];
+ int err = -1;
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
- zhp->zpool_name);
+ if (nvlist_lookup_string(config, ZPOOL_CONFIG_PATH, &path) != 0)
+ return (err);
- if (zpool_get_version(zhp) < ZFS_VERSION_BOOTFS) {
- zfs_error_aux(zhp->zpool_hdl,
- dgettext(TEXT_DOMAIN, "pool must be "
- "upgraded to support pool properties"));
- return (zfs_error(zhp->zpool_hdl, EZFS_BADVERSION, errbuf));
- }
+ (void) snprintf(diskname, sizeof (diskname), "%s%s", RDISK_ROOT,
+ strrchr(path, '/'));
+ if ((fd = open(diskname, O_RDONLY|O_NDELAY)) >= 0) {
+ struct dk_gpt *vtoc;
- if (zhp->zpool_props == NULL && zpool_get_all_props(zhp))
- return (zfs_error(zhp->zpool_hdl, EZFS_POOLPROPS, errbuf));
+ if ((err = efi_alloc_and_read(fd, &vtoc)) >= 0) {
+ if (sb != NULL)
+ *sb = vtoc->efi_parts[0].p_start;
+ efi_free(vtoc);
+ }
+ (void) close(fd);
+ }
+ return (err);
+}
- if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
- nvlist_add_string(nvl, propname, propval) != 0) {
- return (no_memory(zhp->zpool_hdl));
+/*
+ * determine where a partition starts on a disk in the current
+ * configuration
+ */
+static diskaddr_t
+find_start_block(nvlist_t *config)
+{
+ nvlist_t **child;
+ uint_t c, children;
+ diskaddr_t sb = MAXOFFSET_T;
+ uint64_t wholedisk;
+
+ if (nvlist_lookup_nvlist_array(config,
+ ZPOOL_CONFIG_CHILDREN, &child, &children) != 0) {
+ if (nvlist_lookup_uint64(config,
+ ZPOOL_CONFIG_WHOLE_DISK,
+ &wholedisk) != 0 || !wholedisk) {
+ return (MAXOFFSET_T);
+ }
+ if (read_efi_label(config, &sb) < 0)
+ sb = MAXOFFSET_T;
+ return (sb);
}
- if ((realprops = zfs_validate_properties(zhp->zpool_hdl, ZFS_TYPE_POOL,
- zhp->zpool_name, nvl, 0, NULL, errbuf)) == NULL) {
- nvlist_free(nvl);
- return (-1);
+ for (c = 0; c < children; c++) {
+ sb = find_start_block(child[c]);
+ if (sb != MAXOFFSET_T) {
+ return (sb);
+ }
}
+ return (MAXOFFSET_T);
+}
+#endif /* sun */
- nvlist_free(nvl);
- nvl = realprops;
+/*
+ * Label an individual disk. The name provided is the short name,
+ * stripped of any leading /dev path.
+ */
+int
+zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name)
+{
+#if defined(sun)
+ char path[MAXPATHLEN];
+ struct dk_gpt *vtoc;
+ int fd;
+ size_t resv = EFI_MIN_RESV_SIZE;
+ uint64_t slice_size;
+ diskaddr_t start_block;
+ char errbuf[1024];
- /*
- * Execute the corresponding ioctl() to set this property.
- */
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+ /* prepare an error message just in case */
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot label '%s'"), name);
- if (zcmd_write_src_nvlist(zhp->zpool_hdl, &zc, nvl, NULL) != 0)
- return (-1);
+ if (zhp) {
+ nvlist_t *nvroot;
- ret = ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_POOL_SET_PROPS, &zc);
- zcmd_free_nvlists(&zc);
+ verify(nvlist_lookup_nvlist(zhp->zpool_config,
+ ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
- if (ret)
- (void) zpool_standard_error(zhp->zpool_hdl, errno, errbuf);
+ if (zhp->zpool_start_block == 0)
+ start_block = find_start_block(nvroot);
+ else
+ start_block = zhp->zpool_start_block;
+ zhp->zpool_start_block = start_block;
+ } else {
+ /* new pool */
+ start_block = NEW_START_BLOCK;
+ }
- return (ret);
-}
+ (void) snprintf(path, sizeof (path), "%s/%s%s", RDISK_ROOT, name,
+ BACKUP_SLICE);
-int
-zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *propbuf,
- size_t proplen, zfs_source_t *srctype)
-{
- uint64_t value;
- char msg[1024], *strvalue;
- nvlist_t *nvp;
- zfs_source_t src = ZFS_SRC_NONE;
+ if ((fd = open(path, O_RDWR | O_NDELAY)) < 0) {
+ /*
+ * This shouldn't happen. We've long since verified that this
+ * is a valid device.
+ */
+ zfs_error_aux(hdl,
+ dgettext(TEXT_DOMAIN, "unable to open device"));
+ return (zfs_error(hdl, EZFS_OPENFAILED, errbuf));
+ }
- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
- "cannot get property '%s'"), zpool_prop_to_name(prop));
+ if (efi_alloc_and_init(fd, EFI_NUMPAR, &vtoc) != 0) {
+ /*
+ * The only way this can fail is if we run out of memory, or we
+ * were unable to read the disk's capacity
+ */
+ if (errno == ENOMEM)
+ (void) no_memory(hdl);
+
+ (void) close(fd);
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "unable to read disk capacity"), name);
- if (zpool_get_version(zhp) < ZFS_VERSION_BOOTFS) {
- zfs_error_aux(zhp->zpool_hdl,
- dgettext(TEXT_DOMAIN, "pool must be "
- "upgraded to support pool properties"));
- return (zfs_error(zhp->zpool_hdl, EZFS_BADVERSION, msg));
+ return (zfs_error(hdl, EZFS_NOCAP, errbuf));
}
- if (zhp->zpool_props == NULL && zpool_get_all_props(zhp))
- return (zfs_error(zhp->zpool_hdl, EZFS_POOLPROPS, msg));
+ slice_size = vtoc->efi_last_u_lba + 1;
+ slice_size -= EFI_MIN_RESV_SIZE;
+ if (start_block == MAXOFFSET_T)
+ start_block = NEW_START_BLOCK;
+ slice_size -= start_block;
+
+ vtoc->efi_parts[0].p_start = start_block;
+ vtoc->efi_parts[0].p_size = slice_size;
/*
- * the "name" property is special cased
+ * Why we use V_USR: V_BACKUP confuses users, and is considered
+ * disposable by some EFI utilities (since EFI doesn't have a backup
+ * slice). V_UNASSIGNED is supposed to be used only for zero size
+ * partitions, and efi_write() will fail if we use it. V_ROOT, V_BOOT,
+ * etc. were all pretty specific. V_USR is as close to reality as we
+ * can get, in the absence of V_OTHER.
*/
- if (!zfs_prop_valid_for_type(prop, ZFS_TYPE_POOL) &&
- prop != ZFS_PROP_NAME)
- return (-1);
+ vtoc->efi_parts[0].p_tag = V_USR;
+ (void) strcpy(vtoc->efi_parts[0].p_name, "zfs");
- switch (prop) {
- case ZFS_PROP_NAME:
- (void) strlcpy(propbuf, zhp->zpool_name, proplen);
- break;
+ vtoc->efi_parts[8].p_start = slice_size + start_block;
+ vtoc->efi_parts[8].p_size = resv;
+ vtoc->efi_parts[8].p_tag = V_RESERVED;
- case ZFS_PROP_BOOTFS:
- if (nvlist_lookup_nvlist(zhp->zpool_props,
- zpool_prop_to_name(prop), &nvp) != 0) {
- strvalue = (char *)zfs_prop_default_string(prop);
- if (strvalue == NULL)
- strvalue = "-";
- src = ZFS_SRC_DEFAULT;
- } else {
- VERIFY(nvlist_lookup_uint64(nvp,
- ZFS_PROP_SOURCE, &value) == 0);
- src = value;
- VERIFY(nvlist_lookup_string(nvp, ZFS_PROP_VALUE,
- &strvalue) == 0);
- if (strlen(strvalue) >= proplen)
- return (-1);
- }
- (void) strcpy(propbuf, strvalue);
- break;
+ if (efi_write(fd, vtoc) != 0) {
+ /*
+ * Some block drivers (like pcata) may not support EFI
+ * GPT labels. Print out a helpful error message dir-
+ * ecting the user to manually label the disk and give
+ * a specific slice.
+ */
+ (void) close(fd);
+ efi_free(vtoc);
- default:
- return (-1);
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "try using fdisk(1M) and then provide a specific slice"));
+ return (zfs_error(hdl, EZFS_LABELFAILED, errbuf));
}
- if (srctype)
- *srctype = src;
+
+ (void) close(fd);
+ efi_free(vtoc);
+#endif /* sun */
return (0);
}
-int
-zpool_get_proplist(libzfs_handle_t *hdl, char *fields, zpool_proplist_t **listp)
+static boolean_t
+supported_dump_vdev_type(libzfs_handle_t *hdl, nvlist_t *config, char *errbuf)
{
- return (zfs_get_proplist_common(hdl, fields, listp, ZFS_TYPE_POOL));
-}
+ char *type;
+ nvlist_t **child;
+ uint_t children, c;
+ verify(nvlist_lookup_string(config, ZPOOL_CONFIG_TYPE, &type) == 0);
+ if (strcmp(type, VDEV_TYPE_RAIDZ) == 0 ||
+ strcmp(type, VDEV_TYPE_FILE) == 0 ||
+ strcmp(type, VDEV_TYPE_LOG) == 0 ||
+ strcmp(type, VDEV_TYPE_MISSING) == 0) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "vdev type '%s' is not supported"), type);
+ (void) zfs_error(hdl, EZFS_VDEVNOTSUP, errbuf);
+ return (B_FALSE);
+ }
+ if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN,
+ &child, &children) == 0) {
+ for (c = 0; c < children; c++) {
+ if (!supported_dump_vdev_type(hdl, child[c], errbuf))
+ return (B_FALSE);
+ }
+ }
+ return (B_TRUE);
+}
+/*
+ * check if this zvol is allowable for use as a dump device; zero if
+ * it is, > 0 if it isn't, < 0 if it isn't a zvol
+ */
int
-zpool_expand_proplist(zpool_handle_t *zhp, zpool_proplist_t **plp)
+zvol_check_dump_config(char *arg)
{
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- zpool_proplist_t *entry;
- char buf[ZFS_MAXPROPLEN];
+ zpool_handle_t *zhp = NULL;
+ nvlist_t *config, *nvroot;
+ char *p, *volname;
+ nvlist_t **top;
+ uint_t toplevels;
+ libzfs_handle_t *hdl;
+ char errbuf[1024];
+ char poolname[ZPOOL_MAXNAMELEN];
+ int pathlen = strlen(ZVOL_FULL_DEV_DIR);
+ int ret = 1;
- if (zfs_expand_proplist_common(hdl, plp, ZFS_TYPE_POOL) != 0)
+ if (strncmp(arg, ZVOL_FULL_DEV_DIR, pathlen)) {
return (-1);
+ }
- for (entry = *plp; entry != NULL; entry = entry->pl_next) {
+ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+ "dump is not supported on device '%s'"), arg);
- if (entry->pl_fixed)
- continue;
+ if ((hdl = libzfs_init()) == NULL)
+ return (1);
+ libzfs_print_on_error(hdl, B_TRUE);
- if (entry->pl_prop != ZFS_PROP_INVAL &&
- zpool_get_prop(zhp, entry->pl_prop, buf, sizeof (buf),
- NULL) == 0) {
- if (strlen(buf) > entry->pl_width)
- entry->pl_width = strlen(buf);
- }
+ volname = arg + pathlen;
+
+ /* check the configuration of the pool */
+ if ((p = strchr(volname, '/')) == NULL) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "malformed dataset name"));
+ (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
+ return (1);
+ } else if (p - volname >= ZFS_MAXNAMELEN) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "dataset name is too long"));
+ (void) zfs_error(hdl, EZFS_NAMETOOLONG, errbuf);
+ return (1);
+ } else {
+ (void) strncpy(poolname, volname, p - volname);
+ poolname[p - volname] = '\0';
}
- return (0);
+ if ((zhp = zpool_open(hdl, poolname)) == NULL) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "could not open pool '%s'"), poolname);
+ (void) zfs_error(hdl, EZFS_OPENFAILED, errbuf);
+ goto out;
+ }
+ config = zpool_get_config(zhp, NULL);
+ if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
+ &nvroot) != 0) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "could not obtain vdev configuration for '%s'"), poolname);
+ (void) zfs_error(hdl, EZFS_INVALCONFIG, errbuf);
+ goto out;
+ }
+
+ verify(nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
+ &top, &toplevels) == 0);
+ if (toplevels != 1) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "'%s' has multiple top level vdevs"), poolname);
+ (void) zfs_error(hdl, EZFS_DEVOVERFLOW, errbuf);
+ goto out;
+ }
+
+ if (!supported_dump_vdev_type(hdl, top[0], errbuf)) {
+ goto out;
+ }
+ ret = 0;
+
+out:
+ if (zhp)
+ zpool_close(zhp);
+ libzfs_fini(hdl);
+ return (ret);
}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c
new file mode 100644
index 0000000..d1163ce
--- /dev/null
+++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c
@@ -0,0 +1,2103 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL 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.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <fcntl.h>
+#include <sys/mount.h>
+#include <sys/mntent.h>
+#include <sys/mnttab.h>
+#include <sys/avl.h>
+#include <stddef.h>
+
+#include <libzfs.h>
+
+#include "zfs_namecheck.h"
+#include "zfs_prop.h"
+#include "libzfs_impl.h"
+
+#include <fletcher.c> /* XXX */
+
+/* We need to use something for ENODATA. */
+#define ENODATA EIDRM
+
+static int zfs_receive_impl(libzfs_handle_t *, const char *, recvflags_t,
+ int, avl_tree_t *, char **);
+
+/*
+ * Routines for dealing with the AVL tree of fs-nvlists
+ */
+typedef struct fsavl_node {
+ avl_node_t fn_node;
+ nvlist_t *fn_nvfs;
+ char *fn_snapname;
+ uint64_t fn_guid;
+} fsavl_node_t;
+
+static int
+fsavl_compare(const void *arg1, const void *arg2)
+{
+ const fsavl_node_t *fn1 = arg1;
+ const fsavl_node_t *fn2 = arg2;
+
+ if (fn1->fn_guid > fn2->fn_guid)
+ return (+1);
+ else if (fn1->fn_guid < fn2->fn_guid)
+ return (-1);
+ else
+ return (0);
+}
+
+/*
+ * Given the GUID of a snapshot, find its containing filesystem and
+ * (optionally) name.
+ */
+static nvlist_t *
+fsavl_find(avl_tree_t *avl, uint64_t snapguid, char **snapname)
+{
+ fsavl_node_t fn_find;
+ fsavl_node_t *fn;
+
+ fn_find.fn_guid = snapguid;
+
+ fn = avl_find(avl, &fn_find, NULL);
+ if (fn) {
+ if (snapname)
+ *snapname = fn->fn_snapname;
+ return (fn->fn_nvfs);
+ }
+ return (NULL);
+}
+
+static void
+fsavl_destroy(avl_tree_t *avl)
+{
+ fsavl_node_t *fn;
+ void *cookie;
+
+ if (avl == NULL)
+ return;
+
+ cookie = NULL;
+ while ((fn = avl_destroy_nodes(avl, &cookie)) != NULL)
+ free(fn);
+ avl_destroy(avl);
+ free(avl);
+}
+
+static avl_tree_t *
+fsavl_create(nvlist_t *fss)
+{
+ avl_tree_t *fsavl;
+ nvpair_t *fselem = NULL;
+
+ if ((fsavl = malloc(sizeof (avl_tree_t))) == NULL)
+ return (NULL);
+
+ avl_create(fsavl, fsavl_compare, sizeof (fsavl_node_t),
+ offsetof(fsavl_node_t, fn_node));
+
+ while ((fselem = nvlist_next_nvpair(fss, fselem)) != NULL) {
+ nvlist_t *nvfs, *snaps;
+ nvpair_t *snapelem = NULL;
+
+ VERIFY(0 == nvpair_value_nvlist(fselem, &nvfs));
+ VERIFY(0 == nvlist_lookup_nvlist(nvfs, "snaps", &snaps));
+
+ while ((snapelem =
+ nvlist_next_nvpair(snaps, snapelem)) != NULL) {
+ fsavl_node_t *fn;
+ uint64_t guid;
+
+ VERIFY(0 == nvpair_value_uint64(snapelem, &guid));
+ if ((fn = malloc(sizeof (fsavl_node_t))) == NULL) {
+ fsavl_destroy(fsavl);
+ return (NULL);
+ }
+ fn->fn_nvfs = nvfs;
+ fn->fn_snapname = nvpair_name(snapelem);
+ fn->fn_guid = guid;
+
+ /*
+ * Note: if there are multiple snaps with the
+ * same GUID, we ignore all but one.
+ */
+ if (avl_find(fsavl, fn, NULL) == NULL)
+ avl_add(fsavl, fn);
+ else
+ free(fn);
+ }
+ }
+
+ return (fsavl);
+}
+
+/*
+ * Routines for dealing with the giant nvlist of fs-nvlists, etc.
+ */
+typedef struct send_data {
+ uint64_t parent_fromsnap_guid;
+ nvlist_t *parent_snaps;
+ nvlist_t *fss;
+ nvlist_t *snapprops;
+ const char *fromsnap;
+ const char *tosnap;
+
+ /*
+ * The header nvlist is of the following format:
+ * {
+ * "tosnap" -> string
+ * "fromsnap" -> string (if incremental)
+ * "fss" -> {
+ * id -> {
+ *
+ * "name" -> string (full name; for debugging)
+ * "parentfromsnap" -> number (guid of fromsnap in parent)
+ *
+ * "props" -> { name -> value (only if set here) }
+ * "snaps" -> { name (lastname) -> number (guid) }
+ * "snapprops" -> { name (lastname) -> { name -> value } }
+ *
+ * "origin" -> number (guid) (if clone)
+ * "sent" -> boolean (not on-disk)
+ * }
+ * }
+ * }
+ *
+ */
+} send_data_t;
+
+static void send_iterate_prop(zfs_handle_t *zhp, nvlist_t *nv);
+
+static int
+send_iterate_snap(zfs_handle_t *zhp, void *arg)
+{
+ send_data_t *sd = arg;
+ uint64_t guid = zhp->zfs_dmustats.dds_guid;
+ char *snapname;
+ nvlist_t *nv;
+
+ snapname = strrchr(zhp->zfs_name, '@')+1;
+
+ VERIFY(0 == nvlist_add_uint64(sd->parent_snaps, snapname, guid));
+ /*
+ * NB: if there is no fromsnap here (it's a newly created fs in
+ * an incremental replication), we will substitute the tosnap.
+ */
+ if ((sd->fromsnap && strcmp(snapname, sd->fromsnap) == 0) ||
+ (sd->parent_fromsnap_guid == 0 && sd->tosnap &&
+ strcmp(snapname, sd->tosnap) == 0)) {
+ sd->parent_fromsnap_guid = guid;
+ }
+
+ VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0));
+ send_iterate_prop(zhp, nv);
+ VERIFY(0 == nvlist_add_nvlist(sd->snapprops, snapname, nv));
+ nvlist_free(nv);
+
+ zfs_close(zhp);
+ return (0);
+}
+
+static void
+send_iterate_prop(zfs_handle_t *zhp, nvlist_t *nv)
+{
+ nvpair_t *elem = NULL;
+
+ while ((elem = nvlist_next_nvpair(zhp->zfs_props, elem)) != NULL) {
+ char *propname = nvpair_name(elem);
+ zfs_prop_t prop = zfs_name_to_prop(propname);
+ nvlist_t *propnv;
+
+ if (!zfs_prop_user(propname) && zfs_prop_readonly(prop))
+ continue;
+
+ verify(nvpair_value_nvlist(elem, &propnv) == 0);
+ if (prop == ZFS_PROP_QUOTA || prop == ZFS_PROP_RESERVATION) {
+ /* these guys are modifyable, but have no source */
+ uint64_t value;
+ verify(nvlist_lookup_uint64(propnv,
+ ZPROP_VALUE, &value) == 0);
+ if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
+ continue;
+ } else {
+ char *source;
+ if (nvlist_lookup_string(propnv,
+ ZPROP_SOURCE, &source) != 0)
+ continue;
+ if (strcmp(source, zhp->zfs_name) != 0)
+ continue;
+ }
+
+ if (zfs_prop_user(propname) ||
+ zfs_prop_get_type(prop) == PROP_TYPE_STRING) {
+ char *value;
+ verify(nvlist_lookup_string(propnv,
+ ZPROP_VALUE, &value) == 0);
+ VERIFY(0 == nvlist_add_string(nv, propname, value));
+ } else {
+ uint64_t value;
+ verify(nvlist_lookup_uint64(propnv,
+ ZPROP_VALUE, &value) == 0);
+ VERIFY(0 == nvlist_add_uint64(nv, propname, value));
+ }
+ }
+}
+
+static int
+send_iterate_fs(zfs_handle_t *zhp, void *arg)
+{
+ send_data_t *sd = arg;
+ nvlist_t *nvfs, *nv;
+ int rv;
+ uint64_t parent_fromsnap_guid_save = sd->parent_fromsnap_guid;
+ uint64_t guid = zhp->zfs_dmustats.dds_guid;
+ char guidstring[64];
+
+ VERIFY(0 == nvlist_alloc(&nvfs, NV_UNIQUE_NAME, 0));
+ VERIFY(0 == nvlist_add_string(nvfs, "name", zhp->zfs_name));
+ VERIFY(0 == nvlist_add_uint64(nvfs, "parentfromsnap",
+ sd->parent_fromsnap_guid));
+
+ if (zhp->zfs_dmustats.dds_origin[0]) {
+ zfs_handle_t *origin = zfs_open(zhp->zfs_hdl,
+ zhp->zfs_dmustats.dds_origin, ZFS_TYPE_SNAPSHOT);
+ if (origin == NULL)
+ return (-1);
+ VERIFY(0 == nvlist_add_uint64(nvfs, "origin",
+ origin->zfs_dmustats.dds_guid));
+ }
+
+ /* iterate over props */
+ VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0));
+ send_iterate_prop(zhp, nv);
+ VERIFY(0 == nvlist_add_nvlist(nvfs, "props", nv));
+ nvlist_free(nv);
+
+ /* iterate over snaps, and set sd->parent_fromsnap_guid */
+ sd->parent_fromsnap_guid = 0;
+ VERIFY(0 == nvlist_alloc(&sd->parent_snaps, NV_UNIQUE_NAME, 0));
+ VERIFY(0 == nvlist_alloc(&sd->snapprops, NV_UNIQUE_NAME, 0));
+ (void) zfs_iter_snapshots(zhp, send_iterate_snap, sd);
+ VERIFY(0 == nvlist_add_nvlist(nvfs, "snaps", sd->parent_snaps));
+ VERIFY(0 == nvlist_add_nvlist(nvfs, "snapprops", sd->snapprops));
+ nvlist_free(sd->parent_snaps);
+ nvlist_free(sd->snapprops);
+
+ /* add this fs to nvlist */
+ (void) snprintf(guidstring, sizeof (guidstring),
+ "0x%llx", (longlong_t)guid);
+ VERIFY(0 == nvlist_add_nvlist(sd->fss, guidstring, nvfs));
+ nvlist_free(nvfs);
+
+ /* iterate over children */
+ rv = zfs_iter_filesystems(zhp, send_iterate_fs, sd);
+
+ sd->parent_fromsnap_guid = parent_fromsnap_guid_save;
+
+ zfs_close(zhp);
+ return (rv);
+}
+
+static int
+gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
+ const char *tosnap, nvlist_t **nvlp, avl_tree_t **avlp)
+{
+ zfs_handle_t *zhp;
+ send_data_t sd = { 0 };
+ int error;
+
+ zhp = zfs_open(hdl, fsname, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
+ if (zhp == NULL)
+ return (EZFS_BADTYPE);
+
+ VERIFY(0 == nvlist_alloc(&sd.fss, NV_UNIQUE_NAME, 0));
+ sd.fromsnap = fromsnap;
+ sd.tosnap = tosnap;
+
+ if ((error = send_iterate_fs(zhp, &sd)) != 0) {
+ nvlist_free(sd.fss);
+ if (avlp != NULL)
+ *avlp = NULL;
+ *nvlp = NULL;
+ return (error);
+ }
+
+ if (avlp != NULL && (*avlp = fsavl_create(sd.fss)) == NULL) {
+ nvlist_free(sd.fss);
+ *nvlp = NULL;
+ return (EZFS_NOMEM);
+ }
+
+ *nvlp = sd.fss;
+ return (0);
+}
+
+/*
+ * Routines for dealing with the sorted snapshot functionality
+ */
+typedef struct zfs_node {
+ zfs_handle_t *zn_handle;
+ avl_node_t zn_avlnode;
+} zfs_node_t;
+
+static int
+zfs_sort_snaps(zfs_handle_t *zhp, void *data)
+{
+ avl_tree_t *avl = data;
+ zfs_node_t *node = zfs_alloc(zhp->zfs_hdl, sizeof (zfs_node_t));
+
+ node->zn_handle = zhp;
+ avl_add(avl, node);
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+zfs_snapshot_compare(const void *larg, const void *rarg)
+{
+ zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
+ zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
+ uint64_t lcreate, rcreate;
+
+ /*
+ * Sort them according to creation time. We use the hidden
+ * CREATETXG property to get an absolute ordering of snapshots.
+ */
+ lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG);
+ rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG);
+
+ if (lcreate < rcreate)
+ return (-1);
+ else if (lcreate > rcreate)
+ return (+1);
+ else
+ return (0);
+}
+
+static int
+zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data)
+{
+ int ret = 0;
+ zfs_node_t *node;
+ avl_tree_t avl;
+ void *cookie = NULL;
+
+ avl_create(&avl, zfs_snapshot_compare,
+ sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode));
+
+ ret = zfs_iter_snapshots(zhp, zfs_sort_snaps, &avl);
+
+ for (node = avl_first(&avl); node != NULL; node = AVL_NEXT(&avl, node))
+ ret |= callback(node->zn_handle, data);
+
+ while ((node = avl_destroy_nodes(&avl, &cookie)) != NULL)
+ free(node);
+
+ avl_destroy(&avl);
+
+ return (ret);
+}
+
+/*
+ * Routines specific to "zfs send"
+ */
+typedef struct send_dump_data {
+ /* these are all just the short snapname (the part after the @) */
+ const char *fromsnap;
+ const char *tosnap;
+ char lastsnap[ZFS_MAXNAMELEN];
+ boolean_t seenfrom, seento, replicate, doall, fromorigin;
+ boolean_t verbose;
+ int outfd;
+ boolean_t err;
+ nvlist_t *fss;
+ avl_tree_t *fsavl;
+} send_dump_data_t;
+
+/*
+ * Dumps a backup of the given snapshot (incremental from fromsnap if it's not
+ * NULL) to the file descriptor specified by outfd.
+ */
+static int
+dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, boolean_t fromorigin,
+ int outfd)
+{
+ zfs_cmd_t zc = { 0 };
+ libzfs_handle_t *hdl = zhp->zfs_hdl;
+
+ assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
+ assert(fromsnap == NULL || fromsnap[0] == '\0' || !fromorigin);
+
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+ if (fromsnap)
+ (void) strlcpy(zc.zc_value, fromsnap, sizeof (zc.zc_value));
+ zc.zc_cookie = outfd;
+ zc.zc_obj = fromorigin;
+
+ if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SEND, &zc) != 0) {
+ char errbuf[1024];
+ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+ "warning: cannot send '%s'"), zhp->zfs_name);
+
+ switch (errno) {
+
+ case EXDEV:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "not an earlier snapshot from the same fs"));
+ return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
+
+ case ENOENT:
+ if (zfs_dataset_exists(hdl, zc.zc_name,
+ ZFS_TYPE_SNAPSHOT)) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "incremental source (@%s) does not exist"),
+ zc.zc_value);
+ }
+ return (zfs_error(hdl, EZFS_NOENT, errbuf));
+
+ case EDQUOT:
+ case EFBIG:
+ case EIO:
+ case ENOLINK:
+ case ENOSPC:
+ case ENXIO:
+ case EPIPE:
+ case ERANGE:
+ case EFAULT:
+ case EROFS:
+ zfs_error_aux(hdl, strerror(errno));
+ return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
+
+ default:
+ return (zfs_standard_error(hdl, errno, errbuf));
+ }
+ }
+
+ return (0);
+}
+
+static int
+dump_snapshot(zfs_handle_t *zhp, void *arg)
+{
+ send_dump_data_t *sdd = arg;
+ const char *thissnap;
+ int err;
+
+ thissnap = strchr(zhp->zfs_name, '@') + 1;
+
+ if (sdd->fromsnap && !sdd->seenfrom &&
+ strcmp(sdd->fromsnap, thissnap) == 0) {
+ sdd->seenfrom = B_TRUE;
+ (void) strcpy(sdd->lastsnap, thissnap);
+ zfs_close(zhp);
+ return (0);
+ }
+
+ if (sdd->seento || !sdd->seenfrom) {
+ zfs_close(zhp);
+ return (0);
+ }
+
+ /* send it */
+ if (sdd->verbose) {
+ (void) fprintf(stderr, "sending from @%s to %s\n",
+ sdd->lastsnap, zhp->zfs_name);
+ }
+
+ err = dump_ioctl(zhp, sdd->lastsnap,
+ sdd->lastsnap[0] == '\0' && (sdd->fromorigin || sdd->replicate),
+ sdd->outfd);
+
+ if (!sdd->seento && strcmp(sdd->tosnap, thissnap) == 0)
+ sdd->seento = B_TRUE;
+
+ (void) strcpy(sdd->lastsnap, thissnap);
+ zfs_close(zhp);
+ return (err);
+}
+
+static int
+dump_filesystem(zfs_handle_t *zhp, void *arg)
+{
+ int rv = 0;
+ send_dump_data_t *sdd = arg;
+ boolean_t missingfrom = B_FALSE;
+ zfs_cmd_t zc = { 0 };
+
+ (void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s",
+ zhp->zfs_name, sdd->tosnap);
+ if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) {
+ (void) fprintf(stderr, "WARNING: "
+ "could not send %s@%s: does not exist\n",
+ zhp->zfs_name, sdd->tosnap);
+ sdd->err = B_TRUE;
+ return (0);
+ }
+
+ if (sdd->replicate && sdd->fromsnap) {
+ /*
+ * If this fs does not have fromsnap, and we're doing
+ * recursive, we need to send a full stream from the
+ * beginning (or an incremental from the origin if this
+ * is a clone). If we're doing non-recursive, then let
+ * them get the error.
+ */
+ (void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s",
+ zhp->zfs_name, sdd->fromsnap);
+ if (ioctl(zhp->zfs_hdl->libzfs_fd,
+ ZFS_IOC_OBJSET_STATS, &zc) != 0) {
+ missingfrom = B_TRUE;
+ }
+ }
+
+ if (sdd->doall) {
+ sdd->seenfrom = sdd->seento = sdd->lastsnap[0] = 0;
+ if (sdd->fromsnap == NULL || missingfrom)
+ sdd->seenfrom = B_TRUE;
+
+ rv = zfs_iter_snapshots_sorted(zhp, dump_snapshot, arg);
+ if (!sdd->seenfrom) {
+ (void) fprintf(stderr,
+ "WARNING: could not send %s@%s:\n"
+ "incremental source (%s@%s) does not exist\n",
+ zhp->zfs_name, sdd->tosnap,
+ zhp->zfs_name, sdd->fromsnap);
+ sdd->err = B_TRUE;
+ } else if (!sdd->seento) {
+ (void) fprintf(stderr,
+ "WARNING: could not send %s@%s:\n"
+ "incremental source (%s@%s) "
+ "is not earlier than it\n",
+ zhp->zfs_name, sdd->tosnap,
+ zhp->zfs_name, sdd->fromsnap);
+ sdd->err = B_TRUE;
+ }
+ } else {
+ zfs_handle_t *snapzhp;
+ char snapname[ZFS_MAXNAMELEN];
+
+ (void) snprintf(snapname, sizeof (snapname), "%s@%s",
+ zfs_get_name(zhp), sdd->tosnap);
+ snapzhp = zfs_open(zhp->zfs_hdl, snapname, ZFS_TYPE_SNAPSHOT);
+ if (snapzhp == NULL) {
+ rv = -1;
+ } else {
+ rv = dump_ioctl(snapzhp,
+ missingfrom ? NULL : sdd->fromsnap,
+ sdd->fromorigin || missingfrom,
+ sdd->outfd);
+ sdd->seento = B_TRUE;
+ zfs_close(snapzhp);
+ }
+ }
+
+ return (rv);
+}
+
+static int
+dump_filesystems(zfs_handle_t *rzhp, void *arg)
+{
+ send_dump_data_t *sdd = arg;
+ nvpair_t *fspair;
+ boolean_t needagain, progress;
+
+ if (!sdd->replicate)
+ return (dump_filesystem(rzhp, sdd));
+
+again:
+ needagain = progress = B_FALSE;
+ for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
+ fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
+ nvlist_t *fslist;
+ char *fsname;
+ zfs_handle_t *zhp;
+ int err;
+ uint64_t origin_guid = 0;
+ nvlist_t *origin_nv;
+
+ VERIFY(nvpair_value_nvlist(fspair, &fslist) == 0);
+ if (nvlist_lookup_boolean(fslist, "sent") == 0)
+ continue;
+
+ VERIFY(nvlist_lookup_string(fslist, "name", &fsname) == 0);
+ (void) nvlist_lookup_uint64(fslist, "origin", &origin_guid);
+
+ origin_nv = fsavl_find(sdd->fsavl, origin_guid, NULL);
+ if (origin_nv &&
+ nvlist_lookup_boolean(origin_nv, "sent") == ENOENT) {
+ /*
+ * origin has not been sent yet;
+ * skip this clone.
+ */
+ needagain = B_TRUE;
+ continue;
+ }
+
+ zhp = zfs_open(rzhp->zfs_hdl, fsname, ZFS_TYPE_DATASET);
+ if (zhp == NULL)
+ return (-1);
+ err = dump_filesystem(zhp, sdd);
+ VERIFY(nvlist_add_boolean(fslist, "sent") == 0);
+ progress = B_TRUE;
+ zfs_close(zhp);
+ if (err)
+ return (err);
+ }
+ if (needagain) {
+ assert(progress);
+ goto again;
+ }
+ return (0);
+}
+
+/*
+ * Dumps a backup of tosnap, incremental from fromsnap if it isn't NULL.
+ * If 'doall', dump all intermediate snaps.
+ * If 'replicate', dump special header and do recursively.
+ */
+int
+zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
+ boolean_t replicate, boolean_t doall, boolean_t fromorigin,
+ boolean_t verbose, int outfd)
+{
+ char errbuf[1024];
+ send_dump_data_t sdd = { 0 };
+ int err;
+ nvlist_t *fss = NULL;
+ avl_tree_t *fsavl = NULL;
+
+ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+ "cannot send '%s'"), zhp->zfs_name);
+
+ if (fromsnap && fromsnap[0] == '\0') {
+ zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
+ "zero-length incremental source"));
+ return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf));
+ }
+
+ if (replicate || doall) {
+ dmu_replay_record_t drr = { 0 };
+ char *packbuf = NULL;
+ size_t buflen = 0;
+ zio_cksum_t zc = { 0 };
+
+ assert(fromsnap || doall);
+
+ if (replicate) {
+ nvlist_t *hdrnv;
+
+ VERIFY(0 == nvlist_alloc(&hdrnv, NV_UNIQUE_NAME, 0));
+ if (fromsnap) {
+ VERIFY(0 == nvlist_add_string(hdrnv,
+ "fromsnap", fromsnap));
+ }
+ VERIFY(0 == nvlist_add_string(hdrnv, "tosnap", tosnap));
+
+ err = gather_nvlist(zhp->zfs_hdl, zhp->zfs_name,
+ fromsnap, tosnap, &fss, &fsavl);
+ if (err)
+ return (err);
+ VERIFY(0 == nvlist_add_nvlist(hdrnv, "fss", fss));
+ err = nvlist_pack(hdrnv, &packbuf, &buflen,
+ NV_ENCODE_XDR, 0);
+ nvlist_free(hdrnv);
+ if (err) {
+ fsavl_destroy(fsavl);
+ nvlist_free(fss);
+ return (zfs_standard_error(zhp->zfs_hdl,
+ err, errbuf));
+ }
+ }
+
+ /* write first begin record */
+ drr.drr_type = DRR_BEGIN;
+ drr.drr_u.drr_begin.drr_magic = DMU_BACKUP_MAGIC;
+ drr.drr_u.drr_begin.drr_version = DMU_BACKUP_HEADER_VERSION;
+ (void) snprintf(drr.drr_u.drr_begin.drr_toname,
+ sizeof (drr.drr_u.drr_begin.drr_toname),
+ "%s@%s", zhp->zfs_name, tosnap);
+ drr.drr_payloadlen = buflen;
+ fletcher_4_incremental_native(&drr, sizeof (drr), &zc);
+ err = write(outfd, &drr, sizeof (drr));
+
+ /* write header nvlist */
+ if (err != -1) {
+ fletcher_4_incremental_native(packbuf, buflen, &zc);
+ err = write(outfd, packbuf, buflen);
+ }
+ free(packbuf);
+ if (err == -1) {
+ fsavl_destroy(fsavl);
+ nvlist_free(fss);
+ return (zfs_standard_error(zhp->zfs_hdl,
+ errno, errbuf));
+ }
+
+ /* write end record */
+ if (err != -1) {
+ bzero(&drr, sizeof (drr));
+ drr.drr_type = DRR_END;
+ drr.drr_u.drr_end.drr_checksum = zc;
+ err = write(outfd, &drr, sizeof (drr));
+ if (err == -1) {
+ fsavl_destroy(fsavl);
+ nvlist_free(fss);
+ return (zfs_standard_error(zhp->zfs_hdl,
+ errno, errbuf));
+ }
+ }
+ }
+
+ /* dump each stream */
+ sdd.fromsnap = fromsnap;
+ sdd.tosnap = tosnap;
+ sdd.outfd = outfd;
+ sdd.replicate = replicate;
+ sdd.doall = doall;
+ sdd.fromorigin = fromorigin;
+ sdd.fss = fss;
+ sdd.fsavl = fsavl;
+ sdd.verbose = verbose;
+ err = dump_filesystems(zhp, &sdd);
+ fsavl_destroy(fsavl);
+ nvlist_free(fss);
+
+ if (replicate || doall) {
+ /*
+ * write final end record. NB: want to do this even if
+ * there was some error, because it might not be totally
+ * failed.
+ */
+ dmu_replay_record_t drr = { 0 };
+ drr.drr_type = DRR_END;
+ if (write(outfd, &drr, sizeof (drr)) == -1) {
+ return (zfs_standard_error(zhp->zfs_hdl,
+ errno, errbuf));
+ }
+ }
+
+ return (err || sdd.err);
+}
+
+/*
+ * Routines specific to "zfs recv"
+ */
+
+static int
+recv_read(libzfs_handle_t *hdl, int fd, void *buf, int ilen,
+ boolean_t byteswap, zio_cksum_t *zc)
+{
+ char *cp = buf;
+ int rv;
+ int len = ilen;
+
+ do {
+ rv = read(fd, cp, len);
+ cp += rv;
+ len -= rv;
+ } while (rv > 0);
+
+ if (rv < 0 || len != 0) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "failed to read from stream"));
+ return (zfs_error(hdl, EZFS_BADSTREAM, dgettext(TEXT_DOMAIN,
+ "cannot receive")));
+ }
+
+ if (zc) {
+ if (byteswap)
+ fletcher_4_incremental_byteswap(buf, ilen, zc);
+ else
+ fletcher_4_incremental_native(buf, ilen, zc);
+ }
+ return (0);
+}
+
+static int
+recv_read_nvlist(libzfs_handle_t *hdl, int fd, int len, nvlist_t **nvp,
+ boolean_t byteswap, zio_cksum_t *zc)
+{
+ char *buf;
+ int err;
+
+ buf = zfs_alloc(hdl, len);
+ if (buf == NULL)
+ return (ENOMEM);
+
+ err = recv_read(hdl, fd, buf, len, byteswap, zc);
+ if (err != 0) {
+ free(buf);
+ return (err);
+ }
+
+ err = nvlist_unpack(buf, len, nvp, 0);
+ free(buf);
+ if (err != 0) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
+ "stream (malformed nvlist)"));
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static int
+recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
+ int baselen, char *newname, recvflags_t flags)
+{
+ static int seq;
+ zfs_cmd_t zc = { 0 };
+ int err;
+ prop_changelist_t *clp;
+ zfs_handle_t *zhp;
+
+ zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
+ if (zhp == NULL)
+ return (-1);
+ clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
+ flags.force ? MS_FORCE : 0);
+ zfs_close(zhp);
+ if (clp == NULL)
+ return (-1);
+ err = changelist_prefix(clp);
+ if (err)
+ return (err);
+
+ if (tryname) {
+ (void) strcpy(newname, tryname);
+
+ zc.zc_objset_type = DMU_OST_ZFS;
+ (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
+ (void) strlcpy(zc.zc_value, tryname, sizeof (zc.zc_value));
+
+ if (flags.verbose) {
+ (void) printf("attempting rename %s to %s\n",
+ zc.zc_name, zc.zc_value);
+ }
+ err = ioctl(hdl->libzfs_fd, ZFS_IOC_RENAME, &zc);
+ if (err == 0)
+ changelist_rename(clp, name, tryname);
+ } else {
+ err = ENOENT;
+ }
+
+ if (err != 0 && strncmp(name+baselen, "recv-", 5) != 0) {
+ seq++;
+
+ (void) strncpy(newname, name, baselen);
+ (void) snprintf(newname+baselen, ZFS_MAXNAMELEN-baselen,
+ "recv-%u-%u", getpid(), seq);
+ (void) strlcpy(zc.zc_value, newname, sizeof (zc.zc_value));
+
+ if (flags.verbose) {
+ (void) printf("failed - trying rename %s to %s\n",
+ zc.zc_name, zc.zc_value);
+ }
+ err = ioctl(hdl->libzfs_fd, ZFS_IOC_RENAME, &zc);
+ if (err == 0)
+ changelist_rename(clp, name, newname);
+ if (err && flags.verbose) {
+ (void) printf("failed (%u) - "
+ "will try again on next pass\n", errno);
+ }
+ err = EAGAIN;
+ } else if (flags.verbose) {
+ if (err == 0)
+ (void) printf("success\n");
+ else
+ (void) printf("failed (%u)\n", errno);
+ }
+
+ (void) changelist_postfix(clp);
+ changelist_free(clp);
+
+ return (err);
+}
+
+static int
+recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
+ char *newname, recvflags_t flags)
+{
+ zfs_cmd_t zc = { 0 };
+ int err = 0;
+ prop_changelist_t *clp;
+ zfs_handle_t *zhp;
+
+ zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
+ if (zhp == NULL)
+ return (-1);
+ clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
+ flags.force ? MS_FORCE : 0);
+ zfs_close(zhp);
+ if (clp == NULL)
+ return (-1);
+ err = changelist_prefix(clp);
+ if (err)
+ return (err);
+
+ zc.zc_objset_type = DMU_OST_ZFS;
+ (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
+
+ if (flags.verbose)
+ (void) printf("attempting destroy %s\n", zc.zc_name);
+ err = ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc);
+
+ if (err == 0) {
+ if (flags.verbose)
+ (void) printf("success\n");
+ changelist_remove(clp, zc.zc_name);
+ }
+
+ (void) changelist_postfix(clp);
+ changelist_free(clp);
+
+ if (err != 0)
+ err = recv_rename(hdl, name, NULL, baselen, newname, flags);
+
+ return (err);
+}
+
+typedef struct guid_to_name_data {
+ uint64_t guid;
+ char *name;
+} guid_to_name_data_t;
+
+static int
+guid_to_name_cb(zfs_handle_t *zhp, void *arg)
+{
+ guid_to_name_data_t *gtnd = arg;
+ int err;
+
+ if (zhp->zfs_dmustats.dds_guid == gtnd->guid) {
+ (void) strcpy(gtnd->name, zhp->zfs_name);
+ return (EEXIST);
+ }
+ err = zfs_iter_children(zhp, guid_to_name_cb, gtnd);
+ zfs_close(zhp);
+ return (err);
+}
+
+static int
+guid_to_name(libzfs_handle_t *hdl, const char *parent, uint64_t guid,
+ char *name)
+{
+ /* exhaustive search all local snapshots */
+ guid_to_name_data_t gtnd;
+ int err = 0;
+ zfs_handle_t *zhp;
+ char *cp;
+
+ gtnd.guid = guid;
+ gtnd.name = name;
+
+ if (strchr(parent, '@') == NULL) {
+ zhp = make_dataset_handle(hdl, parent);
+ if (zhp != NULL) {
+ err = zfs_iter_children(zhp, guid_to_name_cb, &gtnd);
+ zfs_close(zhp);
+ if (err == EEXIST)
+ return (0);
+ }
+ }
+
+ cp = strchr(parent, '/');
+ if (cp)
+ *cp = '\0';
+ zhp = make_dataset_handle(hdl, parent);
+ if (cp)
+ *cp = '/';
+
+ if (zhp) {
+ err = zfs_iter_children(zhp, guid_to_name_cb, &gtnd);
+ zfs_close(zhp);
+ }
+
+ return (err == EEXIST ? 0 : ENOENT);
+
+}
+
+/*
+ * Return true if dataset guid1 is created before guid2.
+ */
+static int
+created_before(libzfs_handle_t *hdl, avl_tree_t *avl,
+ uint64_t guid1, uint64_t guid2)
+{
+ nvlist_t *nvfs;
+ char *fsname, *snapname;
+ char buf[ZFS_MAXNAMELEN];
+ int rv;
+ zfs_node_t zn1, zn2;
+
+ if (guid2 == 0)
+ return (0);
+ if (guid1 == 0)
+ return (1);
+
+ nvfs = fsavl_find(avl, guid1, &snapname);
+ VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
+ (void) snprintf(buf, sizeof (buf), "%s@%s", fsname, snapname);
+ zn1.zn_handle = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
+ if (zn1.zn_handle == NULL)
+ return (-1);
+
+ nvfs = fsavl_find(avl, guid2, &snapname);
+ VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
+ (void) snprintf(buf, sizeof (buf), "%s@%s", fsname, snapname);
+ zn2.zn_handle = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
+ if (zn2.zn_handle == NULL) {
+ zfs_close(zn2.zn_handle);
+ return (-1);
+ }
+
+ rv = (zfs_snapshot_compare(&zn1, &zn2) == -1);
+
+ zfs_close(zn1.zn_handle);
+ zfs_close(zn2.zn_handle);
+
+ return (rv);
+}
+
+static int
+recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs,
+ recvflags_t flags, nvlist_t *stream_nv, avl_tree_t *stream_avl)
+{
+ nvlist_t *local_nv;
+ avl_tree_t *local_avl;
+ nvpair_t *fselem, *nextfselem;
+ char *tosnap, *fromsnap;
+ char newname[ZFS_MAXNAMELEN];
+ int error;
+ boolean_t needagain, progress;
+
+ VERIFY(0 == nvlist_lookup_string(stream_nv, "fromsnap", &fromsnap));
+ VERIFY(0 == nvlist_lookup_string(stream_nv, "tosnap", &tosnap));
+
+ if (flags.dryrun)
+ return (0);
+
+again:
+ needagain = progress = B_FALSE;
+
+ if ((error = gather_nvlist(hdl, tofs, fromsnap, NULL,
+ &local_nv, &local_avl)) != 0)
+ return (error);
+
+ /*
+ * Process deletes and renames
+ */
+ for (fselem = nvlist_next_nvpair(local_nv, NULL);
+ fselem; fselem = nextfselem) {
+ nvlist_t *nvfs, *snaps;
+ nvlist_t *stream_nvfs = NULL;
+ nvpair_t *snapelem, *nextsnapelem;
+ uint64_t fromguid = 0;
+ uint64_t originguid = 0;
+ uint64_t stream_originguid = 0;
+ uint64_t parent_fromsnap_guid, stream_parent_fromsnap_guid;
+ char *fsname, *stream_fsname;
+
+ nextfselem = nvlist_next_nvpair(local_nv, fselem);
+
+ VERIFY(0 == nvpair_value_nvlist(fselem, &nvfs));
+ VERIFY(0 == nvlist_lookup_nvlist(nvfs, "snaps", &snaps));
+ VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
+ VERIFY(0 == nvlist_lookup_uint64(nvfs, "parentfromsnap",
+ &parent_fromsnap_guid));
+ (void) nvlist_lookup_uint64(nvfs, "origin", &originguid);
+
+ /*
+ * First find the stream's fs, so we can check for
+ * a different origin (due to "zfs promote")
+ */
+ for (snapelem = nvlist_next_nvpair(snaps, NULL);
+ snapelem; snapelem = nvlist_next_nvpair(snaps, snapelem)) {
+ uint64_t thisguid;
+
+ VERIFY(0 == nvpair_value_uint64(snapelem, &thisguid));
+ stream_nvfs = fsavl_find(stream_avl, thisguid, NULL);
+
+ if (stream_nvfs != NULL)
+ break;
+ }
+
+ /* check for promote */
+ (void) nvlist_lookup_uint64(stream_nvfs, "origin",
+ &stream_originguid);
+ if (stream_nvfs && originguid != stream_originguid) {
+ switch (created_before(hdl, local_avl,
+ stream_originguid, originguid)) {
+ case 1: {
+ /* promote it! */
+ zfs_cmd_t zc = { 0 };
+ nvlist_t *origin_nvfs;
+ char *origin_fsname;
+
+ if (flags.verbose)
+ (void) printf("promoting %s\n", fsname);
+
+ origin_nvfs = fsavl_find(local_avl, originguid,
+ NULL);
+ VERIFY(0 == nvlist_lookup_string(origin_nvfs,
+ "name", &origin_fsname));
+ (void) strlcpy(zc.zc_value, origin_fsname,
+ sizeof (zc.zc_value));
+ (void) strlcpy(zc.zc_name, fsname,
+ sizeof (zc.zc_name));
+ error = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc);
+ if (error == 0)
+ progress = B_TRUE;
+ break;
+ }
+ default:
+ break;
+ case -1:
+ fsavl_destroy(local_avl);
+ nvlist_free(local_nv);
+ return (-1);
+ }
+ /*
+ * We had/have the wrong origin, therefore our
+ * list of snapshots is wrong. Need to handle
+ * them on the next pass.
+ */
+ needagain = B_TRUE;
+ continue;
+ }
+
+ for (snapelem = nvlist_next_nvpair(snaps, NULL);
+ snapelem; snapelem = nextsnapelem) {
+ uint64_t thisguid;
+ char *stream_snapname;
+ nvlist_t *found, *props;
+
+ nextsnapelem = nvlist_next_nvpair(snaps, snapelem);
+
+ VERIFY(0 == nvpair_value_uint64(snapelem, &thisguid));
+ found = fsavl_find(stream_avl, thisguid,
+ &stream_snapname);
+
+ /* check for delete */
+ if (found == NULL) {
+ char name[ZFS_MAXNAMELEN];
+
+ if (!flags.force)
+ continue;
+
+ (void) snprintf(name, sizeof (name), "%s@%s",
+ fsname, nvpair_name(snapelem));
+
+ error = recv_destroy(hdl, name,
+ strlen(fsname)+1, newname, flags);
+ if (error)
+ needagain = B_TRUE;
+ else
+ progress = B_TRUE;
+ continue;
+ }
+
+ stream_nvfs = found;
+
+ if (0 == nvlist_lookup_nvlist(stream_nvfs, "snapprops",
+ &props) && 0 == nvlist_lookup_nvlist(props,
+ stream_snapname, &props)) {
+ zfs_cmd_t zc = { 0 };
+
+ zc.zc_cookie = B_TRUE; /* clear current props */
+ (void) snprintf(zc.zc_name, sizeof (zc.zc_name),
+ "%s@%s", fsname, nvpair_name(snapelem));
+ if (zcmd_write_src_nvlist(hdl, &zc,
+ props) == 0) {
+ (void) zfs_ioctl(hdl,
+ ZFS_IOC_SET_PROP, &zc);
+ zcmd_free_nvlists(&zc);
+ }
+ }
+
+ /* check for different snapname */
+ if (strcmp(nvpair_name(snapelem),
+ stream_snapname) != 0) {
+ char name[ZFS_MAXNAMELEN];
+ char tryname[ZFS_MAXNAMELEN];
+
+ (void) snprintf(name, sizeof (name), "%s@%s",
+ fsname, nvpair_name(snapelem));
+ (void) snprintf(tryname, sizeof (name), "%s@%s",
+ fsname, stream_snapname);
+
+ error = recv_rename(hdl, name, tryname,
+ strlen(fsname)+1, newname, flags);
+ if (error)
+ needagain = B_TRUE;
+ else
+ progress = B_TRUE;
+ }
+
+ if (strcmp(stream_snapname, fromsnap) == 0)
+ fromguid = thisguid;
+ }
+
+ /* check for delete */
+ if (stream_nvfs == NULL) {
+ if (!flags.force)
+ continue;
+
+ error = recv_destroy(hdl, fsname, strlen(tofs)+1,
+ newname, flags);
+ if (error)
+ needagain = B_TRUE;
+ else
+ progress = B_TRUE;
+ continue;
+ }
+
+ if (fromguid == 0 && flags.verbose) {
+ (void) printf("local fs %s does not have fromsnap "
+ "(%s in stream); must have been deleted locally; "
+ "ignoring\n", fsname, fromsnap);
+ continue;
+ }
+
+ VERIFY(0 == nvlist_lookup_string(stream_nvfs,
+ "name", &stream_fsname));
+ VERIFY(0 == nvlist_lookup_uint64(stream_nvfs,
+ "parentfromsnap", &stream_parent_fromsnap_guid));
+
+ /* check for rename */
+ if ((stream_parent_fromsnap_guid != 0 &&
+ stream_parent_fromsnap_guid != parent_fromsnap_guid) ||
+ strcmp(strrchr(fsname, '/'),
+ strrchr(stream_fsname, '/')) != 0) {
+ nvlist_t *parent;
+ char tryname[ZFS_MAXNAMELEN];
+
+ parent = fsavl_find(local_avl,
+ stream_parent_fromsnap_guid, NULL);
+ /*
+ * NB: parent might not be found if we used the
+ * tosnap for stream_parent_fromsnap_guid,
+ * because the parent is a newly-created fs;
+ * we'll be able to rename it after we recv the
+ * new fs.
+ */
+ if (parent != NULL) {
+ char *pname;
+
+ VERIFY(0 == nvlist_lookup_string(parent, "name",
+ &pname));
+ (void) snprintf(tryname, sizeof (tryname),
+ "%s%s", pname, strrchr(stream_fsname, '/'));
+ } else {
+ tryname[0] = '\0';
+ if (flags.verbose) {
+ (void) printf("local fs %s new parent "
+ "not found\n", fsname);
+ }
+ }
+
+ error = recv_rename(hdl, fsname, tryname,
+ strlen(tofs)+1, newname, flags);
+ if (error)
+ needagain = B_TRUE;
+ else
+ progress = B_TRUE;
+ }
+ }
+
+ fsavl_destroy(local_avl);
+ nvlist_free(local_nv);
+
+ if (needagain && progress) {
+ /* do another pass to fix up temporary names */
+ if (flags.verbose)
+ (void) printf("another pass:\n");
+ goto again;
+ }
+
+ return (needagain);
+}
+
+static int
+zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
+ recvflags_t flags, dmu_replay_record_t *drr, zio_cksum_t *zc,
+ char **top_zfs)
+{
+ nvlist_t *stream_nv = NULL;
+ avl_tree_t *stream_avl = NULL;
+ char *fromsnap = NULL;
+ char tofs[ZFS_MAXNAMELEN];
+ char errbuf[1024];
+ dmu_replay_record_t drre;
+ int error;
+ boolean_t anyerr = B_FALSE;
+ boolean_t softerr = B_FALSE;
+
+ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+ "cannot receive"));
+
+ if (strchr(destname, '@')) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "can not specify snapshot name for multi-snapshot stream"));
+ return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
+ }
+
+ assert(drr->drr_type == DRR_BEGIN);
+ assert(drr->drr_u.drr_begin.drr_magic == DMU_BACKUP_MAGIC);
+ assert(drr->drr_u.drr_begin.drr_version == DMU_BACKUP_HEADER_VERSION);
+
+ /*
+ * Read in the nvlist from the stream.
+ */
+ if (drr->drr_payloadlen != 0) {
+ if (!flags.isprefix) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "must use -d to receive replication "
+ "(send -R) stream"));
+ return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
+ }
+
+ error = recv_read_nvlist(hdl, fd, drr->drr_payloadlen,
+ &stream_nv, flags.byteswap, zc);
+ if (error) {
+ error = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
+ goto out;
+ }
+ }
+
+ /*
+ * Read in the end record and verify checksum.
+ */
+ if (0 != (error = recv_read(hdl, fd, &drre, sizeof (drre),
+ flags.byteswap, NULL)))
+ goto out;
+ if (flags.byteswap) {
+ drre.drr_type = BSWAP_32(drre.drr_type);
+ drre.drr_u.drr_end.drr_checksum.zc_word[0] =
+ BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[0]);
+ drre.drr_u.drr_end.drr_checksum.zc_word[1] =
+ BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[1]);
+ drre.drr_u.drr_end.drr_checksum.zc_word[2] =
+ BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[2]);
+ drre.drr_u.drr_end.drr_checksum.zc_word[3] =
+ BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[3]);
+ }
+ if (drre.drr_type != DRR_END) {
+ error = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
+ goto out;
+ }
+ if (!ZIO_CHECKSUM_EQUAL(drre.drr_u.drr_end.drr_checksum, *zc)) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "incorrect header checksum"));
+ error = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
+ goto out;
+ }
+
+ (void) nvlist_lookup_string(stream_nv, "fromsnap", &fromsnap);
+
+ if (drr->drr_payloadlen != 0) {
+ nvlist_t *stream_fss;
+
+ VERIFY(0 == nvlist_lookup_nvlist(stream_nv, "fss",
+ &stream_fss));
+ if ((stream_avl = fsavl_create(stream_fss)) == NULL) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "couldn't allocate avl tree"));
+ error = zfs_error(hdl, EZFS_NOMEM, errbuf);
+ goto out;
+ }
+
+ if (fromsnap != NULL) {
+ (void) strlcpy(tofs, destname, ZFS_MAXNAMELEN);
+ if (flags.isprefix) {
+ int i = strcspn(drr->drr_u.drr_begin.drr_toname,
+ "/@");
+ /* zfs_receive_one() will create_parents() */
+ (void) strlcat(tofs,
+ &drr->drr_u.drr_begin.drr_toname[i],
+ ZFS_MAXNAMELEN);
+ *strchr(tofs, '@') = '\0';
+ }
+ softerr = recv_incremental_replication(hdl, tofs,
+ flags, stream_nv, stream_avl);
+ }
+ }
+
+
+ /* Finally, receive each contained stream */
+ do {
+ /*
+ * we should figure out if it has a recoverable
+ * error, in which case do a recv_skip() and drive on.
+ * Note, if we fail due to already having this guid,
+ * zfs_receive_one() will take care of it (ie,
+ * recv_skip() and return 0).
+ */
+ error = zfs_receive_impl(hdl, destname, flags, fd,
+ stream_avl, top_zfs);
+ if (error == ENODATA) {
+ error = 0;
+ break;
+ }
+ anyerr |= error;
+ } while (error == 0);
+
+ if (drr->drr_payloadlen != 0 && fromsnap != NULL) {
+ /*
+ * Now that we have the fs's they sent us, try the
+ * renames again.
+ */
+ softerr = recv_incremental_replication(hdl, tofs, flags,
+ stream_nv, stream_avl);
+ }
+
+out:
+ fsavl_destroy(stream_avl);
+ if (stream_nv)
+ nvlist_free(stream_nv);
+ if (softerr)
+ error = -2;
+ if (anyerr)
+ error = -1;
+ return (error);
+}
+
+static int
+recv_skip(libzfs_handle_t *hdl, int fd, boolean_t byteswap)
+{
+ dmu_replay_record_t *drr;
+ void *buf = malloc(1<<20);
+
+ /* XXX would be great to use lseek if possible... */
+ drr = buf;
+
+ while (recv_read(hdl, fd, drr, sizeof (dmu_replay_record_t),
+ byteswap, NULL) == 0) {
+ if (byteswap)
+ drr->drr_type = BSWAP_32(drr->drr_type);
+
+ switch (drr->drr_type) {
+ case DRR_BEGIN:
+ /* NB: not to be used on v2 stream packages */
+ assert(drr->drr_payloadlen == 0);
+ break;
+
+ case DRR_END:
+ free(buf);
+ return (0);
+
+ case DRR_OBJECT:
+ if (byteswap) {
+ drr->drr_u.drr_object.drr_bonuslen =
+ BSWAP_32(drr->drr_u.drr_object.
+ drr_bonuslen);
+ }
+ (void) recv_read(hdl, fd, buf,
+ P2ROUNDUP(drr->drr_u.drr_object.drr_bonuslen, 8),
+ B_FALSE, NULL);
+ break;
+
+ case DRR_WRITE:
+ if (byteswap) {
+ drr->drr_u.drr_write.drr_length =
+ BSWAP_64(drr->drr_u.drr_write.drr_length);
+ }
+ (void) recv_read(hdl, fd, buf,
+ drr->drr_u.drr_write.drr_length, B_FALSE, NULL);
+ break;
+
+ case DRR_FREEOBJECTS:
+ case DRR_FREE:
+ break;
+
+ default:
+ assert(!"invalid record type");
+ }
+ }
+
+ free(buf);
+ return (-1);
+}
+
+/*
+ * Restores a backup of tosnap from the file descriptor specified by infd.
+ */
+static int
+zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
+ recvflags_t flags, dmu_replay_record_t *drr,
+ dmu_replay_record_t *drr_noswap, avl_tree_t *stream_avl,
+ char **top_zfs)
+{
+ zfs_cmd_t zc = { 0 };
+ time_t begin_time;
+ int ioctl_err, ioctl_errno, err, choplen;
+ char *cp;
+ struct drr_begin *drrb = &drr->drr_u.drr_begin;
+ char errbuf[1024];
+ char chopprefix[ZFS_MAXNAMELEN];
+ boolean_t newfs = B_FALSE;
+ boolean_t stream_wantsnewfs;
+ uint64_t parent_snapguid = 0;
+ prop_changelist_t *clp = NULL;
+ nvlist_t *snapprops_nvlist = NULL;
+
+ begin_time = time(NULL);
+
+ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+ "cannot receive"));
+
+ if (stream_avl != NULL) {
+ char *snapname;
+ nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid,
+ &snapname);
+ nvlist_t *props;
+ int ret;
+
+ (void) nvlist_lookup_uint64(fs, "parentfromsnap",
+ &parent_snapguid);
+ err = nvlist_lookup_nvlist(fs, "props", &props);
+ if (err)
+ VERIFY(0 == nvlist_alloc(&props, NV_UNIQUE_NAME, 0));
+
+ if (flags.canmountoff) {
+ VERIFY(0 == nvlist_add_uint64(props,
+ zfs_prop_to_name(ZFS_PROP_CANMOUNT), 0));
+ }
+ ret = zcmd_write_src_nvlist(hdl, &zc, props);
+ if (err)
+ nvlist_free(props);
+
+ if (0 == nvlist_lookup_nvlist(fs, "snapprops", &props)) {
+ VERIFY(0 == nvlist_lookup_nvlist(props,
+ snapname, &snapprops_nvlist));
+ }
+
+ if (ret != 0)
+ return (-1);
+ }
+
+ /*
+ * Determine how much of the snapshot name stored in the stream
+ * we are going to tack on to the name they specified on the
+ * command line, and how much we are going to chop off.
+ *
+ * If they specified a snapshot, chop the entire name stored in
+ * the stream.
+ */
+ (void) strcpy(chopprefix, drrb->drr_toname);
+ if (flags.isprefix) {
+ /*
+ * They specified a fs with -d, we want to tack on
+ * everything but the pool name stored in the stream
+ */
+ if (strchr(tosnap, '@')) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
+ "argument - snapshot not allowed with -d"));
+ return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
+ }
+ cp = strchr(chopprefix, '/');
+ if (cp == NULL)
+ cp = strchr(chopprefix, '@');
+ *cp = '\0';
+ } else if (strchr(tosnap, '@') == NULL) {
+ /*
+ * If they specified a filesystem without -d, we want to
+ * tack on everything after the fs specified in the
+ * first name from the stream.
+ */
+ cp = strchr(chopprefix, '@');
+ *cp = '\0';
+ }
+ choplen = strlen(chopprefix);
+
+ /*
+ * Determine name of destination snapshot, store in zc_value.
+ */
+ (void) strcpy(zc.zc_value, tosnap);
+ (void) strncat(zc.zc_value, drrb->drr_toname+choplen,
+ sizeof (zc.zc_value));
+ if (!zfs_name_valid(zc.zc_value, ZFS_TYPE_SNAPSHOT)) {
+ zcmd_free_nvlists(&zc);
+ return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
+ }
+
+ /*
+ * Determine the name of the origin snapshot, store in zc_string.
+ */
+ if (drrb->drr_flags & DRR_FLAG_CLONE) {
+ if (guid_to_name(hdl, tosnap,
+ drrb->drr_fromguid, zc.zc_string) != 0) {
+ zcmd_free_nvlists(&zc);
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "local origin for clone %s does not exist"),
+ zc.zc_value);
+ return (zfs_error(hdl, EZFS_NOENT, errbuf));
+ }
+ if (flags.verbose)
+ (void) printf("found clone origin %s\n", zc.zc_string);
+ }
+
+ stream_wantsnewfs = (drrb->drr_fromguid == 0 ||
+ (drrb->drr_flags & DRR_FLAG_CLONE));
+
+ if (stream_wantsnewfs) {
+ /*
+ * if the parent fs does not exist, look for it based on
+ * the parent snap GUID
+ */
+ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+ "cannot receive new filesystem stream"));
+
+ (void) strcpy(zc.zc_name, zc.zc_value);
+ cp = strrchr(zc.zc_name, '/');
+ if (cp)
+ *cp = '\0';
+ if (cp &&
+ !zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
+ char suffix[ZFS_MAXNAMELEN];
+ (void) strcpy(suffix, strrchr(zc.zc_value, '/'));
+ if (guid_to_name(hdl, tosnap, parent_snapguid,
+ zc.zc_value) == 0) {
+ *strchr(zc.zc_value, '@') = '\0';
+ (void) strcat(zc.zc_value, suffix);
+ }
+ }
+ } else {
+ /*
+ * if the fs does not exist, look for it based on the
+ * fromsnap GUID
+ */
+ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+ "cannot receive incremental stream"));
+
+ (void) strcpy(zc.zc_name, zc.zc_value);
+ *strchr(zc.zc_name, '@') = '\0';
+
+ if (!zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
+ char snap[ZFS_MAXNAMELEN];
+ (void) strcpy(snap, strchr(zc.zc_value, '@'));
+ if (guid_to_name(hdl, tosnap, drrb->drr_fromguid,
+ zc.zc_value) == 0) {
+ *strchr(zc.zc_value, '@') = '\0';
+ (void) strcat(zc.zc_value, snap);
+ }
+ }
+ }
+
+ (void) strcpy(zc.zc_name, zc.zc_value);
+ *strchr(zc.zc_name, '@') = '\0';
+
+ if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
+ zfs_handle_t *zhp;
+ /*
+ * Destination fs exists. Therefore this should either
+ * be an incremental, or the stream specifies a new fs
+ * (full stream or clone) and they want us to blow it
+ * away (and have therefore specified -F and removed any
+ * snapshots).
+ */
+
+ if (stream_wantsnewfs) {
+ if (!flags.force) {
+ zcmd_free_nvlists(&zc);
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "destination '%s' exists\n"
+ "must specify -F to overwrite it"),
+ zc.zc_name);
+ return (zfs_error(hdl, EZFS_EXISTS, errbuf));
+ }
+ if (ioctl(hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT,
+ &zc) == 0) {
+ zcmd_free_nvlists(&zc);
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "destination has snapshots (eg. %s)\n"
+ "must destroy them to overwrite it"),
+ zc.zc_name);
+ return (zfs_error(hdl, EZFS_EXISTS, errbuf));
+ }
+ }
+
+ if ((zhp = zfs_open(hdl, zc.zc_name,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) == NULL) {
+ zcmd_free_nvlists(&zc);
+ return (-1);
+ }
+
+ if (stream_wantsnewfs &&
+ zhp->zfs_dmustats.dds_origin[0]) {
+ zcmd_free_nvlists(&zc);
+ zfs_close(zhp);
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "destination '%s' is a clone\n"
+ "must destroy it to overwrite it"),
+ zc.zc_name);
+ return (zfs_error(hdl, EZFS_EXISTS, errbuf));
+ }
+
+ if (!flags.dryrun && zhp->zfs_type == ZFS_TYPE_FILESYSTEM &&
+ stream_wantsnewfs) {
+ /* We can't do online recv in this case */
+ clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0);
+ if (clp == NULL) {
+ zcmd_free_nvlists(&zc);
+ return (-1);
+ }
+ if (changelist_prefix(clp) != 0) {
+ changelist_free(clp);
+ zcmd_free_nvlists(&zc);
+ return (-1);
+ }
+ }
+ if (!flags.dryrun && zhp->zfs_type == ZFS_TYPE_VOLUME &&
+ zvol_remove_link(hdl, zhp->zfs_name) != 0) {
+ zfs_close(zhp);
+ zcmd_free_nvlists(&zc);
+ return (-1);
+ }
+ zfs_close(zhp);
+ } else {
+ /*
+ * Destination filesystem does not exist. Therefore we better
+ * be creating a new filesystem (either from a full backup, or
+ * a clone). It would therefore be invalid if the user
+ * specified only the pool name (i.e. if the destination name
+ * contained no slash character).
+ */
+ if (!stream_wantsnewfs ||
+ (cp = strrchr(zc.zc_name, '/')) == NULL) {
+ zcmd_free_nvlists(&zc);
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "destination '%s' does not exist"), zc.zc_name);
+ return (zfs_error(hdl, EZFS_NOENT, errbuf));
+ }
+
+ /*
+ * Trim off the final dataset component so we perform the
+ * recvbackup ioctl to the filesystems's parent.
+ */
+ *cp = '\0';
+
+ if (flags.isprefix && !flags.dryrun &&
+ create_parents(hdl, zc.zc_value, strlen(tosnap)) != 0) {
+ zcmd_free_nvlists(&zc);
+ return (zfs_error(hdl, EZFS_BADRESTORE, errbuf));
+ }
+
+ newfs = B_TRUE;
+ }
+
+ zc.zc_begin_record = drr_noswap->drr_u.drr_begin;
+ zc.zc_cookie = infd;
+ zc.zc_guid = flags.force;
+ if (flags.verbose) {
+ (void) printf("%s %s stream of %s into %s\n",
+ flags.dryrun ? "would receive" : "receiving",
+ drrb->drr_fromguid ? "incremental" : "full",
+ drrb->drr_toname, zc.zc_value);
+ (void) fflush(stdout);
+ }
+
+ if (flags.dryrun) {
+ zcmd_free_nvlists(&zc);
+ return (recv_skip(hdl, infd, flags.byteswap));
+ }
+
+ err = ioctl_err = zfs_ioctl(hdl, ZFS_IOC_RECV, &zc);
+ ioctl_errno = errno;
+ zcmd_free_nvlists(&zc);
+
+ if (err == 0 && snapprops_nvlist) {
+ zfs_cmd_t zc2 = { 0 };
+
+ (void) strcpy(zc2.zc_name, zc.zc_value);
+ if (zcmd_write_src_nvlist(hdl, &zc2, snapprops_nvlist) == 0) {
+ (void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc2);
+ zcmd_free_nvlists(&zc2);
+ }
+ }
+
+ if (err && (ioctl_errno == ENOENT || ioctl_errno == ENODEV)) {
+ /*
+ * It may be that this snapshot already exists,
+ * in which case we want to consume & ignore it
+ * rather than failing.
+ */
+ avl_tree_t *local_avl;
+ nvlist_t *local_nv, *fs;
+ char *cp = strchr(zc.zc_value, '@');
+
+ /*
+ * XXX Do this faster by just iterating over snaps in
+ * this fs. Also if zc_value does not exist, we will
+ * get a strange "does not exist" error message.
+ */
+ *cp = '\0';
+ if (gather_nvlist(hdl, zc.zc_value, NULL, NULL,
+ &local_nv, &local_avl) == 0) {
+ *cp = '@';
+ fs = fsavl_find(local_avl, drrb->drr_toguid, NULL);
+ fsavl_destroy(local_avl);
+ nvlist_free(local_nv);
+
+ if (fs != NULL) {
+ if (flags.verbose) {
+ (void) printf("snap %s already exists; "
+ "ignoring\n", zc.zc_value);
+ }
+ ioctl_err = recv_skip(hdl, infd,
+ flags.byteswap);
+ }
+ }
+ *cp = '@';
+ }
+
+
+ if (ioctl_err != 0) {
+ switch (ioctl_errno) {
+ case ENODEV:
+ cp = strchr(zc.zc_value, '@');
+ *cp = '\0';
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "most recent snapshot of %s does not\n"
+ "match incremental source"), zc.zc_value);
+ (void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
+ *cp = '@';
+ break;
+ case ETXTBSY:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "destination %s has been modified\n"
+ "since most recent snapshot"), zc.zc_name);
+ (void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
+ break;
+ case EEXIST:
+ cp = strchr(zc.zc_value, '@');
+ if (newfs) {
+ /* it's the containing fs that exists */
+ *cp = '\0';
+ }
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "destination already exists"));
+ (void) zfs_error_fmt(hdl, EZFS_EXISTS,
+ dgettext(TEXT_DOMAIN, "cannot restore to %s"),
+ zc.zc_value);
+ *cp = '@';
+ break;
+ case EINVAL:
+ (void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
+ break;
+ case ECKSUM:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "invalid stream (checksum mismatch)"));
+ (void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
+ break;
+ default:
+ (void) zfs_standard_error(hdl, ioctl_errno, errbuf);
+ }
+ }
+
+ /*
+ * Mount or recreate the /dev links for the target filesystem
+ * (if created, or if we tore them down to do an incremental
+ * restore), and the /dev links for the new snapshot (if
+ * created). Also mount any children of the target filesystem
+ * if we did an incremental receive.
+ */
+ cp = strchr(zc.zc_value, '@');
+ if (cp && (ioctl_err == 0 || !newfs)) {
+ zfs_handle_t *h;
+
+ *cp = '\0';
+ h = zfs_open(hdl, zc.zc_value,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
+ if (h != NULL) {
+ if (h->zfs_type == ZFS_TYPE_VOLUME) {
+ *cp = '@';
+ err = zvol_create_link(hdl, h->zfs_name);
+ if (err == 0 && ioctl_err == 0)
+ err = zvol_create_link(hdl,
+ zc.zc_value);
+ } else if (newfs) {
+ /*
+ * Track the first/top of hierarchy fs,
+ * for mounting and sharing later.
+ */
+ if (top_zfs && *top_zfs == NULL)
+ *top_zfs = zfs_strdup(hdl, zc.zc_value);
+ }
+ zfs_close(h);
+ }
+ *cp = '@';
+ }
+
+ if (clp) {
+ err |= changelist_postfix(clp);
+ changelist_free(clp);
+ }
+
+ if (err || ioctl_err)
+ return (-1);
+
+ if (flags.verbose) {
+ char buf1[64];
+ char buf2[64];
+ uint64_t bytes = zc.zc_cookie;
+ time_t delta = time(NULL) - begin_time;
+ if (delta == 0)
+ delta = 1;
+ zfs_nicenum(bytes, buf1, sizeof (buf1));
+ zfs_nicenum(bytes/delta, buf2, sizeof (buf1));
+
+ (void) printf("received %sB stream in %lu seconds (%sB/sec)\n",
+ buf1, delta, buf2);
+ }
+
+ return (0);
+}
+
+static int
+zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
+ int infd, avl_tree_t *stream_avl, char **top_zfs)
+{
+ int err;
+ dmu_replay_record_t drr, drr_noswap;
+ struct drr_begin *drrb = &drr.drr_u.drr_begin;
+ char errbuf[1024];
+ zio_cksum_t zcksum = { 0 };
+
+ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+ "cannot receive"));
+
+ if (flags.isprefix &&
+ !zfs_dataset_exists(hdl, tosnap, ZFS_TYPE_DATASET)) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "specified fs "
+ "(%s) does not exist"), tosnap);
+ return (zfs_error(hdl, EZFS_NOENT, errbuf));
+ }
+
+ /* read in the BEGIN record */
+ if (0 != (err = recv_read(hdl, infd, &drr, sizeof (drr), B_FALSE,
+ &zcksum)))
+ return (err);
+
+ if (drr.drr_type == DRR_END || drr.drr_type == BSWAP_32(DRR_END)) {
+ /* It's the double end record at the end of a package */
+ return (ENODATA);
+ }
+
+ /* the kernel needs the non-byteswapped begin record */
+ drr_noswap = drr;
+
+ flags.byteswap = B_FALSE;
+ if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) {
+ /*
+ * We computed the checksum in the wrong byteorder in
+ * recv_read() above; do it again correctly.
+ */
+ bzero(&zcksum, sizeof (zio_cksum_t));
+ fletcher_4_incremental_byteswap(&drr, sizeof (drr), &zcksum);
+ flags.byteswap = B_TRUE;
+
+ drr.drr_type = BSWAP_32(drr.drr_type);
+ drr.drr_payloadlen = BSWAP_32(drr.drr_payloadlen);
+ drrb->drr_magic = BSWAP_64(drrb->drr_magic);
+ drrb->drr_version = BSWAP_64(drrb->drr_version);
+ drrb->drr_creation_time = BSWAP_64(drrb->drr_creation_time);
+ drrb->drr_type = BSWAP_32(drrb->drr_type);
+ drrb->drr_flags = BSWAP_32(drrb->drr_flags);
+ drrb->drr_toguid = BSWAP_64(drrb->drr_toguid);
+ drrb->drr_fromguid = BSWAP_64(drrb->drr_fromguid);
+ }
+
+ if (drrb->drr_magic != DMU_BACKUP_MAGIC || drr.drr_type != DRR_BEGIN) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
+ "stream (bad magic number)"));
+ return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
+ }
+
+ if (strchr(drrb->drr_toname, '@') == NULL) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
+ "stream (bad snapshot name)"));
+ return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
+ }
+
+ if (drrb->drr_version == DMU_BACKUP_STREAM_VERSION) {
+ return (zfs_receive_one(hdl, infd, tosnap, flags,
+ &drr, &drr_noswap, stream_avl, top_zfs));
+ } else if (drrb->drr_version == DMU_BACKUP_HEADER_VERSION) {
+ return (zfs_receive_package(hdl, infd, tosnap, flags,
+ &drr, &zcksum, top_zfs));
+ } else {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "stream is unsupported version %llu"),
+ drrb->drr_version);
+ return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
+ }
+}
+
+/*
+ * Restores a backup of tosnap from the file descriptor specified by infd.
+ * Return 0 on total success, -2 if some things couldn't be
+ * destroyed/renamed/promoted, -1 if some things couldn't be received.
+ * (-1 will override -2).
+ */
+int
+zfs_receive(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
+ int infd, avl_tree_t *stream_avl)
+{
+ char *top_zfs = NULL;
+ int err;
+
+ err = zfs_receive_impl(hdl, tosnap, flags, infd, stream_avl, &top_zfs);
+
+ if (err == 0 && top_zfs) {
+ zfs_handle_t *zhp;
+ prop_changelist_t *clp;
+
+ zhp = zfs_open(hdl, top_zfs, ZFS_TYPE_FILESYSTEM);
+ if (zhp != NULL) {
+ clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT,
+ CL_GATHER_MOUNT_ALWAYS, 0);
+ zfs_close(zhp);
+ if (clp != NULL) {
+ /* mount and share received datasets */
+ err = changelist_postfix(clp);
+ changelist_free(clp);
+ }
+ }
+ if (zhp == NULL || clp == NULL || err)
+ err = -1;
+ }
+ if (top_zfs)
+ free(top_zfs);
+
+ return (err);
+}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_status.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_status.c
index 3eba97a..c7eb04e 100644
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_status.c
+++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_status.c
@@ -19,18 +19,16 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* This file contains the functions which analyze the status of a pool. This
* include both the status of an active pool, as well as the status exported
* pools. Returns one of the ZPOOL_STATUS_* defines describing the status of
* the pool. This status is independent (to a certain degree) from the state of
- * the pool. A pool's state descsribes only whether or not it is capable of
+ * the pool. A pool's state describes only whether or not it is capable of
* providing the necessary fault tolerance for data. The status describes the
* overall status of devices. A pool that is online can still have a device
* that is experiencing errors.
@@ -47,7 +45,7 @@
#include "libzfs_impl.h"
/*
- * Message ID table. This must be kep in sync with the ZPOOL_STATUS_* defines
+ * Message ID table. This must be kept in sync with the ZPOOL_STATUS_* defines
* in libzfs.h. Note that there are some status results which go past the end
* of this table, and hence have no associated message ID.
*/
@@ -62,26 +60,10 @@ static char *zfs_msgid_table[] = {
"ZFS-8000-8A",
"ZFS-8000-9P",
"ZFS-8000-A5",
- "ZFS-8000-EY"
-};
-
-/*
- * If the pool is active, a certain class of static errors is overridden by the
- * faults as analayzed by FMA. These faults have separate knowledge articles,
- * and the article referred to by 'zpool status' must match that indicated by
- * the syslog error message. We override missing data as well as corrupt pool.
- */
-static char *zfs_msgid_table_active[] = {
- "ZFS-8000-14",
- "ZFS-8000-D3", /* overridden */
- "ZFS-8000-D3", /* overridden */
- "ZFS-8000-4J",
- "ZFS-8000-5E",
- "ZFS-8000-6X",
- "ZFS-8000-CS", /* overridden */
- "ZFS-8000-8A",
- "ZFS-8000-9P",
- "ZFS-8000-CS", /* overridden */
+ "ZFS-8000-EY",
+ "ZFS-8000-HC",
+ "ZFS-8000-JQ",
+ "ZFS-8000-K4",
};
#define NMSGID (sizeof (zfs_msgid_table) / sizeof (zfs_msgid_table[0]))
@@ -96,9 +78,16 @@ vdev_missing(uint64_t state, uint64_t aux, uint64_t errs)
/* ARGSUSED */
static int
+vdev_faulted(uint64_t state, uint64_t aux, uint64_t errs)
+{
+ return (state == VDEV_STATE_FAULTED);
+}
+
+/* ARGSUSED */
+static int
vdev_errors(uint64_t state, uint64_t aux, uint64_t errs)
{
- return (errs != 0);
+ return (state == VDEV_STATE_DEGRADED || errs != 0);
}
/* ARGSUSED */
@@ -163,9 +152,9 @@ find_vdev_problem(nvlist_t *vdev, int (*func)(uint64_t, uint64_t, uint64_t))
* following:
*
* - Check for a complete and valid configuration
- * - Look for any missing devices in a non-replicated config
+ * - Look for any faulted or missing devices in a non-replicated config
* - Check for any data errors
- * - Check for any missing devices in a replicated config
+ * - Check for any faulted or missing devices in a replicated config
* - Look for any devices showing errors
* - Check for any resilvering devices
*
@@ -181,6 +170,7 @@ check_status(nvlist_t *config, boolean_t isimport)
uint64_t nerr;
uint64_t version;
uint64_t stateval;
+ uint64_t suspended;
uint64_t hostid = 0;
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
@@ -215,9 +205,31 @@ check_status(nvlist_t *config, boolean_t isimport)
return (ZPOOL_STATUS_BAD_GUID_SUM);
/*
- * Missing devices in non-replicated config.
+ * Check whether the pool has suspended due to failed I/O.
+ */
+ if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_SUSPENDED,
+ &suspended) == 0) {
+ if (suspended == ZIO_FAILURE_MODE_CONTINUE)
+ return (ZPOOL_STATUS_IO_FAILURE_CONTINUE);
+ return (ZPOOL_STATUS_IO_FAILURE_WAIT);
+ }
+
+ /*
+ * Could not read a log.
+ */
+ if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
+ vs->vs_aux == VDEV_AUX_BAD_LOG) {
+ return (ZPOOL_STATUS_BAD_LOG);
+ }
+
+ /*
+ * Bad devices in non-replicated config.
*/
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
+ find_vdev_problem(nvroot, vdev_faulted))
+ return (ZPOOL_STATUS_FAULTED_DEV_NR);
+
+ if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
find_vdev_problem(nvroot, vdev_missing))
return (ZPOOL_STATUS_MISSING_DEV_NR);
@@ -244,6 +256,8 @@ check_status(nvlist_t *config, boolean_t isimport)
/*
* Missing devices in a replicated config.
*/
+ if (find_vdev_problem(nvroot, vdev_faulted))
+ return (ZPOOL_STATUS_FAULTED_DEV_R);
if (find_vdev_problem(nvroot, vdev_missing))
return (ZPOOL_STATUS_MISSING_DEV_R);
if (find_vdev_problem(nvroot, vdev_broken))
@@ -270,7 +284,7 @@ check_status(nvlist_t *config, boolean_t isimport)
/*
* Outdated, but usable, version
*/
- if (version < ZFS_VERSION)
+ if (version < SPA_VERSION)
return (ZPOOL_STATUS_VERSION_OLDER);
return (ZPOOL_STATUS_OK);
@@ -284,7 +298,7 @@ zpool_get_status(zpool_handle_t *zhp, char **msgid)
if (ret >= NMSGID)
*msgid = NULL;
else
- *msgid = zfs_msgid_table_active[ret];
+ *msgid = zfs_msgid_table[ret];
return (ret);
}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c
index c706126..9d60d60 100644
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c
+++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* Internal utility routines for the ZFS library.
*/
@@ -37,6 +35,8 @@
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
+#include <ctype.h>
+#include <math.h>
#include <sys/mnttab.h>
#include <sys/mntent.h>
#include <sys/types.h>
@@ -44,6 +44,7 @@
#include <libzfs.h>
#include "libzfs_impl.h"
+#include "zfs_prop.h"
int
libzfs_errno(libzfs_handle_t *hdl)
@@ -133,6 +134,14 @@ libzfs_error_description(libzfs_handle_t *hdl)
return (dgettext(TEXT_DOMAIN, "unshare(1M) failed"));
case EZFS_SHARENFSFAILED:
return (dgettext(TEXT_DOMAIN, "share(1M) failed"));
+ case EZFS_UNSHARESMBFAILED:
+ return (dgettext(TEXT_DOMAIN, "smb remove share failed"));
+ case EZFS_SHARESMBFAILED:
+ return (dgettext(TEXT_DOMAIN, "smb add share failed"));
+ case EZFS_ISCSISVCUNAVAIL:
+ return (dgettext(TEXT_DOMAIN,
+ "iscsitgt service need to be enabled by "
+ "a privileged user"));
case EZFS_DEVLINKS:
return (dgettext(TEXT_DOMAIN, "failed to create /dev links"));
case EZFS_PERM:
@@ -169,6 +178,38 @@ libzfs_error_description(libzfs_handle_t *hdl)
"this pool operation"));
case EZFS_NAMETOOLONG:
return (dgettext(TEXT_DOMAIN, "dataset name is too long"));
+ case EZFS_OPENFAILED:
+ return (dgettext(TEXT_DOMAIN, "open failed"));
+ case EZFS_NOCAP:
+ return (dgettext(TEXT_DOMAIN,
+ "disk capacity information could not be retrieved"));
+ case EZFS_LABELFAILED:
+ return (dgettext(TEXT_DOMAIN, "write of label failed"));
+ case EZFS_BADWHO:
+ return (dgettext(TEXT_DOMAIN, "invalid user/group"));
+ case EZFS_BADPERM:
+ return (dgettext(TEXT_DOMAIN, "invalid permission"));
+ case EZFS_BADPERMSET:
+ return (dgettext(TEXT_DOMAIN, "invalid permission set name"));
+ case EZFS_NODELEGATION:
+ return (dgettext(TEXT_DOMAIN, "delegated administration is "
+ "disabled on pool"));
+ case EZFS_PERMRDONLY:
+ return (dgettext(TEXT_DOMAIN, "snapshot permissions cannot be"
+ " modified"));
+ case EZFS_BADCACHE:
+ return (dgettext(TEXT_DOMAIN, "invalid or missing cache file"));
+ case EZFS_ISL2CACHE:
+ return (dgettext(TEXT_DOMAIN, "device is in use as a cache"));
+ case EZFS_VDEVNOTSUP:
+ return (dgettext(TEXT_DOMAIN, "vdev specification is not "
+ "supported"));
+ case EZFS_NOTSUP:
+ return (dgettext(TEXT_DOMAIN, "operation not supported "
+ "on this dataset"));
+ case EZFS_ACTIVE_SPARE:
+ return (dgettext(TEXT_DOMAIN, "pool has active shared spare "
+ "device"));
case EZFS_UNKNOWN:
return (dgettext(TEXT_DOMAIN, "unknown error"));
default:
@@ -249,6 +290,10 @@ zfs_common_error(libzfs_handle_t *hdl, int error, const char *fmt,
zfs_verror(hdl, EZFS_PERM, fmt, ap);
return (-1);
+ case ECANCELED:
+ zfs_verror(hdl, EZFS_NODELEGATION, fmt, ap);
+ return (-1);
+
case EIO:
zfs_verror(hdl, EZFS_IO, fmt, ap);
return (-1);
@@ -280,9 +325,9 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
return (-1);
}
-
switch (error) {
case ENXIO:
+ case ENODEV:
zfs_verror(hdl, EZFS_IO, fmt, ap);
break;
@@ -308,11 +353,17 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
"dataset is busy"));
zfs_verror(hdl, EZFS_BUSY, fmt, ap);
break;
-
+ case EROFS:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "snapshot permissions cannot be modified"));
+ zfs_verror(hdl, EZFS_PERMRDONLY, fmt, ap);
+ break;
case ENAMETOOLONG:
zfs_verror(hdl, EZFS_NAMETOOLONG, fmt, ap);
break;
-
+ case ENOTSUP:
+ zfs_verror(hdl, EZFS_BADVERSION, fmt, ap);
+ break;
default:
zfs_error_aux(hdl, strerror(errno));
zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
@@ -361,7 +412,7 @@ zpool_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
case EBUSY:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "pool is busy"));
- zfs_verror(hdl, EZFS_EXISTS, fmt, ap);
+ zfs_verror(hdl, EZFS_BUSY, fmt, ap);
break;
case ENXIO:
@@ -382,6 +433,11 @@ zpool_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
zfs_verror(hdl, EZFS_POOL_INVALARG, fmt, ap);
break;
+ case ENOSPC:
+ case EDQUOT:
+ zfs_verror(hdl, EZFS_NOSPC, fmt, ap);
+ return (-1);
+
default:
zfs_error_aux(hdl, strerror(error));
zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
@@ -483,9 +539,8 @@ zfs_nicenum(uint64_t num, char *buf, size_t buflen)
*/
int i;
for (i = 2; i >= 0; i--) {
- (void) snprintf(buf, buflen, "%.*f%c", i,
- (double)num / (1ULL << 10 * index), u);
- if (strlen(buf) <= 5)
+ if (snprintf(buf, buflen, "%.*f%c", i,
+ (double)num / (1ULL << 10 * index), u) <= 5)
break;
}
}
@@ -538,6 +593,9 @@ libzfs_init(void)
hdl->libzfs_sharetab = fopen(ZFS_EXPORTS_PATH, "r");
+ zfs_prop_init();
+ zpool_prop_init();
+
return (hdl);
}
@@ -549,6 +607,10 @@ libzfs_fini(libzfs_handle_t *hdl)
(void) fclose(hdl->libzfs_mnttab);
if (hdl->libzfs_sharetab)
(void) fclose(hdl->libzfs_sharetab);
+ zfs_uninit_libshare(hdl);
+ if (hdl->libzfs_log_str)
+ (void) free(hdl->libzfs_log_str);
+ zpool_free_handles(hdl);
namespace_clear(hdl);
free(hdl);
}
@@ -565,6 +627,12 @@ zfs_get_handle(zfs_handle_t *zhp)
return (zhp->zfs_hdl);
}
+zpool_handle_t *
+zfs_get_pool_handle(const zfs_handle_t *zhp)
+{
+ return (zhp->zpool_hdl);
+}
+
/*
* Given a name, determine whether or not it's a valid path
* (starts with '/' or "./"). If so, walk the mnttab trying
@@ -637,13 +705,14 @@ zcmd_expand_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc)
void
zcmd_free_nvlists(zfs_cmd_t *zc)
{
+ free((void *)(uintptr_t)zc->zc_nvlist_conf);
free((void *)(uintptr_t)zc->zc_nvlist_src);
free((void *)(uintptr_t)zc->zc_nvlist_dst);
}
-int
-zcmd_write_src_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t *nvl,
- size_t *size)
+static int
+zcmd_write_nvlist_com(libzfs_handle_t *hdl, uint64_t *outnv, uint64_t *outlen,
+ nvlist_t *nvl)
{
char *packed;
size_t len;
@@ -655,14 +724,26 @@ zcmd_write_src_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t *nvl,
verify(nvlist_pack(nvl, &packed, &len, NV_ENCODE_NATIVE, 0) == 0);
- zc->zc_nvlist_src = (uint64_t)(uintptr_t)packed;
- zc->zc_nvlist_src_size = len;
+ *outnv = (uint64_t)(uintptr_t)packed;
+ *outlen = len;
- if (size)
- *size = len;
return (0);
}
+int
+zcmd_write_conf_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t *nvl)
+{
+ return (zcmd_write_nvlist_com(hdl, &zc->zc_nvlist_conf,
+ &zc->zc_nvlist_conf_size, nvl));
+}
+
+int
+zcmd_write_src_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t *nvl)
+{
+ return (zcmd_write_nvlist_com(hdl, &zc->zc_nvlist_src,
+ &zc->zc_nvlist_src_size, nvl));
+}
+
/*
* Unpacks an nvlist from the ZFS ioctl command structure.
*/
@@ -676,10 +757,32 @@ zcmd_read_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t **nvlp)
return (0);
}
+int
+zfs_ioctl(libzfs_handle_t *hdl, int request, zfs_cmd_t *zc)
+{
+ int error;
+
+ zc->zc_history = (uint64_t)(uintptr_t)hdl->libzfs_log_str;
+ error = ioctl(hdl->libzfs_fd, request, zc);
+ if (hdl->libzfs_log_str) {
+ free(hdl->libzfs_log_str);
+ hdl->libzfs_log_str = NULL;
+ }
+ zc->zc_history = 0;
+
+ return (error);
+}
+
+/*
+ * ================================================================
+ * API shared by zfs and zpool property management
+ * ================================================================
+ */
+
static void
-zfs_print_prop_headers(libzfs_get_cbdata_t *cbp)
+zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type)
{
- zfs_proplist_t *pl = cbp->cb_proplist;
+ zprop_list_t *pl = cbp->cb_proplist;
int i;
char *title;
size_t len;
@@ -711,8 +814,12 @@ zfs_print_prop_headers(libzfs_get_cbdata_t *cbp)
/*
* 'PROPERTY' column
*/
- if (pl->pl_prop != ZFS_PROP_INVAL) {
- len = strlen(zfs_prop_to_name(pl->pl_prop));
+ if (pl->pl_prop != ZPROP_INVAL) {
+ const char *propname = (type == ZFS_TYPE_POOL) ?
+ zpool_prop_to_name(pl->pl_prop) :
+ zfs_prop_to_name(pl->pl_prop);
+
+ len = strlen(propname);
if (len > cbp->cb_colwidths[GET_COL_PROPERTY])
cbp->cb_colwidths[GET_COL_PROPERTY] = len;
} else {
@@ -731,7 +838,8 @@ zfs_print_prop_headers(libzfs_get_cbdata_t *cbp)
/*
* 'NAME' and 'SOURCE' columns
*/
- if (pl->pl_prop == ZFS_PROP_NAME &&
+ if (pl->pl_prop == (type == ZFS_TYPE_POOL ? ZPOOL_PROP_NAME :
+ ZFS_PROP_NAME) &&
pl->pl_width > cbp->cb_colwidths[GET_COL_NAME]) {
cbp->cb_colwidths[GET_COL_NAME] = pl->pl_width;
cbp->cb_colwidths[GET_COL_SOURCE] = pl->pl_width +
@@ -777,8 +885,8 @@ zfs_print_prop_headers(libzfs_get_cbdata_t *cbp)
* structure.
*/
void
-libzfs_print_one_property(const char *name, libzfs_get_cbdata_t *cbp,
- const char *propname, const char *value, zfs_source_t sourcetype,
+zprop_print_one_property(const char *name, zprop_get_cbdata_t *cbp,
+ const char *propname, const char *value, zprop_source_t sourcetype,
const char *source)
{
int i;
@@ -792,7 +900,7 @@ libzfs_print_one_property(const char *name, libzfs_get_cbdata_t *cbp,
return;
if (cbp->cb_first)
- zfs_print_prop_headers(cbp);
+ zprop_print_headers(cbp, cbp->cb_type);
for (i = 0; i < 4; i++) {
switch (cbp->cb_columns[i]) {
@@ -810,23 +918,23 @@ libzfs_print_one_property(const char *name, libzfs_get_cbdata_t *cbp,
case GET_COL_SOURCE:
switch (sourcetype) {
- case ZFS_SRC_NONE:
+ case ZPROP_SRC_NONE:
str = "-";
break;
- case ZFS_SRC_DEFAULT:
+ case ZPROP_SRC_DEFAULT:
str = "default";
break;
- case ZFS_SRC_LOCAL:
+ case ZPROP_SRC_LOCAL:
str = "local";
break;
- case ZFS_SRC_TEMPORARY:
+ case ZPROP_SRC_TEMPORARY:
str = "temporary";
break;
- case ZFS_SRC_INHERITED:
+ case ZPROP_SRC_INHERITED:
(void) snprintf(buf, sizeof (buf),
"inherited from %s", source);
str = buf;
@@ -851,3 +959,451 @@ libzfs_print_one_property(const char *name, libzfs_get_cbdata_t *cbp,
(void) printf("\n");
}
+
+/*
+ * Given a numeric suffix, convert the value into a number of bits that the
+ * resulting value must be shifted.
+ */
+static int
+str2shift(libzfs_handle_t *hdl, const char *buf)
+{
+ const char *ends = "BKMGTPEZ";
+ int i;
+
+ if (buf[0] == '\0')
+ return (0);
+ for (i = 0; i < strlen(ends); i++) {
+ if (toupper(buf[0]) == ends[i])
+ break;
+ }
+ if (i == strlen(ends)) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "invalid numeric suffix '%s'"), buf);
+ return (-1);
+ }
+
+ /*
+ * We want to allow trailing 'b' characters for 'GB' or 'Mb'. But don't
+ * allow 'BB' - that's just weird.
+ */
+ if (buf[1] == '\0' || (toupper(buf[1]) == 'B' && buf[2] == '\0' &&
+ toupper(buf[0]) != 'B'))
+ return (10*i);
+
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "invalid numeric suffix '%s'"), buf);
+ return (-1);
+}
+
+/*
+ * Convert a string of the form '100G' into a real number. Used when setting
+ * properties or creating a volume. 'buf' is used to place an extended error
+ * message for the caller to use.
+ */
+int
+zfs_nicestrtonum(libzfs_handle_t *hdl, const char *value, uint64_t *num)
+{
+ char *end;
+ int shift;
+
+ *num = 0;
+
+ /* Check to see if this looks like a number. */
+ if ((value[0] < '0' || value[0] > '9') && value[0] != '.') {
+ if (hdl)
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "bad numeric value '%s'"), value);
+ return (-1);
+ }
+
+ /* Rely on stroll() to process the numeric portion. */
+ errno = 0;
+ *num = strtoll(value, &end, 10);
+
+ /*
+ * Check for ERANGE, which indicates that the value is too large to fit
+ * in a 64-bit value.
+ */
+ if (errno == ERANGE) {
+ if (hdl)
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "numeric value is too large"));
+ return (-1);
+ }
+
+ /*
+ * If we have a decimal value, then do the computation with floating
+ * point arithmetic. Otherwise, use standard arithmetic.
+ */
+ if (*end == '.') {
+ double fval = strtod(value, &end);
+
+ if ((shift = str2shift(hdl, end)) == -1)
+ return (-1);
+
+ fval *= pow(2, shift);
+
+ if (fval > UINT64_MAX) {
+ if (hdl)
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "numeric value is too large"));
+ return (-1);
+ }
+
+ *num = (uint64_t)fval;
+ } else {
+ if ((shift = str2shift(hdl, end)) == -1)
+ return (-1);
+
+ /* Check for overflow */
+ if (shift >= 64 || (*num << shift) >> shift != *num) {
+ if (hdl)
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "numeric value is too large"));
+ return (-1);
+ }
+
+ *num <<= shift;
+ }
+
+ return (0);
+}
+
+/*
+ * Given a propname=value nvpair to set, parse any numeric properties
+ * (index, boolean, etc) if they are specified as strings and add the
+ * resulting nvpair to the returned nvlist.
+ *
+ * At the DSL layer, all properties are either 64-bit numbers or strings.
+ * We want the user to be able to ignore this fact and specify properties
+ * as native values (numbers, for example) or as strings (to simplify
+ * command line utilities). This also handles converting index types
+ * (compression, checksum, etc) from strings to their on-disk index.
+ */
+int
+zprop_parse_value(libzfs_handle_t *hdl, nvpair_t *elem, int prop,
+ zfs_type_t type, nvlist_t *ret, char **svalp, uint64_t *ivalp,
+ const char *errbuf)
+{
+ data_type_t datatype = nvpair_type(elem);
+ zprop_type_t proptype;
+ const char *propname;
+ char *value;
+ boolean_t isnone = B_FALSE;
+
+ if (type == ZFS_TYPE_POOL) {
+ proptype = zpool_prop_get_type(prop);
+ propname = zpool_prop_to_name(prop);
+ } else {
+ proptype = zfs_prop_get_type(prop);
+ propname = zfs_prop_to_name(prop);
+ }
+
+ /*
+ * Convert any properties to the internal DSL value types.
+ */
+ *svalp = NULL;
+ *ivalp = 0;
+
+ switch (proptype) {
+ case PROP_TYPE_STRING:
+ if (datatype != DATA_TYPE_STRING) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "'%s' must be a string"), nvpair_name(elem));
+ goto error;
+ }
+ (void) nvpair_value_string(elem, svalp);
+ if (strlen(*svalp) >= ZFS_MAXPROPLEN) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "'%s' is too long"), nvpair_name(elem));
+ goto error;
+ }
+ break;
+
+ case PROP_TYPE_NUMBER:
+ if (datatype == DATA_TYPE_STRING) {
+ (void) nvpair_value_string(elem, &value);
+ if (strcmp(value, "none") == 0) {
+ isnone = B_TRUE;
+ } else if (zfs_nicestrtonum(hdl, value, ivalp)
+ != 0) {
+ goto error;
+ }
+ } else if (datatype == DATA_TYPE_UINT64) {
+ (void) nvpair_value_uint64(elem, ivalp);
+ } else {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "'%s' must be a number"), nvpair_name(elem));
+ goto error;
+ }
+
+ /*
+ * Quota special: force 'none' and don't allow 0.
+ */
+ if ((type & ZFS_TYPE_DATASET) && *ivalp == 0 && !isnone &&
+ (prop == ZFS_PROP_QUOTA || prop == ZFS_PROP_REFQUOTA)) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "use 'none' to disable quota/refquota"));
+ goto error;
+ }
+ break;
+
+ case PROP_TYPE_INDEX:
+ if (datatype != DATA_TYPE_STRING) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "'%s' must be a string"), nvpair_name(elem));
+ goto error;
+ }
+
+ (void) nvpair_value_string(elem, &value);
+
+ if (zprop_string_to_index(prop, value, ivalp, type) != 0) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "'%s' must be one of '%s'"), propname,
+ zprop_values(prop, type));
+ goto error;
+ }
+ break;
+
+ default:
+ abort();
+ }
+
+ /*
+ * Add the result to our return set of properties.
+ */
+ if (*svalp != NULL) {
+ if (nvlist_add_string(ret, propname, *svalp) != 0) {
+ (void) no_memory(hdl);
+ return (-1);
+ }
+ } else {
+ if (nvlist_add_uint64(ret, propname, *ivalp) != 0) {
+ (void) no_memory(hdl);
+ return (-1);
+ }
+ }
+
+ return (0);
+error:
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ return (-1);
+}
+
+static int
+addlist(libzfs_handle_t *hdl, char *propname, zprop_list_t **listp,
+ zfs_type_t type)
+{
+ int prop;
+ zprop_list_t *entry;
+
+ prop = zprop_name_to_prop(propname, type);
+
+ if (prop != ZPROP_INVAL && !zprop_valid_for_type(prop, type))
+ prop = ZPROP_INVAL;
+
+ /*
+ * When no property table entry can be found, return failure if
+ * this is a pool property or if this isn't a user-defined
+ * dataset property,
+ */
+ if (prop == ZPROP_INVAL && (type == ZFS_TYPE_POOL ||
+ !zfs_prop_user(propname))) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "invalid property '%s'"), propname);
+ return (zfs_error(hdl, EZFS_BADPROP,
+ dgettext(TEXT_DOMAIN, "bad property list")));
+ }
+
+ if ((entry = zfs_alloc(hdl, sizeof (zprop_list_t))) == NULL)
+ return (-1);
+
+ entry->pl_prop = prop;
+ if (prop == ZPROP_INVAL) {
+ if ((entry->pl_user_prop = zfs_strdup(hdl, propname)) == NULL) {
+ free(entry);
+ return (-1);
+ }
+ entry->pl_width = strlen(propname);
+ } else {
+ entry->pl_width = zprop_width(prop, &entry->pl_fixed,
+ type);
+ }
+
+ *listp = entry;
+
+ return (0);
+}
+
+/*
+ * Given a comma-separated list of properties, construct a property list
+ * containing both user-defined and native properties. This function will
+ * return a NULL list if 'all' is specified, which can later be expanded
+ * by zprop_expand_list().
+ */
+int
+zprop_get_list(libzfs_handle_t *hdl, char *props, zprop_list_t **listp,
+ zfs_type_t type)
+{
+ *listp = NULL;
+
+ /*
+ * If 'all' is specified, return a NULL list.
+ */
+ if (strcmp(props, "all") == 0)
+ return (0);
+
+ /*
+ * If no props were specified, return an error.
+ */
+ if (props[0] == '\0') {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "no properties specified"));
+ return (zfs_error(hdl, EZFS_BADPROP, dgettext(TEXT_DOMAIN,
+ "bad property list")));
+ }
+
+ /*
+ * It would be nice to use getsubopt() here, but the inclusion of column
+ * aliases makes this more effort than it's worth.
+ */
+ while (*props != '\0') {
+ size_t len;
+ char *p;
+ char c;
+
+ if ((p = strchr(props, ',')) == NULL) {
+ len = strlen(props);
+ p = props + len;
+ } else {
+ len = p - props;
+ }
+
+ /*
+ * Check for empty options.
+ */
+ if (len == 0) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "empty property name"));
+ return (zfs_error(hdl, EZFS_BADPROP,
+ dgettext(TEXT_DOMAIN, "bad property list")));
+ }
+
+ /*
+ * Check all regular property names.
+ */
+ c = props[len];
+ props[len] = '\0';
+
+ if (strcmp(props, "space") == 0) {
+ static char *spaceprops[] = {
+ "name", "avail", "used", "usedbysnapshots",
+ "usedbydataset", "usedbyrefreservation",
+ "usedbychildren", NULL
+ };
+ int i;
+
+ for (i = 0; spaceprops[i]; i++) {
+ if (addlist(hdl, spaceprops[i], listp, type))
+ return (-1);
+ listp = &(*listp)->pl_next;
+ }
+ } else {
+ if (addlist(hdl, props, listp, type))
+ return (-1);
+ listp = &(*listp)->pl_next;
+ }
+
+ props = p;
+ if (c == ',')
+ props++;
+ }
+
+ return (0);
+}
+
+void
+zprop_free_list(zprop_list_t *pl)
+{
+ zprop_list_t *next;
+
+ while (pl != NULL) {
+ next = pl->pl_next;
+ free(pl->pl_user_prop);
+ free(pl);
+ pl = next;
+ }
+}
+
+typedef struct expand_data {
+ zprop_list_t **last;
+ libzfs_handle_t *hdl;
+ zfs_type_t type;
+} expand_data_t;
+
+int
+zprop_expand_list_cb(int prop, void *cb)
+{
+ zprop_list_t *entry;
+ expand_data_t *edp = cb;
+
+ if ((entry = zfs_alloc(edp->hdl, sizeof (zprop_list_t))) == NULL)
+ return (ZPROP_INVAL);
+
+ entry->pl_prop = prop;
+ entry->pl_width = zprop_width(prop, &entry->pl_fixed, edp->type);
+ entry->pl_all = B_TRUE;
+
+ *(edp->last) = entry;
+ edp->last = &entry->pl_next;
+
+ return (ZPROP_CONT);
+}
+
+int
+zprop_expand_list(libzfs_handle_t *hdl, zprop_list_t **plp, zfs_type_t type)
+{
+ zprop_list_t *entry;
+ zprop_list_t **last;
+ expand_data_t exp;
+
+ if (*plp == NULL) {
+ /*
+ * If this is the very first time we've been called for an 'all'
+ * specification, expand the list to include all native
+ * properties.
+ */
+ last = plp;
+
+ exp.last = last;
+ exp.hdl = hdl;
+ exp.type = type;
+
+ if (zprop_iter_common(zprop_expand_list_cb, &exp, B_FALSE,
+ B_FALSE, type) == ZPROP_INVAL)
+ return (-1);
+
+ /*
+ * Add 'name' to the beginning of the list, which is handled
+ * specially.
+ */
+ if ((entry = zfs_alloc(hdl, sizeof (zprop_list_t))) == NULL)
+ return (-1);
+
+ entry->pl_prop = (type == ZFS_TYPE_POOL) ? ZPOOL_PROP_NAME :
+ ZFS_PROP_NAME;
+ entry->pl_width = zprop_width(entry->pl_prop,
+ &entry->pl_fixed, type);
+ entry->pl_all = B_TRUE;
+ entry->pl_next = *plp;
+ *plp = entry;
+ }
+ return (0);
+}
+
+int
+zprop_iter(zprop_func func, void *cb, boolean_t show_all, boolean_t ordered,
+ zfs_type_t type)
+{
+ return (zprop_iter_common(func, cb, show_all, ordered, type));
+}
diff --git a/cddl/contrib/opensolaris/lib/libzpool/common/kernel.c b/cddl/contrib/opensolaris/lib/libzpool/common/kernel.c
index 30c5a0c..467cf5c 100644
--- a/cddl/contrib/opensolaris/lib/libzpool/common/kernel.c
+++ b/cddl/contrib/opensolaris/lib/libzpool/common/kernel.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -101,20 +101,24 @@ void
zmutex_init(kmutex_t *mp)
{
mp->m_owner = NULL;
+ mp->initialized = B_TRUE;
(void) _mutex_init(&mp->m_lock, USYNC_THREAD, NULL);
}
void
zmutex_destroy(kmutex_t *mp)
{
+ ASSERT(mp->initialized == B_TRUE);
ASSERT(mp->m_owner == NULL);
(void) _mutex_destroy(&(mp)->m_lock);
mp->m_owner = (void *)-1UL;
+ mp->initialized = B_FALSE;
}
void
mutex_enter(kmutex_t *mp)
{
+ ASSERT(mp->initialized == B_TRUE);
ASSERT(mp->m_owner != (void *)-1UL);
ASSERT(mp->m_owner != curthread);
VERIFY(mutex_lock(&mp->m_lock) == 0);
@@ -125,6 +129,7 @@ mutex_enter(kmutex_t *mp)
int
mutex_tryenter(kmutex_t *mp)
{
+ ASSERT(mp->initialized == B_TRUE);
ASSERT(mp->m_owner != (void *)-1UL);
if (mutex_trylock(&mp->m_lock) == 0) {
ASSERT(mp->m_owner == NULL);
@@ -138,6 +143,7 @@ mutex_tryenter(kmutex_t *mp)
void
mutex_exit(kmutex_t *mp)
{
+ ASSERT(mp->initialized == B_TRUE);
ASSERT(mp->m_owner == curthread);
mp->m_owner = NULL;
VERIFY(mutex_unlock(&mp->m_lock) == 0);
@@ -146,6 +152,7 @@ mutex_exit(kmutex_t *mp)
void *
mutex_owner(kmutex_t *mp)
{
+ ASSERT(mp->initialized == B_TRUE);
return (mp->m_owner);
}
@@ -160,7 +167,7 @@ rw_init(krwlock_t *rwlp, char *name, int type, void *arg)
{
rwlock_init(&rwlp->rw_lock, USYNC_THREAD, NULL);
rwlp->rw_owner = NULL;
- rwlp->rw_count = 0;
+ rwlp->initialized = B_TRUE;
}
void
@@ -168,22 +175,23 @@ rw_destroy(krwlock_t *rwlp)
{
rwlock_destroy(&rwlp->rw_lock);
rwlp->rw_owner = (void *)-1UL;
- rwlp->rw_count = -2;
+ rwlp->initialized = B_FALSE;
}
void
rw_enter(krwlock_t *rwlp, krw_t rw)
{
//ASSERT(!RW_LOCK_HELD(rwlp));
+ ASSERT(rwlp->initialized == B_TRUE);
ASSERT(rwlp->rw_owner != (void *)-1UL);
ASSERT(rwlp->rw_owner != curthread);
if (rw == RW_READER) {
- (void) rw_rdlock(&rwlp->rw_lock);
+ VERIFY(rw_rdlock(&rwlp->rw_lock) == 0);
ASSERT(rwlp->rw_count >= 0);
atomic_add_int(&rwlp->rw_count, 1);
} else {
- (void) rw_wrlock(&rwlp->rw_lock);
+ VERIFY(rw_wrlock(&rwlp->rw_lock) == 0);
ASSERT(rwlp->rw_count == 0);
rwlp->rw_count = -1;
rwlp->rw_owner = curthread;
@@ -193,6 +201,7 @@ rw_enter(krwlock_t *rwlp, krw_t rw)
void
rw_exit(krwlock_t *rwlp)
{
+ ASSERT(rwlp->initialized == B_TRUE);
ASSERT(rwlp->rw_owner != (void *)-1UL);
if (rwlp->rw_owner == curthread) {
@@ -205,7 +214,7 @@ rw_exit(krwlock_t *rwlp)
ASSERT(rwlp->rw_count > 0);
atomic_add_int(&rwlp->rw_count, -1);
}
- (void) rw_unlock(&rwlp->rw_lock);
+ VERIFY(rw_unlock(&rwlp->rw_lock) == 0);
}
int
@@ -213,6 +222,7 @@ rw_tryenter(krwlock_t *rwlp, krw_t rw)
{
int rv;
+ ASSERT(rwlp->initialized == B_TRUE);
ASSERT(rwlp->rw_owner != (void *)-1UL);
ASSERT(rwlp->rw_owner != curthread);
@@ -241,6 +251,7 @@ rw_tryenter(krwlock_t *rwlp, krw_t rw)
int
rw_tryupgrade(krwlock_t *rwlp)
{
+ ASSERT(rwlp->initialized == B_TRUE);
ASSERT(rwlp->rw_owner != (void *)-1UL);
return (0);
@@ -422,9 +433,10 @@ vn_open(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2, int x3)
return (0);
}
+/*ARGSUSED*/
int
vn_openat(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2,
- int x3, vnode_t *startvp)
+ int x3, vnode_t *startvp, int fd)
{
char *realpath = umem_alloc(strlen(path) + 2, UMEM_NOFAIL);
int ret;
@@ -432,6 +444,7 @@ vn_openat(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2,
ASSERT(startvp == rootdir);
(void) sprintf(realpath, "/%s", path);
+ /* fd ignored for now, need if want to simulate nbmand support */
ret = vn_open(realpath, x1, flags, mode, vpp, x2, x3);
umem_free(realpath, strlen(path) + 2);
@@ -469,7 +482,7 @@ vn_rdwr(int uio, vnode_t *vp, void *addr, ssize_t len, offset_t offset,
}
void
-vn_close(vnode_t *vp)
+vn_close(vnode_t *vp, int openflag, cred_t *cr, kthread_t *td)
{
close(vp->v_fd);
spa_strfree(vp->v_path);
@@ -657,7 +670,8 @@ kobj_open_file(char *name)
vnode_t *vp;
/* set vp as the _fd field of the file */
- if (vn_openat(name, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0, rootdir) != 0)
+ if (vn_openat(name, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0, rootdir,
+ -1) != 0)
return ((void *)-1UL);
file = umem_zalloc(sizeof (struct _buf), UMEM_NOFAIL);
@@ -679,7 +693,7 @@ kobj_read_file(struct _buf *file, char *buf, unsigned size, unsigned off)
void
kobj_close_file(struct _buf *file)
{
- vn_close((vnode_t *)file->_fd);
+ vn_close((vnode_t *)file->_fd, 0, NULL, NULL);
umem_free(file, sizeof (struct _buf));
}
@@ -690,7 +704,7 @@ kobj_get_filesize(struct _buf *file, uint64_t *size)
vnode_t *vp = (vnode_t *)file->_fd;
if (fstat64(vp->v_fd, &st) == -1) {
- vn_close(vp);
+ vn_close(vp, 0, NULL, NULL);
return (errno);
}
*size = st.st_size;
@@ -746,10 +760,11 @@ highbit(ulong_t i)
}
#endif
+static int random_fd = -1, urandom_fd = -1;
+
static int
-random_get_bytes_common(uint8_t *ptr, size_t len, char *devname)
+random_get_bytes_common(uint8_t *ptr, size_t len, int fd)
{
- int fd = open(devname, O_RDONLY);
size_t resid = len;
ssize_t bytes;
@@ -757,26 +772,24 @@ random_get_bytes_common(uint8_t *ptr, size_t len, char *devname)
while (resid != 0) {
bytes = read(fd, ptr, resid);
- ASSERT(bytes >= 0);
+ ASSERT3S(bytes, >=, 0);
ptr += bytes;
resid -= bytes;
}
- close(fd);
-
return (0);
}
int
random_get_bytes(uint8_t *ptr, size_t len)
{
- return (random_get_bytes_common(ptr, len, "/dev/random"));
+ return (random_get_bytes_common(ptr, len, random_fd));
}
int
random_get_pseudo_bytes(uint8_t *ptr, size_t len)
{
- return (random_get_bytes_common(ptr, len, "/dev/urandom"));
+ return (random_get_bytes_common(ptr, len, urandom_fd));
}
int
@@ -815,7 +828,11 @@ kernel_init(int mode)
dprintf("physmem = %llu pages (%.2f GB)\n", physmem,
(double)physmem * sysconf(_SC_PAGE_SIZE) / (1ULL << 30));
- snprintf(hw_serial, sizeof (hw_serial), "%ld", gethostid());
+ snprintf(hw_serial, sizeof (hw_serial), "%lu",
+ (unsigned long)gethostid());
+
+ VERIFY((random_fd = open("/dev/random", O_RDONLY)) != -1);
+ VERIFY((urandom_fd = open("/dev/urandom", O_RDONLY)) != -1);
spa_init(mode);
}
@@ -824,6 +841,12 @@ void
kernel_fini(void)
{
spa_fini();
+
+ close(random_fd);
+ close(urandom_fd);
+
+ random_fd = -1;
+ urandom_fd = -1;
}
int
@@ -850,3 +873,62 @@ z_compress_level(void *dst, size_t *dstlen, const void *src, size_t srclen,
return (ret);
}
+
+uid_t
+crgetuid(cred_t *cr)
+{
+ return (0);
+}
+
+gid_t
+crgetgid(cred_t *cr)
+{
+ return (0);
+}
+
+int
+crgetngroups(cred_t *cr)
+{
+ return (0);
+}
+
+gid_t *
+crgetgroups(cred_t *cr)
+{
+ return (NULL);
+}
+
+int
+zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr)
+{
+ return (0);
+}
+
+int
+zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr)
+{
+ return (0);
+}
+
+int
+zfs_secpolicy_destroy_perms(const char *name, cred_t *cr)
+{
+ return (0);
+}
+
+ksiddomain_t *
+ksid_lookupdomain(const char *dom)
+{
+ ksiddomain_t *kd;
+
+ kd = umem_zalloc(sizeof (ksiddomain_t), UMEM_NOFAIL);
+ kd->kd_name = spa_strdup(dom);
+ return (kd);
+}
+
+void
+ksiddomain_rele(ksiddomain_t *ksid)
+{
+ spa_strfree(ksid->kd_name);
+ umem_free(ksid, sizeof (ksiddomain_t));
+}
diff --git a/cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h b/cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h
index 24572c9..971584d 100644
--- a/cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h
+++ b/cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h
@@ -19,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_ZFS_CONTEXT_H
#define _SYS_ZFS_CONTEXT_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -64,6 +62,7 @@ extern "C" {
#include <fsshare.h>
#include <sys/note.h>
#include <sys/types.h>
+#include <sys/cred.h>
#include <sys/atomic.h>
#include <sys/sysmacros.h>
#include <sys/bitmap.h>
@@ -78,8 +77,10 @@ extern "C" {
#include <sys/debug.h>
#include <sys/sdt.h>
#include <sys/kstat.h>
+#include <sys/u8_textprep.h>
#include <sys/kernel.h>
#include <sys/disk.h>
+#include <sys/sysevent/eventdefs.h>
#include <machine/atomic.h>
#define ZFS_EXPORTS_PATH "/etc/zfs/exports"
@@ -116,11 +117,12 @@ extern void vcmn_err(int, const char *, __va_list);
extern void panic(const char *, ...);
extern void vpanic(const char *, __va_list);
+#define fm_panic panic
+
/* This definition is copied from assert.h. */
#if defined(__STDC__)
#if __STDC_VERSION__ - 0 >= 199901L
-#define verify(EX) (void)((EX) || \
- (__assert_c99(#EX, __FILE__, __LINE__, __func__), 0))
+#define verify(EX) (void)((EX) || (__assert(#EX, __FILE__, __LINE__), 0))
#else
#define verify(EX) (void)((EX) || (__assert(#EX, __FILE__, __LINE__), 0))
#endif /* __STDC_VERSION__ - 0 >= 199901L */
@@ -167,11 +169,16 @@ _NOTE(CONSTCOND) } while (0)
#endif
/*
- * Dtrace SDT probes have different signatures in userland than they do in
+ * DTrace SDT probes have different signatures in userland than they do in
* kernel. If they're being used in kernel code, re-define them out of
* existence for their counterparts in libzpool.
*/
+#ifdef DTRACE_PROBE
+#undef DTRACE_PROBE
+#define DTRACE_PROBE(a) ((void)0)
+#endif /* DTRACE_PROBE */
+
#ifdef DTRACE_PROBE1
#undef DTRACE_PROBE1
#define DTRACE_PROBE1(a, b, c) ((void)0)
@@ -212,8 +219,9 @@ extern kthread_t *zk_thread_create(void (*func)(), void *arg);
* Mutexes
*/
typedef struct kmutex {
- void *m_owner;
- mutex_t m_lock;
+ void *m_owner;
+ boolean_t initialized;
+ mutex_t m_lock;
} kmutex_t;
#define MUTEX_DEFAULT USYNC_THREAD
@@ -243,6 +251,7 @@ extern void *mutex_owner(kmutex_t *mp);
typedef struct krwlock {
int rw_count;
void *rw_owner;
+ boolean_t initialized;
rwlock_t rw_lock;
} krwlock_t;
@@ -253,6 +262,7 @@ typedef int krw_t;
#define RW_DEFAULT USYNC_THREAD
#undef RW_READ_HELD
+#define RW_READ_HELD(x) ((x)->rw_owner == NULL && (x)->rw_count > 0)
#undef RW_WRITE_HELD
#define RW_WRITE_HELD(x) ((x)->rw_owner == curthread)
@@ -267,6 +277,11 @@ extern void rw_exit(krwlock_t *rwlp);
extern int rw_lock_held(krwlock_t *rwlp);
#define rw_downgrade(rwlp) do { } while (0)
+extern uid_t crgetuid(cred_t *cr);
+extern gid_t crgetgid(cred_t *cr);
+extern int crgetngroups(cred_t *cr);
+extern gid_t *crgetgroups(cred_t *cr);
+
/*
* Condition variables
*/
@@ -285,6 +300,7 @@ extern void cv_broadcast(kcondvar_t *cv);
* Kernel memory
*/
#define KM_SLEEP UMEM_NOFAIL
+#define KM_PUSHPAGE KM_SLEEP
#define KM_NOSLEEP UMEM_DEFAULT
#define KMC_NODEBUG UMC_NODEBUG
#define kmem_alloc(_s, _f) umem_alloc(_s, _f)
@@ -322,6 +338,9 @@ extern void taskq_destroy(taskq_t *);
extern void taskq_wait(taskq_t *);
extern int taskq_member(taskq_t *, void *);
+#define XVA_MAPSIZE 3
+#define XVA_MAGIC 0x78766174
+
/*
* vnodes
*/
@@ -331,44 +350,93 @@ typedef struct vnode {
char *v_path;
} vnode_t;
+
+typedef struct xoptattr {
+ timestruc_t xoa_createtime; /* Create time of file */
+ uint8_t xoa_archive;
+ uint8_t xoa_system;
+ uint8_t xoa_readonly;
+ uint8_t xoa_hidden;
+ uint8_t xoa_nounlink;
+ uint8_t xoa_immutable;
+ uint8_t xoa_appendonly;
+ uint8_t xoa_nodump;
+ uint8_t xoa_settable;
+ uint8_t xoa_opaque;
+ uint8_t xoa_av_quarantined;
+ uint8_t xoa_av_modified;
+} xoptattr_t;
+
typedef struct vattr {
uint_t va_mask; /* bit-mask of attributes */
u_offset_t va_size; /* file size in bytes */
} vattr_t;
-#define AT_TYPE 0x0001
-#define AT_MODE 0x0002
-#define AT_UID 0x0004
-#define AT_GID 0x0008
-#define AT_FSID 0x0010
-#define AT_NODEID 0x0020
-#define AT_NLINK 0x0040
-#define AT_SIZE 0x0080
-#define AT_ATIME 0x0100
-#define AT_MTIME 0x0200
-#define AT_CTIME 0x0400
-#define AT_RDEV 0x0800
-#define AT_BLKSIZE 0x1000
-#define AT_NBLOCKS 0x2000
-#define AT_SEQ 0x8000
+
+typedef struct xvattr {
+ vattr_t xva_vattr; /* Embedded vattr structure */
+ uint32_t xva_magic; /* Magic Number */
+ uint32_t xva_mapsize; /* Size of attr bitmap (32-bit words) */
+ uint32_t *xva_rtnattrmapp; /* Ptr to xva_rtnattrmap[] */
+ uint32_t xva_reqattrmap[XVA_MAPSIZE]; /* Requested attrs */
+ uint32_t xva_rtnattrmap[XVA_MAPSIZE]; /* Returned attrs */
+ xoptattr_t xva_xoptattrs; /* Optional attributes */
+} xvattr_t;
+
+typedef struct vsecattr {
+ uint_t vsa_mask; /* See below */
+ int vsa_aclcnt; /* ACL entry count */
+ void *vsa_aclentp; /* pointer to ACL entries */
+ int vsa_dfaclcnt; /* default ACL entry count */
+ void *vsa_dfaclentp; /* pointer to default ACL entries */
+ size_t vsa_aclentsz; /* ACE size in bytes of vsa_aclentp */
+} vsecattr_t;
+
+#define AT_TYPE 0x00001
+#define AT_MODE 0x00002
+#define AT_UID 0x00004
+#define AT_GID 0x00008
+#define AT_FSID 0x00010
+#define AT_NODEID 0x00020
+#define AT_NLINK 0x00040
+#define AT_SIZE 0x00080
+#define AT_ATIME 0x00100
+#define AT_MTIME 0x00200
+#define AT_CTIME 0x00400
+#define AT_RDEV 0x00800
+#define AT_BLKSIZE 0x01000
+#define AT_NBLOCKS 0x02000
+#define AT_SEQ 0x08000
+#define AT_XVATTR 0x10000
#define CRCREAT 0
-#define VOP_CLOSE(vp, f, c, o, cr) 0
-#define VOP_PUTPAGE(vp, of, sz, fl, cr) 0
-#define VOP_GETATTR(vp, vap, fl) ((vap)->va_size = (vp)->v_size, 0)
+#define VOP_CLOSE(vp, f, c, o, cr, ct) 0
+#define VOP_PUTPAGE(vp, of, sz, fl, cr, ct) 0
+#define VOP_GETATTR(vp, vap, cr) ((vap)->va_size = (vp)->v_size, 0)
-#define VOP_FSYNC(vp, f, cr) fsync((vp)->v_fd)
+#define VOP_FSYNC(vp, f, cr, ct) fsync((vp)->v_fd)
-#define VN_RELE(vp) vn_close(vp)
+#define VN_RELE(vp) vn_close(vp, 0, NULL, NULL)
+
+#define vn_lock(vp, type)
+#define VOP_UNLOCK(vp, type)
+#ifdef VFS_LOCK_GIANT
+#undef VFS_LOCK_GIANT
+#endif
+#define VFS_LOCK_GIANT(mp) 0
+#ifdef VFS_UNLOCK_GIANT
+#undef VFS_UNLOCK_GIANT
+#endif
+#define VFS_UNLOCK_GIANT(vfslocked)
extern int vn_open(char *path, int x1, int oflags, int mode, vnode_t **vpp,
int x2, int x3);
extern int vn_openat(char *path, int x1, int oflags, int mode, vnode_t **vpp,
- int x2, int x3, vnode_t *vp);
+ int x2, int x3, vnode_t *vp, int fd);
extern int vn_rdwr(int uio, vnode_t *vp, void *addr, ssize_t len,
offset_t offset, int x1, int x2, rlim64_t x3, void *x4, ssize_t *residp);
-extern void vn_close(vnode_t *vp);
+extern void vn_close(vnode_t *vp, int openflag, cred_t *cr, kthread_t *td);
#define vn_remove(path, x1, x2) remove(path)
#define vn_rename(from, to, seg) rename((from), (to))
@@ -397,8 +465,9 @@ extern void delay(clock_t ticks);
#define CPU_SEQID (thr_self() & (max_ncpus - 1))
-#define kcred NULL
-#define CRED() NULL
+#ifndef ptob
+#define ptob(x) ((x) * PAGESIZE)
+#endif
extern uint64_t physmem;
@@ -455,11 +524,31 @@ struct bootstat {
uint64_t st_size;
};
+typedef struct ace_object {
+ uid_t a_who;
+ uint32_t a_access_mask;
+ uint16_t a_flags;
+ uint16_t a_type;
+ uint8_t a_obj_type[16];
+ uint8_t a_inherit_obj_type[16];
+} ace_object_t;
+
+
+#define ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05
+#define ACE_ACCESS_DENIED_OBJECT_ACE_TYPE 0x06
+#define ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07
+#define ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08
+
extern struct _buf *kobj_open_file(char *name);
extern int kobj_read_file(struct _buf *file, char *buf, unsigned size,
unsigned off);
extern void kobj_close_file(struct _buf *file);
extern int kobj_get_filesize(struct _buf *file, uint64_t *size);
+extern int zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr);
+extern int zfs_secpolicy_rename_perms(const char *from, const char *to,
+ cred_t *cr);
+extern int zfs_secpolicy_destroy_perms(const char *name, cred_t *cr);
+extern zoneid_t getzoneid(void);
/* Random compatibility stuff. */
#define lbolt (gethrtime() >> 23)
#define lbolt64 (gethrtime() >> 23)
@@ -482,18 +571,32 @@ struct file {
#define FCREAT O_CREAT
#define FOFFMAX 0x0
+/* SID stuff */
+typedef struct ksiddomain {
+ uint_t kd_ref;
+ uint_t kd_len;
+ char *kd_name;
+} ksiddomain_t;
+
+ksiddomain_t *ksid_lookupdomain(const char *);
+void ksiddomain_rele(ksiddomain_t *);
+
#define SX_SYSINIT(name, lock, desc)
#define SYSCTL_DECL(...)
#define SYSCTL_NODE(...)
#define SYSCTL_INT(...)
+#define SYSCTL_UINT(...)
#define SYSCTL_ULONG(...)
+#define SYSCTL_QUAD(...)
#ifdef TUNABLE_INT
#undef TUNABLE_INT
#undef TUNABLE_ULONG
+#undef TUNABLE_QUAD
#endif
#define TUNABLE_INT(...)
#define TUNABLE_ULONG(...)
+#define TUNABLE_QUAD(...)
/* Errors */
diff --git a/cddl/contrib/opensolaris/lib/libzpool/common/taskq.c b/cddl/contrib/opensolaris/lib/libzpool/common/taskq.c
index f7b6571..ccf5b4d 100644
--- a/cddl/contrib/opensolaris/lib/libzpool/common/taskq.c
+++ b/cddl/contrib/opensolaris/lib/libzpool/common/taskq.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -177,6 +176,9 @@ taskq_create(const char *name, int nthreads, pri_t pri,
int t;
rw_init(&tq->tq_threadlock, NULL, RW_DEFAULT, NULL);
+ mutex_init(&tq->tq_lock, NULL, MUTEX_DEFAULT, NULL);
+ cv_init(&tq->tq_dispatch_cv, NULL, CV_DEFAULT, NULL);
+ cv_init(&tq->tq_wait_cv, NULL, CV_DEFAULT, NULL);
tq->tq_flags = flags | TASKQ_ACTIVE;
tq->tq_active = nthreads;
tq->tq_nthreads = nthreads;
@@ -230,6 +232,9 @@ taskq_destroy(taskq_t *tq)
kmem_free(tq->tq_threadlist, nthreads * sizeof (thread_t));
rw_destroy(&tq->tq_threadlock);
+ mutex_destroy(&tq->tq_lock);
+ cv_destroy(&tq->tq_dispatch_cv);
+ cv_destroy(&tq->tq_wait_cv);
kmem_free(tq, sizeof (taskq_t));
}
diff --git a/cddl/contrib/opensolaris/lib/libzpool/common/util.c b/cddl/contrib/opensolaris/lib/libzpool/common/util.c
index df49adb..781edb6 100644
--- a/cddl/contrib/opensolaris/lib/libzpool/common/util.c
+++ b/cddl/contrib/opensolaris/lib/libzpool/common/util.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <assert.h>
#include <sys/zfs_context.h>
#include <sys/avl.h>
@@ -67,46 +65,58 @@ nicenum(uint64_t num, char *buf)
}
static void
-show_vdev_stats(const char *desc, nvlist_t *nv, int indent)
+show_vdev_stats(const char *desc, const char *ctype, nvlist_t *nv, int indent)
{
- nvlist_t **child;
- uint_t c, children;
vdev_stat_t *vs;
+ vdev_stat_t v0 = { 0 };
uint64_t sec;
+ uint64_t is_log = 0;
+ nvlist_t **child;
+ uint_t c, children;
char used[6], avail[6];
char rops[6], wops[6], rbytes[6], wbytes[6], rerr[6], werr[6], cerr[6];
+ char *prefix = "";
- if (indent == 0) {
- (void) printf(" "
+ if (indent == 0 && desc != NULL) {
+ (void) printf(" "
" capacity operations bandwidth ---- errors ----\n");
- (void) printf("description "
+ (void) printf("description "
"used avail read write read write read write cksum\n");
}
- VERIFY(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_STATS,
- (uint64_t **)&vs, &c) == 0);
-
- sec = MAX(1, vs->vs_timestamp / NANOSEC);
-
- nicenum(vs->vs_alloc, used);
- nicenum(vs->vs_space - vs->vs_alloc, avail);
- nicenum(vs->vs_ops[ZIO_TYPE_READ] / sec, rops);
- nicenum(vs->vs_ops[ZIO_TYPE_WRITE] / sec, wops);
- nicenum(vs->vs_bytes[ZIO_TYPE_READ] / sec, rbytes);
- nicenum(vs->vs_bytes[ZIO_TYPE_WRITE] / sec, wbytes);
- nicenum(vs->vs_read_errors, rerr);
- nicenum(vs->vs_write_errors, werr);
- nicenum(vs->vs_checksum_errors, cerr);
-
- (void) printf("%*s%*s%*s%*s %5s %5s %5s %5s %5s %5s %5s\n",
- indent, "",
- indent - 19 - (vs->vs_space ? 0 : 12), desc,
- vs->vs_space ? 6 : 0, vs->vs_space ? used : "",
- vs->vs_space ? 6 : 0, vs->vs_space ? avail : "",
- rops, wops, rbytes, wbytes, rerr, werr, cerr);
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
- &child, &children) != 0)
+ if (desc != NULL) {
+ (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_IS_LOG, &is_log);
+
+ if (is_log)
+ prefix = "log ";
+
+ if (nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_STATS,
+ (uint64_t **)&vs, &c) != 0)
+ vs = &v0;
+
+ sec = MAX(1, vs->vs_timestamp / NANOSEC);
+
+ nicenum(vs->vs_alloc, used);
+ nicenum(vs->vs_space - vs->vs_alloc, avail);
+ nicenum(vs->vs_ops[ZIO_TYPE_READ] / sec, rops);
+ nicenum(vs->vs_ops[ZIO_TYPE_WRITE] / sec, wops);
+ nicenum(vs->vs_bytes[ZIO_TYPE_READ] / sec, rbytes);
+ nicenum(vs->vs_bytes[ZIO_TYPE_WRITE] / sec, wbytes);
+ nicenum(vs->vs_read_errors, rerr);
+ nicenum(vs->vs_write_errors, werr);
+ nicenum(vs->vs_checksum_errors, cerr);
+
+ (void) printf("%*s%s%*s%*s%*s %5s %5s %5s %5s %5s %5s %5s\n",
+ indent, "",
+ prefix,
+ indent + strlen(prefix) - 25 - (vs->vs_space ? 0 : 12),
+ desc,
+ vs->vs_space ? 6 : 0, vs->vs_space ? used : "",
+ vs->vs_space ? 6 : 0, vs->vs_space ? avail : "",
+ rops, wops, rbytes, wbytes, rerr, werr, cerr);
+ }
+
+ if (nvlist_lookup_nvlist_array(nv, ctype, &child, &children) != 0)
return;
for (c = 0; c < children; c++) {
@@ -120,7 +130,7 @@ show_vdev_stats(const char *desc, nvlist_t *nv, int indent)
(void) strcpy(tname, cname);
if (nvlist_lookup_uint64(cnv, ZPOOL_CONFIG_NPARITY, &np) == 0)
tname[strlen(tname)] = '0' + np;
- show_vdev_stats(tname, cnv, indent + 2);
+ show_vdev_stats(tname, ctype, cnv, indent + 2);
free(tname);
}
}
@@ -131,14 +141,16 @@ show_pool_stats(spa_t *spa)
nvlist_t *config, *nvroot;
char *name;
- spa_config_enter(spa, RW_READER, FTAG);
- config = spa_config_generate(spa, NULL, -1ULL, B_TRUE);
- spa_config_exit(spa, FTAG);
+ VERIFY(spa_get_stats(spa_name(spa), &config, NULL, 0) == 0);
VERIFY(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
&nvroot) == 0);
VERIFY(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
&name) == 0);
- show_vdev_stats(name, nvroot, 0);
+ show_vdev_stats(name, ZPOOL_CONFIG_CHILDREN, nvroot, 0);
+ show_vdev_stats(NULL, ZPOOL_CONFIG_L2CACHE, nvroot, 0);
+ show_vdev_stats(NULL, ZPOOL_CONFIG_SPARES, nvroot, 0);
+
+ nvlist_free(config);
}
diff --git a/cddl/lib/libzfs/Makefile b/cddl/lib/libzfs/Makefile
index a896b8f..30c516b 100644
--- a/cddl/lib/libzfs/Makefile
+++ b/cddl/lib/libzfs/Makefile
@@ -10,14 +10,17 @@ DPADD= ${LIBUTIL}
LDADD= -lutil
SRCS= deviceid.c \
- mnttab.c \
+ fsshare.c \
mkdirp.c \
+ mnttab.c \
zmount.c \
- fsshare.c \
zone.c
-SRCS+= zfs_namecheck.c \
+SRCS+= zfs_deleg.c \
+ zfs_namecheck.c \
zfs_prop.c \
+ zpool_prop.c \
+ zprop_common.c \
libzfs_dataset.c \
libzfs_util.c \
libzfs_graph.c \
@@ -26,6 +29,7 @@ SRCS+= zfs_namecheck.c \
libzfs_changelist.c \
libzfs_config.c \
libzfs_import.c \
+ libzfs_sendrecv.c \
libzfs_status.c
CFLAGS+= -DZFS_NO_ACL
diff --git a/cddl/lib/libzpool/Makefile b/cddl/lib/libzpool/Makefile
index 0890073..26d5499 100644
--- a/cddl/lib/libzpool/Makefile
+++ b/cddl/lib/libzpool/Makefile
@@ -18,6 +18,8 @@ ATOMIC_SRCS= atomic.S
.PATH: ${.CURDIR}/../../../sys/cddl/compat/opensolaris/kern
ATOMIC_SRCS= opensolaris_atomic.c
.endif
+# UNICODE_SRCS
+.PATH: ${.CURDIR}/../../../sys/cddl/contrib/opensolaris/common/unicode
LIB= zpool
@@ -25,9 +27,13 @@ ZFS_COMMON_SRCS= ${ZFS_COMMON_OBJS:C/.o$/.c/} vdev_file.c
ZFS_SHARED_SRCS= ${ZFS_SHARED_OBJS:C/.o$/.c/}
KERNEL_SRCS= kernel.c taskq.c util.c
LIST_SRCS= list.c
+UNICODE_SRCS= u8_textprep.c
SRCS= ${ZFS_COMMON_SRCS} ${ZFS_SHARED_SRCS} \
- ${KERNEL_SRCS} ${LIST_SRCS} ${ATOMIC_SRCS}
+ ${KERNEL_SRCS} ${LIST_SRCS} ${ATOMIC_SRCS} \
+ ${UNICODE_SRCS}
+
+CFLAGS+= -std=c99
CFLAGS+= -I${.CURDIR}/../../../sys/cddl/compat/opensolaris
CFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris/include
@@ -40,6 +46,13 @@ CFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common
CFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/head
CFLAGS+= -I${.CURDIR}/../../../cddl/lib/libumem
CFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libnvpair
+# XXX: pthread doesn't have mutex_owned() equivalent, so we need to look
+# into libthr private structures. That's sooo evil, but it's only for
+# ZFS debugging tools needs.
+CFLAGS+= -DWANTS_MUTEX_OWNED
+CFLAGS+= -I${.CURDIR}/../../../lib/libpthread/thread
+CFLAGS+= -I${.CURDIR}/../../../lib/libpthread/sys
+CFLAGS+= -I${.CURDIR}/../../../lib/libthr/arch/${MACHINE_ARCH}/include
DPADD= ${LIBPTHREAD} ${LIBZ}
LDADD= -lpthread -lz
diff --git a/cddl/sbin/zpool/Makefile b/cddl/sbin/zpool/Makefile
index 04d6c13..4b24dfb 100644
--- a/cddl/sbin/zpool/Makefile
+++ b/cddl/sbin/zpool/Makefile
@@ -1,10 +1,11 @@
# $FreeBSD$
-.PATH: ${.CURDIR}/../../../cddl/contrib/opensolaris/cmd/zpool
+.PATH: ${.CURDIR}/../../../cddl/contrib/opensolaris/cmd/zpool \
+ ${.CURDIR}/../../../sys/cddl/contrib/opensolaris/common/zfs
PROG= zpool
MAN= zpool.8
-SRCS= zpool_main.c zpool_vdev.c zpool_iter.c zpool_util.c
+SRCS= zpool_main.c zpool_vdev.c zpool_iter.c zpool_util.c zfs_comutil.c
CFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libzpool/common
CFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris/include
@@ -15,6 +16,7 @@ CFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libuutil/common
CFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libumem/common
CFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libzfs/common
CFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libnvpair
+CFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/common/zfs
CFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common
CFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/fs/zfs
CFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/sys
diff --git a/cddl/usr.bin/Makefile b/cddl/usr.bin/Makefile
index 4112e8a..c6b1341 100644
--- a/cddl/usr.bin/Makefile
+++ b/cddl/usr.bin/Makefile
@@ -7,9 +7,11 @@ SUBDIR= \
ctfdump \
ctfmerge \
sgsmsg \
+ ${_zinject} \
${_ztest}
.if ${MK_ZFS} != "no"
+_zinject= zinject
.if ${MK_LIBTHR} != "no"
_ztest= ztest
.endif
diff --git a/cddl/usr.bin/zinject/Makefile b/cddl/usr.bin/zinject/Makefile
new file mode 100644
index 0000000..c8693fe
--- /dev/null
+++ b/cddl/usr.bin/zinject/Makefile
@@ -0,0 +1,25 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../contrib/opensolaris/cmd/zinject
+
+PROG= zinject
+SRCS= zinject.c translate.c
+NO_MAN=
+
+CFLAGS+= -I${.CURDIR}/../../../sys/cddl/compat/opensolaris
+CFLAGS+= -I${.CURDIR}/../../compat/opensolaris/include
+CFLAGS+= -I${.CURDIR}/../../compat/opensolaris/lib/libumem
+CFLAGS+= -I${.CURDIR}/../../contrib/opensolaris/lib/libzfs/common
+CFLAGS+= -I${.CURDIR}/../../contrib/opensolaris/lib/libzpool/common
+CFLAGS+= -I${.CURDIR}/../../contrib/opensolaris/lib/libnvpair
+CFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/fs/zfs
+CFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/sys
+CFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common
+CFLAGS+= -I${.CURDIR}/../../contrib/opensolaris/head
+CFLAGS+= -I${.CURDIR}/../../lib/libumem
+
+DPADD= ${LIBAVL} ${LIBGEOM} ${LIBM} ${LIBNVPAIR} ${LIBUMEM} ${LIBUUTIL} \
+ ${LIBZFS} ${LIBZPOOL} ${LIBUUTIL}
+LDADD= -lavl -lgeom -lm -lnvpair -lumem -luutil -lzfs -lzpool
+
+.include <bsd.prog.mk>
diff --git a/cddl/usr.bin/ztest/Makefile b/cddl/usr.bin/ztest/Makefile
index 0bdded6..cc0bca4 100644
--- a/cddl/usr.bin/ztest/Makefile
+++ b/cddl/usr.bin/ztest/Makefile
@@ -1,18 +1,20 @@
# $FreeBSD$
-.PATH: ${.CURDIR}/../../../cddl/contrib/opensolaris/cmd/ztest
+.PATH: ${.CURDIR}/../..//contrib/opensolaris/cmd/ztest
PROG= ztest
NO_MAN=
+CFLAGS+= -std=c99
+
CFLAGS+= -I${.CURDIR}/../../../sys/cddl/compat/opensolaris
-CFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris/include
-CFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris/lib/libumem
-CFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libzpool/common
+CFLAGS+= -I${.CURDIR}/../../compat/opensolaris/include
+CFLAGS+= -I${.CURDIR}/../../compat/opensolaris/lib/libumem
+CFLAGS+= -I${.CURDIR}/../../contrib/opensolaris/lib/libzpool/common
CFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/fs/zfs
CFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/sys
CFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common
-CFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/head
+CFLAGS+= -I${.CURDIR}/../../contrib/opensolaris/head
CFLAGS+= -I${.CURDIR}/../../lib/libumem
DPADD= ${LIBM} ${LIBNVPAIR} ${LIBUMEM} ${LIBZPOOL} \
diff --git a/cddl/usr.sbin/zdb/Makefile b/cddl/usr.sbin/zdb/Makefile
index 92a7352..f0c1b22 100644
--- a/cddl/usr.sbin/zdb/Makefile
+++ b/cddl/usr.sbin/zdb/Makefile
@@ -6,9 +6,14 @@ PROG= zdb
MAN= zdb.8
SRCS= zdb.c zdb_il.c
+CFLAGS+= -std=c99
+
CFLAGS+= -I${.CURDIR}/../../../sys/cddl/compat/opensolaris
CFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris/include
CFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris/lib/libumem
+CFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libnvpair
+CFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libuutil/common
+CFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libzfs/common
CFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libzpool/common
CFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/fs/zfs
CFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common
@@ -16,8 +21,8 @@ CFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/sys
CFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/head
CFLAGS+= -I${.CURDIR}/../../lib/libumem
-DPADD= ${LIBM} ${LIBNVPAIR} ${LIBUMEM} ${LIBZPOOL} \
- ${LIBPTHREAD} ${LIBZ} ${LIBAVL}
-LDADD= -lm -lnvpair -lumem -lzpool -lpthread -lz -lavl
+DPADD= ${LIBAVL} ${LIBGEOM} ${LIBM} ${LIBNVPAIR} ${LIBPTHREAD} ${LIBUMEM} \
+ ${LIBUUTIL} ${LIBZ} ${LIBZFS} ${LIBZPOOL}
+LDADD= -lavl -lgeom -lm -lnvpair -lpthread -lumem -luutil -lz -lzfs -lzpool
.include <bsd.prog.mk>
diff --git a/lib/libc/gen/getvfsbyname.3 b/lib/libc/gen/getvfsbyname.3
index 65da50b..f2a5965 100644
--- a/lib/libc/gen/getvfsbyname.3
+++ b/lib/libc/gen/getvfsbyname.3
@@ -68,7 +68,7 @@ flag bits, as described below
.Pp
The flags are defined as follows:
.Pp
-.Bl -tag -width VFCF_SYNTHETIC -compact
+.Bl -tag -width VFCF_DELEGADMIN -compact
.It Dv VFCF_STATIC
statically compiled into kernel
.It Dv VFCF_NETWORK
@@ -85,7 +85,12 @@ stores file names as Unicode
can be mounted from within a jail if
.Va security.jail.mount_allowed
sysctl is set to
-.Dv 1 .
+.Dv 1
+.It Dv VFCF_DELEGADMIN
+supports delegated administration if
+.Va vfs.usermount
+sysctl is set to
+.Dv 1
.El
.Sh RETURN VALUES
.Rv -std getvfsbyname
diff --git a/share/man/man9/VFS_SET.9 b/share/man/man9/VFS_SET.9
index 656b728..f30f431 100644
--- a/share/man/man9/VFS_SET.9
+++ b/share/man/man9/VFS_SET.9
@@ -57,7 +57,7 @@ as the event handler.
Possible values for the
.Fa flags
argument are:
-.Bl -hang -width ".Dv VFCF_SYNTHETIC"
+.Bl -hang -width ".Dv VFCF_DELEGADMIN"
.It Dv VFCF_STATIC
File system should be statically available in the kernel.
.It Dv VFCF_NETWORK
@@ -71,10 +71,15 @@ Loopback file system layer.
.It Dv VFCF_UNICODE
File names are stored as Unicode.
.It Dv VFCF_JAIL
-can be mounted from within a jail if
+Can be mounted from within a jail if
.Va security.jail.mount_allowed
sysctl is set to
.Dv 1 .
+.It Dv VFCF_DELEGADMIN
+Supports delegated administration if
+.Va vfs.usermount
+sysctl is set to
+.Dv 1 .
.El
.Sh PSEUDOCODE
.Bd -literal
diff --git a/sys/boot/Makefile b/sys/boot/Makefile
index 1af1457..27cb7e3 100644
--- a/sys/boot/Makefile
+++ b/sys/boot/Makefile
@@ -26,6 +26,10 @@ SUBDIR+= ofw
SUBDIR+= uboot
.endif
+.if defined(LOADER_ZFS_SUPPORT)
+SUBDIR+= zfs
+.endif
+
# Pick the machine-dependent subdir based on the target architecture.
ADIR= ${MACHINE:S/amd64/i386/:S/sun4v/sparc64/}
.if exists(${.CURDIR}/${ADIR}/.)
diff --git a/sys/boot/common/bootstrap.h b/sys/boot/common/bootstrap.h
index 57982d1..5f08480 100644
--- a/sys/boot/common/bootstrap.h
+++ b/sys/boot/common/bootstrap.h
@@ -43,6 +43,7 @@ struct devdesc
#define DEVT_DISK 1
#define DEVT_NET 2
#define DEVT_CD 3
+#define DEVT_ZFS 4
int d_unit;
};
diff --git a/sys/boot/i386/Makefile b/sys/boot/i386/Makefile
index b89222d..6af8642 100644
--- a/sys/boot/i386/Makefile
+++ b/sys/boot/i386/Makefile
@@ -1,7 +1,7 @@
# $FreeBSD$
-SUBDIR= mbr pmbr boot0 boot0sio btx boot2 cdboot gptboot kgzldr \
- libi386 libfirewire loader
+SUBDIR= mbr pmbr boot0 boot0sio btx boot2 cdboot gptboot zfsboot \
+ kgzldr libi386 libfirewire loader
# special boot programs, 'self-extracting boot2+loader'
SUBDIR+= pxeldr
diff --git a/sys/boot/i386/libi386/bootinfo32.c b/sys/boot/i386/libi386/bootinfo32.c
index 6b517c5..d434427 100644
--- a/sys/boot/i386/libi386/bootinfo32.c
+++ b/sys/boot/i386/libi386/bootinfo32.c
@@ -183,6 +183,7 @@ bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, vm_offset_t
break;
case DEVT_NET:
+ case DEVT_ZFS:
break;
default:
diff --git a/sys/boot/i386/libi386/devicename.c b/sys/boot/i386/libi386/devicename.c
index e1035aa..79a562b 100644
--- a/sys/boot/i386/libi386/devicename.c
+++ b/sys/boot/i386/libi386/devicename.c
@@ -167,6 +167,7 @@ i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path)
case DEVT_CD:
case DEVT_NET:
+ case DEVT_ZFS:
unit = 0;
if (*np && (*np != ':')) {
@@ -238,6 +239,7 @@ i386_fmtdev(void *vdev)
break;
case DEVT_NET:
+ case DEVT_ZFS:
sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit);
break;
}
diff --git a/sys/boot/i386/loader/Makefile b/sys/boot/i386/loader/Makefile
index df2ccc0..79aceca 100644
--- a/sys/boot/i386/loader/Makefile
+++ b/sys/boot/i386/loader/Makefile
@@ -17,6 +17,12 @@ CFLAGS+= -DLOADER_FIREWIRE_SUPPORT
LIBFIREWIRE= ${.OBJDIR}/../libfirewire/libfirewire.a
.endif
+# Put LOADER_ZFS_SUPPORT=yes in /etc/make.conf for ZFS support
+.if defined(LOADER_ZFS_SUPPORT)
+CFLAGS+= -DLOADER_ZFS_SUPPORT
+LIBZFS= ${.OBJDIR}/../../zfs/libzfsboot.a
+.endif
+
# Enable PXE TFTP or NFS support, not both.
.if defined(LOADER_TFTP_SUPPORT)
CFLAGS+= -DLOADER_TFTP_SUPPORT
@@ -98,8 +104,8 @@ FILES+= loader.rc
# XXX crt0.o needs to be first for pxeboot(8) to work
OBJS= ${BTXCRT}
-DPADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBI386} ${LIBSTAND}
-LDADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBI386} -lstand
+DPADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBZFS} ${LIBI386} ${LIBSTAND}
+LDADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBZFS} ${LIBI386} -lstand
.include <bsd.prog.mk>
diff --git a/sys/boot/i386/loader/conf.c b/sys/boot/i386/loader/conf.c
index 245f960..05c9a9e9 100644
--- a/sys/boot/i386/loader/conf.c
+++ b/sys/boot/i386/loader/conf.c
@@ -50,6 +50,10 @@ __FBSDID("$FreeBSD$");
extern struct devsw fwohci;
#endif
+#if defined(LOADER_ZFS_SUPPORT)
+extern struct devsw zfs_dev;
+#endif
+
/* Exported for libstand */
struct devsw *devsw[] = {
&bioscd,
@@ -60,15 +64,25 @@ struct devsw *devsw[] = {
#if defined(LOADER_FIREWIRE_SUPPORT)
&fwohci,
#endif
+#if defined(LOADER_ZFS_SUPPORT)
+ &zfs_dev,
+#endif
NULL
};
+#if defined(LOADER_ZFS_SUPPORT)
+extern struct fs_ops zfs_fsops;
+#endif
+
struct fs_ops *file_system[] = {
&ufs_fsops,
&ext2fs_fsops,
&dosfs_fsops,
&cd9660_fsops,
&splitfs_fsops,
+#if defined(LOADER_ZFS_SUPPORT)
+ &zfs_fsops,
+#endif
#ifdef LOADER_GZIP_SUPPORT
&gzipfs_fsops,
#endif
diff --git a/sys/boot/i386/loader/main.c b/sys/boot/i386/loader/main.c
index 5b23670..cac28ae 100644
--- a/sys/boot/i386/loader/main.c
+++ b/sys/boot/i386/loader/main.c
@@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$");
#define KARGS_FLAGS_CD 0x1
#define KARGS_FLAGS_PXE 0x2
+#define KARGS_FLAGS_ZFS 0x4
/* Arguments passed in from the boot1/boot2 loader */
static struct
@@ -51,8 +52,13 @@ static struct
u_int32_t howto;
u_int32_t bootdev;
u_int32_t bootflags;
- u_int32_t pxeinfo;
- u_int32_t res2;
+ union {
+ struct {
+ u_int32_t pxeinfo;
+ u_int32_t res2;
+ };
+ uint64_t zfspool;
+ };
u_int32_t bootinfo;
} *kargs;
@@ -96,7 +102,7 @@ main(void)
*/
bios_getmem();
-#if defined(LOADER_BZIP2_SUPPORT) || defined(LOADER_FIREWIRE_SUPPORT)
+#if defined(LOADER_BZIP2_SUPPORT) || defined(LOADER_FIREWIRE_SUPPORT) || defined(LOADER_ZFS_SUPPORT)
heap_top = PTOV(memtop_copyin);
memtop_copyin -= 0x300000;
heap_bottom = PTOV(memtop_copyin);
@@ -145,6 +151,14 @@ main(void)
bc_add(initial_bootdev);
}
+ archsw.arch_autoload = i386_autoload;
+ archsw.arch_getdev = i386_getdev;
+ archsw.arch_copyin = i386_copyin;
+ archsw.arch_copyout = i386_copyout;
+ archsw.arch_readin = i386_readin;
+ archsw.arch_isainb = isa_inb;
+ archsw.arch_isaoutb = isa_outb;
+
/*
* March through the device switch probing for things.
*/
@@ -172,14 +186,6 @@ main(void)
bios_getsmap();
- archsw.arch_autoload = i386_autoload;
- archsw.arch_getdev = i386_getdev;
- archsw.arch_copyin = i386_copyin;
- archsw.arch_copyout = i386_copyout;
- archsw.arch_readin = i386_readin;
- archsw.arch_isainb = isa_inb;
- archsw.arch_isaoutb = isa_outb;
-
interact(); /* doesn't return */
/* if we ever get here, it is an error */
@@ -252,6 +258,29 @@ extract_currdev(void)
i386_setcurrdev, env_nounset);
env_setenv("loaddev", EV_VOLATILE, i386_fmtdev(&new_currdev), env_noset,
env_nounset);
+
+#ifdef LOADER_ZFS_SUPPORT
+ /*
+ * If we were started from a ZFS-aware boot2, we can work out
+ * which ZFS pool we are booting from.
+ */
+ if (kargs->bootflags & KARGS_FLAGS_ZFS) {
+ /*
+ * Dig out the pool guid and convert it to a 'unit number'
+ */
+ uint64_t guid;
+ int unit;
+ char devname[32];
+ extern int zfs_guid_to_unit(uint64_t);
+
+ guid = kargs->zfspool;
+ unit = zfs_guid_to_unit(guid);
+ if (unit >= 0) {
+ sprintf(devname, "zfs%d", unit);
+ setenv("currdev", devname, 1);
+ }
+ }
+#endif
}
COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
diff --git a/sys/boot/i386/zfsboot/Makefile b/sys/boot/i386/zfsboot/Makefile
new file mode 100644
index 0000000..41f1672
--- /dev/null
+++ b/sys/boot/i386/zfsboot/Makefile
@@ -0,0 +1,108 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../boot2
+
+FILES= zfsboot
+
+NM?= nm
+
+# A value of 0x80 enables LBA support.
+BOOT_BOOT1_FLAGS?= 0x80
+
+BOOT_COMCONSOLE_PORT?= 0x3f8
+BOOT_COMCONSOLE_SPEED?= 9600
+B2SIOFMT?= 0x3
+
+REL1= 0x700
+ORG1= 0x7c00
+ORG2= 0x2000
+
+CFLAGS= -Os -g \
+ -fno-guess-branch-probability \
+ -fomit-frame-pointer \
+ -fno-unit-at-a-time \
+ -mno-align-long-strings \
+ -mrtd \
+ -mno-mmx -mno-3dnow -mno-sse -mno-sse2 -mno-sse3 \
+ -DBOOT2 \
+ -DFLAGS=${BOOT_BOOT1_FLAGS} \
+ -DSIOPRT=${BOOT_COMCONSOLE_PORT} \
+ -DSIOFMT=${B2SIOFMT} \
+ -DSIOSPD=${BOOT_COMCONSOLE_SPEED} \
+ -I${.CURDIR}/../../zfs \
+ -I${.CURDIR}/../../../cddl/boot/zfs \
+ -I${.CURDIR}/../btx/lib -I. \
+ -I${.CURDIR}/../boot2 \
+ -Wall -Waggregate-return -Wbad-function-cast -Wcast-align \
+ -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \
+ -Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \
+ -Winline --param max-inline-insns-single=100
+
+LDFLAGS=-static -N --gc-sections
+
+# Pick up ../Makefile.inc early.
+.include <bsd.init.mk>
+
+CLEANFILES= zfsboot
+
+zfsboot: zfsboot1 zfsboot2
+ cat zfsboot1 zfsboot2 > zfsboot
+
+CLEANFILES+= zfsboot1 zfsldr.out zfsldr.o
+
+zfsboot1: zfsldr.out
+ objcopy -S -O binary zfsldr.out ${.TARGET}
+
+zfsldr.out: zfsldr.o
+ ${LD} ${LDFLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} zfsldr.o
+
+CLEANFILES+= zfsboot2 zfsboot.ld zfsboot.ldr zfsboot.bin zfsboot.out \
+ zfsboot.o zfsboot.s zfsboot.s.tmp zfsboot.h sio.o
+
+# We currently allow 32768 bytes for zfsboot - in practice it could be
+# any size up to 3.5Mb but keeping it fixed size simplifies zfsldr.
+#
+BOOT2SIZE= 32768
+
+zfsboot2: zfsboot.ld
+ @set -- `ls -l zfsboot.ld`; x=$$((${BOOT2SIZE}-$$5)); \
+ echo "$$x bytes available"; test $$x -ge 0
+ dd if=zfsboot.ld of=${.TARGET} obs=${BOOT2SIZE} conv=osync
+
+zfsboot.ld: zfsboot.ldr zfsboot.bin ${BTXKERN}
+ btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l zfsboot.ldr \
+ -o ${.TARGET} -P 1 zfsboot.bin
+
+zfsboot.ldr:
+ cp /dev/null ${.TARGET}
+
+zfsboot.bin: zfsboot.out
+ objcopy -S -O binary zfsboot.out ${.TARGET}
+
+zfsboot.out: ${BTXCRT} zfsboot.o sio.o
+ ${LD} ${LDFLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC}
+
+zfsboot.o: zfsboot.s
+
+SRCS= zfsboot.c zfsboot.h
+
+zfsboot.s: zfsboot.c zfsboot.h ${.CURDIR}/../../zfs/zfsimpl.c
+ ${CC} ${CFLAGS} -S -o zfsboot.s.tmp ${.CURDIR}/zfsboot.c
+ sed -e '/align/d' -e '/nop/d' < zfsboot.s.tmp > zfsboot.s
+ rm -f zfsboot.s.tmp
+
+zfsboot.h: zfsldr.out
+ ${NM} -t d ${.ALLSRC} | awk '/([0-9])+ T xread/ \
+ { x = $$1 - ORG1; \
+ printf("#define XREADORG %#x\n", REL1 + x) }' \
+ ORG1=`printf "%d" ${ORG1}` \
+ REL1=`printf "%d" ${REL1}` > ${.TARGET}
+
+.if ${MACHINE_ARCH} == "amd64"
+beforedepend zfsboot.s: machine
+CLEANFILES+= machine
+machine:
+ ln -sf ${.CURDIR}/../../../i386/include machine
+.endif
+
+.include <bsd.prog.mk>
diff --git a/sys/boot/i386/zfsboot/zfsboot.c b/sys/boot/i386/zfsboot/zfsboot.c
new file mode 100644
index 0000000..9b0a465
--- /dev/null
+++ b/sys/boot/i386/zfsboot/zfsboot.c
@@ -0,0 +1,944 @@
+/*-
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/diskmbr.h>
+#include <sys/reboot.h>
+#include <sys/queue.h>
+
+#include <machine/bootinfo.h>
+#include <machine/elf.h>
+
+#include <stdarg.h>
+#include <stddef.h>
+
+#include <a.out.h>
+
+#include <btxv86.h>
+
+#include "zfsboot.h"
+#include "lib.h"
+
+#define IO_KEYBOARD 1
+#define IO_SERIAL 2
+
+#define SECOND 18 /* Circa that many ticks in a second. */
+
+#define RBX_ASKNAME 0x0 /* -a */
+#define RBX_SINGLE 0x1 /* -s */
+/* 0x2 is reserved for log2(RB_NOSYNC). */
+/* 0x3 is reserved for log2(RB_HALT). */
+/* 0x4 is reserved for log2(RB_INITNAME). */
+#define RBX_DFLTROOT 0x5 /* -r */
+#define RBX_KDB 0x6 /* -d */
+/* 0x7 is reserved for log2(RB_RDONLY). */
+/* 0x8 is reserved for log2(RB_DUMP). */
+/* 0x9 is reserved for log2(RB_MINIROOT). */
+#define RBX_CONFIG 0xa /* -c */
+#define RBX_VERBOSE 0xb /* -v */
+#define RBX_SERIAL 0xc /* -h */
+#define RBX_CDROM 0xd /* -C */
+/* 0xe is reserved for log2(RB_POWEROFF). */
+#define RBX_GDB 0xf /* -g */
+#define RBX_MUTE 0x10 /* -m */
+/* 0x11 is reserved for log2(RB_SELFTEST). */
+/* 0x12 is reserved for boot programs. */
+/* 0x13 is reserved for boot programs. */
+#define RBX_PAUSE 0x14 /* -p */
+#define RBX_QUIET 0x15 /* -q */
+#define RBX_NOINTR 0x1c /* -n */
+/* 0x1d is reserved for log2(RB_MULTIPLE) and is just misnamed here. */
+#define RBX_DUAL 0x1d /* -D */
+/* 0x1f is reserved for log2(RB_BOOTINFO). */
+
+/* pass: -a, -s, -r, -d, -c, -v, -h, -C, -g, -m, -p, -D */
+#define RBX_MASK (OPT_SET(RBX_ASKNAME) | OPT_SET(RBX_SINGLE) | \
+ OPT_SET(RBX_DFLTROOT) | OPT_SET(RBX_KDB ) | \
+ OPT_SET(RBX_CONFIG) | OPT_SET(RBX_VERBOSE) | \
+ OPT_SET(RBX_SERIAL) | OPT_SET(RBX_CDROM) | \
+ OPT_SET(RBX_GDB ) | OPT_SET(RBX_MUTE) | \
+ OPT_SET(RBX_PAUSE) | OPT_SET(RBX_DUAL))
+
+/* Hint to loader that we came from ZFS */
+#define KARGS_FLAGS_ZFS 0x4
+
+#define PATH_CONFIG "/boot.config"
+#define PATH_BOOT3 "/boot/loader"
+#define PATH_KERNEL "/boot/kernel/kernel"
+
+#define ARGS 0x900
+#define NOPT 14
+#define NDEV 3
+#define MEM_BASE 0x12
+#define MEM_EXT 0x15
+#define V86_CY(x) ((x) & 1)
+#define V86_ZR(x) ((x) & 0x40)
+
+#define DRV_HARD 0x80
+#define DRV_MASK 0x7f
+
+#define TYPE_AD 0
+#define TYPE_DA 1
+#define TYPE_MAXHARD TYPE_DA
+#define TYPE_FD 2
+
+#define OPT_SET(opt) (1 << (opt))
+#define OPT_CHECK(opt) ((opts) & OPT_SET(opt))
+
+extern uint32_t _end;
+
+static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
+static const unsigned char flags[NOPT] = {
+ RBX_DUAL,
+ RBX_SERIAL,
+ RBX_ASKNAME,
+ RBX_CDROM,
+ RBX_CONFIG,
+ RBX_KDB,
+ RBX_GDB,
+ RBX_MUTE,
+ RBX_NOINTR,
+ RBX_PAUSE,
+ RBX_QUIET,
+ RBX_DFLTROOT,
+ RBX_SINGLE,
+ RBX_VERBOSE
+};
+
+static const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
+static const unsigned char dev_maj[NDEV] = {30, 4, 2};
+
+struct dsk {
+ unsigned drive;
+ unsigned type;
+ unsigned unit;
+ unsigned slice;
+ unsigned part;
+ unsigned start;
+ int init;
+};
+static char cmd[512];
+static char kname[1024];
+static uint32_t opts;
+static int comspeed = SIOSPD;
+static struct bootinfo bootinfo;
+static uint32_t bootdev;
+static uint8_t ioctrl = IO_KEYBOARD;
+
+/* Buffers that must not span a 64k boundary. */
+#define READ_BUF_SIZE 8192
+struct dmadat {
+ char rdbuf[READ_BUF_SIZE]; /* for reading large things */
+ char secbuf[READ_BUF_SIZE]; /* for MBR/disklabel */
+};
+static struct dmadat *dmadat;
+
+void exit(int);
+static void load(void);
+static int parse(void);
+static void printf(const char *,...);
+static void putchar(int);
+static uint32_t memsize(void);
+static int drvread(struct dsk *, void *, unsigned, unsigned);
+static int keyhit(unsigned);
+static int xputc(int);
+static int xgetc(int);
+static int getc(int);
+
+static void memcpy(void *, const void *, int);
+static void
+memcpy(void *dst, const void *src, int len)
+{
+ const char *s = src;
+ char *d = dst;
+
+ while (len--)
+ *d++ = *s++;
+}
+
+static void
+strcpy(char *dst, const char *src)
+{
+ while (*src)
+ *dst++ = *src++;
+ *dst++ = 0;
+}
+
+static void
+strcat(char *dst, const char *src)
+{
+ while (*dst)
+ dst++;
+ while (*src)
+ *dst++ = *src++;
+ *dst++ = 0;
+}
+
+static int
+strcmp(const char *s1, const char *s2)
+{
+ for (; *s1 == *s2 && *s1; s1++, s2++);
+ return (unsigned char)*s1 - (unsigned char)*s2;
+}
+
+static const char *
+strchr(const char *s, char ch)
+{
+ for (; *s; s++)
+ if (*s == ch)
+ return s;
+ return 0;
+}
+
+static int
+memcmp(const void *p1, const void *p2, size_t n)
+{
+ const char *s1 = (const char *) p1;
+ const char *s2 = (const char *) p2;
+ for (; n > 0 && *s1 == *s2; s1++, s2++, n--);
+ if (n)
+ return (unsigned char)*s1 - (unsigned char)*s2;
+ else
+ return 0;
+}
+
+static void
+memset(void *p, char val, size_t n)
+{
+ char *s = (char *) p;
+ while (n--)
+ *s++ = val;
+}
+
+static void *
+malloc(size_t n)
+{
+ static char *heap_next;
+ static char *heap_end;
+
+ if (!heap_next) {
+ heap_next = (char *) dmadat + sizeof(*dmadat);
+ heap_end = (char *) (640*1024);
+ }
+
+ char *p = heap_next;
+ if (p + n > heap_end) {
+ printf("malloc failure\n");
+ for (;;)
+ ;
+ return 0;
+ }
+ heap_next += n;
+ return p;
+}
+
+static size_t
+strlen(const char *s)
+{
+ size_t len = 0;
+ while (*s++)
+ len++;
+ return len;
+}
+
+static char *
+strdup(const char *s)
+{
+ char *p = malloc(strlen(s) + 1);
+ strcpy(p, s);
+ return p;
+}
+
+#include "zfsimpl.c"
+
+/*
+ * Read from a dnode (which must be from a ZPL filesystem).
+ */
+static int
+zfs_read(spa_t *spa, const dnode_phys_t *dnode, off_t *offp, void *start, size_t size)
+{
+ const znode_phys_t *zp = (const znode_phys_t *) dnode->dn_bonus;
+ size_t n;
+ int rc;
+
+ n = size;
+ if (*offp + n > zp->zp_size)
+ n = zp->zp_size - *offp;
+
+ rc = dnode_read(spa, dnode, *offp, start, n);
+ if (rc)
+ return (-1);
+ *offp += n;
+
+ return (n);
+}
+
+/*
+ * Current ZFS pool
+ */
+spa_t *spa;
+
+/*
+ * A wrapper for dskread that doesn't have to worry about whether the
+ * buffer pointer crosses a 64k boundary.
+ */
+static int
+vdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes)
+{
+ char *p;
+ unsigned int lba, nb;
+ struct dsk *dsk = (struct dsk *) priv;
+
+ if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1)))
+ return -1;
+
+ p = buf;
+ lba = off / DEV_BSIZE;
+ while (bytes > 0) {
+ nb = bytes / DEV_BSIZE;
+ if (nb > READ_BUF_SIZE / DEV_BSIZE)
+ nb = READ_BUF_SIZE / DEV_BSIZE;
+ if (drvread(dsk, dmadat->rdbuf, lba, nb))
+ return -1;
+ memcpy(p, dmadat->rdbuf, nb * DEV_BSIZE);
+ p += nb * DEV_BSIZE;
+ lba += nb;
+ bytes -= nb * DEV_BSIZE;
+ }
+
+ return 0;
+}
+
+static int
+xfsread(const dnode_phys_t *dnode, off_t *offp, void *buf, size_t nbyte)
+{
+ if ((size_t)zfs_read(spa, dnode, offp, buf, nbyte) != nbyte) {
+ printf("Invalid %s\n", "format");
+ return -1;
+ }
+ return 0;
+}
+
+static inline uint32_t
+memsize(void)
+{
+ v86.addr = MEM_EXT;
+ v86.eax = 0x8800;
+ v86int();
+ return v86.eax;
+}
+
+static inline void
+getstr(void)
+{
+ char *s;
+ int c;
+
+ s = cmd;
+ for (;;) {
+ switch (c = xgetc(0)) {
+ case 0:
+ break;
+ case '\177':
+ case '\b':
+ if (s > cmd) {
+ s--;
+ printf("\b \b");
+ }
+ break;
+ case '\n':
+ case '\r':
+ *s = 0;
+ return;
+ default:
+ if (s - cmd < sizeof(cmd) - 1)
+ *s++ = c;
+ putchar(c);
+ }
+ }
+}
+
+static inline void
+putc(int c)
+{
+ v86.addr = 0x10;
+ v86.eax = 0xe00 | (c & 0xff);
+ v86.ebx = 0x7;
+ v86int();
+}
+
+/*
+ * Try to detect a device supported by the legacy int13 BIOS
+ */
+static int
+int13probe(int drive)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x800;
+ v86.edx = drive;
+ v86int();
+
+ if (!(v86.efl & 0x1) && /* carry clear */
+ ((v86.edx & 0xff) != (drive & DRV_MASK))) { /* unit # OK */
+ if ((v86.ecx & 0x3f) == 0) { /* absurd sector size */
+ return(0); /* skip device */
+ }
+ return (1);
+ }
+ return(0);
+}
+
+static void
+probe_drive(struct dsk *dsk, spa_t **spap)
+{
+ struct dos_partition *dp;
+ char *sec;
+ unsigned i;
+
+ if (!int13probe(dsk->drive))
+ return;
+
+ /*
+ * If we find a vdev on the whole disk, stop here. Otherwise dig
+ * out the MBR and probe each slice in turn for a vdev.
+ */
+ if (vdev_probe(vdev_read, dsk, spap) == 0)
+ return;
+
+ sec = dmadat->secbuf;
+ dsk->start = 0;
+ if (drvread(dsk, sec, DOSBBSECTOR, 1))
+ return;
+ dp = (void *)(sec + DOSPARTOFF);
+
+ for (i = 0; i < NDOSPART; i++) {
+ if (!dp[i].dp_typ)
+ continue;
+ dsk->start = dp[i].dp_start;
+ if (vdev_probe(vdev_read, dsk, spap) == 0) {
+ /*
+ * We record the first pool we find (we will try to boot
+ * from that one.
+ */
+ spap = 0;
+
+ /*
+ * This slice had a vdev. We need a new dsk structure now
+ * sice the vdev now owns this one.
+ */
+ struct dsk *newdsk;
+ newdsk = malloc(sizeof(struct dsk));
+ *newdsk = *dsk;
+ dsk = newdsk;
+ }
+ }
+}
+
+int
+main(void)
+{
+ int autoboot, i;
+ dnode_phys_t dn;
+ off_t off;
+ struct dsk *dsk;
+
+ dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
+ v86.ctl = V86_FLAGS;
+
+ dsk = malloc(sizeof(struct dsk));
+ dsk->drive = *(uint8_t *)PTOV(ARGS);
+ dsk->type = dsk->drive & DRV_HARD ? TYPE_AD : TYPE_FD;
+ dsk->unit = dsk->drive & DRV_MASK;
+ dsk->slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
+ dsk->part = 0;
+ dsk->start = 0;
+ dsk->init = 0;
+
+ bootinfo.bi_version = BOOTINFO_VERSION;
+ bootinfo.bi_size = sizeof(bootinfo);
+ bootinfo.bi_basemem = 0; /* XXX will be filled by loader or kernel */
+ bootinfo.bi_extmem = memsize();
+ bootinfo.bi_memsizes_valid++;
+ bootinfo.bi_bios_dev = dsk->drive;
+
+ bootdev = MAKEBOOTDEV(dev_maj[dsk->type],
+ dsk->slice, dsk->unit, dsk->part),
+
+ /* Process configuration file */
+
+ autoboot = 1;
+
+ zfs_init();
+
+ /*
+ * Probe the boot drive first - we will try to boot from whatever
+ * pool we find on that drive.
+ */
+ probe_drive(dsk, &spa);
+
+ /*
+ * Probe the rest of the drives that the bios knows about. This
+ * will find any other available pools and it may fill in missing
+ * vdevs for the boot pool.
+ */
+ for (i = 0; i < 4; i++) {
+ if ((i | DRV_HARD) == *(uint8_t *)PTOV(ARGS))
+ continue;
+
+ dsk = malloc(sizeof(struct dsk));
+ dsk->drive = i | DRV_HARD;
+ dsk->type = dsk->drive & TYPE_AD;
+ dsk->unit = i;
+ dsk->slice = 0;
+ dsk->part = 0;
+ dsk->start = 0;
+ dsk->init = 0;
+ probe_drive(dsk, 0);
+ }
+
+ /*
+ * If we didn't find a pool on the boot drive, default to the
+ * first pool we found, if any.
+ */
+ if (!spa) {
+ spa = STAILQ_FIRST(&zfs_pools);
+ if (!spa) {
+ printf("No ZFS pools located, can't boot\n");
+ for (;;)
+ ;
+ }
+ }
+
+ zfs_mount_pool(spa);
+
+ if (zfs_lookup(spa, PATH_CONFIG, &dn) == 0) {
+ off = 0;
+ xfsread(&dn, &off, cmd, sizeof(cmd));
+ }
+
+ if (*cmd) {
+ if (parse())
+ autoboot = 0;
+ if (!OPT_CHECK(RBX_QUIET))
+ printf("%s: %s", PATH_CONFIG, cmd);
+ /* Do not process this command twice */
+ *cmd = 0;
+ }
+
+ /*
+ * Try to exec stage 3 boot loader. If interrupted by a keypress,
+ * or in case of failure, try to load a kernel directly instead.
+ */
+
+ if (autoboot && !*kname) {
+ memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3));
+ if (!keyhit(3*SECOND)) {
+ load();
+ memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
+ }
+ }
+
+ /* Present the user with the boot2 prompt. */
+
+ for (;;) {
+ if (!autoboot || !OPT_CHECK(RBX_QUIET))
+ printf("\nFreeBSD/i386 boot\n"
+ "Default: %s:%s\n"
+ "boot: ",
+ spa->spa_name, kname);
+ if (ioctrl & IO_SERIAL)
+ sio_flush();
+ if (!autoboot || keyhit(5*SECOND))
+ getstr();
+ else if (!autoboot || !OPT_CHECK(RBX_QUIET))
+ putchar('\n');
+ autoboot = 0;
+ if (parse())
+ putchar('\a');
+ else
+ load();
+ }
+}
+
+/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
+void
+exit(int x)
+{
+}
+
+static void
+load(void)
+{
+ union {
+ struct exec ex;
+ Elf32_Ehdr eh;
+ } hdr;
+ static Elf32_Phdr ep[2];
+ static Elf32_Shdr es[2];
+ caddr_t p;
+ dnode_phys_t dn;
+ off_t off;
+ uint32_t addr, x;
+ int fmt, i, j;
+
+ if (zfs_lookup(spa, kname, &dn)) {
+ return;
+ }
+ off = 0;
+ if (xfsread(&dn, &off, &hdr, sizeof(hdr)))
+ return;
+ if (N_GETMAGIC(hdr.ex) == ZMAGIC)
+ fmt = 0;
+ else if (IS_ELF(hdr.eh))
+ fmt = 1;
+ else {
+ printf("Invalid %s\n", "format");
+ return;
+ }
+ if (fmt == 0) {
+ addr = hdr.ex.a_entry & 0xffffff;
+ p = PTOV(addr);
+ off = PAGE_SIZE;
+ if (xfsread(&dn, &off, p, hdr.ex.a_text))
+ return;
+ p += roundup2(hdr.ex.a_text, PAGE_SIZE);
+ if (xfsread(&dn, &off, p, hdr.ex.a_data))
+ return;
+ p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
+ bootinfo.bi_symtab = VTOP(p);
+ memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
+ p += sizeof(hdr.ex.a_syms);
+ if (hdr.ex.a_syms) {
+ if (xfsread(&dn, &off, p, hdr.ex.a_syms))
+ return;
+ p += hdr.ex.a_syms;
+ if (xfsread(&dn, &off, p, sizeof(int)))
+ return;
+ x = *(uint32_t *)p;
+ p += sizeof(int);
+ x -= sizeof(int);
+ if (xfsread(&dn, &off, p, x))
+ return;
+ p += x;
+ }
+ } else {
+ off = hdr.eh.e_phoff;
+ for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
+ if (xfsread(&dn, &off, ep + j, sizeof(ep[0])))
+ return;
+ if (ep[j].p_type == PT_LOAD)
+ j++;
+ }
+ for (i = 0; i < 2; i++) {
+ p = PTOV(ep[i].p_paddr & 0xffffff);
+ off = ep[i].p_offset;
+ if (xfsread(&dn, &off, p, ep[i].p_filesz))
+ return;
+ }
+ p += roundup2(ep[1].p_memsz, PAGE_SIZE);
+ bootinfo.bi_symtab = VTOP(p);
+ if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
+ off = hdr.eh.e_shoff + sizeof(es[0]) *
+ (hdr.eh.e_shstrndx + 1);
+ if (xfsread(&dn, &off, &es, sizeof(es)))
+ return;
+ for (i = 0; i < 2; i++) {
+ memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size));
+ p += sizeof(es[i].sh_size);
+ off = es[i].sh_offset;
+ if (xfsread(&dn, &off, p, es[i].sh_size))
+ return;
+ p += es[i].sh_size;
+ }
+ }
+ addr = hdr.eh.e_entry & 0xffffff;
+ }
+ bootinfo.bi_esymtab = VTOP(p);
+ bootinfo.bi_kernelname = VTOP(kname);
+ __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
+ bootdev,
+ KARGS_FLAGS_ZFS,
+ (uint32_t) spa->spa_guid,
+ (uint32_t) (spa->spa_guid >> 32),
+ VTOP(&bootinfo));
+}
+
+static int
+parse()
+{
+ char *arg = cmd;
+ char *ep, *p, *q;
+ const char *cp;
+ //unsigned int drv;
+ int c, i, j;
+
+ while ((c = *arg++)) {
+ if (c == ' ' || c == '\t' || c == '\n')
+ continue;
+ for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
+ ep = p;
+ if (*p)
+ *p++ = 0;
+ if (c == '-') {
+ while ((c = *arg++)) {
+ if (c == 'P') {
+ if (*(uint8_t *)PTOV(0x496) & 0x10) {
+ cp = "yes";
+ } else {
+ opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL);
+ cp = "no";
+ }
+ printf("Keyboard: %s\n", cp);
+ continue;
+ } else if (c == 'S') {
+ j = 0;
+ while ((unsigned int)(i = *arg++ - '0') <= 9)
+ j = j * 10 + i;
+ if (j > 0 && i == -'0') {
+ comspeed = j;
+ break;
+ }
+ /* Fall through to error below ('S' not in optstr[]). */
+ }
+ for (i = 0; c != optstr[i]; i++)
+ if (i == NOPT - 1)
+ return -1;
+ opts ^= OPT_SET(flags[i]);
+ }
+ ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
+ OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
+ if (ioctrl & IO_SERIAL)
+ sio_init(115200 / comspeed);
+ } if (c == '?') {
+ dnode_phys_t dn;
+
+ if (zfs_lookup(spa, arg, &dn) == 0) {
+ zap_list(spa, &dn);
+ }
+ return -1;
+ } else {
+ arg--;
+
+ /*
+ * Report pool status if the comment is 'status'. Lets
+ * hope no-one wants to load /status as a kernel.
+ */
+ if (!strcmp(arg, "status")) {
+ spa_all_status();
+ return -1;
+ }
+
+ /*
+ * If there is a colon, switch pools.
+ */
+ q = (char *) strchr(arg, ':');
+ if (q) {
+ spa_t *newspa;
+
+ *q++ = 0;
+ newspa = spa_find_by_name(arg);
+ if (newspa) {
+ spa = newspa;
+ zfs_mount_pool(spa);
+ } else {
+ printf("\nCan't find ZFS pool %s\n", arg);
+ return -1;
+ }
+ arg = q;
+ }
+ if ((i = ep - arg)) {
+ if ((size_t)i >= sizeof(kname))
+ return -1;
+ memcpy(kname, arg, i + 1);
+ }
+ }
+ arg = p;
+ }
+ return 0;
+}
+
+static void
+printf(const char *fmt,...)
+{
+ va_list ap;
+ char buf[10];
+ char *s;
+ unsigned u;
+ int c;
+ int minus;
+ int prec;
+ int len;
+ int pad;
+
+ va_start(ap, fmt);
+ while ((c = *fmt++)) {
+ if (c == '%') {
+ minus = 0;
+ prec = 0;
+ nextfmt:
+ c = *fmt++;
+ switch (c) {
+ case '-':
+ minus = 1;
+ goto nextfmt;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ prec = 10 * prec + (c - '0');
+ goto nextfmt;
+ case 'c':
+ putchar(va_arg(ap, int));
+ continue;
+ case 's':
+ s = va_arg(ap, char *);
+ if (prec) {
+ len = strlen(s);
+ if (len < prec)
+ pad = prec - len;
+ else
+ pad = 0;
+ if (minus)
+ while (pad--)
+ putchar(' ');
+ for (; *s; s++)
+ putchar(*s);
+ if (!minus)
+ while (pad--)
+ putchar(' ');
+ } else {
+ for (; *s; s++)
+ putchar(*s);
+ }
+ continue;
+ case 'u':
+ u = va_arg(ap, unsigned);
+ s = buf;
+ do
+ *s++ = '0' + u % 10U;
+ while (u /= 10U);
+ while (--s >= buf)
+ putchar(*s);
+ continue;
+ }
+ }
+ putchar(c);
+ }
+ va_end(ap);
+ return;
+}
+
+static void
+putchar(int c)
+{
+ if (c == '\n')
+ xputc('\r');
+ xputc(c);
+}
+
+static int
+drvread(struct dsk *dsk, void *buf, unsigned lba, unsigned nblk)
+{
+ static unsigned c = 0x2d5c7c2f;
+
+ lba += dsk->start;
+ if (!OPT_CHECK(RBX_QUIET))
+ printf("%c\b", c = c << 8 | c >> 24);
+ v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
+ v86.addr = XREADORG; /* call to xread in boot1 */
+ v86.es = VTOPSEG(buf);
+ v86.eax = lba;
+ v86.ebx = VTOPOFF(buf);
+ v86.ecx = lba >> 16;
+ v86.edx = nblk << 8 | dsk->drive;
+ v86int();
+ v86.ctl = V86_FLAGS;
+ if (V86_CY(v86.efl)) {
+ printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+keyhit(unsigned ticks)
+{
+ uint32_t t0, t1;
+
+ if (OPT_CHECK(RBX_NOINTR))
+ return 0;
+ t0 = 0;
+ for (;;) {
+ if (xgetc(1))
+ return 1;
+ t1 = *(uint32_t *)PTOV(0x46c);
+ if (!t0)
+ t0 = t1;
+ if (t1 < t0 || t1 >= t0 + ticks)
+ return 0;
+ }
+}
+
+static int
+xputc(int c)
+{
+ if (ioctrl & IO_KEYBOARD)
+ putc(c);
+ if (ioctrl & IO_SERIAL)
+ sio_putc(c);
+ return c;
+}
+
+static int
+xgetc(int fn)
+{
+ if (OPT_CHECK(RBX_NOINTR))
+ return 0;
+ for (;;) {
+ if (ioctrl & IO_KEYBOARD && getc(1))
+ return fn ? 1 : getc(0);
+ if (ioctrl & IO_SERIAL && sio_ischar())
+ return fn ? 1 : sio_getc();
+ if (fn)
+ return 0;
+ }
+}
+
+static int
+getc(int fn)
+{
+ /*
+ * The extra comparison against zero is an attempt to work around
+ * what appears to be a bug in QEMU and Bochs. Both emulators
+ * sometimes report a key-press with scancode one and ascii zero
+ * when no such key is pressed in reality. As far as I can tell,
+ * this only happens shortly after a reboot.
+ */
+ v86.addr = 0x16;
+ v86.eax = fn << 8;
+ v86int();
+ return fn == 0 ? v86.eax & 0xff : (!V86_ZR(v86.efl) && (v86.eax & 0xff));
+}
diff --git a/sys/boot/i386/zfsboot/zfsldr.S b/sys/boot/i386/zfsboot/zfsldr.S
new file mode 100644
index 0000000..a256d30
--- /dev/null
+++ b/sys/boot/i386/zfsboot/zfsldr.S
@@ -0,0 +1,402 @@
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ *
+ * $FreeBSD$
+ */
+
+/* Memory Locations */
+ .set MEM_REL,0x700 # Relocation address
+ .set MEM_ARG,0x900 # Arguments
+ .set MEM_ORG,0x7c00 # Origin
+ .set MEM_BUF,0x8000 # Load area
+ .set MEM_BTX,0x9000 # BTX start
+ .set MEM_JMP,0x9010 # BTX entry point
+ .set MEM_USR,0xa000 # Client start
+ .set BDA_BOOT,0x472 # Boot howto flag
+
+/* Partition Constants */
+ .set PRT_OFF,0x1be # Partition offset
+ .set PRT_NUM,0x4 # Partitions
+ .set PRT_BSD,0xa5 # Partition type
+
+/* Flag Bits */
+ .set FL_PACKET,0x80 # Packet mode
+
+/* Misc. Constants */
+ .set SIZ_PAG,0x1000 # Page size
+ .set SIZ_SEC,0x200 # Sector size
+
+ .set NSECT,0x40
+ .globl start
+ .globl xread
+ .code16
+
+start: jmp main # Start recognizably
+
+/*
+ * This is the start of a standard BIOS Parameter Block (BPB). Most bootable
+ * FAT disks have this at the start of their MBR. While normal BIOS's will
+ * work fine without this section, IBM's El Torito emulation "fixes" up the
+ * BPB by writing into the memory copy of the MBR. Rather than have data
+ * written into our xread routine, we'll define a BPB to work around it.
+ * The data marked with (T) indicates a field required for a ThinkPad to
+ * recognize the disk and (W) indicates fields written from IBM BIOS code.
+ * The use of the BPB is based on what OpenBSD and NetBSD implemented in
+ * their boot code but the required fields were determined by trial and error.
+ *
+ * Note: If additional space is needed in boot1, one solution would be to
+ * move the "prompt" message data (below) to replace the OEM ID.
+ */
+ .org 0x03, 0x00
+oemid: .space 0x08, 0x00 # OEM ID
+
+ .org 0x0b, 0x00
+bpb: .word 512 # sector size (T)
+ .byte 0 # sectors/clustor
+ .word 0 # reserved sectors
+ .byte 0 # number of FATs
+ .word 0 # root entries
+ .word 0 # small sectors
+ .byte 0 # media type (W)
+ .word 0 # sectors/fat
+ .word 18 # sectors per track (T)
+ .word 2 # number of heads (T)
+ .long 0 # hidden sectors (W)
+ .long 0 # large sectors
+
+ .org 0x24, 0x00
+ebpb: .byte 0 # BIOS physical drive number (W)
+
+ .org 0x25,0x90
+/*
+ * Trampoline used by boot2 to call read to read data from the disk via
+ * the BIOS. Call with:
+ *
+ * %cx:%ax - long - LBA to read in
+ * %es:(%bx) - caddr_t - buffer to read data into
+ * %dl - byte - drive to read from
+ * %dh - byte - num sectors to read
+ */
+
+xread: push %ss # Address
+ pop %ds # data
+/*
+ * Setup an EDD disk packet and pass it to read
+ */
+xread.1: # Starting
+ pushl $0x0 # absolute
+ push %cx # block
+ push %ax # number
+ push %es # Address of
+ push %bx # transfer buffer
+ xor %ax,%ax # Number of
+ movb %dh,%al # blocks to
+ push %ax # transfer
+ push $0x10 # Size of packet
+ mov %sp,%bp # Packet pointer
+ callw read # Read from disk
+ lea 0x10(%bp),%sp # Clear stack
+ lret # To far caller
+/*
+ * Load the rest of boot2 and BTX up, copy the parts to the right locations,
+ * and start it all up.
+ */
+
+/*
+ * Setup the segment registers to flat addressing (segment 0) and setup the
+ * stack to end just below the start of our code.
+ */
+main: cld # String ops inc
+ xor %cx,%cx # Zero
+ mov %cx,%es # Address
+ mov %cx,%ds # data
+ mov %cx,%ss # Set up
+ mov $start,%sp # stack
+/*
+ * Relocate ourself to MEM_REL. Since %cx == 0, the inc %ch sets
+ * %cx == 0x100.
+ */
+ mov %sp,%si # Source
+ mov $MEM_REL,%di # Destination
+ incb %ch # Word count
+ rep # Copy
+ movsw # code
+/*
+ * If we are on a hard drive, then load the MBR and look for the first
+ * FreeBSD slice. We use the fake partition entry below that points to
+ * the MBR when we call nread. The first pass looks for the first active
+ * FreeBSD slice. The second pass looks for the first non-active FreeBSD
+ * slice if the first one fails.
+ */
+ mov $part4,%si # Partition
+ cmpb $0x80,%dl # Hard drive?
+ jb main.4 # No
+ movb $0x1,%dh # Block count
+ callw nread # Read MBR
+ mov $0x1,%cx # Two passes
+main.1: mov $MEM_BUF+PRT_OFF,%si # Partition table
+ movb $0x1,%dh # Partition
+main.2: cmpb $PRT_BSD,0x4(%si) # Our partition type?
+ jne main.3 # No
+ jcxz main.5 # If second pass
+ testb $0x80,(%si) # Active?
+ jnz main.5 # Yes
+main.3: add $0x10,%si # Next entry
+ incb %dh # Partition
+ cmpb $0x1+PRT_NUM,%dh # In table?
+ jb main.2 # Yes
+ dec %cx # Do two
+ jcxz main.1 # passes
+/*
+ * If we get here, we didn't find any FreeBSD slices at all, so print an
+ * error message and die.
+ */
+ mov $msg_part,%si # Message
+ jmp error # Error
+/*
+ * Floppies use partition 0 of drive 0.
+ */
+main.4: xor %dx,%dx # Partition:drive
+
+/*
+ * Ok, we have a slice and drive in %dx now, so use that to locate and
+ * load boot2. %si references the start of the slice we are looking
+ * for, so go ahead and load up the 64 sectors starting at sector 1024
+ * (i.e. after the two vdev labels). We don't have do anything fancy
+ * here to allow for an extra copy of boot1 and a partition table
+ * (compare to this section of the UFS bootstrap) so we just load it
+ * all at 0x8000. The first part of boot2 is BTX, which wants to run
+ * at 0x9000. The boot2.bin binary starts right after the end of BTX,
+ * so we have to figure out where the start of it is and then move the
+ * binary to 0xc000. After we have moved the client, we relocate BTX
+ * itself to 0x9000 - doing it in this order means that none of the
+ * memcpy regions overlap which would corrupt the copy. Normally, BTX
+ * clients start at MEM_USR, or 0xa000, but when we use btxld to
+ * create boot2, we use an entry point of 0x2000. That entry point is
+ * relative to MEM_USR; thus boot2.bin starts at 0xc000.
+ *
+ * The load area and the target area for the client overlap so we have
+ * to use a decrementing string move. We also play segment register
+ * games with the destination address for the move so that the client
+ * can be larger than 16k (which would overflow the zero segment since
+ * the client starts at 0xc000). Relocating BTX is easy since the load
+ * area and target area do not overlap.
+ */
+main.5: mov %dx,MEM_ARG # Save args
+ movb $NSECT,%dh # Sector count
+ movw $1024,%ax # Offset to boot2
+ callw nread.1 # Read disk
+main.6: mov $MEM_BUF,%si # BTX (before reloc)
+ mov 0xa(%si),%bx # Get BTX length and set
+ mov $NSECT*SIZ_SEC-1,%di # Size of load area (less one)
+ mov %di,%si # End of load
+ add $MEM_BUF,%si # area
+ sub %bx,%di # End of client, 0xc000 rel
+ mov %di,%cx # Size of
+ inc %cx # client
+ mov $(MEM_USR+2*SIZ_PAG)>>4,%dx # Segment
+ mov %dx,%es # addressing 0xc000
+ std # Move with decrement
+ rep # Relocate
+ movsb # client
+ mov %ds,%dx # Back to
+ mov %dx,%es # zero segment
+ mov $MEM_BUF,%si # BTX (before reloc)
+ mov $MEM_BTX,%di # BTX
+ mov %bx,%cx # Get BTX length
+ cld # Increment this time
+ rep # Relocate
+ movsb # BTX
+
+/*
+ * Enable A20 so we can access memory above 1 meg.
+ * Use the zero-valued %cx as a timeout for embedded hardware which do not
+ * have a keyboard controller.
+ */
+seta20: cli # Disable interrupts
+seta20.1: dec %cx # Timeout?
+ jz seta20.3 # Yes
+ inb $0x64,%al # Get status
+ testb $0x2,%al # Busy?
+ jnz seta20.1 # Yes
+ movb $0xd1,%al # Command: Write
+ outb %al,$0x64 # output port
+seta20.2: inb $0x64,%al # Get status
+ testb $0x2,%al # Busy?
+ jnz seta20.2 # Yes
+ movb $0xdf,%al # Enable
+ outb %al,$0x60 # A20
+seta20.3: sti # Enable interrupts
+
+ jmp start+MEM_JMP-MEM_ORG # Start BTX
+
+
+/*
+ * Trampoline used to call read from within boot1.
+ */
+nread: xor %ax,%ax # Sector offset in partition
+nread.1: mov $MEM_BUF,%bx # Transfer buffer
+ add 0x8(%si),%ax # Get
+ mov 0xa(%si),%cx # LBA
+ push %cs # Read from
+ callw xread.1 # disk
+ jnc return # If success, return
+ mov $msg_read,%si # Otherwise, set the error
+ # message and fall through to
+ # the error routine
+/*
+ * Print out the error message pointed to by %ds:(%si) followed
+ * by a prompt, wait for a keypress, and then reboot the machine.
+ */
+error: callw putstr # Display message
+ mov $prompt,%si # Display
+ callw putstr # prompt
+ xorb %ah,%ah # BIOS: Get
+ int $0x16 # keypress
+ movw $0x1234, BDA_BOOT # Do a warm boot
+ ljmp $0xffff,$0x0 # reboot the machine
+/*
+ * Display a null-terminated string using the BIOS output.
+ */
+putstr.0: mov $0x7,%bx # Page:attribute
+ movb $0xe,%ah # BIOS: Display
+ int $0x10 # character
+putstr: lodsb # Get char
+ testb %al,%al # End of string?
+ jne putstr.0 # No
+
+/*
+ * Overused return code. ereturn is used to return an error from the
+ * read function. Since we assume putstr succeeds, we (ab)use the
+ * same code when we return from putstr.
+ */
+ereturn: movb $0x1,%ah # Invalid
+ stc # argument
+return: retw # To caller
+/*
+ * Reads sectors from the disk. If EDD is enabled, then check if it is
+ * installed and use it if it is. If it is not installed or not enabled, then
+ * fall back to using CHS. Since we use a LBA, if we are using CHS, we have to
+ * fetch the drive parameters from the BIOS and divide it out ourselves.
+ * Call with:
+ *
+ * %dl - byte - drive number
+ * stack - 10 bytes - EDD Packet
+ */
+read: testb $FL_PACKET,%cs:MEM_REL+flags-start # LBA support enabled?
+ jz read.1 # No, use CHS
+ cmpb $0x80,%dl # Hard drive?
+ jb read.1 # No, use CHS
+ mov $0x55aa,%bx # Magic
+ push %dx # Save
+ movb $0x41,%ah # BIOS: Check
+ int $0x13 # extensions present
+ pop %dx # Restore
+ jc read.1 # If error, use CHS
+ cmp $0xaa55,%bx # Magic?
+ jne read.1 # No, so use CHS
+ testb $0x1,%cl # Packet interface?
+ jz read.1 # No, so use CHS
+ mov %bp,%si # Disk packet
+ movb $0x42,%ah # BIOS: Extended
+ int $0x13 # read
+ retw # To caller
+#if 0
+read.1: push %dx # Save
+ movb $0x8,%ah # BIOS: Get drive
+ int $0x13 # parameters
+ movb %dh,%ch # Max head number
+ pop %dx # Restore
+ jc return # If error
+ andb $0x3f,%cl # Sectors per track
+ jz ereturn # If zero
+ cli # Disable interrupts
+ mov 0x8(%bp),%eax # Get LBA
+ push %dx # Save
+ movzbl %cl,%ebx # Divide by
+ xor %edx,%edx # sectors
+ div %ebx # per track
+ movb %ch,%bl # Max head number
+ movb %dl,%ch # Sector number
+ inc %bx # Divide by
+ xorb %dl,%dl # number
+ div %ebx # of heads
+ movb %dl,%bh # Head number
+ pop %dx # Restore
+ cmpl $0x3ff,%eax # Cylinder number supportable?
+ sti # Enable interrupts
+ ja ereturn # No, return an error
+ xchgb %al,%ah # Set up cylinder
+ rorb $0x2,%al # number
+ orb %ch,%al # Merge
+ inc %ax # sector
+ xchg %ax,%cx # number
+ movb %bh,%dh # Head number
+ subb %ah,%al # Sectors this track
+ mov 0x2(%bp),%ah # Blocks to read
+ cmpb %ah,%al # To read
+ jb read.2 # this
+#ifdef TRACK_AT_A_TIME
+ movb %ah,%al # track
+#else
+ movb $1,%al # one sector
+#endif
+read.2: mov $0x5,%di # Try count
+read.3: les 0x4(%bp),%bx # Transfer buffer
+ push %ax # Save
+ movb $0x2,%ah # BIOS: Read
+ int $0x13 # from disk
+ pop %bx # Restore
+ jnc read.4 # If success
+ dec %di # Retry?
+ jz read.6 # No
+ xorb %ah,%ah # BIOS: Reset
+ int $0x13 # disk system
+ xchg %bx,%ax # Block count
+ jmp read.3 # Continue
+read.4: movzbw %bl,%ax # Sectors read
+ add %ax,0x8(%bp) # Adjust
+ jnc read.5 # LBA,
+ incw 0xa(%bp) # transfer
+read.5: shlb %bl # buffer
+ add %bl,0x5(%bp) # pointer,
+ sub %al,0x2(%bp) # block count
+ ja read.1 # If not done
+read.6: retw # To caller
+#else
+read.1: mov $msg_chs,%si
+ jmp error
+msg_chs: .asciz "CHS not supported"
+#endif
+
+/* Messages */
+
+msg_read: .asciz "Read"
+msg_part: .asciz "Boot"
+
+prompt: .asciz " error\r\n"
+
+flags: .byte FLAGS # Flags
+
+ .org PRT_OFF,0x90
+
+/* Partition table */
+
+ .fill 0x30,0x1,0x0
+part4: .byte 0x80, 0x00, 0x01, 0x00
+ .byte 0xa5, 0xfe, 0xff, 0xff
+ .byte 0x00, 0x00, 0x00, 0x00
+ .byte 0x50, 0xc3, 0x00, 0x00 # 50000 sectors long, bleh
+
+ .word 0xaa55 # Magic number
diff --git a/sys/boot/zfs/Makefile b/sys/boot/zfs/Makefile
new file mode 100644
index 0000000..723233c
--- /dev/null
+++ b/sys/boot/zfs/Makefile
@@ -0,0 +1,29 @@
+# $FreeBSD$
+
+LIB= zfsboot
+INTERNALLIB=
+
+SRCS+= zfs.c
+
+CFLAGS+= -I${.CURDIR}/../common -I${.CURDIR}/../.. -I.
+CFLAGS+= -I${.CURDIR}/../../../lib/libstand
+CFLAGS+= -I${.CURDIR}/../../cddl/boot/zfs
+
+# XXX need arch-specific bootstrap CFLAGS here
+#
+CFLAGS+= -ffreestanding -mpreferred-stack-boundary=2 \
+ -mno-mmx -mno-3dnow -mno-sse -mno-sse2 -mno-sse3
+
+CFLAGS+= -Wformat -Wall
+
+.if ${MACHINE_ARCH} == "amd64"
+CLEANFILES+= machine
+machine:
+ ln -sf ${.CURDIR}/../../../i386/include machine
+.endif
+
+.include <bsd.lib.mk>
+
+.if ${MACHINE_ARCH} == "amd64"
+beforedepend ${OBJS}: machine
+.endif
diff --git a/sys/boot/zfs/zfs.c b/sys/boot/zfs/zfs.c
new file mode 100644
index 0000000..cf0bb9c
--- /dev/null
+++ b/sys/boot/zfs/zfs.c
@@ -0,0 +1,514 @@
+/*-
+ * Copyright (c) 2007 Doug Rabson
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Stand-alone file reading package.
+ */
+
+#include <sys/param.h>
+#include <sys/disklabel.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stand.h>
+#include <bootstrap.h>
+
+#include "zfsimpl.c"
+
+static int zfs_open(const char *path, struct open_file *f);
+static int zfs_write(struct open_file *f, void *buf, size_t size, size_t *resid);
+static int zfs_close(struct open_file *f);
+static int zfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
+static off_t zfs_seek(struct open_file *f, off_t offset, int where);
+static int zfs_stat(struct open_file *f, struct stat *sb);
+static int zfs_readdir(struct open_file *f, struct dirent *d);
+
+struct devsw zfs_dev;
+
+struct fs_ops zfs_fsops = {
+ "zfs",
+ zfs_open,
+ zfs_close,
+ zfs_read,
+ zfs_write,
+ zfs_seek,
+ zfs_stat,
+ zfs_readdir
+};
+
+/*
+ * In-core open file.
+ */
+struct file {
+ off_t f_seekp; /* seek pointer */
+ dnode_phys_t f_dnode;
+ uint64_t f_zap_type; /* zap type for readdir */
+ uint64_t f_num_leafs; /* number of fzap leaf blocks */
+ zap_leaf_phys_t *f_zap_leaf; /* zap leaf buffer */
+};
+
+/*
+ * Open a file.
+ */
+static int
+zfs_open(const char *upath, struct open_file *f)
+{
+ spa_t *spa = (spa_t *) f->f_devdata;
+ struct file *fp;
+ int rc;
+
+ if (f->f_dev != &zfs_dev)
+ return (EINVAL);
+
+ rc = zfs_mount_pool(spa);
+ if (rc)
+ return (rc);
+
+ /* allocate file system specific data structure */
+ fp = malloc(sizeof(struct file));
+ bzero(fp, sizeof(struct file));
+ f->f_fsdata = (void *)fp;
+
+ if (spa->spa_root_objset.os_type != DMU_OST_ZFS) {
+ printf("Unexpected object set type %lld\n",
+ spa->spa_root_objset.os_type);
+ rc = EIO;
+ goto out;
+ }
+
+ rc = zfs_lookup(spa, upath, &fp->f_dnode);
+ if (rc)
+ goto out;
+
+ fp->f_seekp = 0;
+out:
+ if (rc) {
+ f->f_fsdata = NULL;
+ free(fp);
+ }
+ return (rc);
+}
+
+static int
+zfs_close(struct open_file *f)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+
+ dnode_cache_obj = 0;
+ f->f_fsdata = (void *)0;
+ if (fp == (struct file *)0)
+ return (0);
+
+ free(fp);
+ return (0);
+}
+
+/*
+ * Copy a portion of a file into kernel memory.
+ * Cross block boundaries when necessary.
+ */
+static int
+zfs_read(struct open_file *f, void *start, size_t size, size_t *resid /* out */)
+{
+ spa_t *spa = (spa_t *) f->f_devdata;
+ struct file *fp = (struct file *)f->f_fsdata;
+ const znode_phys_t *zp = (const znode_phys_t *) fp->f_dnode.dn_bonus;
+ size_t n;
+ int rc;
+
+ n = size;
+ if (fp->f_seekp + n > zp->zp_size)
+ n = zp->zp_size - fp->f_seekp;
+
+ rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp, start, n);
+ if (rc)
+ return (rc);
+
+ if (0) {
+ int i;
+ for (i = 0; i < n; i++)
+ putchar(((char*) start)[i]);
+ }
+ fp->f_seekp += n;
+ if (resid)
+ *resid = size - n;
+
+ return (0);
+}
+
+/*
+ * Don't be silly - the bootstrap has no business writing anything.
+ */
+static int
+zfs_write(struct open_file *f, void *start, size_t size, size_t *resid /* out */)
+{
+
+ return (EROFS);
+}
+
+static off_t
+zfs_seek(struct open_file *f, off_t offset, int where)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ znode_phys_t *zp = (znode_phys_t *) fp->f_dnode.dn_bonus;
+
+ switch (where) {
+ case SEEK_SET:
+ fp->f_seekp = offset;
+ break;
+ case SEEK_CUR:
+ fp->f_seekp += offset;
+ break;
+ case SEEK_END:
+ fp->f_seekp = zp->zp_size - offset;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+ return (fp->f_seekp);
+}
+
+static int
+zfs_stat(struct open_file *f, struct stat *sb)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ znode_phys_t *zp = (znode_phys_t *) fp->f_dnode.dn_bonus;
+
+ /* only important stuff */
+ sb->st_mode = zp->zp_mode;
+ sb->st_uid = zp->zp_uid;
+ sb->st_gid = zp->zp_gid;
+ sb->st_size = zp->zp_size;
+
+ return (0);
+}
+
+static int
+zfs_readdir(struct open_file *f, struct dirent *d)
+{
+ spa_t *spa = (spa_t *) f->f_devdata;
+ struct file *fp = (struct file *)f->f_fsdata;
+ znode_phys_t *zp = (znode_phys_t *) fp->f_dnode.dn_bonus;
+ mzap_ent_phys_t mze;
+ size_t bsize = fp->f_dnode.dn_datablkszsec << SPA_MINBLOCKSHIFT;
+ int rc;
+
+ if ((zp->zp_mode >> 12) != 0x4) {
+ return (ENOTDIR);
+ }
+
+ /*
+ * If this is the first read, get the zap type.
+ */
+ if (fp->f_seekp == 0) {
+ rc = dnode_read(spa, &fp->f_dnode,
+ 0, &fp->f_zap_type, sizeof(fp->f_zap_type));
+ if (rc)
+ return (rc);
+
+ if (fp->f_zap_type == ZBT_MICRO) {
+ fp->f_seekp = offsetof(mzap_phys_t, mz_chunk);
+ } else {
+ rc = dnode_read(spa, &fp->f_dnode,
+ offsetof(zap_phys_t, zap_num_leafs),
+ &fp->f_num_leafs,
+ sizeof(fp->f_num_leafs));
+ if (rc)
+ return (rc);
+
+ fp->f_seekp = bsize;
+ fp->f_zap_leaf = (zap_leaf_phys_t *)malloc(bsize);
+ rc = dnode_read(spa, &fp->f_dnode,
+ fp->f_seekp,
+ fp->f_zap_leaf,
+ bsize);
+ if (rc)
+ return (rc);
+ }
+ }
+
+ if (fp->f_zap_type == ZBT_MICRO) {
+ mzap_next:
+ if (fp->f_seekp >= bsize)
+ return (ENOENT);
+
+ rc = dnode_read(spa, &fp->f_dnode,
+ fp->f_seekp, &mze, sizeof(mze));
+ fp->f_seekp += sizeof(mze);
+
+ if (!mze.mze_name[0])
+ goto mzap_next;
+
+ d->d_fileno = ZFS_DIRENT_OBJ(mze.mze_value);
+ d->d_type = ZFS_DIRENT_TYPE(mze.mze_value);
+ strcpy(d->d_name, mze.mze_name);
+ d->d_namlen = strlen(d->d_name);
+ return (0);
+ } else {
+ zap_leaf_t zl;
+ zap_leaf_chunk_t *zc, *nc;
+ int chunk;
+ size_t namelen;
+ char *p;
+ uint64_t value;
+
+ /*
+ * Initialise this so we can use the ZAP size
+ * calculating macros.
+ */
+ zl.l_bs = ilog2(bsize);
+ zl.l_phys = fp->f_zap_leaf;
+
+ /*
+ * Figure out which chunk we are currently looking at
+ * and consider seeking to the next leaf. We use the
+ * low bits of f_seekp as a simple chunk index.
+ */
+ fzap_next:
+ chunk = fp->f_seekp & (bsize - 1);
+ if (chunk == ZAP_LEAF_NUMCHUNKS(&zl)) {
+ fp->f_seekp = (fp->f_seekp & ~(bsize - 1)) + bsize;
+ chunk = 0;
+
+ /*
+ * Check for EOF and read the new leaf.
+ */
+ if (fp->f_seekp >= bsize * fp->f_num_leafs)
+ return (ENOENT);
+
+ rc = dnode_read(spa, &fp->f_dnode,
+ fp->f_seekp,
+ fp->f_zap_leaf,
+ bsize);
+ if (rc)
+ return (rc);
+ }
+
+ zc = &ZAP_LEAF_CHUNK(&zl, chunk);
+ fp->f_seekp++;
+ if (zc->l_entry.le_type != ZAP_CHUNK_ENTRY)
+ goto fzap_next;
+
+ namelen = zc->l_entry.le_name_length;
+ if (namelen > sizeof(d->d_name))
+ namelen = sizeof(d->d_name);
+
+ /*
+ * Paste the name back together.
+ */
+ nc = &ZAP_LEAF_CHUNK(&zl, zc->l_entry.le_name_chunk);
+ p = d->d_name;
+ while (namelen > 0) {
+ int len;
+ len = namelen;
+ if (len > ZAP_LEAF_ARRAY_BYTES)
+ len = ZAP_LEAF_ARRAY_BYTES;
+ memcpy(p, nc->l_array.la_array, len);
+ p += len;
+ namelen -= len;
+ nc = &ZAP_LEAF_CHUNK(&zl, nc->l_array.la_next);
+ }
+ d->d_name[sizeof(d->d_name) - 1] = 0;
+
+ /*
+ * Assume the first eight bytes of the value are
+ * a uint64_t.
+ */
+ value = fzap_leaf_value(&zl, zc);
+
+ d->d_fileno = ZFS_DIRENT_OBJ(value);
+ d->d_type = ZFS_DIRENT_TYPE(value);
+ d->d_namlen = strlen(d->d_name);
+
+ return (0);
+ }
+}
+
+static int
+vdev_read(vdev_t *vdev, void *priv, off_t offset, void *buf, size_t size)
+{
+ int fd;
+
+ fd = (uintptr_t) priv;
+ lseek(fd, offset, SEEK_SET);
+ if (read(fd, buf, size) == size) {
+ return 0;
+ } else {
+ return (EIO);
+ }
+}
+
+/*
+ * Convert a pool guid to a 'unit number' suitable for use with zfs_dev_open.
+ */
+int
+zfs_guid_to_unit(uint64_t guid)
+{
+ spa_t *spa;
+ int unit;
+
+ unit = 0;
+ STAILQ_FOREACH(spa, &zfs_pools, spa_link) {
+ if (spa->spa_guid == guid)
+ return unit;
+ unit++;
+ }
+ return (-1);
+}
+
+static int
+zfs_dev_init(void)
+{
+ char devname[512];
+ int unit, slice;
+ int fd;
+
+ /*
+ * Open all the disks we can find and see if we can reconstruct
+ * ZFS pools from them. Bogusly assumes that the disks are named
+ * diskN or diskNsM.
+ */
+ zfs_init();
+ for (unit = 0; unit < 32 /* XXX */; unit++) {
+ sprintf(devname, "disk%d:", unit);
+ fd = open(devname, O_RDONLY);
+ if (fd == -1)
+ continue;
+
+ /*
+ * If we find a vdev, the zfs code will eat the fd, otherwise
+ * we close it.
+ */
+ if (vdev_probe(vdev_read, (void*) (uintptr_t) fd, 0))
+ close(fd);
+
+ for (slice = 1; slice <= 4; slice++) {
+ sprintf(devname, "disk%ds%d:", unit, slice);
+ fd = open(devname, O_RDONLY);
+ if (fd == -1)
+ continue;
+ if (vdev_probe(vdev_read, (void*) (uintptr_t) fd, 0))
+ close(fd);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Print information about ZFS pools
+ */
+static void
+zfs_dev_print(int verbose)
+{
+ spa_t *spa;
+ char line[80];
+ int unit;
+
+ if (verbose) {
+ spa_all_status();
+ return;
+ }
+ unit = 0;
+ STAILQ_FOREACH(spa, &zfs_pools, spa_link) {
+ sprintf(line, " zfs%d: %s\n", unit, spa->spa_name);
+ pager_output(line);
+ unit++;
+ }
+}
+
+/*
+ * Attempt to open the pool described by (dev) for use by (f).
+ */
+static int
+zfs_dev_open(struct open_file *f, ...)
+{
+ va_list args;
+ struct devdesc *dev;
+ int unit, i;
+ spa_t *spa;
+
+ va_start(args, f);
+ dev = va_arg(args, struct devdesc*);
+ va_end(args);
+
+ /*
+ * We mostly ignore the stuff that devopen sends us. For now,
+ * use the unit to find a pool - later we will override the
+ * devname parsing so that we can name a pool and a fs within
+ * the pool.
+ */
+ unit = dev->d_unit;
+ free(dev);
+
+ i = 0;
+ STAILQ_FOREACH(spa, &zfs_pools, spa_link) {
+ if (i == unit)
+ break;
+ i++;
+ }
+ if (!spa) {
+ return (ENXIO);
+ }
+
+ f->f_devdata = spa;
+ return (0);
+}
+
+static int
+zfs_dev_close(struct open_file *f)
+{
+
+ f->f_devdata = NULL;
+ return (0);
+}
+
+static int
+zfs_dev_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize)
+{
+
+ return (ENOSYS);
+}
+
+struct devsw zfs_dev = {
+ .dv_name = "zfs",
+ .dv_type = DEVT_ZFS,
+ .dv_init = zfs_dev_init,
+ .dv_strategy = zfs_dev_strategy,
+ .dv_open = zfs_dev_open,
+ .dv_close = zfs_dev_close,
+ .dv_ioctl = noioctl,
+ .dv_print = zfs_dev_print,
+ .dv_cleanup = NULL
+};
diff --git a/sys/boot/zfs/zfsimpl.c b/sys/boot/zfs/zfsimpl.c
new file mode 100644
index 0000000..5bbc351
--- /dev/null
+++ b/sys/boot/zfs/zfsimpl.c
@@ -0,0 +1,1443 @@
+/*-
+ * Copyright (c) 2007 Doug Rabson
+ * 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>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Stand-alone ZFS file reader.
+ */
+
+#include "zfsimpl.h"
+#include "zfssubr.c"
+
+/*
+ * List of all vdevs, chained through v_alllink.
+ */
+static vdev_list_t zfs_vdevs;
+
+/*
+ * List of all pools, chained through spa_link.
+ */
+static spa_list_t zfs_pools;
+
+static uint64_t zfs_crc64_table[256];
+static char *zfs_decomp_buf;
+static const dnode_phys_t *dnode_cache_obj = 0;
+static uint64_t dnode_cache_bn;
+static char *dnode_cache_buf;
+static char *zap_scratch;
+
+/*
+ * Forward declarations.
+ */
+static int zio_read_phys(vdev_t *vdev, const blkptr_t *bp, void *buf, off_t offset);
+
+static void
+zfs_init(void)
+{
+ STAILQ_INIT(&zfs_vdevs);
+ STAILQ_INIT(&zfs_pools);
+
+ zfs_decomp_buf = malloc(128*1024);
+ dnode_cache_buf = malloc(128*1024);
+ zap_scratch = malloc(128*1024);
+
+ zfs_init_crc();
+}
+
+static int
+xdr_int(const unsigned char **xdr, int *ip)
+{
+ *ip = ((*xdr)[0] << 24)
+ | ((*xdr)[1] << 16)
+ | ((*xdr)[2] << 8)
+ | ((*xdr)[3] << 0);
+ (*xdr) += 4;
+ return (0);
+}
+
+static int
+xdr_u_int(const unsigned char **xdr, u_int *ip)
+{
+ *ip = ((*xdr)[0] << 24)
+ | ((*xdr)[1] << 16)
+ | ((*xdr)[2] << 8)
+ | ((*xdr)[3] << 0);
+ (*xdr) += 4;
+ return (0);
+}
+
+static int
+xdr_uint64_t(const unsigned char **xdr, uint64_t *lp)
+{
+ u_int hi, lo;
+
+ xdr_u_int(xdr, &hi);
+ xdr_u_int(xdr, &lo);
+ *lp = (((uint64_t) hi) << 32) | lo;
+ return (0);
+}
+
+static int
+nvlist_find(const unsigned char *nvlist, const char *name, int type,
+ int* elementsp, void *valuep)
+{
+ const unsigned char *p, *pair;
+ int junk;
+ int encoded_size, decoded_size;
+
+ p = nvlist;
+ xdr_int(&p, &junk);
+ xdr_int(&p, &junk);
+
+ pair = p;
+ xdr_int(&p, &encoded_size);
+ xdr_int(&p, &decoded_size);
+ while (encoded_size && decoded_size) {
+ int namelen, pairtype, elements;
+ const char *pairname;
+
+ xdr_int(&p, &namelen);
+ pairname = (const char*) p;
+ p += roundup(namelen, 4);
+ xdr_int(&p, &pairtype);
+
+ if (!memcmp(name, pairname, namelen) && type == pairtype) {
+ xdr_int(&p, &elements);
+ if (elementsp)
+ *elementsp = elements;
+ if (type == DATA_TYPE_UINT64) {
+ xdr_uint64_t(&p, (uint64_t *) valuep);
+ return (0);
+ } else if (type == DATA_TYPE_STRING) {
+ int len;
+ xdr_int(&p, &len);
+ (*(const char**) valuep) = (const char*) p;
+ return (0);
+ } else if (type == DATA_TYPE_NVLIST
+ || type == DATA_TYPE_NVLIST_ARRAY) {
+ (*(const unsigned char**) valuep) =
+ (const unsigned char*) p;
+ return (0);
+ } else {
+ return (EIO);
+ }
+ } else {
+ /*
+ * Not the pair we are looking for, skip to the next one.
+ */
+ p = pair + encoded_size;
+ }
+
+ pair = p;
+ xdr_int(&p, &encoded_size);
+ xdr_int(&p, &decoded_size);
+ }
+
+ return (EIO);
+}
+
+/*
+ * Return the next nvlist in an nvlist array.
+ */
+static const unsigned char *
+nvlist_next(const unsigned char *nvlist)
+{
+ const unsigned char *p, *pair;
+ int junk;
+ int encoded_size, decoded_size;
+
+ p = nvlist;
+ xdr_int(&p, &junk);
+ xdr_int(&p, &junk);
+
+ pair = p;
+ xdr_int(&p, &encoded_size);
+ xdr_int(&p, &decoded_size);
+ while (encoded_size && decoded_size) {
+ p = pair + encoded_size;
+
+ pair = p;
+ xdr_int(&p, &encoded_size);
+ xdr_int(&p, &decoded_size);
+ }
+
+ return p;
+}
+
+#ifdef TEST
+
+static const unsigned char *
+nvlist_print(const unsigned char *nvlist, unsigned int indent)
+{
+ static const char* typenames[] = {
+ "DATA_TYPE_UNKNOWN",
+ "DATA_TYPE_BOOLEAN",
+ "DATA_TYPE_BYTE",
+ "DATA_TYPE_INT16",
+ "DATA_TYPE_UINT16",
+ "DATA_TYPE_INT32",
+ "DATA_TYPE_UINT32",
+ "DATA_TYPE_INT64",
+ "DATA_TYPE_UINT64",
+ "DATA_TYPE_STRING",
+ "DATA_TYPE_BYTE_ARRAY",
+ "DATA_TYPE_INT16_ARRAY",
+ "DATA_TYPE_UINT16_ARRAY",
+ "DATA_TYPE_INT32_ARRAY",
+ "DATA_TYPE_UINT32_ARRAY",
+ "DATA_TYPE_INT64_ARRAY",
+ "DATA_TYPE_UINT64_ARRAY",
+ "DATA_TYPE_STRING_ARRAY",
+ "DATA_TYPE_HRTIME",
+ "DATA_TYPE_NVLIST",
+ "DATA_TYPE_NVLIST_ARRAY",
+ "DATA_TYPE_BOOLEAN_VALUE",
+ "DATA_TYPE_INT8",
+ "DATA_TYPE_UINT8",
+ "DATA_TYPE_BOOLEAN_ARRAY",
+ "DATA_TYPE_INT8_ARRAY",
+ "DATA_TYPE_UINT8_ARRAY"
+ };
+
+ unsigned int i, j;
+ const unsigned char *p, *pair;
+ int junk;
+ int encoded_size, decoded_size;
+
+ p = nvlist;
+ xdr_int(&p, &junk);
+ xdr_int(&p, &junk);
+
+ pair = p;
+ xdr_int(&p, &encoded_size);
+ xdr_int(&p, &decoded_size);
+ while (encoded_size && decoded_size) {
+ int namelen, pairtype, elements;
+ const char *pairname;
+
+ xdr_int(&p, &namelen);
+ pairname = (const char*) p;
+ p += roundup(namelen, 4);
+ xdr_int(&p, &pairtype);
+
+ for (i = 0; i < indent; i++)
+ printf(" ");
+ printf("%s %s", typenames[pairtype], pairname);
+
+ xdr_int(&p, &elements);
+ switch (pairtype) {
+ case DATA_TYPE_UINT64: {
+ uint64_t val;
+ xdr_uint64_t(&p, &val);
+ printf(" = 0x%llx\n", val);
+ break;
+ }
+
+ case DATA_TYPE_STRING: {
+ int len;
+ xdr_int(&p, &len);
+ printf(" = \"%s\"\n", p);
+ break;
+ }
+
+ case DATA_TYPE_NVLIST:
+ printf("\n");
+ nvlist_print(p, indent + 1);
+ break;
+
+ case DATA_TYPE_NVLIST_ARRAY:
+ for (j = 0; j < elements; j++) {
+ printf("[%d]\n", j);
+ p = nvlist_print(p, indent + 1);
+ if (j != elements - 1) {
+ for (i = 0; i < indent; i++)
+ printf(" ");
+ printf("%s %s", typenames[pairtype], pairname);
+ }
+ }
+ break;
+
+ default:
+ printf("\n");
+ }
+
+ p = pair + encoded_size;
+
+ pair = p;
+ xdr_int(&p, &encoded_size);
+ xdr_int(&p, &decoded_size);
+ }
+
+ return p;
+}
+
+#endif
+
+static int
+vdev_mirror_read(vdev_t *vdev, void *priv, off_t offset, void *buf, size_t size)
+{
+ vdev_t *kid;
+ int rc;
+
+ rc = EIO;
+ STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) {
+ if (kid->v_state != VDEV_STATE_HEALTHY)
+ continue;
+ rc = kid->v_read(kid, kid->v_read_priv, offset, buf, size);
+ if (!rc)
+ return (0);
+ }
+
+ return (rc);
+}
+
+static vdev_t *
+vdev_find(uint64_t guid)
+{
+ vdev_t *vdev;
+
+ STAILQ_FOREACH(vdev, &zfs_vdevs, v_alllink)
+ if (vdev->v_guid == guid)
+ return (vdev);
+
+ return (0);
+}
+
+static vdev_t *
+vdev_create(uint64_t guid, vdev_read_t *read, void *read_priv)
+{
+ vdev_t *vdev;
+
+ vdev = malloc(sizeof(vdev_t));
+ memset(vdev, 0, sizeof(vdev_t));
+ STAILQ_INIT(&vdev->v_children);
+ vdev->v_guid = guid;
+ vdev->v_state = VDEV_STATE_OFFLINE;
+ vdev->v_read = read;
+ vdev->v_read_priv = read_priv;
+ STAILQ_INSERT_TAIL(&zfs_vdevs, vdev, v_alllink);
+
+ return (vdev);
+}
+
+static int
+vdev_init_from_nvlist(const unsigned char *nvlist, vdev_t **vdevp)
+{
+ int rc;
+ uint64_t guid, id;
+ const char *type;
+ const char *path;
+ vdev_t *vdev, *kid;
+ const unsigned char *kids;
+ int nkids, i;
+
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_GUID,
+ DATA_TYPE_UINT64, 0, &guid)
+ || nvlist_find(nvlist, ZPOOL_CONFIG_ID,
+ DATA_TYPE_UINT64, 0, &id)
+ || nvlist_find(nvlist, ZPOOL_CONFIG_TYPE,
+ DATA_TYPE_STRING, 0, &type)) {
+ printf("ZFS: can't find vdev details\n");
+ return (ENOENT);
+ }
+
+ /*
+ * Assume that if we've seen this vdev tree before, this one
+ * will be identical.
+ */
+ vdev = vdev_find(guid);
+ if (vdev) {
+ if (vdevp)
+ *vdevp = vdev;
+ return (0);
+ }
+
+ if (strcmp(type, VDEV_TYPE_MIRROR)
+ && strcmp(type, VDEV_TYPE_DISK)) {
+ printf("ZFS: can only boot from disk or mirror vdevs\n");
+ return (EIO);
+ }
+
+ if (!strcmp(type, VDEV_TYPE_MIRROR))
+ vdev = vdev_create(guid, vdev_mirror_read, 0);
+ else
+ vdev = vdev_create(guid, 0, 0);
+
+
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_PATH,
+ DATA_TYPE_STRING, 0, &path) == 0) {
+ if (strlen(path) > 5
+ && path[0] == '/'
+ && path[1] == 'd'
+ && path[2] == 'e'
+ && path[3] == 'v'
+ && path[4] == '/')
+ path += 5;
+ vdev->v_name = strdup(path);
+ } else {
+ vdev->v_name = strdup(type);
+ }
+ vdev->v_id = id;
+ rc = nvlist_find(nvlist, ZPOOL_CONFIG_CHILDREN,
+ DATA_TYPE_NVLIST_ARRAY, &nkids, &kids);
+ /*
+ * Its ok if we don't have any kids.
+ */
+ if (rc == 0) {
+ for (i = 0; i < nkids; i++) {
+ rc = vdev_init_from_nvlist(kids, &kid);
+ if (rc)
+ return (rc);
+ STAILQ_INSERT_TAIL(&vdev->v_children, kid, v_childlink);
+ kids = nvlist_next(kids);
+ }
+ }
+
+ if (vdevp)
+ *vdevp = vdev;
+ return (0);
+}
+
+static void
+vdev_set_state(vdev_t *vdev)
+{
+ vdev_t *kid;
+ int good_kids;
+ int bad_kids;
+
+ /*
+ * We assume that if we have kids, we are a mirror. A mirror
+ * is healthy if all its kids are healthy. Its degraded (but
+ * working) if at least one kid is healty.
+ */
+
+ if (STAILQ_FIRST(&vdev->v_children)) {
+ good_kids = 0;
+ bad_kids = 0;
+ STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) {
+ if (kid->v_state == VDEV_STATE_HEALTHY)
+ good_kids++;
+ else
+ bad_kids++;
+ }
+ if (good_kids) {
+ if (!bad_kids && good_kids)
+ vdev->v_state = VDEV_STATE_HEALTHY;
+ else
+ vdev->v_state = VDEV_STATE_DEGRADED;
+ } else {
+ vdev->v_state = VDEV_STATE_OFFLINE;
+ }
+ }
+}
+
+static spa_t *
+spa_find_by_guid(uint64_t guid)
+{
+ spa_t *spa;
+
+ STAILQ_FOREACH(spa, &zfs_pools, spa_link)
+ if (spa->spa_guid == guid)
+ return (spa);
+
+ return (0);
+}
+
+#ifdef BOOT2
+
+static spa_t *
+spa_find_by_name(const char *name)
+{
+ spa_t *spa;
+
+ STAILQ_FOREACH(spa, &zfs_pools, spa_link)
+ if (!strcmp(spa->spa_name, name))
+ return (spa);
+
+ return (0);
+}
+
+#endif
+
+static spa_t *
+spa_create(uint64_t guid)
+{
+ spa_t *spa;
+
+ spa = malloc(sizeof(spa_t));
+ memset(spa, 0, sizeof(spa_t));
+ STAILQ_INIT(&spa->spa_vdevs);
+ spa->spa_guid = guid;
+ STAILQ_INSERT_TAIL(&zfs_pools, spa, spa_link);
+
+ return (spa);
+}
+
+static const char *
+state_name(vdev_state_t state)
+{
+ static const char* names[] = {
+ "UNKNOWN",
+ "CLOSED",
+ "OFFLINE",
+ "CANT_OPEN",
+ "DEGRADED",
+ "ONLINE"
+ };
+ return names[state];
+}
+
+#ifdef BOOT2
+
+#define pager_printf printf
+
+#else
+
+static void
+pager_printf(const char *fmt, ...)
+{
+ char line[80];
+ va_list args;
+
+ va_start(args, fmt);
+ vsprintf(line, fmt, args);
+ va_end(args);
+ pager_output(line);
+}
+
+#endif
+
+#define STATUS_FORMAT " %-16s %-10s\n"
+
+static void
+print_state(int indent, const char *name, vdev_state_t state)
+{
+ int i;
+ char buf[512];
+
+ buf[0] = 0;
+ for (i = 0; i < indent; i++)
+ strcat(buf, " ");
+ strcat(buf, name);
+ pager_printf(STATUS_FORMAT, buf, state_name(state));
+
+}
+
+static void
+vdev_status(vdev_t *vdev, int indent)
+{
+ vdev_t *kid;
+ print_state(indent, vdev->v_name, vdev->v_state);
+
+ STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) {
+ vdev_status(kid, indent + 1);
+ }
+}
+
+static void
+spa_status(spa_t *spa)
+{
+ vdev_t *vdev;
+ int good_kids, bad_kids, degraded_kids;
+ vdev_state_t state;
+
+ pager_printf(" pool: %s\n", spa->spa_name);
+ pager_printf("config:\n\n");
+ pager_printf(STATUS_FORMAT, "NAME", "STATE");
+
+ good_kids = 0;
+ degraded_kids = 0;
+ bad_kids = 0;
+ STAILQ_FOREACH(vdev, &spa->spa_vdevs, v_childlink) {
+ if (vdev->v_state == VDEV_STATE_HEALTHY)
+ good_kids++;
+ else if (vdev->v_state == VDEV_STATE_DEGRADED)
+ degraded_kids++;
+ else
+ bad_kids++;
+ }
+
+ state = VDEV_STATE_CLOSED;
+ if (good_kids > 0 && (degraded_kids + bad_kids) == 0)
+ state = VDEV_STATE_HEALTHY;
+ else if ((good_kids + degraded_kids) > 0)
+ state = VDEV_STATE_DEGRADED;
+
+ print_state(0, spa->spa_name, state);
+ STAILQ_FOREACH(vdev, &spa->spa_vdevs, v_childlink) {
+ vdev_status(vdev, 1);
+ }
+}
+
+static void
+spa_all_status(void)
+{
+ spa_t *spa;
+ int first = 1;
+
+ STAILQ_FOREACH(spa, &zfs_pools, spa_link) {
+ if (!first)
+ pager_printf("\n");
+ first = 0;
+ spa_status(spa);
+ }
+}
+
+static int
+vdev_probe(vdev_read_t *read, void *read_priv, spa_t **spap)
+{
+ vdev_t vtmp;
+ vdev_phys_t *vdev_label = (vdev_phys_t *) zap_scratch;
+ spa_t *spa;
+ vdev_t *vdev, *top_vdev, *pool_vdev;
+ off_t off;
+ blkptr_t bp;
+ const unsigned char *nvlist;
+ uint64_t val;
+ uint64_t guid;
+ uint64_t pool_txg, pool_guid;
+ const char *pool_name;
+ const unsigned char *vdevs;
+ int i;
+ char upbuf[1024];
+ const struct uberblock *up;
+
+ /*
+ * Load the vdev label and figure out which
+ * uberblock is most current.
+ */
+ memset(&vtmp, 0, sizeof(vtmp));
+ vtmp.v_read = read;
+ vtmp.v_read_priv = read_priv;
+ off = offsetof(vdev_label_t, vl_vdev_phys);
+ BP_ZERO(&bp);
+ BP_SET_LSIZE(&bp, sizeof(vdev_phys_t));
+ BP_SET_PSIZE(&bp, sizeof(vdev_phys_t));
+ BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL);
+ BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF);
+ ZIO_SET_CHECKSUM(&bp.blk_cksum, off, 0, 0, 0);
+ if (zio_read_phys(&vtmp, &bp, vdev_label, off))
+ return (EIO);
+
+ if (vdev_label->vp_nvlist[0] != NV_ENCODE_XDR) {
+ return (EIO);
+ }
+
+ nvlist = (const unsigned char *) vdev_label->vp_nvlist + 4;
+
+ if (nvlist_find(nvlist,
+ ZPOOL_CONFIG_VERSION,
+ DATA_TYPE_UINT64, 0, &val)) {
+ return (EIO);
+ }
+
+ if (val != ZFS_VERSION) {
+ printf("ZFS: unsupported ZFS version %d\n", (int) val);
+ return (EIO);
+ }
+
+ if (nvlist_find(nvlist,
+ ZPOOL_CONFIG_POOL_STATE,
+ DATA_TYPE_UINT64, 0, &val)) {
+ return (EIO);
+ }
+
+ if (val != POOL_STATE_ACTIVE) {
+ /*
+ * Don't print a message here. If we happen to reboot
+ * while where is an exported pool around, we don't
+ * need a cascade of confusing messages during boot.
+ */
+ /*printf("ZFS: pool is not active\n");*/
+ return (EIO);
+ }
+
+ if (nvlist_find(nvlist,
+ ZPOOL_CONFIG_POOL_TXG,
+ DATA_TYPE_UINT64, 0, &pool_txg)
+ || nvlist_find(nvlist,
+ ZPOOL_CONFIG_POOL_GUID,
+ DATA_TYPE_UINT64, 0, &pool_guid)
+ || nvlist_find(nvlist,
+ ZPOOL_CONFIG_POOL_NAME,
+ DATA_TYPE_STRING, 0, &pool_name)) {
+ printf("ZFS: can't find pool details\n");
+ return (EIO);
+ }
+
+ /*
+ * Create the pool if this is the first time we've seen it.
+ */
+ spa = spa_find_by_guid(pool_guid);
+ if (!spa) {
+ spa = spa_create(pool_guid);
+ spa->spa_name = strdup(pool_name);
+ }
+ if (pool_txg > spa->spa_txg)
+ spa->spa_txg = pool_txg;
+
+ /*
+ * Get the vdev tree and create our in-core copy of it.
+ * If we already have a healthy vdev with this guid, this must
+ * be some kind of alias (overlapping slices, dangerously dedicated
+ * disks etc).
+ */
+ if (nvlist_find(nvlist,
+ ZPOOL_CONFIG_GUID,
+ DATA_TYPE_UINT64, 0, &guid)) {
+ return (EIO);
+ }
+ vdev = vdev_find(guid);
+ if (vdev && vdev->v_state == VDEV_STATE_HEALTHY) {
+ return (EIO);
+ }
+
+ if (nvlist_find(nvlist,
+ ZPOOL_CONFIG_VDEV_TREE,
+ DATA_TYPE_NVLIST, 0, &vdevs)) {
+ return (EIO);
+ }
+ vdev_init_from_nvlist(vdevs, &top_vdev);
+
+ /*
+ * Add the toplevel vdev to the pool if its not already there.
+ */
+ STAILQ_FOREACH(pool_vdev, &spa->spa_vdevs, v_childlink)
+ if (top_vdev == pool_vdev)
+ break;
+ if (!pool_vdev && top_vdev)
+ STAILQ_INSERT_TAIL(&spa->spa_vdevs, top_vdev, v_childlink);
+
+ /*
+ * We should already have created an incomplete vdev for this
+ * vdev. Find it and initialise it with our read proc.
+ */
+ vdev = vdev_find(guid);
+ if (vdev) {
+ vdev->v_read = read;
+ vdev->v_read_priv = read_priv;
+ vdev->v_state = VDEV_STATE_HEALTHY;
+ } else {
+ printf("ZFS: inconsistent nvlist contents\n");
+ return (EIO);
+ }
+
+ /*
+ * Re-evaluate top-level vdev state.
+ */
+ vdev_set_state(top_vdev);
+
+ /*
+ * Ok, we are happy with the pool so far. Lets find
+ * the best uberblock and then we can actually access
+ * the contents of the pool.
+ */
+ for (i = 0;
+ i < VDEV_UBERBLOCK_RING >> UBERBLOCK_SHIFT;
+ i++) {
+ off = offsetof(vdev_label_t, vl_uberblock);
+ off += i << UBERBLOCK_SHIFT;
+ BP_ZERO(&bp);
+ DVA_SET_OFFSET(&bp.blk_dva[0], off);
+ BP_SET_LSIZE(&bp, 1 << UBERBLOCK_SHIFT);
+ BP_SET_PSIZE(&bp, 1 << UBERBLOCK_SHIFT);
+ BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL);
+ BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF);
+ ZIO_SET_CHECKSUM(&bp.blk_cksum, off, 0, 0, 0);
+ if (zio_read_phys(vdev, &bp, upbuf, off))
+ continue;
+
+ up = (const struct uberblock *) upbuf;
+ if (up->ub_magic != UBERBLOCK_MAGIC)
+ continue;
+ if (up->ub_txg < spa->spa_txg)
+ continue;
+ if (up->ub_txg > spa->spa_uberblock.ub_txg) {
+ spa->spa_uberblock = *up;
+ } else if (up->ub_txg == spa->spa_uberblock.ub_txg) {
+ if (up->ub_timestamp > spa->spa_uberblock.ub_timestamp)
+ spa->spa_uberblock = *up;
+ }
+ }
+
+ if (spap)
+ *spap = spa;
+ return (0);
+}
+
+static int
+ilog2(int n)
+{
+ int v;
+
+ for (v = 0; v < 32; v++)
+ if (n == (1 << v))
+ return v;
+ return -1;
+}
+
+static int
+zio_read_phys(vdev_t *vdev, const blkptr_t *bp, void *buf, off_t offset)
+{
+ int cpfunc = BP_GET_COMPRESS(bp);
+ size_t lsize = BP_GET_LSIZE(bp);
+ size_t psize = BP_GET_PSIZE(bp);
+ int rc;
+
+ /*printf("ZFS: reading %d bytes at 0x%llx to %p\n", psize, offset, buf);*/
+ if (cpfunc != ZIO_COMPRESS_OFF) {
+ rc = vdev->v_read(vdev, vdev->v_read_priv, offset, zfs_decomp_buf, psize);
+ if (rc)
+ return (rc);
+ if (zio_checksum_error(bp, zfs_decomp_buf))
+ return (EIO);
+ if (zio_decompress_data(cpfunc, zfs_decomp_buf, psize,
+ buf, lsize))
+ return (EIO);
+ } else {
+ rc = vdev->v_read(vdev, vdev->v_read_priv, offset, buf, psize);
+ if (rc)
+ return (rc);
+
+ if (zio_checksum_error(bp, buf))
+ return (EIO);
+ }
+ return (0);
+}
+
+static int
+zio_read(spa_t *spa, const blkptr_t *bp, void *buf)
+{
+ int i;
+
+ for (i = 0; i < SPA_DVAS_PER_BP; i++) {
+ const dva_t *dva = &bp->blk_dva[i];
+ vdev_t *vdev;
+ int vdevid;
+ off_t offset;
+
+ if (!dva->dva_word[0] && !dva->dva_word[1])
+ continue;
+
+ vdevid = DVA_GET_VDEV(dva);
+ offset = DVA_GET_OFFSET(dva) + VDEV_LABEL_START_SIZE;
+ STAILQ_FOREACH(vdev, &spa->spa_vdevs, v_childlink)
+ if (vdev->v_id == vdevid)
+ break;
+ if (!vdev || !vdev->v_read)
+ continue;
+ if (zio_read_phys(vdev, bp, buf, offset))
+ continue;
+
+ return (0);
+ }
+ printf("ZFS: i/o error - all block copies unavailable\n");
+
+ return (EIO);
+}
+
+static int
+dnode_read(spa_t *spa, const dnode_phys_t *dnode, off_t offset, void *buf, size_t buflen)
+{
+ int ibshift = dnode->dn_indblkshift - SPA_BLKPTRSHIFT;
+ int bsize = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT;
+ int nlevels = dnode->dn_nlevels;
+ int i, rc;
+
+ /*
+ * We truncate the offset to 32bits, mainly so that I don't
+ * have to find a copy of __divdi3 to put into the bootstrap.
+ * I don't think the bootstrap needs to access anything bigger
+ * than 2G anyway. Note that block addresses are still 64bit
+ * so it doesn't affect the possible size of the media.
+ * We still use 64bit block numbers so that the bitshifts
+ * work correctly. Note: bsize may not be a power of two here.
+ */
+ while (buflen > 0) {
+ uint64_t bn = ((int) offset) / bsize;
+ int boff = ((int) offset) % bsize;
+ int ibn;
+ const blkptr_t *indbp;
+ blkptr_t bp;
+
+ if (bn > dnode->dn_maxblkid)
+ return (EIO);
+
+ if (dnode == dnode_cache_obj && bn == dnode_cache_bn)
+ goto cached;
+
+ indbp = dnode->dn_blkptr;
+ for (i = 0; i < nlevels; i++) {
+ /*
+ * Copy the bp from the indirect array so that
+ * we can re-use the scratch buffer for multi-level
+ * objects.
+ */
+ ibn = bn >> ((nlevels - i - 1) * ibshift);
+ ibn &= ((1 << ibshift) - 1);
+ bp = indbp[ibn];
+ rc = zio_read(spa, &bp, dnode_cache_buf);
+ if (rc)
+ return (rc);
+ indbp = (const blkptr_t *) dnode_cache_buf;
+ }
+ dnode_cache_obj = dnode;
+ dnode_cache_bn = bn;
+ cached:
+
+ /*
+ * The buffer contains our data block. Copy what we
+ * need from it and loop.
+ */
+ i = bsize - boff;
+ if (i > buflen) i = buflen;
+ memcpy(buf, &dnode_cache_buf[boff], i);
+ buf = ((char*) buf) + i;
+ offset += i;
+ buflen -= i;
+ }
+
+ return (0);
+}
+
+/*
+ * Lookup a value in a microzap directory. Assumes that the zap
+ * scratch buffer contains the directory contents.
+ */
+static int
+mzap_lookup(spa_t *spa, const dnode_phys_t *dnode, const char *name, uint64_t *value)
+{
+ const mzap_phys_t *mz;
+ const mzap_ent_phys_t *mze;
+ size_t size;
+ int chunks, i;
+
+ /*
+ * Microzap objects use exactly one block. Read the whole
+ * thing.
+ */
+ size = dnode->dn_datablkszsec * 512;
+
+ mz = (const mzap_phys_t *) zap_scratch;
+ chunks = size / MZAP_ENT_LEN - 1;
+
+ for (i = 0; i < chunks; i++) {
+ mze = &mz->mz_chunk[i];
+ if (!strcmp(mze->mze_name, name)) {
+ *value = mze->mze_value;
+ return (0);
+ }
+ }
+
+ return (ENOENT);
+}
+
+/*
+ * Compare a name with a zap leaf entry. Return non-zero if the name
+ * matches.
+ */
+static int
+fzap_name_equal(const zap_leaf_t *zl, const zap_leaf_chunk_t *zc, const char *name)
+{
+ size_t namelen;
+ const zap_leaf_chunk_t *nc;
+ const char *p;
+
+ namelen = zc->l_entry.le_name_length;
+
+ nc = &ZAP_LEAF_CHUNK(zl, zc->l_entry.le_name_chunk);
+ p = name;
+ while (namelen > 0) {
+ size_t len;
+ len = namelen;
+ if (len > ZAP_LEAF_ARRAY_BYTES)
+ len = ZAP_LEAF_ARRAY_BYTES;
+ if (memcmp(p, nc->l_array.la_array, len))
+ return (0);
+ p += len;
+ namelen -= len;
+ nc = &ZAP_LEAF_CHUNK(zl, nc->l_array.la_next);
+ }
+
+ return 1;
+}
+
+/*
+ * Extract a uint64_t value from a zap leaf entry.
+ */
+static uint64_t
+fzap_leaf_value(const zap_leaf_t *zl, const zap_leaf_chunk_t *zc)
+{
+ const zap_leaf_chunk_t *vc;
+ int i;
+ uint64_t value;
+ const uint8_t *p;
+
+ vc = &ZAP_LEAF_CHUNK(zl, zc->l_entry.le_value_chunk);
+ for (i = 0, value = 0, p = vc->l_array.la_array; i < 8; i++) {
+ value = (value << 8) | p[i];
+ }
+
+ return value;
+}
+
+/*
+ * Lookup a value in a fatzap directory. Assumes that the zap scratch
+ * buffer contains the directory header.
+ */
+static int
+fzap_lookup(spa_t *spa, const dnode_phys_t *dnode, const char *name, uint64_t *value)
+{
+ int bsize = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT;
+ zap_phys_t zh = *(zap_phys_t *) zap_scratch;
+ fat_zap_t z;
+ uint64_t *ptrtbl;
+ uint64_t hash;
+ int rc;
+
+ if (zh.zap_magic != ZAP_MAGIC)
+ return (EIO);
+
+ z.zap_block_shift = ilog2(bsize);
+ z.zap_phys = (zap_phys_t *) zap_scratch;
+
+ /*
+ * Figure out where the pointer table is and read it in if necessary.
+ */
+ if (zh.zap_ptrtbl.zt_blk) {
+ rc = dnode_read(spa, dnode, zh.zap_ptrtbl.zt_blk * bsize,
+ zap_scratch, bsize);
+ if (rc)
+ return (rc);
+ ptrtbl = (uint64_t *) zap_scratch;
+ } else {
+ ptrtbl = &ZAP_EMBEDDED_PTRTBL_ENT(&z, 0);
+ }
+
+ hash = zap_hash(zh.zap_salt, name);
+
+ zap_leaf_t zl;
+ zl.l_bs = z.zap_block_shift;
+
+ off_t off = ptrtbl[hash >> (64 - zh.zap_ptrtbl.zt_shift)] << zl.l_bs;
+ zap_leaf_chunk_t *zc;
+
+ rc = dnode_read(spa, dnode, off, zap_scratch, bsize);
+ if (rc)
+ return (rc);
+
+ zl.l_phys = (zap_leaf_phys_t *) zap_scratch;
+
+ /*
+ * Make sure this chunk matches our hash.
+ */
+ if (zl.l_phys->l_hdr.lh_prefix_len > 0
+ && zl.l_phys->l_hdr.lh_prefix
+ != hash >> (64 - zl.l_phys->l_hdr.lh_prefix_len))
+ return (ENOENT);
+
+ /*
+ * Hash within the chunk to find our entry.
+ */
+ int shift = (64 - ZAP_LEAF_HASH_SHIFT(&zl) - zl.l_phys->l_hdr.lh_prefix_len);
+ int h = (hash >> shift) & ((1 << ZAP_LEAF_HASH_SHIFT(&zl)) - 1);
+ h = zl.l_phys->l_hash[h];
+ if (h == 0xffff)
+ return (ENOENT);
+ zc = &ZAP_LEAF_CHUNK(&zl, h);
+ while (zc->l_entry.le_hash != hash) {
+ if (zc->l_entry.le_next == 0xffff) {
+ zc = 0;
+ break;
+ }
+ zc = &ZAP_LEAF_CHUNK(&zl, zc->l_entry.le_next);
+ }
+ if (fzap_name_equal(&zl, zc, name)) {
+ *value = fzap_leaf_value(&zl, zc);
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+/*
+ * Lookup a name in a zap object and return its value as a uint64_t.
+ */
+static int
+zap_lookup(spa_t *spa, const dnode_phys_t *dnode, const char *name, uint64_t *value)
+{
+ int rc;
+ uint64_t zap_type;
+ size_t size = dnode->dn_datablkszsec * 512;
+
+ rc = dnode_read(spa, dnode, 0, zap_scratch, size);
+ if (rc)
+ return (rc);
+
+ zap_type = *(uint64_t *) zap_scratch;
+ if (zap_type == ZBT_MICRO)
+ return mzap_lookup(spa, dnode, name, value);
+ else
+ return fzap_lookup(spa, dnode, name, value);
+}
+
+#ifdef BOOT2
+
+/*
+ * List a microzap directory. Assumes that the zap scratch buffer contains
+ * the directory contents.
+ */
+static int
+mzap_list(spa_t *spa, const dnode_phys_t *dnode)
+{
+ const mzap_phys_t *mz;
+ const mzap_ent_phys_t *mze;
+ size_t size;
+ int chunks, i;
+
+ /*
+ * Microzap objects use exactly one block. Read the whole
+ * thing.
+ */
+ size = dnode->dn_datablkszsec * 512;
+ mz = (const mzap_phys_t *) zap_scratch;
+ chunks = size / MZAP_ENT_LEN - 1;
+
+ for (i = 0; i < chunks; i++) {
+ mze = &mz->mz_chunk[i];
+ if (mze->mze_name[0])
+ //printf("%-32s 0x%llx\n", mze->mze_name, mze->mze_value);
+ printf("%s\n", mze->mze_name);
+ }
+
+ return (0);
+}
+
+/*
+ * List a fatzap directory. Assumes that the zap scratch buffer contains
+ * the directory header.
+ */
+static int
+fzap_list(spa_t *spa, const dnode_phys_t *dnode)
+{
+ int bsize = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT;
+ zap_phys_t zh = *(zap_phys_t *) zap_scratch;
+ fat_zap_t z;
+ int i, j;
+
+ if (zh.zap_magic != ZAP_MAGIC)
+ return (EIO);
+
+ z.zap_block_shift = ilog2(bsize);
+ z.zap_phys = (zap_phys_t *) zap_scratch;
+
+ /*
+ * This assumes that the leaf blocks start at block 1. The
+ * documentation isn't exactly clear on this.
+ */
+ zap_leaf_t zl;
+ zl.l_bs = z.zap_block_shift;
+ for (i = 0; i < zh.zap_num_leafs; i++) {
+ off_t off = (i + 1) << zl.l_bs;
+ char name[256], *p;
+ uint64_t value;
+
+ if (dnode_read(spa, dnode, off, zap_scratch, bsize))
+ return (EIO);
+
+ zl.l_phys = (zap_leaf_phys_t *) zap_scratch;
+
+ for (j = 0; j < ZAP_LEAF_NUMCHUNKS(&zl); j++) {
+ zap_leaf_chunk_t *zc, *nc;
+ int namelen;
+
+ zc = &ZAP_LEAF_CHUNK(&zl, j);
+ if (zc->l_entry.le_type != ZAP_CHUNK_ENTRY)
+ continue;
+ namelen = zc->l_entry.le_name_length;
+ if (namelen > sizeof(name))
+ namelen = sizeof(name);
+
+ /*
+ * Paste the name back together.
+ */
+ nc = &ZAP_LEAF_CHUNK(&zl, zc->l_entry.le_name_chunk);
+ p = name;
+ while (namelen > 0) {
+ int len;
+ len = namelen;
+ if (len > ZAP_LEAF_ARRAY_BYTES)
+ len = ZAP_LEAF_ARRAY_BYTES;
+ memcpy(p, nc->l_array.la_array, len);
+ p += len;
+ namelen -= len;
+ nc = &ZAP_LEAF_CHUNK(&zl, nc->l_array.la_next);
+ }
+
+ /*
+ * Assume the first eight bytes of the value are
+ * a uint64_t.
+ */
+ value = fzap_leaf_value(&zl, zc);
+
+ printf("%-32s 0x%llx\n", name, value);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * List a zap directory.
+ */
+static int
+zap_list(spa_t *spa, const dnode_phys_t *dnode)
+{
+ uint64_t zap_type;
+ size_t size = dnode->dn_datablkszsec * 512;
+
+ if (dnode_read(spa, dnode, 0, zap_scratch, size))
+ return (EIO);
+
+ zap_type = *(uint64_t *) zap_scratch;
+ if (zap_type == ZBT_MICRO)
+ return mzap_list(spa, dnode);
+ else
+ return fzap_list(spa, dnode);
+}
+
+#endif
+
+static int
+objset_get_dnode(spa_t *spa, const objset_phys_t *os, uint64_t objnum, dnode_phys_t *dnode)
+{
+ off_t offset;
+
+ offset = objnum * sizeof(dnode_phys_t);
+ return dnode_read(spa, &os->os_meta_dnode, offset,
+ dnode, sizeof(dnode_phys_t));
+}
+
+/*
+ * Find the object set given the object number of its dataset object
+ * and return its details in *objset
+ */
+static int
+zfs_mount_dataset(spa_t *spa, uint64_t objnum, objset_phys_t *objset)
+{
+ dnode_phys_t dataset;
+ dsl_dataset_phys_t *ds;
+
+ if (objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset)) {
+ printf("ZFS: can't find dataset %lld\n", objnum);
+ return (EIO);
+ }
+
+ ds = (dsl_dataset_phys_t *) &dataset.dn_bonus;
+ if (zio_read(spa, &ds->ds_bp, objset)) {
+ printf("ZFS: can't read object set for dataset %lld\n", objnum);
+ return (EIO);
+ }
+
+ return (0);
+}
+
+/*
+ * Find the object set pointed to by the BOOTFS property or the root
+ * dataset if there is none and return its details in *objset
+ */
+static int
+zfs_mount_root(spa_t *spa, objset_phys_t *objset)
+{
+ dnode_phys_t dir, propdir;
+ uint64_t props, bootfs, root;
+
+ /*
+ * Start with the MOS directory object.
+ */
+ if (objset_get_dnode(spa, &spa->spa_mos, DMU_POOL_DIRECTORY_OBJECT, &dir)) {
+ printf("ZFS: can't read MOS object directory\n");
+ return (EIO);
+ }
+
+ /*
+ * Lookup the pool_props and see if we can find a bootfs.
+ */
+ if (zap_lookup(spa, &dir, DMU_POOL_PROPS, &props) == 0
+ && objset_get_dnode(spa, &spa->spa_mos, props, &propdir) == 0
+ && zap_lookup(spa, &propdir, "bootfs", &bootfs) == 0)
+ return zfs_mount_dataset(spa, bootfs, objset);
+
+ /*
+ * Lookup the root dataset directory
+ */
+ if (zap_lookup(spa, &dir, DMU_POOL_ROOT_DATASET, &root)
+ || objset_get_dnode(spa, &spa->spa_mos, root, &dir)) {
+ printf("ZFS: can't find root dsl_dir\n");
+ return (EIO);
+ }
+
+ /*
+ * Use the information from the dataset directory's bonus buffer
+ * to find the dataset object and from that the object set itself.
+ */
+ dsl_dir_phys_t *dd = (dsl_dir_phys_t *) &dir.dn_bonus;
+ return zfs_mount_dataset(spa, dd->dd_head_dataset_obj, objset);
+}
+
+static int
+zfs_mount_pool(spa_t *spa)
+{
+ /*
+ * Find the MOS and work our way in from there.
+ */
+ if (zio_read(spa, &spa->spa_uberblock.ub_rootbp, &spa->spa_mos)) {
+ printf("ZFS: can't read MOS\n");
+ return (EIO);
+ }
+
+ /*
+ * Find the root object set
+ */
+ if (zfs_mount_root(spa, &spa->spa_root_objset)) {
+ printf("Can't find root filesystem - giving up\n");
+ return (EIO);
+ }
+
+ return (0);
+}
+
+/*
+ * Lookup a file and return its dnode.
+ */
+static int
+zfs_lookup(spa_t *spa, const char *upath, dnode_phys_t *dnode)
+{
+ int rc;
+ uint64_t objnum, rootnum, parentnum;
+ dnode_phys_t dn;
+ const znode_phys_t *zp = (const znode_phys_t *) dn.dn_bonus;
+ const char *p, *q;
+ char element[256];
+ char path[1024];
+ int symlinks_followed = 0;
+
+ if (spa->spa_root_objset.os_type != DMU_OST_ZFS) {
+ printf("ZFS: unexpected object set type %lld\n",
+ spa->spa_root_objset.os_type);
+ return (EIO);
+ }
+
+ /*
+ * Get the root directory dnode.
+ */
+ rc = objset_get_dnode(spa, &spa->spa_root_objset, MASTER_NODE_OBJ, &dn);
+ if (rc)
+ return (rc);
+
+ rc = zap_lookup(spa, &dn, ZFS_ROOT_OBJ, &rootnum);
+ if (rc)
+ return (rc);
+
+ rc = objset_get_dnode(spa, &spa->spa_root_objset, rootnum, &dn);
+ if (rc)
+ return (rc);
+
+ objnum = rootnum;
+ p = upath;
+ while (p && *p) {
+ while (*p == '/')
+ p++;
+ if (!*p)
+ break;
+ q = strchr(p, '/');
+ if (q) {
+ memcpy(element, p, q - p);
+ element[q - p] = 0;
+ p = q;
+ } else {
+ strcpy(element, p);
+ p = 0;
+ }
+
+ if ((zp->zp_mode >> 12) != 0x4) {
+ return (ENOTDIR);
+ }
+
+ parentnum = objnum;
+ rc = zap_lookup(spa, &dn, element, &objnum);
+ if (rc)
+ return (rc);
+ objnum = ZFS_DIRENT_OBJ(objnum);
+
+ rc = objset_get_dnode(spa, &spa->spa_root_objset, objnum, &dn);
+ if (rc)
+ return (rc);
+
+ /*
+ * Check for symlink.
+ */
+ if ((zp->zp_mode >> 12) == 0xa) {
+ if (symlinks_followed > 10)
+ return (EMLINK);
+ symlinks_followed++;
+
+ /*
+ * Read the link value and copy the tail of our
+ * current path onto the end.
+ */
+ if (p)
+ strcpy(&path[zp->zp_size], p);
+ else
+ path[zp->zp_size] = 0;
+ if (zp->zp_size + sizeof(znode_phys_t) <= dn.dn_bonuslen) {
+ memcpy(path, &dn.dn_bonus[sizeof(znode_phys_t)],
+ zp->zp_size);
+ } else {
+ rc = dnode_read(spa, &dn, 0, path, zp->zp_size);
+ if (rc)
+ return (rc);
+ }
+
+ /*
+ * Restart with the new path, starting either at
+ * the root or at the parent depending whether or
+ * not the link is relative.
+ */
+ p = path;
+ if (*p == '/')
+ objnum = rootnum;
+ else
+ objnum = parentnum;
+ objset_get_dnode(spa, &spa->spa_root_objset, objnum, &dn);
+ }
+ }
+
+ *dnode = dn;
+ return (0);
+}
diff --git a/sys/cddl/boot/zfs/README b/sys/cddl/boot/zfs/README
new file mode 100644
index 0000000..4b62181
--- /dev/null
+++ b/sys/cddl/boot/zfs/README
@@ -0,0 +1,14 @@
+$FreeBSD$
+
+This directory contains various files derived from CDDL sources that
+are used by the ZFS bootstrap:
+
+ fletcher.c checksum support
+ sha256.c checksum support
+ lzjb.c compression support
+ zfssubr.c mostly checksum and compression support
+ zfsimpl.h mostly describing the physical layout
+
+The files fletcher.c, lzjb.c and sha256.c are largely identical to the
+ZFS base code (with write support removed) and could be shared but
+that might complicate future imports from OpenSolaris.
diff --git a/sys/cddl/boot/zfs/fletcher.c b/sys/cddl/boot/zfs/fletcher.c
new file mode 100644
index 0000000..2b9728d
--- /dev/null
+++ b/sys/cddl/boot/zfs/fletcher.c
@@ -0,0 +1,60 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*#pragma ident "%Z%%M% %I% %E% SMI"*/
+
+static void
+fletcher_2_native(const void *buf, uint64_t size, zio_cksum_t *zcp)
+{
+ const uint64_t *ip = buf;
+ const uint64_t *ipend = ip + (size / sizeof (uint64_t));
+ uint64_t a0, b0, a1, b1;
+
+ for (a0 = b0 = a1 = b1 = 0; ip < ipend; ip += 2) {
+ a0 += ip[0];
+ a1 += ip[1];
+ b0 += a0;
+ b1 += a1;
+ }
+
+ ZIO_SET_CHECKSUM(zcp, a0, a1, b0, b1);
+}
+
+static void
+fletcher_4_native(const void *buf, uint64_t size, zio_cksum_t *zcp)
+{
+ const uint32_t *ip = buf;
+ const uint32_t *ipend = ip + (size / sizeof (uint32_t));
+ uint64_t a, b, c, d;
+
+ for (a = b = c = d = 0; ip < ipend; ip++) {
+ a += ip[0];
+ b += a;
+ c += b;
+ d += c;
+ }
+
+ ZIO_SET_CHECKSUM(zcp, a, b, c, d);
+}
diff --git a/sys/cddl/boot/zfs/lzjb.c b/sys/cddl/boot/zfs/lzjb.c
new file mode 100644
index 0000000..1283a6c
--- /dev/null
+++ b/sys/cddl/boot/zfs/lzjb.c
@@ -0,0 +1,74 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*#pragma ident "%Z%%M% %I% %E% SMI"*/
+
+/*
+ * We keep our own copy of this algorithm for 2 main reasons:
+ * 1. If we didn't, anyone modifying common/os/compress.c would
+ * directly break our on disk format
+ * 2. Our version of lzjb does not have a number of checks that the
+ * common/os version needs and uses
+ * In particular, we are adding the "feature" that compress() can
+ * take a destination buffer size and return -1 if the data will not
+ * compress to d_len or less.
+ */
+
+#define MATCH_BITS 6
+#define MATCH_MIN 3
+#define MATCH_MAX ((1 << MATCH_BITS) + (MATCH_MIN - 1))
+#define OFFSET_MASK ((1 << (16 - MATCH_BITS)) - 1)
+#define LEMPEL_SIZE 256
+
+/*ARGSUSED*/
+static int
+lzjb_decompress(void *s_start, void *d_start, size_t s_len, size_t d_len, int n)
+{
+ unsigned char *src = s_start;
+ unsigned char *dst = d_start;
+ unsigned char *d_end = (unsigned char *)d_start + d_len;
+ unsigned char *cpy, copymap = 0;
+ int copymask = 1 << (NBBY - 1);
+
+ while (dst < d_end) {
+ if ((copymask <<= 1) == (1 << NBBY)) {
+ copymask = 1;
+ copymap = *src++;
+ }
+ if (copymap & copymask) {
+ int mlen = (src[0] >> (NBBY - MATCH_BITS)) + MATCH_MIN;
+ int offset = ((src[0] << NBBY) | src[1]) & OFFSET_MASK;
+ src += 2;
+ if ((cpy = dst - offset) < (unsigned char *)d_start)
+ return (-1);
+ while (--mlen >= 0 && dst < d_end)
+ *dst++ = *cpy++;
+ } else {
+ *dst++ = *src++;
+ }
+ }
+ return (0);
+}
diff --git a/sys/cddl/boot/zfs/sha256.c b/sys/cddl/boot/zfs/sha256.c
new file mode 100644
index 0000000..f0d83ac
--- /dev/null
+++ b/sys/cddl/boot/zfs/sha256.c
@@ -0,0 +1,127 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*#pragma ident "%Z%%M% %I% %E% SMI"*/
+
+/*
+ * SHA-256 checksum, as specified in FIPS 180-2, available at:
+ * http://csrc.nist.gov/cryptval
+ *
+ * This is a very compact implementation of SHA-256.
+ * It is designed to be simple and portable, not to be fast.
+ */
+
+/*
+ * The literal definitions according to FIPS180-2 would be:
+ *
+ * Ch(x, y, z) (((x) & (y)) ^ ((~(x)) & (z)))
+ * Maj(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
+ *
+ * We use logical equivalents which require one less op.
+ */
+#define Ch(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define Maj(x, y, z) (((x) & (y)) ^ ((z) & ((x) ^ (y))))
+#define Rot32(x, s) (((x) >> s) | ((x) << (32 - s)))
+#define SIGMA0(x) (Rot32(x, 2) ^ Rot32(x, 13) ^ Rot32(x, 22))
+#define SIGMA1(x) (Rot32(x, 6) ^ Rot32(x, 11) ^ Rot32(x, 25))
+#define sigma0(x) (Rot32(x, 7) ^ Rot32(x, 18) ^ ((x) >> 3))
+#define sigma1(x) (Rot32(x, 17) ^ Rot32(x, 19) ^ ((x) >> 10))
+
+static const uint32_t SHA256_K[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+static void
+SHA256Transform(uint32_t *H, const uint8_t *cp)
+{
+ uint32_t a, b, c, d, e, f, g, h, t, T1, T2, W[64];
+
+ for (t = 0; t < 16; t++, cp += 4)
+ W[t] = (cp[0] << 24) | (cp[1] << 16) | (cp[2] << 8) | cp[3];
+
+ for (t = 16; t < 64; t++)
+ W[t] = sigma1(W[t - 2]) + W[t - 7] +
+ sigma0(W[t - 15]) + W[t - 16];
+
+ a = H[0]; b = H[1]; c = H[2]; d = H[3];
+ e = H[4]; f = H[5]; g = H[6]; h = H[7];
+
+ for (t = 0; t < 64; t++) {
+ T1 = h + SIGMA1(e) + Ch(e, f, g) + SHA256_K[t] + W[t];
+ T2 = SIGMA0(a) + Maj(a, b, c);
+ h = g; g = f; f = e; e = d + T1;
+ d = c; c = b; b = a; a = T1 + T2;
+ }
+
+ H[0] += a; H[1] += b; H[2] += c; H[3] += d;
+ H[4] += e; H[5] += f; H[6] += g; H[7] += h;
+}
+
+static void
+zio_checksum_SHA256(const void *buf, uint64_t size, zio_cksum_t *zcp)
+{
+ uint32_t H[8] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
+ 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 };
+ uint8_t pad[128];
+ int padsize = size & 63;
+ int i;
+
+ for (i = 0; i < size - padsize; i += 64)
+ SHA256Transform(H, (uint8_t *)buf + i);
+
+ for (i = 0; i < padsize; i++)
+ pad[i] = ((uint8_t *)buf)[i];
+
+ for (pad[padsize++] = 0x80; (padsize & 63) != 56; padsize++)
+ pad[padsize] = 0;
+
+ for (i = 0; i < 8; i++)
+ pad[padsize++] = (size << 3) >> (56 - 8 * i);
+
+ for (i = 0; i < padsize; i += 64)
+ SHA256Transform(H, pad + i);
+
+ ZIO_SET_CHECKSUM(zcp,
+ (uint64_t)H[0] << 32 | H[1],
+ (uint64_t)H[2] << 32 | H[3],
+ (uint64_t)H[4] << 32 | H[5],
+ (uint64_t)H[6] << 32 | H[7]);
+}
diff --git a/sys/cddl/boot/zfs/zfsimpl.h b/sys/cddl/boot/zfs/zfsimpl.h
new file mode 100644
index 0000000..3d178b4
--- /dev/null
+++ b/sys/cddl/boot/zfs/zfsimpl.h
@@ -0,0 +1,1151 @@
+/*-
+ * Copyright (c) 2002 McAfee, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Marshall
+ * Kirk McKusick and McAfee Research,, the Security Research Division of
+ * McAfee, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as
+ * part of the DARPA CHATS research program
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* CRC64 table */
+#define ZFS_CRC64_POLY 0xC96C5795D7870F42ULL /* ECMA-182, reflected form */
+
+/*
+ * Macros for various sorts of alignment and rounding when the alignment
+ * is known to be a power of 2.
+ */
+#define P2ALIGN(x, align) ((x) & -(align))
+#define P2PHASE(x, align) ((x) & ((align) - 1))
+#define P2NPHASE(x, align) (-(x) & ((align) - 1))
+#define P2ROUNDUP(x, align) (-(-(x) & -(align)))
+#define P2END(x, align) (-(~(x) & -(align)))
+#define P2PHASEUP(x, align, phase) ((phase) - (((phase) - (x)) & -(align)))
+#define P2CROSS(x, y, align) (((x) ^ (y)) > (align) - 1)
+
+/*
+ * General-purpose 32-bit and 64-bit bitfield encodings.
+ */
+#define BF32_DECODE(x, low, len) P2PHASE((x) >> (low), 1U << (len))
+#define BF64_DECODE(x, low, len) P2PHASE((x) >> (low), 1ULL << (len))
+#define BF32_ENCODE(x, low, len) (P2PHASE((x), 1U << (len)) << (low))
+#define BF64_ENCODE(x, low, len) (P2PHASE((x), 1ULL << (len)) << (low))
+
+#define BF32_GET(x, low, len) BF32_DECODE(x, low, len)
+#define BF64_GET(x, low, len) BF64_DECODE(x, low, len)
+
+#define BF32_SET(x, low, len, val) \
+ ((x) ^= BF32_ENCODE((x >> low) ^ (val), low, len))
+#define BF64_SET(x, low, len, val) \
+ ((x) ^= BF64_ENCODE((x >> low) ^ (val), low, len))
+
+#define BF32_GET_SB(x, low, len, shift, bias) \
+ ((BF32_GET(x, low, len) + (bias)) << (shift))
+#define BF64_GET_SB(x, low, len, shift, bias) \
+ ((BF64_GET(x, low, len) + (bias)) << (shift))
+
+#define BF32_SET_SB(x, low, len, shift, bias, val) \
+ BF32_SET(x, low, len, ((val) >> (shift)) - (bias))
+#define BF64_SET_SB(x, low, len, shift, bias, val) \
+ BF64_SET(x, low, len, ((val) >> (shift)) - (bias))
+
+/*
+ * We currently support nine block sizes, from 512 bytes to 128K.
+ * We could go higher, but the benefits are near-zero and the cost
+ * of COWing a giant block to modify one byte would become excessive.
+ */
+#define SPA_MINBLOCKSHIFT 9
+#define SPA_MAXBLOCKSHIFT 17
+#define SPA_MINBLOCKSIZE (1ULL << SPA_MINBLOCKSHIFT)
+#define SPA_MAXBLOCKSIZE (1ULL << SPA_MAXBLOCKSHIFT)
+
+#define SPA_BLOCKSIZES (SPA_MAXBLOCKSHIFT - SPA_MINBLOCKSHIFT + 1)
+
+/*
+ * The DVA size encodings for LSIZE and PSIZE support blocks up to 32MB.
+ * The ASIZE encoding should be at least 64 times larger (6 more bits)
+ * to support up to 4-way RAID-Z mirror mode with worst-case gang block
+ * overhead, three DVAs per bp, plus one more bit in case we do anything
+ * else that expands the ASIZE.
+ */
+#define SPA_LSIZEBITS 16 /* LSIZE up to 32M (2^16 * 512) */
+#define SPA_PSIZEBITS 16 /* PSIZE up to 32M (2^16 * 512) */
+#define SPA_ASIZEBITS 24 /* ASIZE up to 64 times larger */
+
+/*
+ * All SPA data is represented by 128-bit data virtual addresses (DVAs).
+ * The members of the dva_t should be considered opaque outside the SPA.
+ */
+typedef struct dva {
+ uint64_t dva_word[2];
+} dva_t;
+
+/*
+ * Each block has a 256-bit checksum -- strong enough for cryptographic hashes.
+ */
+typedef struct zio_cksum {
+ uint64_t zc_word[4];
+} zio_cksum_t;
+
+/*
+ * Each block is described by its DVAs, time of birth, checksum, etc.
+ * The word-by-word, bit-by-bit layout of the blkptr is as follows:
+ *
+ * 64 56 48 40 32 24 16 8 0
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * 0 | vdev1 | GRID | ASIZE |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * 1 |G| offset1 |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * 2 | vdev2 | GRID | ASIZE |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * 3 |G| offset2 |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * 4 | vdev3 | GRID | ASIZE |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * 5 |G| offset3 |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * 6 |E| lvl | type | cksum | comp | PSIZE | LSIZE |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * 7 | padding |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * 8 | padding |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * 9 | padding |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * a | birth txg |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * b | fill count |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * c | checksum[0] |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * d | checksum[1] |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * e | checksum[2] |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * f | checksum[3] |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ *
+ * Legend:
+ *
+ * vdev virtual device ID
+ * offset offset into virtual device
+ * LSIZE logical size
+ * PSIZE physical size (after compression)
+ * ASIZE allocated size (including RAID-Z parity and gang block headers)
+ * GRID RAID-Z layout information (reserved for future use)
+ * cksum checksum function
+ * comp compression function
+ * G gang block indicator
+ * E endianness
+ * type DMU object type
+ * lvl level of indirection
+ * birth txg transaction group in which the block was born
+ * fill count number of non-zero blocks under this bp
+ * checksum[4] 256-bit checksum of the data this bp describes
+ */
+typedef struct blkptr {
+ dva_t blk_dva[3]; /* 128-bit Data Virtual Address */
+ uint64_t blk_prop; /* size, compression, type, etc */
+ uint64_t blk_pad[3]; /* Extra space for the future */
+ uint64_t blk_birth; /* transaction group at birth */
+ uint64_t blk_fill; /* fill count */
+ zio_cksum_t blk_cksum; /* 256-bit checksum */
+} blkptr_t;
+
+#define SPA_BLKPTRSHIFT 7 /* blkptr_t is 128 bytes */
+#define SPA_DVAS_PER_BP 3 /* Number of DVAs in a bp */
+
+/*
+ * Macros to get and set fields in a bp or DVA.
+ */
+#define DVA_GET_ASIZE(dva) \
+ BF64_GET_SB((dva)->dva_word[0], 0, 24, SPA_MINBLOCKSHIFT, 0)
+#define DVA_SET_ASIZE(dva, x) \
+ BF64_SET_SB((dva)->dva_word[0], 0, 24, SPA_MINBLOCKSHIFT, 0, x)
+
+#define DVA_GET_GRID(dva) BF64_GET((dva)->dva_word[0], 24, 8)
+#define DVA_SET_GRID(dva, x) BF64_SET((dva)->dva_word[0], 24, 8, x)
+
+#define DVA_GET_VDEV(dva) BF64_GET((dva)->dva_word[0], 32, 32)
+#define DVA_SET_VDEV(dva, x) BF64_SET((dva)->dva_word[0], 32, 32, x)
+
+#define DVA_GET_OFFSET(dva) \
+ BF64_GET_SB((dva)->dva_word[1], 0, 63, SPA_MINBLOCKSHIFT, 0)
+#define DVA_SET_OFFSET(dva, x) \
+ BF64_SET_SB((dva)->dva_word[1], 0, 63, SPA_MINBLOCKSHIFT, 0, x)
+
+#define DVA_GET_GANG(dva) BF64_GET((dva)->dva_word[1], 63, 1)
+#define DVA_SET_GANG(dva, x) BF64_SET((dva)->dva_word[1], 63, 1, x)
+
+#define BP_GET_LSIZE(bp) \
+ (BP_IS_HOLE(bp) ? 0 : \
+ BF64_GET_SB((bp)->blk_prop, 0, 16, SPA_MINBLOCKSHIFT, 1))
+#define BP_SET_LSIZE(bp, x) \
+ BF64_SET_SB((bp)->blk_prop, 0, 16, SPA_MINBLOCKSHIFT, 1, x)
+
+#define BP_GET_PSIZE(bp) \
+ BF64_GET_SB((bp)->blk_prop, 16, 16, SPA_MINBLOCKSHIFT, 1)
+#define BP_SET_PSIZE(bp, x) \
+ BF64_SET_SB((bp)->blk_prop, 16, 16, SPA_MINBLOCKSHIFT, 1, x)
+
+#define BP_GET_COMPRESS(bp) BF64_GET((bp)->blk_prop, 32, 8)
+#define BP_SET_COMPRESS(bp, x) BF64_SET((bp)->blk_prop, 32, 8, x)
+
+#define BP_GET_CHECKSUM(bp) BF64_GET((bp)->blk_prop, 40, 8)
+#define BP_SET_CHECKSUM(bp, x) BF64_SET((bp)->blk_prop, 40, 8, x)
+
+#define BP_GET_TYPE(bp) BF64_GET((bp)->blk_prop, 48, 8)
+#define BP_SET_TYPE(bp, x) BF64_SET((bp)->blk_prop, 48, 8, x)
+
+#define BP_GET_LEVEL(bp) BF64_GET((bp)->blk_prop, 56, 5)
+#define BP_SET_LEVEL(bp, x) BF64_SET((bp)->blk_prop, 56, 5, x)
+
+#define BP_GET_BYTEORDER(bp) (0 - BF64_GET((bp)->blk_prop, 63, 1))
+#define BP_SET_BYTEORDER(bp, x) BF64_SET((bp)->blk_prop, 63, 1, x)
+
+#define BP_GET_ASIZE(bp) \
+ (DVA_GET_ASIZE(&(bp)->blk_dva[0]) + DVA_GET_ASIZE(&(bp)->blk_dva[1]) + \
+ DVA_GET_ASIZE(&(bp)->blk_dva[2]))
+
+#define BP_GET_UCSIZE(bp) \
+ ((BP_GET_LEVEL(bp) > 0 || dmu_ot[BP_GET_TYPE(bp)].ot_metadata) ? \
+ BP_GET_PSIZE(bp) : BP_GET_LSIZE(bp));
+
+#define BP_GET_NDVAS(bp) \
+ (!!DVA_GET_ASIZE(&(bp)->blk_dva[0]) + \
+ !!DVA_GET_ASIZE(&(bp)->blk_dva[1]) + \
+ !!DVA_GET_ASIZE(&(bp)->blk_dva[2]))
+
+#define BP_COUNT_GANG(bp) \
+ (DVA_GET_GANG(&(bp)->blk_dva[0]) + \
+ DVA_GET_GANG(&(bp)->blk_dva[1]) + \
+ DVA_GET_GANG(&(bp)->blk_dva[2]))
+
+#define DVA_EQUAL(dva1, dva2) \
+ ((dva1)->dva_word[1] == (dva2)->dva_word[1] && \
+ (dva1)->dva_word[0] == (dva2)->dva_word[0])
+
+#define ZIO_CHECKSUM_EQUAL(zc1, zc2) \
+ (0 == (((zc1).zc_word[0] - (zc2).zc_word[0]) | \
+ ((zc1).zc_word[1] - (zc2).zc_word[1]) | \
+ ((zc1).zc_word[2] - (zc2).zc_word[2]) | \
+ ((zc1).zc_word[3] - (zc2).zc_word[3])))
+
+
+#define DVA_IS_VALID(dva) (DVA_GET_ASIZE(dva) != 0)
+
+#define ZIO_SET_CHECKSUM(zcp, w0, w1, w2, w3) \
+{ \
+ (zcp)->zc_word[0] = w0; \
+ (zcp)->zc_word[1] = w1; \
+ (zcp)->zc_word[2] = w2; \
+ (zcp)->zc_word[3] = w3; \
+}
+
+#define BP_IDENTITY(bp) (&(bp)->blk_dva[0])
+#define BP_IS_GANG(bp) DVA_GET_GANG(BP_IDENTITY(bp))
+#define BP_IS_HOLE(bp) ((bp)->blk_birth == 0)
+#define BP_IS_OLDER(bp, txg) (!BP_IS_HOLE(bp) && (bp)->blk_birth < (txg))
+
+#define BP_ZERO(bp) \
+{ \
+ (bp)->blk_dva[0].dva_word[0] = 0; \
+ (bp)->blk_dva[0].dva_word[1] = 0; \
+ (bp)->blk_dva[1].dva_word[0] = 0; \
+ (bp)->blk_dva[1].dva_word[1] = 0; \
+ (bp)->blk_dva[2].dva_word[0] = 0; \
+ (bp)->blk_dva[2].dva_word[1] = 0; \
+ (bp)->blk_prop = 0; \
+ (bp)->blk_pad[0] = 0; \
+ (bp)->blk_pad[1] = 0; \
+ (bp)->blk_pad[2] = 0; \
+ (bp)->blk_birth = 0; \
+ (bp)->blk_fill = 0; \
+ ZIO_SET_CHECKSUM(&(bp)->blk_cksum, 0, 0, 0, 0); \
+}
+
+#define ZBT_MAGIC 0x210da7ab10c7a11ULL /* zio data bloc tail */
+
+typedef struct zio_block_tail {
+ uint64_t zbt_magic; /* for validation, endianness */
+ zio_cksum_t zbt_cksum; /* 256-bit checksum */
+} zio_block_tail_t;
+
+#define VDEV_SKIP_SIZE (8 << 10)
+#define VDEV_BOOT_HEADER_SIZE (8 << 10)
+#define VDEV_PHYS_SIZE (112 << 10)
+#define VDEV_UBERBLOCK_RING (128 << 10)
+
+#define VDEV_UBERBLOCK_SHIFT(vd) \
+ MAX((vd)->vdev_top->vdev_ashift, UBERBLOCK_SHIFT)
+#define VDEV_UBERBLOCK_COUNT(vd) \
+ (VDEV_UBERBLOCK_RING >> VDEV_UBERBLOCK_SHIFT(vd))
+#define VDEV_UBERBLOCK_OFFSET(vd, n) \
+ offsetof(vdev_label_t, vl_uberblock[(n) << VDEV_UBERBLOCK_SHIFT(vd)])
+#define VDEV_UBERBLOCK_SIZE(vd) (1ULL << VDEV_UBERBLOCK_SHIFT(vd))
+
+/* ZFS boot block */
+#define VDEV_BOOT_MAGIC 0x2f5b007b10cULL
+#define VDEV_BOOT_VERSION 1 /* version number */
+
+typedef struct vdev_boot_header {
+ uint64_t vb_magic; /* VDEV_BOOT_MAGIC */
+ uint64_t vb_version; /* VDEV_BOOT_VERSION */
+ uint64_t vb_offset; /* start offset (bytes) */
+ uint64_t vb_size; /* size (bytes) */
+ char vb_pad[VDEV_BOOT_HEADER_SIZE - 4 * sizeof (uint64_t)];
+} vdev_boot_header_t;
+
+typedef struct vdev_phys {
+ char vp_nvlist[VDEV_PHYS_SIZE - sizeof (zio_block_tail_t)];
+ zio_block_tail_t vp_zbt;
+} vdev_phys_t;
+
+typedef struct vdev_label {
+ char vl_pad[VDEV_SKIP_SIZE]; /* 8K */
+ vdev_boot_header_t vl_boot_header; /* 8K */
+ vdev_phys_t vl_vdev_phys; /* 112K */
+ char vl_uberblock[VDEV_UBERBLOCK_RING]; /* 128K */
+} vdev_label_t; /* 256K total */
+
+/*
+ * vdev_dirty() flags
+ */
+#define VDD_METASLAB 0x01
+#define VDD_DTL 0x02
+
+/*
+ * Size and offset of embedded boot loader region on each label.
+ * The total size of the first two labels plus the boot area is 4MB.
+ */
+#define VDEV_BOOT_OFFSET (2 * sizeof (vdev_label_t))
+#define VDEV_BOOT_SIZE (7ULL << 19) /* 3.5M */
+
+/*
+ * Size of label regions at the start and end of each leaf device.
+ */
+#define VDEV_LABEL_START_SIZE (2 * sizeof (vdev_label_t) + VDEV_BOOT_SIZE)
+#define VDEV_LABEL_END_SIZE (2 * sizeof (vdev_label_t))
+#define VDEV_LABELS 4
+
+enum zio_checksum {
+ ZIO_CHECKSUM_INHERIT = 0,
+ ZIO_CHECKSUM_ON,
+ ZIO_CHECKSUM_OFF,
+ ZIO_CHECKSUM_LABEL,
+ ZIO_CHECKSUM_GANG_HEADER,
+ ZIO_CHECKSUM_ZILOG,
+ ZIO_CHECKSUM_FLETCHER_2,
+ ZIO_CHECKSUM_FLETCHER_4,
+ ZIO_CHECKSUM_SHA256,
+ ZIO_CHECKSUM_FUNCTIONS
+};
+
+#define ZIO_CHECKSUM_ON_VALUE ZIO_CHECKSUM_FLETCHER_2
+#define ZIO_CHECKSUM_DEFAULT ZIO_CHECKSUM_ON
+
+enum zio_compress {
+ ZIO_COMPRESS_INHERIT = 0,
+ ZIO_COMPRESS_ON,
+ ZIO_COMPRESS_OFF,
+ ZIO_COMPRESS_LZJB,
+ ZIO_COMPRESS_EMPTY,
+ ZIO_COMPRESS_GZIP_1,
+ ZIO_COMPRESS_GZIP_2,
+ ZIO_COMPRESS_GZIP_3,
+ ZIO_COMPRESS_GZIP_4,
+ ZIO_COMPRESS_GZIP_5,
+ ZIO_COMPRESS_GZIP_6,
+ ZIO_COMPRESS_GZIP_7,
+ ZIO_COMPRESS_GZIP_8,
+ ZIO_COMPRESS_GZIP_9,
+ ZIO_COMPRESS_FUNCTIONS
+};
+
+#define ZIO_COMPRESS_ON_VALUE ZIO_COMPRESS_LZJB
+#define ZIO_COMPRESS_DEFAULT ZIO_COMPRESS_OFF
+
+/* nvlist pack encoding */
+#define NV_ENCODE_NATIVE 0
+#define NV_ENCODE_XDR 1
+
+typedef enum {
+ DATA_TYPE_UNKNOWN = 0,
+ DATA_TYPE_BOOLEAN,
+ DATA_TYPE_BYTE,
+ DATA_TYPE_INT16,
+ DATA_TYPE_UINT16,
+ DATA_TYPE_INT32,
+ DATA_TYPE_UINT32,
+ DATA_TYPE_INT64,
+ DATA_TYPE_UINT64,
+ DATA_TYPE_STRING,
+ DATA_TYPE_BYTE_ARRAY,
+ DATA_TYPE_INT16_ARRAY,
+ DATA_TYPE_UINT16_ARRAY,
+ DATA_TYPE_INT32_ARRAY,
+ DATA_TYPE_UINT32_ARRAY,
+ DATA_TYPE_INT64_ARRAY,
+ DATA_TYPE_UINT64_ARRAY,
+ DATA_TYPE_STRING_ARRAY,
+ DATA_TYPE_HRTIME,
+ DATA_TYPE_NVLIST,
+ DATA_TYPE_NVLIST_ARRAY,
+ DATA_TYPE_BOOLEAN_VALUE,
+ DATA_TYPE_INT8,
+ DATA_TYPE_UINT8,
+ DATA_TYPE_BOOLEAN_ARRAY,
+ DATA_TYPE_INT8_ARRAY,
+ DATA_TYPE_UINT8_ARRAY
+} data_type_t;
+
+/*
+ * On-disk version number.
+ */
+#define ZFS_VERSION_1 1ULL
+#define ZFS_VERSION_2 2ULL
+#define ZFS_VERSION_3 3ULL
+#define ZFS_VERSION_4 4ULL
+#define ZFS_VERSION_5 5ULL
+#define ZFS_VERSION_6 6ULL
+/*
+ * When bumping up ZFS_VERSION, make sure GRUB ZFS understand the on-disk
+ * format change. Go to usr/src/grub/grub-0.95/stage2/{zfs-include/, fsys_zfs*},
+ * and do the appropriate changes.
+ */
+#define ZFS_VERSION ZFS_VERSION_6
+#define ZFS_VERSION_STRING "6"
+
+/*
+ * Symbolic names for the changes that caused a ZFS_VERSION switch.
+ * Used in the code when checking for presence or absence of a feature.
+ * Feel free to define multiple symbolic names for each version if there
+ * were multiple changes to on-disk structures during that version.
+ *
+ * NOTE: When checking the current ZFS_VERSION in your code, be sure
+ * to use spa_version() since it reports the version of the
+ * last synced uberblock. Checking the in-flight version can
+ * be dangerous in some cases.
+ */
+#define ZFS_VERSION_INITIAL ZFS_VERSION_1
+#define ZFS_VERSION_DITTO_BLOCKS ZFS_VERSION_2
+#define ZFS_VERSION_SPARES ZFS_VERSION_3
+#define ZFS_VERSION_RAID6 ZFS_VERSION_3
+#define ZFS_VERSION_BPLIST_ACCOUNT ZFS_VERSION_3
+#define ZFS_VERSION_RAIDZ_DEFLATE ZFS_VERSION_3
+#define ZFS_VERSION_DNODE_BYTES ZFS_VERSION_3
+#define ZFS_VERSION_ZPOOL_HISTORY ZFS_VERSION_4
+#define ZFS_VERSION_GZIP_COMPRESSION ZFS_VERSION_5
+#define ZFS_VERSION_BOOTFS ZFS_VERSION_6
+
+/*
+ * The following are configuration names used in the nvlist describing a pool's
+ * configuration.
+ */
+#define ZPOOL_CONFIG_VERSION "version"
+#define ZPOOL_CONFIG_POOL_NAME "name"
+#define ZPOOL_CONFIG_POOL_STATE "state"
+#define ZPOOL_CONFIG_POOL_TXG "txg"
+#define ZPOOL_CONFIG_POOL_GUID "pool_guid"
+#define ZPOOL_CONFIG_CREATE_TXG "create_txg"
+#define ZPOOL_CONFIG_TOP_GUID "top_guid"
+#define ZPOOL_CONFIG_VDEV_TREE "vdev_tree"
+#define ZPOOL_CONFIG_TYPE "type"
+#define ZPOOL_CONFIG_CHILDREN "children"
+#define ZPOOL_CONFIG_ID "id"
+#define ZPOOL_CONFIG_GUID "guid"
+#define ZPOOL_CONFIG_PATH "path"
+#define ZPOOL_CONFIG_DEVID "devid"
+#define ZPOOL_CONFIG_METASLAB_ARRAY "metaslab_array"
+#define ZPOOL_CONFIG_METASLAB_SHIFT "metaslab_shift"
+#define ZPOOL_CONFIG_ASHIFT "ashift"
+#define ZPOOL_CONFIG_ASIZE "asize"
+#define ZPOOL_CONFIG_DTL "DTL"
+#define ZPOOL_CONFIG_STATS "stats"
+#define ZPOOL_CONFIG_WHOLE_DISK "whole_disk"
+#define ZPOOL_CONFIG_OFFLINE "offline"
+#define ZPOOL_CONFIG_ERRCOUNT "error_count"
+#define ZPOOL_CONFIG_NOT_PRESENT "not_present"
+#define ZPOOL_CONFIG_SPARES "spares"
+#define ZPOOL_CONFIG_IS_SPARE "is_spare"
+#define ZPOOL_CONFIG_NPARITY "nparity"
+#define ZPOOL_CONFIG_HOSTID "hostid"
+#define ZPOOL_CONFIG_HOSTNAME "hostname"
+#define ZPOOL_CONFIG_TIMESTAMP "timestamp" /* not stored on disk */
+
+#define VDEV_TYPE_ROOT "root"
+#define VDEV_TYPE_MIRROR "mirror"
+#define VDEV_TYPE_REPLACING "replacing"
+#define VDEV_TYPE_RAIDZ "raidz"
+#define VDEV_TYPE_DISK "disk"
+#define VDEV_TYPE_FILE "file"
+#define VDEV_TYPE_MISSING "missing"
+#define VDEV_TYPE_SPARE "spare"
+
+/*
+ * This is needed in userland to report the minimum necessary device size.
+ */
+#define SPA_MINDEVSIZE (64ULL << 20)
+
+/*
+ * The location of the pool configuration repository, shared between kernel and
+ * userland.
+ */
+#define ZPOOL_CACHE_DIR "/boot/zfs"
+#define ZPOOL_CACHE_FILE "zpool.cache"
+#define ZPOOL_CACHE_TMP ".zpool.cache"
+
+#define ZPOOL_CACHE ZPOOL_CACHE_DIR "/" ZPOOL_CACHE_FILE
+
+/*
+ * vdev states are ordered from least to most healthy.
+ * A vdev that's CANT_OPEN or below is considered unusable.
+ */
+typedef enum vdev_state {
+ VDEV_STATE_UNKNOWN = 0, /* Uninitialized vdev */
+ VDEV_STATE_CLOSED, /* Not currently open */
+ VDEV_STATE_OFFLINE, /* Not allowed to open */
+ VDEV_STATE_CANT_OPEN, /* Tried to open, but failed */
+ VDEV_STATE_DEGRADED, /* Replicated vdev with unhealthy kids */
+ VDEV_STATE_HEALTHY /* Presumed good */
+} vdev_state_t;
+
+/*
+ * vdev aux states. When a vdev is in the CANT_OPEN state, the aux field
+ * of the vdev stats structure uses these constants to distinguish why.
+ */
+typedef enum vdev_aux {
+ VDEV_AUX_NONE, /* no error */
+ VDEV_AUX_OPEN_FAILED, /* ldi_open_*() or vn_open() failed */
+ VDEV_AUX_CORRUPT_DATA, /* bad label or disk contents */
+ VDEV_AUX_NO_REPLICAS, /* insufficient number of replicas */
+ VDEV_AUX_BAD_GUID_SUM, /* vdev guid sum doesn't match */
+ VDEV_AUX_TOO_SMALL, /* vdev size is too small */
+ VDEV_AUX_BAD_LABEL, /* the label is OK but invalid */
+ VDEV_AUX_VERSION_NEWER, /* on-disk version is too new */
+ VDEV_AUX_VERSION_OLDER, /* on-disk version is too old */
+ VDEV_AUX_SPARED /* hot spare used in another pool */
+} vdev_aux_t;
+
+/*
+ * pool state. The following states are written to disk as part of the normal
+ * SPA lifecycle: ACTIVE, EXPORTED, DESTROYED, SPARE. The remaining states are
+ * software abstractions used at various levels to communicate pool state.
+ */
+typedef enum pool_state {
+ POOL_STATE_ACTIVE = 0, /* In active use */
+ POOL_STATE_EXPORTED, /* Explicitly exported */
+ POOL_STATE_DESTROYED, /* Explicitly destroyed */
+ POOL_STATE_SPARE, /* Reserved for hot spare use */
+ POOL_STATE_UNINITIALIZED, /* Internal spa_t state */
+ POOL_STATE_UNAVAIL, /* Internal libzfs state */
+ POOL_STATE_POTENTIALLY_ACTIVE /* Internal libzfs state */
+} pool_state_t;
+
+/*
+ * The uberblock version is incremented whenever an incompatible on-disk
+ * format change is made to the SPA, DMU, or ZAP.
+ *
+ * Note: the first two fields should never be moved. When a storage pool
+ * is opened, the uberblock must be read off the disk before the version
+ * can be checked. If the ub_version field is moved, we may not detect
+ * version mismatch. If the ub_magic field is moved, applications that
+ * expect the magic number in the first word won't work.
+ */
+#define UBERBLOCK_MAGIC 0x00bab10c /* oo-ba-bloc! */
+#define UBERBLOCK_SHIFT 10 /* up to 1K */
+
+struct uberblock {
+ uint64_t ub_magic; /* UBERBLOCK_MAGIC */
+ uint64_t ub_version; /* ZFS_VERSION */
+ uint64_t ub_txg; /* txg of last sync */
+ uint64_t ub_guid_sum; /* sum of all vdev guids */
+ uint64_t ub_timestamp; /* UTC time of last sync */
+ blkptr_t ub_rootbp; /* MOS objset_phys_t */
+};
+
+/*
+ * Flags.
+ */
+#define DNODE_MUST_BE_ALLOCATED 1
+#define DNODE_MUST_BE_FREE 2
+
+/*
+ * Fixed constants.
+ */
+#define DNODE_SHIFT 9 /* 512 bytes */
+#define DN_MIN_INDBLKSHIFT 10 /* 1k */
+#define DN_MAX_INDBLKSHIFT 14 /* 16k */
+#define DNODE_BLOCK_SHIFT 14 /* 16k */
+#define DNODE_CORE_SIZE 64 /* 64 bytes for dnode sans blkptrs */
+#define DN_MAX_OBJECT_SHIFT 48 /* 256 trillion (zfs_fid_t limit) */
+#define DN_MAX_OFFSET_SHIFT 64 /* 2^64 bytes in a dnode */
+
+/*
+ * Derived constants.
+ */
+#define DNODE_SIZE (1 << DNODE_SHIFT)
+#define DN_MAX_NBLKPTR ((DNODE_SIZE - DNODE_CORE_SIZE) >> SPA_BLKPTRSHIFT)
+#define DN_MAX_BONUSLEN (DNODE_SIZE - DNODE_CORE_SIZE - (1 << SPA_BLKPTRSHIFT))
+#define DN_MAX_OBJECT (1ULL << DN_MAX_OBJECT_SHIFT)
+
+#define DNODES_PER_BLOCK_SHIFT (DNODE_BLOCK_SHIFT - DNODE_SHIFT)
+#define DNODES_PER_BLOCK (1ULL << DNODES_PER_BLOCK_SHIFT)
+#define DNODES_PER_LEVEL_SHIFT (DN_MAX_INDBLKSHIFT - SPA_BLKPTRSHIFT)
+
+/* The +2 here is a cheesy way to round up */
+#define DN_MAX_LEVELS (2 + ((DN_MAX_OFFSET_SHIFT - SPA_MINBLOCKSHIFT) / \
+ (DN_MIN_INDBLKSHIFT - SPA_BLKPTRSHIFT)))
+
+#define DN_BONUS(dnp) ((void*)((dnp)->dn_bonus + \
+ (((dnp)->dn_nblkptr - 1) * sizeof (blkptr_t))))
+
+#define DN_USED_BYTES(dnp) (((dnp)->dn_flags & DNODE_FLAG_USED_BYTES) ? \
+ (dnp)->dn_used : (dnp)->dn_used << SPA_MINBLOCKSHIFT)
+
+#define EPB(blkshift, typeshift) (1 << (blkshift - typeshift))
+
+/* Is dn_used in bytes? if not, it's in multiples of SPA_MINBLOCKSIZE */
+#define DNODE_FLAG_USED_BYTES (1<<0)
+
+typedef struct dnode_phys {
+ uint8_t dn_type; /* dmu_object_type_t */
+ uint8_t dn_indblkshift; /* ln2(indirect block size) */
+ uint8_t dn_nlevels; /* 1=dn_blkptr->data blocks */
+ uint8_t dn_nblkptr; /* length of dn_blkptr */
+ uint8_t dn_bonustype; /* type of data in bonus buffer */
+ uint8_t dn_checksum; /* ZIO_CHECKSUM type */
+ uint8_t dn_compress; /* ZIO_COMPRESS type */
+ uint8_t dn_flags; /* DNODE_FLAG_* */
+ uint16_t dn_datablkszsec; /* data block size in 512b sectors */
+ uint16_t dn_bonuslen; /* length of dn_bonus */
+ uint8_t dn_pad2[4];
+
+ /* accounting is protected by dn_dirty_mtx */
+ uint64_t dn_maxblkid; /* largest allocated block ID */
+ uint64_t dn_used; /* bytes (or sectors) of disk space */
+
+ uint64_t dn_pad3[4];
+
+ blkptr_t dn_blkptr[1];
+ uint8_t dn_bonus[DN_MAX_BONUSLEN];
+} dnode_phys_t;
+
+typedef enum dmu_object_type {
+ DMU_OT_NONE,
+ /* general: */
+ DMU_OT_OBJECT_DIRECTORY, /* ZAP */
+ DMU_OT_OBJECT_ARRAY, /* UINT64 */
+ DMU_OT_PACKED_NVLIST, /* UINT8 (XDR by nvlist_pack/unpack) */
+ DMU_OT_PACKED_NVLIST_SIZE, /* UINT64 */
+ DMU_OT_BPLIST, /* UINT64 */
+ DMU_OT_BPLIST_HDR, /* UINT64 */
+ /* spa: */
+ DMU_OT_SPACE_MAP_HEADER, /* UINT64 */
+ DMU_OT_SPACE_MAP, /* UINT64 */
+ /* zil: */
+ DMU_OT_INTENT_LOG, /* UINT64 */
+ /* dmu: */
+ DMU_OT_DNODE, /* DNODE */
+ DMU_OT_OBJSET, /* OBJSET */
+ /* dsl: */
+ DMU_OT_DSL_DIR, /* UINT64 */
+ DMU_OT_DSL_DIR_CHILD_MAP, /* ZAP */
+ DMU_OT_DSL_DS_SNAP_MAP, /* ZAP */
+ DMU_OT_DSL_PROPS, /* ZAP */
+ DMU_OT_DSL_DATASET, /* UINT64 */
+ /* zpl: */
+ DMU_OT_ZNODE, /* ZNODE */
+ DMU_OT_ACL, /* ACL */
+ DMU_OT_PLAIN_FILE_CONTENTS, /* UINT8 */
+ DMU_OT_DIRECTORY_CONTENTS, /* ZAP */
+ DMU_OT_MASTER_NODE, /* ZAP */
+ DMU_OT_UNLINKED_SET, /* ZAP */
+ /* zvol: */
+ DMU_OT_ZVOL, /* UINT8 */
+ DMU_OT_ZVOL_PROP, /* ZAP */
+ /* other; for testing only! */
+ DMU_OT_PLAIN_OTHER, /* UINT8 */
+ DMU_OT_UINT64_OTHER, /* UINT64 */
+ DMU_OT_ZAP_OTHER, /* ZAP */
+ /* new object types: */
+ DMU_OT_ERROR_LOG, /* ZAP */
+ DMU_OT_SPA_HISTORY, /* UINT8 */
+ DMU_OT_SPA_HISTORY_OFFSETS, /* spa_his_phys_t */
+ DMU_OT_POOL_PROPS, /* ZAP */
+
+ DMU_OT_NUMTYPES
+} dmu_object_type_t;
+
+typedef enum dmu_objset_type {
+ DMU_OST_NONE,
+ DMU_OST_META,
+ DMU_OST_ZFS,
+ DMU_OST_ZVOL,
+ DMU_OST_OTHER, /* For testing only! */
+ DMU_OST_ANY, /* Be careful! */
+ DMU_OST_NUMTYPES
+} dmu_objset_type_t;
+
+/*
+ * Intent log header - this on disk structure holds fields to manage
+ * the log. All fields are 64 bit to easily handle cross architectures.
+ */
+typedef struct zil_header {
+ uint64_t zh_claim_txg; /* txg in which log blocks were claimed */
+ uint64_t zh_replay_seq; /* highest replayed sequence number */
+ blkptr_t zh_log; /* log chain */
+ uint64_t zh_claim_seq; /* highest claimed sequence number */
+ uint64_t zh_pad[5];
+} zil_header_t;
+
+typedef struct objset_phys {
+ dnode_phys_t os_meta_dnode;
+ zil_header_t os_zil_header;
+ uint64_t os_type;
+ char os_pad[1024 - sizeof (dnode_phys_t) - sizeof (zil_header_t) -
+ sizeof (uint64_t)];
+} objset_phys_t;
+
+typedef struct dsl_dir_phys {
+ uint64_t dd_creation_time; /* not actually used */
+ uint64_t dd_head_dataset_obj;
+ uint64_t dd_parent_obj;
+ uint64_t dd_clone_parent_obj;
+ uint64_t dd_child_dir_zapobj;
+ /*
+ * how much space our children are accounting for; for leaf
+ * datasets, == physical space used by fs + snaps
+ */
+ uint64_t dd_used_bytes;
+ uint64_t dd_compressed_bytes;
+ uint64_t dd_uncompressed_bytes;
+ /* Administrative quota setting */
+ uint64_t dd_quota;
+ /* Administrative reservation setting */
+ uint64_t dd_reserved;
+ uint64_t dd_props_zapobj;
+ uint64_t dd_pad[21]; /* pad out to 256 bytes for good measure */
+} dsl_dir_phys_t;
+
+typedef struct dsl_dataset_phys {
+ uint64_t ds_dir_obj;
+ uint64_t ds_prev_snap_obj;
+ uint64_t ds_prev_snap_txg;
+ uint64_t ds_next_snap_obj;
+ uint64_t ds_snapnames_zapobj; /* zap obj of snaps; ==0 for snaps */
+ uint64_t ds_num_children; /* clone/snap children; ==0 for head */
+ uint64_t ds_creation_time; /* seconds since 1970 */
+ uint64_t ds_creation_txg;
+ uint64_t ds_deadlist_obj;
+ uint64_t ds_used_bytes;
+ uint64_t ds_compressed_bytes;
+ uint64_t ds_uncompressed_bytes;
+ uint64_t ds_unique_bytes; /* only relevant to snapshots */
+ /*
+ * The ds_fsid_guid is a 56-bit ID that can change to avoid
+ * collisions. The ds_guid is a 64-bit ID that will never
+ * change, so there is a small probability that it will collide.
+ */
+ uint64_t ds_fsid_guid;
+ uint64_t ds_guid;
+ uint64_t ds_flags;
+ blkptr_t ds_bp;
+ uint64_t ds_pad[8]; /* pad out to 320 bytes for good measure */
+} dsl_dataset_phys_t;
+
+/*
+ * The names of zap entries in the DIRECTORY_OBJECT of the MOS.
+ */
+#define DMU_POOL_DIRECTORY_OBJECT 1
+#define DMU_POOL_CONFIG "config"
+#define DMU_POOL_ROOT_DATASET "root_dataset"
+#define DMU_POOL_SYNC_BPLIST "sync_bplist"
+#define DMU_POOL_ERRLOG_SCRUB "errlog_scrub"
+#define DMU_POOL_ERRLOG_LAST "errlog_last"
+#define DMU_POOL_SPARES "spares"
+#define DMU_POOL_DEFLATE "deflate"
+#define DMU_POOL_HISTORY "history"
+#define DMU_POOL_PROPS "pool_props"
+
+#define ZAP_MAGIC 0x2F52AB2ABULL
+
+#define FZAP_BLOCK_SHIFT(zap) ((zap)->zap_block_shift)
+
+#define ZAP_MAXCD (uint32_t)(-1)
+#define ZAP_HASHBITS 28
+#define MZAP_ENT_LEN 64
+#define MZAP_NAME_LEN (MZAP_ENT_LEN - 8 - 4 - 2)
+#define MZAP_MAX_BLKSHIFT SPA_MAXBLOCKSHIFT
+#define MZAP_MAX_BLKSZ (1 << MZAP_MAX_BLKSHIFT)
+
+typedef struct mzap_ent_phys {
+ uint64_t mze_value;
+ uint32_t mze_cd;
+ uint16_t mze_pad; /* in case we want to chain them someday */
+ char mze_name[MZAP_NAME_LEN];
+} mzap_ent_phys_t;
+
+typedef struct mzap_phys {
+ uint64_t mz_block_type; /* ZBT_MICRO */
+ uint64_t mz_salt;
+ uint64_t mz_pad[6];
+ mzap_ent_phys_t mz_chunk[1];
+ /* actually variable size depending on block size */
+} mzap_phys_t;
+
+/*
+ * The (fat) zap is stored in one object. It is an array of
+ * 1<<FZAP_BLOCK_SHIFT byte blocks. The layout looks like one of:
+ *
+ * ptrtbl fits in first block:
+ * [zap_phys_t zap_ptrtbl_shift < 6] [zap_leaf_t] ...
+ *
+ * ptrtbl too big for first block:
+ * [zap_phys_t zap_ptrtbl_shift >= 6] [zap_leaf_t] [ptrtbl] ...
+ *
+ */
+
+#define ZBT_LEAF ((1ULL << 63) + 0)
+#define ZBT_HEADER ((1ULL << 63) + 1)
+#define ZBT_MICRO ((1ULL << 63) + 3)
+/* any other values are ptrtbl blocks */
+
+/*
+ * the embedded pointer table takes up half a block:
+ * block size / entry size (2^3) / 2
+ */
+#define ZAP_EMBEDDED_PTRTBL_SHIFT(zap) (FZAP_BLOCK_SHIFT(zap) - 3 - 1)
+
+/*
+ * The embedded pointer table starts half-way through the block. Since
+ * the pointer table itself is half the block, it starts at (64-bit)
+ * word number (1<<ZAP_EMBEDDED_PTRTBL_SHIFT(zap)).
+ */
+#define ZAP_EMBEDDED_PTRTBL_ENT(zap, idx) \
+ ((uint64_t *)(zap)->zap_phys) \
+ [(idx) + (1<<ZAP_EMBEDDED_PTRTBL_SHIFT(zap))]
+
+/*
+ * TAKE NOTE:
+ * If zap_phys_t is modified, zap_byteswap() must be modified.
+ */
+typedef struct zap_phys {
+ uint64_t zap_block_type; /* ZBT_HEADER */
+ uint64_t zap_magic; /* ZAP_MAGIC */
+
+ struct zap_table_phys {
+ uint64_t zt_blk; /* starting block number */
+ uint64_t zt_numblks; /* number of blocks */
+ uint64_t zt_shift; /* bits to index it */
+ uint64_t zt_nextblk; /* next (larger) copy start block */
+ uint64_t zt_blks_copied; /* number source blocks copied */
+ } zap_ptrtbl;
+
+ uint64_t zap_freeblk; /* the next free block */
+ uint64_t zap_num_leafs; /* number of leafs */
+ uint64_t zap_num_entries; /* number of entries */
+ uint64_t zap_salt; /* salt to stir into hash function */
+ /*
+ * This structure is followed by padding, and then the embedded
+ * pointer table. The embedded pointer table takes up second
+ * half of the block. It is accessed using the
+ * ZAP_EMBEDDED_PTRTBL_ENT() macro.
+ */
+} zap_phys_t;
+
+typedef struct zap_table_phys zap_table_phys_t;
+
+typedef struct fat_zap {
+ int zap_block_shift; /* block size shift */
+ zap_phys_t *zap_phys;
+} fat_zap_t;
+
+#define ZAP_LEAF_MAGIC 0x2AB1EAF
+
+/* chunk size = 24 bytes */
+#define ZAP_LEAF_CHUNKSIZE 24
+
+/*
+ * The amount of space available for chunks is:
+ * block size (1<<l->l_bs) - hash entry size (2) * number of hash
+ * entries - header space (2*chunksize)
+ */
+#define ZAP_LEAF_NUMCHUNKS(l) \
+ (((1<<(l)->l_bs) - 2*ZAP_LEAF_HASH_NUMENTRIES(l)) / \
+ ZAP_LEAF_CHUNKSIZE - 2)
+
+/*
+ * The amount of space within the chunk available for the array is:
+ * chunk size - space for type (1) - space for next pointer (2)
+ */
+#define ZAP_LEAF_ARRAY_BYTES (ZAP_LEAF_CHUNKSIZE - 3)
+
+#define ZAP_LEAF_ARRAY_NCHUNKS(bytes) \
+ (((bytes)+ZAP_LEAF_ARRAY_BYTES-1)/ZAP_LEAF_ARRAY_BYTES)
+
+/*
+ * Low water mark: when there are only this many chunks free, start
+ * growing the ptrtbl. Ideally, this should be larger than a
+ * "reasonably-sized" entry. 20 chunks is more than enough for the
+ * largest directory entry (MAXNAMELEN (256) byte name, 8-byte value),
+ * while still being only around 3% for 16k blocks.
+ */
+#define ZAP_LEAF_LOW_WATER (20)
+
+/*
+ * The leaf hash table has block size / 2^5 (32) number of entries,
+ * which should be more than enough for the maximum number of entries,
+ * which is less than block size / CHUNKSIZE (24) / minimum number of
+ * chunks per entry (3).
+ */
+#define ZAP_LEAF_HASH_SHIFT(l) ((l)->l_bs - 5)
+#define ZAP_LEAF_HASH_NUMENTRIES(l) (1 << ZAP_LEAF_HASH_SHIFT(l))
+
+/*
+ * The chunks start immediately after the hash table. The end of the
+ * hash table is at l_hash + HASH_NUMENTRIES, which we simply cast to a
+ * chunk_t.
+ */
+#define ZAP_LEAF_CHUNK(l, idx) \
+ ((zap_leaf_chunk_t *) \
+ ((l)->l_phys->l_hash + ZAP_LEAF_HASH_NUMENTRIES(l)))[idx]
+#define ZAP_LEAF_ENTRY(l, idx) (&ZAP_LEAF_CHUNK(l, idx).l_entry)
+
+typedef enum zap_chunk_type {
+ ZAP_CHUNK_FREE = 253,
+ ZAP_CHUNK_ENTRY = 252,
+ ZAP_CHUNK_ARRAY = 251,
+ ZAP_CHUNK_TYPE_MAX = 250
+} zap_chunk_type_t;
+
+/*
+ * TAKE NOTE:
+ * If zap_leaf_phys_t is modified, zap_leaf_byteswap() must be modified.
+ */
+typedef struct zap_leaf_phys {
+ struct zap_leaf_header {
+ uint64_t lh_block_type; /* ZBT_LEAF */
+ uint64_t lh_pad1;
+ uint64_t lh_prefix; /* hash prefix of this leaf */
+ uint32_t lh_magic; /* ZAP_LEAF_MAGIC */
+ uint16_t lh_nfree; /* number free chunks */
+ uint16_t lh_nentries; /* number of entries */
+ uint16_t lh_prefix_len; /* num bits used to id this */
+
+/* above is accessable to zap, below is zap_leaf private */
+
+ uint16_t lh_freelist; /* chunk head of free list */
+ uint8_t lh_pad2[12];
+ } l_hdr; /* 2 24-byte chunks */
+
+ /*
+ * The header is followed by a hash table with
+ * ZAP_LEAF_HASH_NUMENTRIES(zap) entries. The hash table is
+ * followed by an array of ZAP_LEAF_NUMCHUNKS(zap)
+ * zap_leaf_chunk structures. These structures are accessed
+ * with the ZAP_LEAF_CHUNK() macro.
+ */
+
+ uint16_t l_hash[1];
+} zap_leaf_phys_t;
+
+typedef union zap_leaf_chunk {
+ struct zap_leaf_entry {
+ uint8_t le_type; /* always ZAP_CHUNK_ENTRY */
+ uint8_t le_int_size; /* size of ints */
+ uint16_t le_next; /* next entry in hash chain */
+ uint16_t le_name_chunk; /* first chunk of the name */
+ uint16_t le_name_length; /* bytes in name, incl null */
+ uint16_t le_value_chunk; /* first chunk of the value */
+ uint16_t le_value_length; /* value length in ints */
+ uint32_t le_cd; /* collision differentiator */
+ uint64_t le_hash; /* hash value of the name */
+ } l_entry;
+ struct zap_leaf_array {
+ uint8_t la_type; /* always ZAP_CHUNK_ARRAY */
+ uint8_t la_array[ZAP_LEAF_ARRAY_BYTES];
+ uint16_t la_next; /* next blk or CHAIN_END */
+ } l_array;
+ struct zap_leaf_free {
+ uint8_t lf_type; /* always ZAP_CHUNK_FREE */
+ uint8_t lf_pad[ZAP_LEAF_ARRAY_BYTES];
+ uint16_t lf_next; /* next in free list, or CHAIN_END */
+ } l_free;
+} zap_leaf_chunk_t;
+
+typedef struct zap_leaf {
+ int l_bs; /* block size shift */
+ zap_leaf_phys_t *l_phys;
+} zap_leaf_t;
+
+/*
+ * Define special zfs pflags
+ */
+#define ZFS_XATTR 0x1 /* is an extended attribute */
+#define ZFS_INHERIT_ACE 0x2 /* ace has inheritable ACEs */
+#define ZFS_ACL_TRIVIAL 0x4 /* files ACL is trivial */
+
+#define MASTER_NODE_OBJ 1
+
+/*
+ * special attributes for master node.
+ */
+
+#define ZFS_FSID "FSID"
+#define ZFS_UNLINKED_SET "DELETE_QUEUE"
+#define ZFS_ROOT_OBJ "ROOT"
+#define ZPL_VERSION_OBJ "VERSION"
+#define ZFS_PROP_BLOCKPERPAGE "BLOCKPERPAGE"
+#define ZFS_PROP_NOGROWBLOCKS "NOGROWBLOCKS"
+
+#define ZFS_FLAG_BLOCKPERPAGE 0x1
+#define ZFS_FLAG_NOGROWBLOCKS 0x2
+
+/*
+ * ZPL version - rev'd whenever an incompatible on-disk format change
+ * occurs. Independent of SPA/DMU/ZAP versioning.
+ */
+
+#define ZPL_VERSION 1ULL
+
+/*
+ * The directory entry has the type (currently unused on Solaris) in the
+ * top 4 bits, and the object number in the low 48 bits. The "middle"
+ * 12 bits are unused.
+ */
+#define ZFS_DIRENT_TYPE(de) BF64_GET(de, 60, 4)
+#define ZFS_DIRENT_OBJ(de) BF64_GET(de, 0, 48)
+#define ZFS_DIRENT_MAKE(type, obj) (((uint64_t)type << 60) | obj)
+
+typedef struct ace {
+ uid_t a_who; /* uid or gid */
+ uint32_t a_access_mask; /* read,write,... */
+ uint16_t a_flags; /* see below */
+ uint16_t a_type; /* allow or deny */
+} ace_t;
+
+#define ACE_SLOT_CNT 6
+
+typedef struct zfs_znode_acl {
+ uint64_t z_acl_extern_obj; /* ext acl pieces */
+ uint32_t z_acl_count; /* Number of ACEs */
+ uint16_t z_acl_version; /* acl version */
+ uint16_t z_acl_pad; /* pad */
+ ace_t z_ace_data[ACE_SLOT_CNT]; /* 6 standard ACEs */
+} zfs_znode_acl_t;
+
+/*
+ * This is the persistent portion of the znode. It is stored
+ * in the "bonus buffer" of the file. Short symbolic links
+ * are also stored in the bonus buffer.
+ */
+typedef struct znode_phys {
+ uint64_t zp_atime[2]; /* 0 - last file access time */
+ uint64_t zp_mtime[2]; /* 16 - last file modification time */
+ uint64_t zp_ctime[2]; /* 32 - last file change time */
+ uint64_t zp_crtime[2]; /* 48 - creation time */
+ uint64_t zp_gen; /* 64 - generation (txg of creation) */
+ uint64_t zp_mode; /* 72 - file mode bits */
+ uint64_t zp_size; /* 80 - size of file */
+ uint64_t zp_parent; /* 88 - directory parent (`..') */
+ uint64_t zp_links; /* 96 - number of links to file */
+ uint64_t zp_xattr; /* 104 - DMU object for xattrs */
+ uint64_t zp_rdev; /* 112 - dev_t for VBLK & VCHR files */
+ uint64_t zp_flags; /* 120 - persistent flags */
+ uint64_t zp_uid; /* 128 - file owner */
+ uint64_t zp_gid; /* 136 - owning group */
+ uint64_t zp_pad[4]; /* 144 - future */
+ zfs_znode_acl_t zp_acl; /* 176 - 263 ACL */
+ /*
+ * Data may pad out any remaining bytes in the znode buffer, eg:
+ *
+ * |<---------------------- dnode_phys (512) ------------------------>|
+ * |<-- dnode (192) --->|<----------- "bonus" buffer (320) ---------->|
+ * |<---- znode (264) ---->|<---- data (56) ---->|
+ *
+ * At present, we only use this space to store symbolic links.
+ */
+} znode_phys_t;
+
+/*
+ * In-core vdev representation.
+ */
+struct vdev;
+typedef int vdev_read_t(struct vdev *vdev, void *priv, off_t offset, void *buf, size_t bytes);
+
+typedef STAILQ_HEAD(vdev_list, vdev) vdev_list_t;
+
+typedef struct vdev {
+ STAILQ_ENTRY(vdev) v_childlink; /* link in parent's child list */
+ STAILQ_ENTRY(vdev) v_alllink; /* link in global vdev list */
+ vdev_list_t v_children; /* children of this vdev */
+ char *v_name; /* vdev name */
+ uint64_t v_guid; /* vdev guid */
+ int v_id; /* index in parent */
+ vdev_state_t v_state; /* current state */
+ vdev_read_t *v_read; /* function to read from this vdev */
+ void *v_read_priv; /* private data for read function */
+} vdev_t;
+
+/*
+ * In-core pool representation.
+ */
+typedef STAILQ_HEAD(spa_list, spa) spa_list_t;
+
+typedef struct spa {
+ STAILQ_ENTRY(spa) spa_link; /* link in global pool list */
+ char *spa_name; /* pool name */
+ uint64_t spa_guid; /* pool guid */
+ uint64_t spa_txg; /* most recent transaction */
+ struct uberblock spa_uberblock; /* best uberblock so far */
+ vdev_list_t spa_vdevs; /* list of all toplevel vdevs */
+ objset_phys_t spa_mos; /* MOS for this pool */
+ objset_phys_t spa_root_objset; /* current mounted ZPL objset */
+} spa_t;
diff --git a/sys/cddl/boot/zfs/zfssubr.c b/sys/cddl/boot/zfs/zfssubr.c
new file mode 100644
index 0000000..1c859c0
--- /dev/null
+++ b/sys/cddl/boot/zfs/zfssubr.c
@@ -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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+static uint64_t zfs_crc64_table[256];
+
+static void
+zfs_init_crc(void)
+{
+ int i, j;
+ uint64_t *ct;
+
+ /*
+ * Calculate the crc64 table (used for the zap hash
+ * function).
+ */
+ if (zfs_crc64_table[128] != ZFS_CRC64_POLY) {
+ memset(zfs_crc64_table, 0, sizeof(zfs_crc64_table));
+ for (i = 0; i < 256; i++)
+ for (ct = zfs_crc64_table + i, *ct = i, j = 8; j > 0; j--)
+ *ct = (*ct >> 1) ^ (-(*ct & 1) & ZFS_CRC64_POLY);
+ }
+}
+
+static void
+zio_checksum_off(const void *buf, uint64_t size, zio_cksum_t *zcp)
+{
+ ZIO_SET_CHECKSUM(zcp, 0, 0, 0, 0);
+}
+
+/*
+ * Signature for checksum functions.
+ */
+typedef void zio_checksum_t(const void *data, uint64_t size, zio_cksum_t *zcp);
+
+/*
+ * Information about each checksum function.
+ */
+typedef struct zio_checksum_info {
+ zio_checksum_t *ci_func[2]; /* checksum function for each byteorder */
+ int ci_correctable; /* number of correctable bits */
+ int ci_zbt; /* uses zio block tail? */
+ const char *ci_name; /* descriptive name */
+} zio_checksum_info_t;
+
+#include "fletcher.c"
+#include "sha256.c"
+
+static zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = {
+ {{NULL, NULL}, 0, 0, "inherit"},
+ {{NULL, NULL}, 0, 0, "on"},
+ {{zio_checksum_off, zio_checksum_off}, 0, 0, "off"},
+ {{zio_checksum_SHA256, NULL}, 1, 1, "label"},
+ {{zio_checksum_SHA256, NULL}, 1, 1, "gang_header"},
+ {{fletcher_2_native, NULL}, 0, 1, "zilog"},
+ {{fletcher_2_native, NULL}, 0, 0, "fletcher2"},
+ {{fletcher_4_native, NULL}, 1, 0, "fletcher4"},
+ {{zio_checksum_SHA256, NULL}, 1, 0, "SHA256"},
+};
+
+/*
+ * Common signature for all zio compress/decompress functions.
+ */
+typedef size_t zio_compress_func_t(void *src, void *dst,
+ size_t s_len, size_t d_len, int);
+typedef int zio_decompress_func_t(void *src, void *dst,
+ size_t s_len, size_t d_len, int);
+
+/*
+ * Information about each compression function.
+ */
+typedef struct zio_compress_info {
+ zio_compress_func_t *ci_compress; /* compression function */
+ zio_decompress_func_t *ci_decompress; /* decompression function */
+ int ci_level; /* level parameter */
+ const char *ci_name; /* algorithm name */
+} zio_compress_info_t;
+
+#include "lzjb.c"
+
+/*
+ * Compression vectors.
+ */
+static zio_compress_info_t zio_compress_table[ZIO_COMPRESS_FUNCTIONS] = {
+ {NULL, NULL, 0, "inherit"},
+ {NULL, NULL, 0, "on"},
+ {NULL, NULL, 0, "uncompressed"},
+ {NULL, lzjb_decompress, 0, "lzjb"},
+ {NULL, NULL, 0, "empty"},
+ {NULL, NULL, 1, "gzip-1"},
+ {NULL, NULL, 2, "gzip-2"},
+ {NULL, NULL, 3, "gzip-3"},
+ {NULL, NULL, 4, "gzip-4"},
+ {NULL, NULL, 5, "gzip-5"},
+ {NULL, NULL, 6, "gzip-6"},
+ {NULL, NULL, 7, "gzip-7"},
+ {NULL, NULL, 8, "gzip-8"},
+ {NULL, NULL, 9, "gzip-9"},
+};
+
+static int
+zio_checksum_error(const blkptr_t *bp, void *data)
+{
+ zio_cksum_t zc = bp->blk_cksum;
+ unsigned int checksum = BP_GET_CHECKSUM(bp);
+ uint64_t size = BP_GET_PSIZE(bp);
+ zio_block_tail_t *zbt = (zio_block_tail_t *)((char *)data + size) - 1;
+ zio_checksum_info_t *ci = &zio_checksum_table[checksum];
+ zio_cksum_t actual_cksum, expected_cksum;
+
+ if (checksum >= ZIO_CHECKSUM_FUNCTIONS || ci->ci_func[0] == NULL)
+ return (EINVAL);
+
+ if (ci->ci_zbt) {
+ expected_cksum = zbt->zbt_cksum;
+ zbt->zbt_cksum = zc;
+ ci->ci_func[0](data, size, &actual_cksum);
+ zbt->zbt_cksum = expected_cksum;
+ zc = expected_cksum;
+ } else {
+ /* ASSERT(!BP_IS_GANG(bp)); */
+ ci->ci_func[0](data, size, &actual_cksum);
+ }
+
+ if (!ZIO_CHECKSUM_EQUAL(actual_cksum, zc)) {
+ /*printf("ZFS: read checksum failed\n");*/
+ return (EIO);
+ }
+
+ return (0);
+}
+
+static int
+zio_decompress_data(int cpfunc, void *src, uint64_t srcsize,
+ void *dest, uint64_t destsize)
+{
+ zio_compress_info_t *ci = &zio_compress_table[cpfunc];
+
+ /* ASSERT((uint_t)cpfunc < ZIO_COMPRESS_FUNCTIONS); */
+ if (!ci->ci_decompress) {
+ printf("ZFS: unsupported compression algorithm %d\n", cpfunc);
+ return (EIO);
+ }
+
+ return (ci->ci_decompress(src, dest, srcsize, destsize, ci->ci_level));
+}
+
+static uint64_t
+zap_hash(uint64_t salt, const char *name)
+{
+ const uint8_t *cp;
+ uint8_t c;
+ uint64_t crc = salt;
+
+ /*ASSERT(crc != 0);*/
+ /*ASSERT(zfs_crc64_table[128] == ZFS_CRC64_POLY);*/
+ for (cp = (const uint8_t *)name; (c = *cp) != '\0'; cp++)
+ crc = (crc >> 8) ^ zfs_crc64_table[(crc ^ c) & 0xFF];
+
+ /*
+ * Only use 28 bits, since we need 4 bits in the cookie for the
+ * collision differentiator. We MUST use the high bits, since
+ * those are the onces that we first pay attention to when
+ * chosing the bucket.
+ */
+ crc &= ~((1ULL << (64 - ZAP_HASHBITS)) - 1);
+
+ return (crc);
+}
diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris_atomic.c b/sys/cddl/compat/opensolaris/kern/opensolaris_atomic.c
index 37de21f..bdb8f02 100644
--- a/sys/cddl/compat/opensolaris/kern/opensolaris_atomic.c
+++ b/sys/cddl/compat/opensolaris/kern/opensolaris_atomic.c
@@ -61,6 +61,15 @@ atomic_add_64(volatile uint64_t *target, int64_t delta)
*target += delta;
mtx_unlock(&atomic_mtx);
}
+
+void
+atomic_dec_64(volatile uint64_t *target)
+{
+
+ mtx_lock(&atomic_mtx);
+ *target -= 1;
+ mtx_unlock(&atomic_mtx);
+}
#endif
uint64_t
diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris_kmem.c b/sys/cddl/compat/opensolaris/kern/opensolaris_kmem.c
index 139d018..a24ca83 100644
--- a/sys/cddl/compat/opensolaris/kern/opensolaris_kmem.c
+++ b/sys/cddl/compat/opensolaris/kern/opensolaris_kmem.c
@@ -94,7 +94,7 @@ zfs_kmem_free(void *buf, size_t size __unused)
{
#ifdef KMEM_DEBUG
if (buf == NULL) {
- printf("%s: attempt to free NULL\n",__func__);
+ printf("%s: attempt to free NULL\n", __func__);
return;
}
struct kmem_item *i;
@@ -156,7 +156,7 @@ kmem_cache_create(char *name, size_t bufsize, size_t align,
cache->kc_constructor = constructor;
cache->kc_destructor = destructor;
cache->kc_private = private;
-#ifdef _KERNEL
+#if defined(_KERNEL) && !defined(KMEM_DEBUG)
cache->kc_zone = uma_zcreate(cache->kc_name, bufsize,
constructor != NULL ? kmem_std_constructor : NULL,
destructor != NULL ? kmem_std_destructor : NULL,
@@ -171,23 +171,23 @@ kmem_cache_create(char *name, size_t bufsize, size_t align,
void
kmem_cache_destroy(kmem_cache_t *cache)
{
+#if defined(_KERNEL) && !defined(KMEM_DEBUG)
uma_zdestroy(cache->kc_zone);
+#endif
kmem_free(cache, sizeof(*cache));
}
void *
kmem_cache_alloc(kmem_cache_t *cache, int flags)
{
-#ifdef _KERNEL
+#if defined(_KERNEL) && !defined(KMEM_DEBUG)
return (uma_zalloc_arg(cache->kc_zone, cache, flags));
#else
void *p;
p = kmem_alloc(cache->kc_size, flags);
- if (p != NULL) {
- kmem_std_constructor(p, cache->kc_size, cache->kc_private,
- flags);
- }
+ if (p != NULL && cache->kc_constructor != NULL)
+ kmem_std_constructor(p, cache->kc_size, cache, flags);
return (p);
#endif
}
@@ -195,10 +195,11 @@ kmem_cache_alloc(kmem_cache_t *cache, int flags)
void
kmem_cache_free(kmem_cache_t *cache, void *buf)
{
-#ifdef _KERNEL
+#if defined(_KERNEL) && !defined(KMEM_DEBUG)
uma_zfree_arg(cache->kc_zone, buf, cache);
#else
- kmem_std_destructor(buf, cache->kc_size, cache->kc_private);
+ if (cache->kc_destructor != NULL)
+ kmem_std_destructor(buf, cache->kc_size, cache);
kmem_free(buf, cache->kc_size);
#endif
}
@@ -207,7 +208,9 @@ kmem_cache_free(kmem_cache_t *cache, void *buf)
void
kmem_cache_reap_now(kmem_cache_t *cache)
{
+#ifndef KMEM_DEBUG
zone_drain(cache->kc_zone);
+#endif
}
void
@@ -253,6 +256,8 @@ kmem_show(void *dummy __unused)
printf("KMEM_DEBUG: Leaked elements:\n\n");
LIST_FOREACH(i, &kmem_items, next) {
printf("address=%p\n", i);
+ stack_print_ddb(&i->stack);
+ printf("\n");
}
}
mtx_unlock(&kmem_items_mtx);
diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris_lookup.c b/sys/cddl/compat/opensolaris/kern/opensolaris_lookup.c
new file mode 100644
index 0000000..47df799
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/kern/opensolaris_lookup.c
@@ -0,0 +1,112 @@
+/*-
+ * Copyright (c) 2007 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/pathname.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+
+int
+lookupname(char *dirname, enum uio_seg seg, enum symfollow follow,
+ vnode_t **dirvpp, vnode_t **compvpp)
+{
+
+ return (lookupnameat(dirname, seg, follow, dirvpp, compvpp, NULL));
+}
+
+int
+lookupnameat(char *dirname, enum uio_seg seg, enum symfollow follow,
+ vnode_t **dirvpp, vnode_t **compvpp, vnode_t *startvp)
+{
+ struct nameidata nd;
+ int error, ltype;
+
+ ASSERT(dirvpp == NULL);
+
+ vref(startvp);
+ ltype = VOP_ISLOCKED(startvp);
+ VOP_UNLOCK(startvp, 0);
+ NDINIT_ATVP(&nd, LOOKUP, LOCKLEAF | MPSAFE | follow, seg, dirname,
+ startvp, curthread);
+ error = namei(&nd);
+ *compvpp = nd.ni_vp;
+ NDFREE(&nd, NDF_ONLY_PNBUF);
+ vn_lock(startvp, ltype | LK_RETRY);
+ return (error);
+}
+
+int
+traverse(vnode_t **cvpp, int lktype)
+{
+ kthread_t *td = curthread;
+ vnode_t *cvp;
+ vnode_t *tvp;
+ vfs_t *vfsp;
+ int error;
+
+ cvp = *cvpp;
+ tvp = NULL;
+
+ /*
+ * If this vnode is mounted on, then we transparently indirect
+ * to the vnode which is the root of the mounted file system.
+ * Before we do this we must check that an unmount is not in
+ * progress on this vnode.
+ */
+
+ for (;;) {
+ /*
+ * Reached the end of the mount chain?
+ */
+ vfsp = vn_mountedvfs(cvp);
+ if (vfsp == NULL)
+ break;
+ /*
+ * tvp is NULL for *cvpp vnode, which we can't unlock.
+ */
+ if (tvp != NULL)
+ vput(cvp);
+ else
+ vrele(cvp);
+
+ /*
+ * The read lock must be held across the call to VFS_ROOT() to
+ * prevent a concurrent unmount from destroying the vfs.
+ */
+ error = VFS_ROOT(vfsp, lktype, &tvp, td);
+ if (error != 0)
+ return (error);
+ cvp = tvp;
+ }
+
+ *cvpp = cvp;
+ return (0);
+}
diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris_misc.c b/sys/cddl/compat/opensolaris/kern/opensolaris_misc.c
index a89d478..279ae4c 100644
--- a/sys/cddl/compat/opensolaris/kern/opensolaris_misc.c
+++ b/sys/cddl/compat/opensolaris/kern/opensolaris_misc.c
@@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/libkern.h>
+#include <sys/limits.h>
#include <sys/misc.h>
#include <sys/sunddi.h>
@@ -40,17 +41,30 @@ struct opensolaris_utsname utsname = {
};
int
+ddi_strtol(const char *str, char **nptr, int base, long *result)
+{
+
+ *result = strtol(str, nptr, base);
+ if (*result == 0)
+ return (EINVAL);
+ else if (*result == LONG_MIN || *result == LONG_MAX)
+ return (ERANGE);
+ return (0);
+}
+
+int
ddi_strtoul(const char *str, char **nptr, int base, unsigned long *result)
{
- char *end;
if (str == hw_serial) {
*result = hostid;
return (0);
}
- *result = strtoul(str, &end, base);
+ *result = strtoul(str, nptr, base);
if (*result == 0)
return (EINVAL);
+ else if (*result == ULONG_MAX)
+ return (ERANGE);
return (0);
}
diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris_policy.c b/sys/cddl/compat/opensolaris/kern/opensolaris_policy.c
index 272fe59..837d736 100644
--- a/sys/cddl/compat/opensolaris/kern/opensolaris_policy.c
+++ b/sys/cddl/compat/opensolaris/kern/opensolaris_policy.c
@@ -30,9 +30,20 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/priv.h>
#include <sys/vnode.h>
+#include <sys/mntent.h>
#include <sys/mount.h>
#include <sys/stat.h>
+#include <sys/jail.h>
#include <sys/policy.h>
+#include <sys/zfs_vfsops.h>
+
+int
+secpolicy_nfs(struct ucred *cred)
+{
+
+ /* TODO: Change PRIV_ROOT! */
+ return (priv_check_cred(cred, PRIV_ROOT, 0));
+}
int
secpolicy_zfs(struct ucred *cred)
@@ -62,16 +73,32 @@ secpolicy_fs_unmount(struct ucred *cred, struct mount *vfsp __unused)
return (priv_check_cred(cred, PRIV_VFS_UNMOUNT, 0));
}
+int
+secpolicy_fs_owner(struct mount *mp, struct ucred *cred)
+{
+
+ if (zfs_super_owner) {
+ if (cred->cr_uid == mp->mnt_cred->cr_uid &&
+ (!jailed(cred) ||
+ cred->cr_prison == mp->mnt_cred->cr_prison)) {
+ return (0);
+ }
+ }
+ return (priv_check_cred(cred, PRIV_VFS_MOUNT_OWNER, 0));
+}
+
/*
* This check is done in kern_link(), so we could just return 0 here.
*/
extern int hardlink_check_uid;
int
-secpolicy_basic_link(struct ucred *cred)
+secpolicy_basic_link(struct vnode *vp, struct ucred *cred)
{
if (!hardlink_check_uid)
return (0);
+ if (secpolicy_fs_owner(vp->v_mount, cred) == 0)
+ return (0);
return (priv_check_cred(cred, PRIV_VFS_LINK, 0));
}
@@ -83,9 +110,11 @@ secpolicy_vnode_stky_modify(struct ucred *cred)
}
int
-secpolicy_vnode_remove(struct ucred *cred)
+secpolicy_vnode_remove(struct vnode *vp, struct ucred *cred)
{
+ if (secpolicy_fs_owner(vp->v_mount, cred) == 0)
+ return (0);
return (priv_check_cred(cred, PRIV_VFS_ADMIN, 0));
}
@@ -94,9 +123,11 @@ secpolicy_vnode_access(struct ucred *cred, struct vnode *vp, uint64_t owner,
accmode_t accmode)
{
- if ((accmode & VREAD) && priv_check_cred(cred, PRIV_VFS_READ, 0) != 0) {
+ if (secpolicy_fs_owner(vp->v_mount, cred) == 0)
+ return (0);
+
+ if ((accmode & VREAD) && priv_check_cred(cred, PRIV_VFS_READ, 0) != 0)
return (EACCES);
- }
if ((accmode & VWRITE) &&
priv_check_cred(cred, PRIV_VFS_WRITE, 0) != 0) {
return (EACCES);
@@ -116,11 +147,13 @@ secpolicy_vnode_access(struct ucred *cred, struct vnode *vp, uint64_t owner,
}
int
-secpolicy_vnode_setdac(struct ucred *cred, uid_t owner)
+secpolicy_vnode_setdac(struct vnode *vp, struct ucred *cred, uid_t owner)
{
if (owner == cred->cr_uid)
return (0);
+ if (secpolicy_fs_owner(vp->v_mount, cred) == 0)
+ return (0);
return (priv_check_cred(cred, PRIV_VFS_ADMIN, 0));
}
@@ -148,7 +181,7 @@ secpolicy_vnode_setattr(struct ucred *cred, struct vnode *vp, struct vattr *vap,
* In the specific case of creating a set-uid root
* file, we need even more permissions.
*/
- error = secpolicy_vnode_setdac(cred, ovap->va_uid);
+ error = secpolicy_vnode_setdac(vp, cred, ovap->va_uid);
if (error)
return (error);
error = secpolicy_setid_setsticky_clear(vp, vap, ovap, cred);
@@ -158,7 +191,7 @@ secpolicy_vnode_setattr(struct ucred *cred, struct vnode *vp, struct vattr *vap,
vap->va_mode = ovap->va_mode;
}
if (mask & (AT_UID | AT_GID)) {
- error = secpolicy_vnode_setdac(cred, ovap->va_uid);
+ error = secpolicy_vnode_setdac(vp, cred, ovap->va_uid);
if (error)
return (error);
@@ -170,14 +203,16 @@ secpolicy_vnode_setattr(struct ucred *cred, struct vnode *vp, struct vattr *vap,
if (((mask & AT_UID) && vap->va_uid != ovap->va_uid) ||
((mask & AT_GID) && vap->va_gid != ovap->va_gid &&
!groupmember(vap->va_gid, cred))) {
- error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0);
- if (error)
- return (error);
+ if (secpolicy_fs_owner(vp->v_mount, cred) != 0) {
+ error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0);
+ if (error)
+ return (error);
+ }
}
if (((mask & AT_UID) && vap->va_uid != ovap->va_uid) ||
((mask & AT_GID) && vap->va_gid != ovap->va_gid)) {
- secpolicy_setid_clear(vap, cred);
+ secpolicy_setid_clear(vap, vp, cred);
}
}
if (mask & (AT_ATIME | AT_MTIME)) {
@@ -189,7 +224,7 @@ secpolicy_vnode_setattr(struct ucred *cred, struct vnode *vp, struct vattr *vap,
* If times is non-NULL, ... The caller must be the owner of
* the file or be the super-user.
*/
- error = secpolicy_vnode_setdac(cred, ovap->va_uid);
+ error = secpolicy_vnode_setdac(vp, cred, ovap->va_uid);
if (error && (vap->va_vaflags & VA_UTIMES_NULL))
error = unlocked_access(node, VWRITE, cred);
if (error)
@@ -206,25 +241,33 @@ secpolicy_vnode_create_gid(struct ucred *cred)
}
int
-secpolicy_vnode_setids_setgids(struct ucred *cred, gid_t gid)
+secpolicy_vnode_setids_setgids(struct vnode *vp, struct ucred *cred, gid_t gid)
{
- if (!groupmember(gid, cred))
- return (priv_check_cred(cred, PRIV_VFS_SETGID, 0));
- return (0);
+ if (groupmember(gid, cred))
+ return (0);
+ if (secpolicy_fs_owner(vp->v_mount, cred) == 0)
+ return (0);
+ return (priv_check_cred(cred, PRIV_VFS_SETGID, 0));
}
int
-secpolicy_vnode_setid_retain(struct ucred *cred, boolean_t issuidroot __unused)
+secpolicy_vnode_setid_retain(struct vnode *vp, struct ucred *cred,
+ boolean_t issuidroot __unused)
{
+ if (secpolicy_fs_owner(vp->v_mount, cred) == 0)
+ return (0);
return (priv_check_cred(cred, PRIV_VFS_RETAINSUGID, 0));
}
void
-secpolicy_setid_clear(struct vattr *vap, struct ucred *cred)
+secpolicy_setid_clear(struct vattr *vap, struct vnode *vp, struct ucred *cred)
{
+ if (secpolicy_fs_owner(vp->v_mount, cred) == 0)
+ return;
+
if ((vap->va_mode & (S_ISUID | S_ISGID)) != 0) {
if (priv_check_cred(cred, PRIV_VFS_RETAINSUGID, 0)) {
vap->va_mask |= AT_MODE;
@@ -239,6 +282,9 @@ secpolicy_setid_setsticky_clear(struct vnode *vp, struct vattr *vap,
{
int error;
+ if (secpolicy_fs_owner(vp->v_mount, cred) == 0)
+ return (0);
+
/*
* Privileged processes may set the sticky bit on non-directories,
* as well as set the setgid bit on a file with a group that the process
@@ -253,9 +299,61 @@ secpolicy_setid_setsticky_clear(struct vnode *vp, struct vattr *vap,
* group-id bit.
*/
if ((vap->va_mode & S_ISGID) != 0) {
- error = secpolicy_vnode_setids_setgids(cred, ovap->va_gid);
+ error = secpolicy_vnode_setids_setgids(vp, cred, ovap->va_gid);
if (error)
return (error);
}
return (0);
}
+
+int
+secpolicy_fs_mount(cred_t *cr, vnode_t *mvp, struct mount *vfsp)
+{
+
+ return (priv_check_cred(cr, PRIV_VFS_MOUNT, 0));
+}
+
+int
+secpolicy_vnode_owner(struct vnode *vp, cred_t *cred, uid_t owner)
+{
+
+ if (owner == cred->cr_uid)
+ return (0);
+ if (secpolicy_fs_owner(vp->v_mount, cred) == 0)
+ return (0);
+
+ /* XXX: vfs_suser()? */
+ return (priv_check_cred(cred, PRIV_VFS_MOUNT_OWNER, 0));
+}
+
+int
+secpolicy_vnode_chown(struct vnode *vp, cred_t *cred, boolean_t check_self)
+{
+
+ if (secpolicy_fs_owner(vp->v_mount, cred) == 0)
+ return (0);
+ return (priv_check_cred(cred, PRIV_VFS_CHOWN, 0));
+}
+
+void
+secpolicy_fs_mount_clearopts(cred_t *cr, struct mount *vfsp)
+{
+
+ if (priv_check_cred(cr, PRIV_VFS_MOUNT_NONUSER, 0) != 0) {
+ MNT_ILOCK(vfsp);
+ vfsp->vfs_flag |= VFS_NOSETUID | MNT_USER;
+ vfs_clearmntopt(vfsp, MNTOPT_SETUID);
+ vfs_setmntopt(vfsp, MNTOPT_NOSETUID, NULL, 0);
+ MNT_IUNLOCK(vfsp);
+ }
+}
+
+/*
+ * Check privileges for setting xvattr attributes
+ */
+int
+secpolicy_xvattr(xvattr_t *xvap, uid_t owner, cred_t *cr, vtype_t vtype)
+{
+
+ return (priv_check_cred(cr, PRIV_VFS_SYSFLAGS, 0));
+}
diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c b/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c
index e9120ee..f1bb4e2 100644
--- a/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c
+++ b/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c
@@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
+#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/cred.h>
#include <sys/vfs.h>
@@ -110,60 +111,12 @@ vfs_optionisset(const vfs_t *vfsp, const char *opt, char **argp)
}
int
-traverse(vnode_t **cvpp, int lktype)
-{
- kthread_t *td = curthread;
- vnode_t *cvp;
- vnode_t *tvp;
- vfs_t *vfsp;
- int error;
-
- cvp = *cvpp;
- tvp = NULL;
-
- /*
- * If this vnode is mounted on, then we transparently indirect
- * to the vnode which is the root of the mounted file system.
- * Before we do this we must check that an unmount is not in
- * progress on this vnode.
- */
-
- for (;;) {
- /*
- * Reached the end of the mount chain?
- */
- vfsp = vn_mountedvfs(cvp);
- if (vfsp == NULL)
- break;
- /*
- * tvp is NULL for *cvpp vnode, which we can't unlock.
- */
- if (tvp != NULL)
- vput(cvp);
- else
- vrele(cvp);
-
- /*
- * The read lock must be held across the call to VFS_ROOT() to
- * prevent a concurrent unmount from destroying the vfs.
- */
- error = VFS_ROOT(vfsp, lktype, &tvp, td);
- if (error != 0)
- return (error);
- cvp = tvp;
- }
-
- *cvpp = cvp;
- return (0);
-}
-
-int
domount(kthread_t *td, vnode_t *vp, const char *fstype, char *fspath,
char *fspec, int fsflags)
{
struct mount *mp;
struct vfsconf *vfsp;
- struct ucred *newcr, *oldcr;
+ struct ucred *cr;
int error;
/*
@@ -203,29 +156,31 @@ domount(kthread_t *td, vnode_t *vp, const char *fstype, char *fspath,
/*
* Set the mount level flags.
- * crdup() can sleep, so do it before acquiring a mutex.
*/
- newcr = crdup(kcred);
- MNT_ILOCK(mp);
if (fsflags & MNT_RDONLY)
mp->mnt_flag |= MNT_RDONLY;
mp->mnt_flag &=~ MNT_UPDATEMASK;
mp->mnt_flag |= fsflags & (MNT_UPDATEMASK | MNT_FORCE | MNT_ROOTFS);
/*
* Unprivileged user can trigger mounting a snapshot, but we don't want
- * him to unmount it, so we switch to privileged credentials.
+ * him to unmount it, so we switch to privileged of original mount.
*/
- oldcr = mp->mnt_cred;
- mp->mnt_cred = newcr;
+ crfree(mp->mnt_cred);
+ mp->mnt_cred = crdup(vp->v_mount->mnt_cred);
mp->mnt_stat.f_owner = mp->mnt_cred->cr_uid;
- MNT_IUNLOCK(mp);
- crfree(oldcr);
/*
* Mount the filesystem.
* XXX The final recipients of VFS_MOUNT just overwrite the ndp they
* get. No freeing of cn_pnbuf.
*/
+ /*
+ * XXX: This is evil, but we can't mount a snapshot as a regular user.
+ * XXX: Is is safe when snapshot is mounted from within a jail?
+ */
+ cr = td->td_ucred;
+ td->td_ucred = kcred;
error = VFS_MOUNT(mp, td);
+ td->td_ucred = cr;
if (!error) {
if (mp->mnt_opt != NULL)
diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris_zone.c b/sys/cddl/compat/opensolaris/kern/opensolaris_zone.c
index 3059a78..8489052 100644
--- a/sys/cddl/compat/opensolaris/kern/opensolaris_zone.c
+++ b/sys/cddl/compat/opensolaris/kern/opensolaris_zone.c
@@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$");
#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/jail.h>
+#include <sys/osd.h>
#include <sys/priv.h>
#include <sys/zone.h>
@@ -52,7 +53,7 @@ typedef struct zone_dataset {
LIST_HEAD(zone_dataset_head, zone_dataset);
-static struct prison_service *zone_prison_service = NULL;
+static int zone_slot;
int
zone_dataset_attach(struct ucred *cred, const char *dataset, int jailid)
@@ -60,7 +61,7 @@ zone_dataset_attach(struct ucred *cred, const char *dataset, int jailid)
struct zone_dataset_head *head;
zone_dataset_t *zd, *zd2;
struct prison *pr;
- int error;
+ int dofree, error;
if ((error = priv_check_cred(cred, PRIV_ZFS_JAIL, 0)) != 0)
return (error);
@@ -76,18 +77,33 @@ zone_dataset_attach(struct ucred *cred, const char *dataset, int jailid)
return (ENOENT);
}
- head = prison_service_data_get(zone_prison_service, pr);
- LIST_FOREACH(zd2, head, zd_next) {
- if (strcmp(dataset, zd2->zd_dataset) == 0) {
- free(zd, M_ZONES);
- error = EEXIST;
- goto failure;
+ head = osd_jail_get(pr, zone_slot);
+ if (head != NULL) {
+ dofree = 0;
+ LIST_FOREACH(zd2, head, zd_next) {
+ if (strcmp(dataset, zd2->zd_dataset) == 0) {
+ free(zd, M_ZONES);
+ error = EEXIST;
+ goto end;
+ }
}
+ } else {
+ dofree = 1;
+ prison_hold_locked(pr);
+ mtx_unlock(&pr->pr_mtx);
+ head = malloc(sizeof(*head), M_ZONES, M_WAITOK);
+ LIST_INIT(head);
+ mtx_lock(&pr->pr_mtx);
+ error = osd_jail_set(pr, zone_slot, head);
+ KASSERT(error == 0, ("osd_jail_set() failed (error=%d)", error));
}
strcpy(zd->zd_dataset, dataset);
LIST_INSERT_HEAD(head, zd, zd_next);
-failure:
- mtx_unlock(&pr->pr_mtx);
+end:
+ if (dofree)
+ prison_free_locked(pr);
+ else
+ mtx_unlock(&pr->pr_mtx);
return (error);
}
@@ -107,16 +123,25 @@ zone_dataset_detach(struct ucred *cred, const char *dataset, int jailid)
sx_sunlock(&allprison_lock);
if (pr == NULL)
return (ENOENT);
- head = prison_service_data_get(zone_prison_service, pr);
+ head = osd_jail_get(pr, zone_slot);
+ if (head == NULL) {
+ error = ENOENT;
+ goto end;
+ }
LIST_FOREACH(zd, head, zd_next) {
- if (strcmp(dataset, zd->zd_dataset) == 0) {
- LIST_REMOVE(zd, zd_next);
- free(zd, M_ZONES);
- goto success;
- }
+ if (strcmp(dataset, zd->zd_dataset) == 0)
+ break;
}
- error = ENOENT;
-success:
+ if (zd == NULL)
+ error = ENOENT;
+ else {
+ LIST_REMOVE(zd, zd_next);
+ free(zd, M_ZONES);
+ if (LIST_EMPTY(head))
+ osd_jail_del(pr, zone_slot);
+ error = 0;
+ }
+end:
mtx_unlock(&pr->pr_mtx);
return (error);
}
@@ -136,14 +161,16 @@ zone_dataset_visible(const char *dataset, int *write)
if (dataset[0] == '\0')
return (0);
- if (INGLOBALZONE(curproc)) {
+ if (INGLOBALZONE(curthread)) {
if (write != NULL)
*write = 1;
return (1);
}
pr = curthread->td_ucred->cr_prison;
mtx_lock(&pr->pr_mtx);
- head = prison_service_data_get(zone_prison_service, pr);
+ head = osd_jail_get(pr, zone_slot);
+ if (head == NULL)
+ goto end;
/*
* Walk the list once, looking for datasets which match exactly, or
@@ -188,49 +215,32 @@ end:
return (ret);
}
-static int
-zone_create(struct prison_service *psrv, struct prison *pr)
-{
- struct zone_dataset_head *head;
-
- head = malloc(sizeof(*head), M_ZONES, M_WAITOK);
- LIST_INIT(head);
- mtx_lock(&pr->pr_mtx);
- prison_service_data_set(psrv, pr, head);
- mtx_unlock(&pr->pr_mtx);
- return (0);
-}
-
-static int
-zone_destroy(struct prison_service *psrv, struct prison *pr)
+static void
+zone_destroy(void *arg)
{
struct zone_dataset_head *head;
zone_dataset_t *zd;
- mtx_lock(&pr->pr_mtx);
- head = prison_service_data_del(psrv, pr);
- mtx_unlock(&pr->pr_mtx);
- while ((zd = LIST_FIRST(head)) != NULL) {
- LIST_REMOVE(zd, zd_next);
- free(zd, M_ZONES);
- }
- free(head, M_ZONES);
- return (0);
+ head = arg;
+ while ((zd = LIST_FIRST(head)) != NULL) {
+ LIST_REMOVE(zd, zd_next);
+ free(zd, M_ZONES);
+ }
+ free(head, M_ZONES);
}
static void
zone_sysinit(void *arg __unused)
{
- zone_prison_service = prison_service_register("zfs", zone_create,
- zone_destroy);
+ zone_slot = osd_jail_register(zone_destroy);
}
static void
zone_sysuninit(void *arg __unused)
{
- prison_service_deregister(zone_prison_service);
+ osd_jail_deregister(zone_slot);
}
SYSINIT(zone_sysinit, SI_SUB_DRIVERS, SI_ORDER_ANY, zone_sysinit, NULL);
diff --git a/sys/cddl/compat/opensolaris/sys/atomic.h b/sys/cddl/compat/opensolaris/sys/atomic.h
index 46752a5..1a8bd45 100644
--- a/sys/cddl/compat/opensolaris/sys/atomic.h
+++ b/sys/cddl/compat/opensolaris/sys/atomic.h
@@ -38,6 +38,7 @@
#ifndef __LP64__
extern void atomic_add_64(volatile uint64_t *target, int64_t delta);
+extern void atomic_dec_64(volatile uint64_t *target);
extern void *atomic_cas_ptr(volatile void *target, void *cmp, void *newval);
#endif
#ifndef __sparc64__
@@ -83,6 +84,14 @@ atomic_dec_32_nv(volatile uint32_t *target)
return (atomic_fetchadd_32(target, -1) - 1);
}
+#ifdef __LP64__
+static __inline void
+atomic_dec_64(volatile uint64_t *target)
+{
+ atomic_subtract_64(target, 1);
+}
+#endif
+
static __inline void
atomic_inc_32(volatile uint32_t *target)
{
diff --git a/sys/cddl/compat/opensolaris/sys/callb.h b/sys/cddl/compat/opensolaris/sys/callb.h
deleted file mode 100644
index 070d0f9..0000000
--- a/sys/cddl/compat/opensolaris/sys/callb.h
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- * $FreeBSD$
- */
-/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#ifndef _SYS_CALLB_H
-#define _SYS_CALLB_H
-
-#pragma ident "@(#)callb.h 1.29 05/06/23 SMI"
-
-#include <sys/kcondvar.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * definitions of callback classes (c_class)
- *
- * Callbacks belong in the same class if (1) their callback routines
- * do the same kind of processing (ideally, using the same callback function)
- * and (2) they can/should be executed at the same time in a cpr
- * suspend/resume operation.
- *
- * Note: The DAEMON class, in particular, is for stopping kernel threads
- * and nothing else. The CALLB_* macros below should be used to deal
- * with kernel threads, and the callback function should be callb_generic_cpr.
- * Another idiosyncrasy of the DAEMON class is that if a suspend operation
- * fails, some of the callback functions may be called with the RESUME
- * code which were never called with SUSPEND. Not a problem currently,
- * but see bug 4201851.
- */
-#define CB_CL_CPR_DAEMON 0
-#define CB_CL_CPR_VM 1
-#define CB_CL_CPR_CALLOUT 2
-#define CB_CL_CPR_OBP 3
-#define CB_CL_CPR_FB 4
-#define CB_CL_PANIC 5
-#define CB_CL_CPR_RPC 6
-#define CB_CL_CPR_PROMPRINTF 7
-#define CB_CL_UADMIN 8
-#define CB_CL_CPR_PM 9
-#define CB_CL_HALT 10
-#define CB_CL_CPR_DMA 11
-#define CB_CL_CPR_POST_USER 12
-#define CB_CL_UADMIN_PRE_VFS 13
-#define CB_CL_MDBOOT CB_CL_UADMIN
-#define CB_CL_ENTER_DEBUGGER 14
-#define CB_CL_CPR_POST_KERNEL 15
-#define NCBCLASS 16 /* CHANGE ME if classes are added/removed */
-
-/*
- * CB_CL_CPR_DAEMON class specific definitions are given below:
- */
-
-/*
- * code for CPR callb_execute_class
- */
-#define CB_CODE_CPR_CHKPT 0
-#define CB_CODE_CPR_RESUME 1
-
-typedef void * callb_id_t;
-/*
- * Per kernel thread structure for CPR daemon callbacks.
- * Must be protected by either a existing lock in the daemon or
- * a new lock created for such a purpose.
- */
-typedef struct callb_cpr {
- kmutex_t *cc_lockp; /* lock to protect this struct */
- char cc_events; /* various events for CPR */
- callb_id_t cc_id; /* callb id address */
- kcondvar_t cc_callb_cv; /* cv for callback waiting */
- kcondvar_t cc_stop_cv; /* cv to checkpoint block */
-} callb_cpr_t;
-
-/*
- * cc_events definitions
- */
-#define CALLB_CPR_START 1 /* a checkpoint request's started */
-#define CALLB_CPR_SAFE 2 /* thread is safe for CPR */
-#define CALLB_CPR_ALWAYS_SAFE 4 /* thread is ALWAYS safe for CPR */
-
-/*
- * Used when checking that all kernel threads are stopped.
- */
-#define CALLB_MAX_RETRY 3 /* when waiting for kthread to sleep */
-#define CALLB_THREAD_DELAY 10 /* ticks allowed to reach sleep */
-#define CPR_KTHREAD_TIMEOUT_SEC 90 /* secs before callback times out -- */
- /* due to pwr mgmt of disks, make -- */
- /* big enough for worst spinup time */
-
-#ifdef _KERNEL
-/*
- *
- * CALLB_CPR_INIT macro is used by kernel threads to add their entry to
- * the callback table and perform other initialization. It automatically
- * adds the thread as being in the callback class CB_CL_CPR_DAEMON.
- *
- * cp - ptr to the callb_cpr_t structure for this kernel thread
- *
- * lockp - pointer to mutex protecting the callb_cpr_t stuct
- *
- * func - pointer to the callback function for this kernel thread.
- * It has the prototype boolean_t <func>(void *arg, int code)
- * where: arg - ptr to the callb_cpr_t structure
- * code - not used for this type of callback
- * returns: B_TRUE if successful; B_FALSE if unsuccessful.
- *
- * name - a string giving the name of the kernel thread
- *
- * Note: lockp is the lock to protect the callb_cpr_t (cp) structure
- * later on. No lock held is needed for this initialization.
- */
-#define CALLB_CPR_INIT(cp, lockp, func, name) { \
- strlcpy(curthread->td_name, (name), \
- sizeof(curthread->td_name)); \
- strlcpy(curthread->td_proc->p_comm, (name), \
- sizeof(curthread->td_proc->p_comm)); \
- bzero((caddr_t)(cp), sizeof (callb_cpr_t)); \
- (cp)->cc_lockp = lockp; \
- (cp)->cc_id = callb_add(func, (void *)(cp), \
- CB_CL_CPR_DAEMON, name); \
- }
-
-#ifndef __lock_lint
-#define CALLB_CPR_ASSERT(cp) ASSERT(MUTEX_HELD((cp)->cc_lockp));
-#else
-#define CALLB_CPR_ASSERT(cp)
-#endif
-/*
- * Some threads (like the idle threads) do not adhere to the callback
- * protocol and are always considered safe. Such threads must never exit.
- * They register their presence by calling this macro during their
- * initialization.
- *
- * Args:
- * t - thread pointer of the client kernel thread
- * name - a string giving the name of the kernel thread
- */
-#define CALLB_CPR_INIT_SAFE(t, name) { \
- (void) callb_add_thread(callb_generic_cpr_safe, \
- (void *) &callb_cprinfo_safe, CB_CL_CPR_DAEMON, \
- name, t); \
- }
-/*
- * The lock to protect cp's content must be held before
- * calling the following two macros.
- *
- * Any code region between CALLB_CPR_SAFE_BEGIN and CALLB_CPR_SAFE_END
- * is safe for checkpoint/resume.
- */
-#define CALLB_CPR_SAFE_BEGIN(cp) { \
- CALLB_CPR_ASSERT(cp) \
- (cp)->cc_events |= CALLB_CPR_SAFE; \
- if ((cp)->cc_events & CALLB_CPR_START) \
- cv_signal(&(cp)->cc_callb_cv); \
- }
-#define CALLB_CPR_SAFE_END(cp, lockp) { \
- CALLB_CPR_ASSERT(cp) \
- while ((cp)->cc_events & CALLB_CPR_START) \
- cv_wait(&(cp)->cc_stop_cv, lockp); \
- (cp)->cc_events &= ~CALLB_CPR_SAFE; \
- }
-/*
- * cv_destroy is nop right now but may be needed in the future.
- */
-#define CALLB_CPR_EXIT(cp) { \
- CALLB_CPR_ASSERT(cp) \
- (cp)->cc_events |= CALLB_CPR_SAFE; \
- if ((cp)->cc_events & CALLB_CPR_START) \
- cv_signal(&(cp)->cc_callb_cv); \
- mutex_exit((cp)->cc_lockp); \
- (void) callb_delete((cp)->cc_id); \
- cv_destroy(&(cp)->cc_callb_cv); \
- cv_destroy(&(cp)->cc_stop_cv); \
- }
-
-extern callb_cpr_t callb_cprinfo_safe;
-extern callb_id_t callb_add(boolean_t (*)(void *, int), void *, int, char *);
-extern callb_id_t callb_add_thread(boolean_t (*)(void *, int),
- void *, int, char *, kthread_id_t);
-extern int callb_delete(callb_id_t);
-extern void callb_execute(callb_id_t, int);
-extern void *callb_execute_class(int, int);
-extern boolean_t callb_generic_cpr(void *, int);
-extern boolean_t callb_generic_cpr_safe(void *, int);
-extern boolean_t callb_is_stopped(kthread_id_t, caddr_t *);
-extern void callb_lock_table(void);
-extern void callb_unlock_table(void);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _SYS_CALLB_H */
diff --git a/sys/cddl/compat/opensolaris/sys/cred.h b/sys/cddl/compat/opensolaris/sys/cred.h
index 85e79db..b13ef6c 100644
--- a/sys/cddl/compat/opensolaris/sys/cred.h
+++ b/sys/cddl/compat/opensolaris/sys/cred.h
@@ -30,12 +30,14 @@
#define _OPENSOLARIS_SYS_CRED_H_
#include <sys/param.h>
-#include_next <sys/ucred.h>
-
-#ifdef _KERNEL
+#define _WANT_UCRED
+#include <sys/ucred.h>
+#undef _WANT_UCRED
typedef struct ucred cred_t;
+typedef struct ucred ucred_t;
+#ifdef _KERNEL
#define CRED() (curthread->td_ucred)
/*
@@ -43,9 +45,14 @@ typedef struct ucred cred_t;
*/
#define kcred (thread0.td_ucred)
-#define crgetuid(cred) ((cred)->cr_uid)
-#define crgetgid(cred) ((cred)->cr_gid)
-
-#endif /* _KERNEL */
+#define crgetuid(cred) ((cred)->cr_uid)
+#define crgetgid(cred) ((cred)->cr_gid)
+#define crgetgroups(cred) ((cred)->cr_groups)
+#define crgetngroups(cred) ((cred)->cr_ngroups)
+#define crgetsid(cred, i) (NULL)
+#else /* !_KERNEL */
+#define kcred NULL
+#define CRED() NULL
+#endif /* !_KERNEL */
#endif /* _OPENSOLARIS_SYS_CRED_H_ */
diff --git a/sys/cddl/compat/opensolaris/sys/dnlc.h b/sys/cddl/compat/opensolaris/sys/dnlc.h
index a2d4f01..e978e975 100644
--- a/sys/cddl/compat/opensolaris/sys/dnlc.h
+++ b/sys/cddl/compat/opensolaris/sys/dnlc.h
@@ -35,6 +35,6 @@
#define dnlc_update(dvp, name, vp) do { } while (0)
#define dnlc_remove(dvp, name) do { } while (0)
#define dnlc_purge_vfsp(vfsp, count) (0)
-#define dnlc_reduce_cache(percent) do { } while (0)
+#define dnlc_reduce_cache(percent) EVENTHANDLER_INVOKE(vfs_lowvnodes, (int)(intptr_t)(percent))
#endif /* !_OPENSOLARIS_SYS_DNLC_H_ */
diff --git a/sys/cddl/compat/opensolaris/sys/file.h b/sys/cddl/compat/opensolaris/sys/file.h
new file mode 100644
index 0000000..afd1050
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/sys/file.h
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 2007 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _OPENSOLARIS_SYS_FILE_H_
+#define _OPENSOLARIS_SYS_FILE_H_
+
+#include_next <sys/file.h>
+
+#ifdef _KERNEL
+typedef struct file file_t;
+
+static __inline file_t *
+getf(int fd, int write)
+{
+ struct file *fp;
+
+ if (write && fget_write(curthread, fd, &fp) == 0)
+ return (fp);
+ else if (!write && fget_read(curthread, fd, &fp) == 0)
+ return (fp);
+ return (NULL);
+}
+
+static __inline void
+releasef(file_t *fp)
+{
+
+ fdrop(fp, curthread);
+}
+#endif /* _KERNEL */
+
+#endif /* !_OPENSOLARIS_SYS_FILE_H_ */
diff --git a/sys/cddl/compat/opensolaris/sys/kidmap.h b/sys/cddl/compat/opensolaris/sys/kidmap.h
new file mode 100644
index 0000000..c2a33d2
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/sys/kidmap.h
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 2007 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _OPENSOLARIS_SYS_KIDMAP_H_
+#define _OPENSOLARIS_SYS_KIDMAP_H_
+
+#include <sys/idmap.h>
+
+typedef int32_t idmap_stat;
+typedef void idmap_get_handle_t;
+
+#define kidmap_get_create() (NULL)
+#define kidmap_get_destroy(hdl) do { } while (0)
+#define kidmap_get_mappings(hdl) (NULL)
+
+#endif /* _OPENSOLARIS_SYS_KIDMAP_H_ */
diff --git a/sys/cddl/compat/opensolaris/sys/kmem.h b/sys/cddl/compat/opensolaris/sys/kmem.h
index 5258cff..c103d18 100644
--- a/sys/cddl/compat/opensolaris/sys/kmem.h
+++ b/sys/cddl/compat/opensolaris/sys/kmem.h
@@ -38,15 +38,16 @@
#include <vm/vm_extern.h>
#define KM_SLEEP M_WAITOK
+#define KM_PUSHPAGE M_WAITOK
#define KM_NOSLEEP M_NOWAIT
#define KMC_NODEBUG 0
typedef struct kmem_cache {
char kc_name[32];
-#ifdef _KERNEL
+#if defined(_KERNEL) && !defined(KMEM_DEBUG)
uma_zone_t kc_zone;
#else
- size_t size;
+ size_t kc_size;
#endif
int (*kc_constructor)(void *, void *, int);
void (*kc_destructor)(void *, void *);
diff --git a/sys/cddl/compat/opensolaris/sys/misc.h b/sys/cddl/compat/opensolaris/sys/misc.h
index a5a52b7..8e1a637 100644
--- a/sys/cddl/compat/opensolaris/sys/misc.h
+++ b/sys/cddl/compat/opensolaris/sys/misc.h
@@ -29,6 +29,13 @@
#ifndef _OPENSOLARIS_SYS_MISC_H_
#define _OPENSOLARIS_SYS_MISC_H_
+#define MAXUID 2147483647
+
+#define SPEC_MAXOFFSET_T OFF_MAX
+
+#define _ACL_ACLENT_ENABLED 0x1
+#define _ACL_ACE_ENABLED 0x2
+
#define _FIOFFS (INT_MIN)
#define _FIOGDIO (INT_MIN+1)
#define _FIOSDIO (INT_MIN+2)
diff --git a/sys/cddl/compat/opensolaris/sys/mntent.h b/sys/cddl/compat/opensolaris/sys/mntent.h
index e4bbc9d..3faea6b 100644
--- a/sys/cddl/compat/opensolaris/sys/mntent.h
+++ b/sys/cddl/compat/opensolaris/sys/mntent.h
@@ -54,5 +54,7 @@
#define MNTOPT_EXEC "exec" /* enable executables */
#define MNTOPT_NOEXEC "noexec" /* disable executables */
#define MNTOPT_RESTRICT "restrict" /* restricted autofs mount */
+#define MNTOPT_NBMAND "nbmand" /* allow non-blocking mandatory locks */
+#define MNTOPT_NONBMAND "nonbmand" /* deny non-blocking mandatory locks */
#endif /* !_OPENSOLARIS_MNTENT_H_ */
diff --git a/sys/cddl/compat/opensolaris/sys/param.h b/sys/cddl/compat/opensolaris/sys/param.h
index 8d36a9d..609d22a 100644
--- a/sys/cddl/compat/opensolaris/sys/param.h
+++ b/sys/cddl/compat/opensolaris/sys/param.h
@@ -34,4 +34,8 @@
#define PAGESIZE PAGE_SIZE
+#ifdef _KERNEL
+#define ptob(x) ((uint64_t)(x) << PAGE_SHIFT)
+#endif
+
#endif
diff --git a/sys/cddl/compat/opensolaris/sys/pathname.h b/sys/cddl/compat/opensolaris/sys/pathname.h
new file mode 100644
index 0000000..0d39623
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/sys/pathname.h
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 2007 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _OPENSOLARIS_SYS_PATHNAME_H_
+#define _OPENSOLARIS_SYS_PATHNAME_H_
+
+#ifdef _KERNEL
+
+#include <sys/param.h>
+#include <sys/vnode.h>
+
+typedef struct pathname {
+ char *pn_buf; /* underlying storage */
+ char *pn_path; /* remaining pathname */
+ size_t pn_pathlen; /* remaining length */
+ size_t pn_bufsize; /* total size of pn_buf */
+} pathname_t;
+
+#define pn_alloc(pnp) panic("pn_alloc() called")
+#define pn_free(pnp) panic("pn_free() called")
+
+int lookupname(char *, enum uio_seg, enum symfollow, vnode_t **, vnode_t **);
+int lookupnameat(char *, enum uio_seg, enum symfollow, vnode_t **, vnode_t **,
+ vnode_t *);
+int traverse(vnode_t **, int);
+
+#endif /* _KERNEL */
+
+#endif /* _OPENSOLARIS_SYS_PATHNAME_H_ */
diff --git a/sys/cddl/compat/opensolaris/sys/policy.h b/sys/cddl/compat/opensolaris/sys/policy.h
index 2c764ef..08db5ca 100644
--- a/sys/cddl/compat/opensolaris/sys/policy.h
+++ b/sys/cddl/compat/opensolaris/sys/policy.h
@@ -33,30 +33,44 @@
#ifdef _KERNEL
+#include <sys/vnode.h>
+
struct mount;
struct ucred;
struct vattr;
struct vnode;
-int secpolicy_zfs(struct ucred *cred);
-int secpolicy_sys_config(struct ucred *cred, int checkonly);
-int secpolicy_zinject(struct ucred *cred);
-int secpolicy_fs_unmount(struct ucred *cred, struct mount *vfsp);
-int secpolicy_basic_link(struct ucred *cred);
+int secpolicy_nfs(struct ucred *cred);
+int secpolicy_zfs(struct ucred *cred);
+int secpolicy_sys_config(struct ucred *cred, int checkonly);
+int secpolicy_zinject(struct ucred *cred);
+int secpolicy_fs_unmount(struct ucred *cred, struct mount *vfsp);
+int secpolicy_basic_link(struct vnode *vp, struct ucred *cred);
+int secpolicy_vnode_owner(struct vnode *vp, cred_t *cred, uid_t owner);
+int secpolicy_vnode_chown(struct vnode *vp, cred_t *cred,
+ boolean_t check_self);
int secpolicy_vnode_stky_modify(struct ucred *cred);
-int secpolicy_vnode_remove(struct ucred *cred);
+int secpolicy_vnode_remove(struct vnode *vp, struct ucred *cred);
int secpolicy_vnode_access(struct ucred *cred, struct vnode *vp,
uint64_t owner, accmode_t accmode);
-int secpolicy_vnode_setdac(struct ucred *cred, uid_t owner);
+int secpolicy_vnode_setdac(struct vnode *vp, struct ucred *cred,
+ uid_t owner);
int secpolicy_vnode_setattr(struct ucred *cred, struct vnode *vp,
struct vattr *vap, const struct vattr *ovap, int flags,
int unlocked_access(void *, int, struct ucred *), void *node);
int secpolicy_vnode_create_gid(struct ucred *cred);
-int secpolicy_vnode_setids_setgids(struct ucred *cred, gid_t gid);
-int secpolicy_vnode_setid_retain(struct ucred *cred, boolean_t issuidroot);
-void secpolicy_setid_clear(struct vattr *vap, struct ucred *cred);
+int secpolicy_vnode_setids_setgids(struct vnode *vp, struct ucred *cred,
+ gid_t gid);
+int secpolicy_vnode_setid_retain(struct vnode *vp, struct ucred *cred,
+ boolean_t issuidroot);
+void secpolicy_setid_clear(struct vattr *vap, struct vnode *vp,
+ struct ucred *cred);
int secpolicy_setid_setsticky_clear(struct vnode *vp, struct vattr *vap,
const struct vattr *ovap, struct ucred *cred);
+int secpolicy_fs_owner(struct mount *vfsp, struct ucred *cred);
+int secpolicy_fs_mount(cred_t *cr, vnode_t *mvp, struct mount *vfsp);
+void secpolicy_fs_mount_clearopts(cred_t *cr, struct mount *vfsp);
+int secpolicy_xvattr(xvattr_t *xvap, uid_t owner, cred_t *cr, vtype_t vtype);
#endif /* _KERNEL */
diff --git a/sys/cddl/compat/opensolaris/sys/proc.h b/sys/cddl/compat/opensolaris/sys/proc.h
index 2410396..73fbcda 100644
--- a/sys/cddl/compat/opensolaris/sys/proc.h
+++ b/sys/cddl/compat/opensolaris/sys/proc.h
@@ -54,12 +54,6 @@ typedef struct thread kthread_t;
typedef struct thread *kthread_id_t;
typedef struct proc proc_t;
-#if (KSTACK_PAGES * PAGE_SIZE) < 16384
-#define ZFS_KSTACK_PAGES (16384 / PAGE_SIZE)
-#else
-#define ZFS_KSTACK_PAGES 0
-#endif
-
static __inline kthread_t *
thread_create(caddr_t stk, size_t stksize, void (*proc)(void *), void *arg,
size_t len, proc_t *pp, int state, pri_t pri)
@@ -71,11 +65,10 @@ thread_create(caddr_t stk, size_t stksize, void (*proc)(void *), void *arg,
* Be sure there are no surprises.
*/
ASSERT(stk == NULL);
- ASSERT(stksize == 0);
ASSERT(len == 0);
ASSERT(state == TS_RUN);
- error = kproc_create(proc, arg, &p, 0, ZFS_KSTACK_PAGES,
+ error = kproc_create(proc, arg, &p, 0, stksize / PAGE_SIZE,
"solthread %p", proc);
return (error == 0 ? FIRST_THREAD_IN_PROC(p) : NULL);
}
diff --git a/sys/cddl/compat/opensolaris/sys/refstr.h b/sys/cddl/compat/opensolaris/sys/refstr.h
new file mode 100644
index 0000000..e4e177b
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/sys/refstr.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 2007 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.
+ *
+ $ $FreeBSD$
+ */
+
+#ifndef _OPENSOLARIS_SYS_REFSTR_H_
+#define _OPENSOLARIS_SYS_REFSTR_H_
+
+#define refstr_value(str) (str)
+
+#endif /* _OPENSOLARIS_SYS_REFSTR_H_ */
diff --git a/sys/cddl/compat/opensolaris/sys/sid.h b/sys/cddl/compat/opensolaris/sys/sid.h
new file mode 100644
index 0000000..eb8d0be
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/sys/sid.h
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 2007 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _OPENSOLARIS_SYS_SID_H_
+#define _OPENSOLARIS_SYS_SID_H_
+
+typedef struct ksiddomain {
+ char kd_name[16]; /* Domain part of SID */
+} ksiddomain_t;
+typedef void ksid_t;
+
+static __inline ksiddomain_t *
+ksid_lookupdomain(const char *domain)
+{
+ ksiddomain_t *kd;
+
+ kd = kmem_alloc(sizeof(*kd), KM_SLEEP);
+ strlcpy(kd->kd_name, "FreeBSD", sizeof(kd->kd_name));
+ return (kd);
+}
+
+static __inline void
+ksiddomain_rele(ksiddomain_t *kd)
+{
+
+ kmem_free(kd, sizeof(*kd));
+}
+
+#endif /* _OPENSOLARIS_SYS_SID_H_ */
diff --git a/sys/cddl/compat/opensolaris/sys/sig.h b/sys/cddl/compat/opensolaris/sys/sig.h
new file mode 100644
index 0000000..985896e
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/sys/sig.h
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 2008 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _OPENSOLARIS_SYS_SIG_H_
+#define _OPENSOLARIS_SYS_SIG_H_
+
+#ifdef _KERNEL
+
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/signalvar.h>
+#include <sys/debug.h>
+
+#define FORREAL 0
+#define JUSTLOOKING 1
+
+static __inline int
+issig(int why)
+{
+ struct thread *td = curthread;
+ struct proc *p;
+ int sig;
+
+ ASSERT(why == FORREAL || why == JUSTLOOKING);
+ if (SIGPENDING(td)) {
+ if (why == JUSTLOOKING)
+ return (1);
+ p = td->td_proc;
+ PROC_LOCK(p);
+ mtx_lock(&p->p_sigacts->ps_mtx);
+ sig = cursig(td);
+ mtx_unlock(&p->p_sigacts->ps_mtx);
+ PROC_UNLOCK(p);
+ if (sig != 0)
+ return (1);
+ }
+ return (0);
+}
+
+#endif /* _KERNEL */
+
+#endif /* _OPENSOLARIS_SYS_SIG_H_ */
diff --git a/sys/cddl/compat/opensolaris/sys/sunddi.h b/sys/cddl/compat/opensolaris/sys/sunddi.h
index 192d5a9..1ca2bf0 100644
--- a/sys/cddl/compat/opensolaris/sys/sunddi.h
+++ b/sys/cddl/compat/opensolaris/sys/sunddi.h
@@ -29,8 +29,10 @@
#ifndef _OPENSOLARIS_SYS_SUNDDI_H_
#define _OPENSOLARIS_SYS_SUNDDI_H_
+#define ddi_driver_major(zfs_dip) (0)
#define ddi_copyin(from, to, size, flag) (bcopy((from), (to), (size)), 0)
#define ddi_copyout(from, to, size, flag) (bcopy((from), (to), (size)), 0)
+int ddi_strtol(const char *str, char **nptr, int base, long *result);
int ddi_strtoul(const char *str, char **nptr, int base, unsigned long *result);
#endif /* _OPENSOLARIS_SYS_SUNDDI_H_ */
diff --git a/sys/cddl/compat/opensolaris/sys/sysmacros.h b/sys/cddl/compat/opensolaris/sys/sysmacros.h
index a179c5f..7f4885b 100644
--- a/sys/cddl/compat/opensolaris/sys/sysmacros.h
+++ b/sys/cddl/compat/opensolaris/sys/sysmacros.h
@@ -39,6 +39,10 @@
extern "C" {
#endif
+#ifndef ABS
+#define ABS(a) ((a) < 0 ? -(a) : (a))
+#endif
+
/*
* Macro for checking power of 2 address alignment.
*/
diff --git a/sys/cddl/compat/opensolaris/sys/time.h b/sys/cddl/compat/opensolaris/sys/time.h
index 770b251..0bf1e9b 100644
--- a/sys/cddl/compat/opensolaris/sys/time.h
+++ b/sys/cddl/compat/opensolaris/sys/time.h
@@ -40,6 +40,9 @@ typedef longlong_t hrtime_t;
#define LBOLT ((gethrtime() * hz) / NANOSEC)
+#define TIMESPEC_OVERFLOW(ts) \
+ ((ts)->tv_sec < INT32_MIN || (ts)->tv_sec > INT32_MAX)
+
#ifdef _KERNEL
#define lbolt64 (int64_t)(LBOLT)
diff --git a/sys/cddl/compat/opensolaris/sys/types.h b/sys/cddl/compat/opensolaris/sys/types.h
index 7d5d9e4..069ad45 100644
--- a/sys/cddl/compat/opensolaris/sys/types.h
+++ b/sys/cddl/compat/opensolaris/sys/types.h
@@ -44,13 +44,15 @@ typedef u_char uchar_t;
typedef u_short ushort_t;
typedef u_long ulong_t;
typedef long long longlong_t;
-typedef unsigned long long u_longlong_t;
+typedef unsigned long long u_longlong_t;
typedef off_t off64_t;
typedef id_t taskid_t;
typedef id_t projid_t;
typedef id_t poolid_t;
typedef id_t zoneid_t;
typedef id_t ctid_t;
+typedef mode_t o_mode_t;
+typedef uint64_t pgcnt_t;
#ifdef _KERNEL
@@ -60,8 +62,8 @@ typedef id_t ctid_t;
typedef short index_t;
typedef off_t offset_t;
typedef long ptrdiff_t; /* pointer difference */
-typedef void pathname_t;
typedef int64_t rlim64_t;
+typedef int major_t;
#else
#ifdef NEED_SOLARIS_BOOLEAN
@@ -80,7 +82,6 @@ typedef short pri_t;
typedef int32_t daddr32_t;
typedef int32_t time32_t;
typedef u_longlong_t diskaddr_t;
-typedef ushort_t o_mode_t; /* old file attribute type */
#endif /* !_KERNEL */
diff --git a/sys/cddl/compat/opensolaris/sys/uio.h b/sys/cddl/compat/opensolaris/sys/uio.h
index d219ff0..9e53457 100644
--- a/sys/cddl/compat/opensolaris/sys/uio.h
+++ b/sys/cddl/compat/opensolaris/sys/uio.h
@@ -60,6 +60,6 @@ zfs_uiomove(void *cp, size_t n, enum uio_rw dir, uio_t *uio)
return (uiomove(cp, (int)n, uio));
}
#define uiomove(cp, n, dir, uio) zfs_uiomove((cp), (n), (dir), (uio))
-#endif
+#endif /* BUILDING_ZFS */
#endif /* !_OPENSOLARIS_SYS_UIO_H_ */
diff --git a/sys/cddl/compat/opensolaris/sys/vfs.h b/sys/cddl/compat/opensolaris/sys/vfs.h
index c2d8a6b..be3e3cf 100644
--- a/sys/cddl/compat/opensolaris/sys/vfs.h
+++ b/sys/cddl/compat/opensolaris/sys/vfs.h
@@ -45,6 +45,7 @@ typedef struct mount vfs_t;
#define vfs_count mnt_ref
#define vfs_fsid mnt_stat.f_fsid
#define vfs_bsize mnt_stat.f_bsize
+#define vfs_resource mnt_stat.f_mntfromname
#define v_flag v_vflag
#define v_vfsp v_mount
@@ -64,6 +65,8 @@ typedef struct mount vfs_t;
MNT_IUNLOCK(vfsp); \
} while (0)
+#define fs_vscan(vp, cr, async) (0)
+
#define VROOT VV_ROOT
/*
@@ -107,10 +110,21 @@ void vfs_setmntopt(vfs_t *vfsp, const char *name, const char *arg,
int flags __unused);
void vfs_clearmntopt(vfs_t *vfsp, const char *name);
int vfs_optionisset(const vfs_t *vfsp, const char *opt, char **argp);
-int traverse(vnode_t **cvpp, int lktype);
int domount(kthread_t *td, vnode_t *vp, const char *fstype, char *fspath,
char *fspec, int fsflags);
+typedef uint64_t vfs_feature_t;
+
+#define VFSFT_XVATTR 0x100000001 /* Supports xvattr for attrs */
+#define VFSFT_CASEINSENSITIVE 0x100000002 /* Supports case-insensitive */
+#define VFSFT_NOCASESENSITIVE 0x100000004 /* NOT case-sensitive */
+#define VFSFT_DIRENTFLAGS 0x100000008 /* Supports dirent flags */
+#define VFSFT_ACLONCREATE 0x100000010 /* Supports ACL on create */
+#define VFSFT_ACEMASKONACCESS 0x100000020 /* Can use ACEMASK for access */
+
+#define vfs_set_feature(vfsp, feature) do { } while (0)
+#define vfs_has_feature(vfsp, feature) (0)
+
#endif /* _KERNEL */
#endif /* _OPENSOLARIS_SYS_VFS_H_ */
diff --git a/sys/cddl/compat/opensolaris/sys/vnode.h b/sys/cddl/compat/opensolaris/sys/vnode.h
index a8a261c..b490d33 100644
--- a/sys/cddl/compat/opensolaris/sys/vnode.h
+++ b/sys/cddl/compat/opensolaris/sys/vnode.h
@@ -29,24 +29,32 @@
#ifndef _OPENSOLARIS_SYS_VNODE_H_
#define _OPENSOLARIS_SYS_VNODE_H_
+struct vnode;
+struct vattr;
+
+typedef struct vnode vnode_t;
+typedef struct vattr vattr_t;
+typedef enum vtype vtype_t;
+
+#include <sys/namei.h>
+enum symfollow { NO_FOLLOW = NOFOLLOW };
+
+#include <sys/proc.h>
#include_next <sys/vnode.h>
#include <sys/mount.h>
#include <sys/cred.h>
#include <sys/fcntl.h>
-#include <sys/namei.h>
-#include <sys/proc.h>
+#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/syscallsubr.h>
-typedef struct vnode vnode_t;
-typedef struct vattr vattr_t;
-typedef void caller_context_t;
-
typedef struct vop_vector vnodeops_t;
#define vop_fid vop_vptofh
#define vop_fid_args vop_vptofh_args
#define a_fid a_fhp
+#define IS_XATTRDIR(dvp) (0)
+
#define v_count v_usecount
static __inline int
@@ -59,23 +67,24 @@ vn_is_readonly(vnode_t *vp)
#define vn_ismntpt(vp) ((vp)->v_type == VDIR && (vp)->v_mountedhere != NULL)
#define vn_mountedvfs(vp) ((vp)->v_mountedhere)
#define vn_has_cached_data(vp) ((vp)->v_object != NULL && (vp)->v_object->resident_page_count > 0)
+#define vn_exists(vp) do { } while (0)
+#define vn_invalid(vp) do { } while (0)
+#define vn_renamepath(tdvp, svp, tnm, lentnm) do { } while (0)
+#define vn_free(vp) do { } while (0)
#define VN_HOLD(v) vref(v)
#define VN_RELE(v) vrele(v)
#define VN_URELE(v) vput(v)
-#define VOP_REALVP(vp, vpp) (*(vpp) = (vp), 0)
-
-#define vnevent_remove(vp) do { } while (0)
-#define vnevent_rmdir(vp) do { } while (0)
-#define vnevent_rename_src(vp) do { } while (0)
-#define vnevent_rename_dest(vp) do { } while (0)
-
+#define VOP_REALVP(vp, vpp, ct) (*(vpp) = (vp), 0)
-#define IS_DEVVP(vp) \
- ((vp)->v_type == VCHR || (vp)->v_type == VBLK || (vp)->v_type == VFIFO)
-
-#define MODEMASK ALLPERMS
+#define vnevent_create(vp, ct) do { } while (0)
+#define vnevent_link(vp, ct) do { } while (0)
+#define vnevent_remove(vp, dvp, name, ct) do { } while (0)
+#define vnevent_rmdir(vp, dvp, name, ct) do { } while (0)
+#define vnevent_rename_src(vp, dvp, name, ct) do { } while (0)
+#define vnevent_rename_dest(vp, dvp, name, ct) do { } while (0)
+#define vnevent_rename_dest_dir(vp, ct) do { } while (0)
#define specvp(vp, rdev, type, cr) (VN_HOLD(vp), (vp))
#define MANDMODE(mode) (0)
@@ -98,24 +107,6 @@ vn_is_readonly(vnode_t *vp)
#define MAXOFFSET_T OFF_MAX
#define EXCL 0
-#define AT_TYPE 0x0001
-#define AT_MODE 0x0002
-#define AT_UID 0x0004
-#define AT_GID 0x0008
-#define AT_FSID 0x0010
-#define AT_NODEID 0x0020
-#define AT_NLINK 0x0040
-#define AT_SIZE 0x0080
-#define AT_ATIME 0x0100
-#define AT_MTIME 0x0200
-#define AT_CTIME 0x0400
-#define AT_RDEV 0x0800
-#define AT_BLKSIZE 0x1000
-#define AT_NBLOCKS 0x2000
-#define AT_SEQ 0x4000
-#define AT_NOSET (AT_NLINK|AT_RDEV|AT_FSID|AT_NODEID|AT_TYPE|\
- AT_BLKSIZE|AT_NBLOCKS|AT_SEQ)
-
#define ACCESSED (AT_ATIME)
#define STATE_CHANGED (AT_CTIME)
#define CONTENT_MODIFIED (AT_MTIME | AT_CTIME)
@@ -140,28 +131,37 @@ vattr_init_mask(vattr_t *vap)
vap->va_mask |= AT_MTIME;
if (vap->va_mode != (u_short)VNOVAL)
vap->va_mask |= AT_MODE;
+ if (vap->va_flags != VNOVAL)
+ vap->va_mask |= AT_XVATTR;
}
-#define FCREAT O_CREAT
-#define FTRUNC O_TRUNC
-#define FDSYNC FFSYNC
-#define FRSYNC FFSYNC
-#define FSYNC FFSYNC
-#define FOFFMAX 0x00
-
-enum create { CRCREAT };
+#define FCREAT O_CREAT
+#define FTRUNC O_TRUNC
+#define FDSYNC FFSYNC
+#define FRSYNC FFSYNC
+#define FSYNC FFSYNC
+#define FOFFMAX 0x00
+#define FIGNORECASE 0x00
static __inline int
-zfs_vn_open(char *pnamep, enum uio_seg seg, int filemode, int createmode,
- vnode_t **vpp, enum create crwhy, mode_t umask)
+vn_openat(char *pnamep, enum uio_seg seg, int filemode, int createmode,
+ vnode_t **vpp, enum create crwhy, mode_t umask, struct vnode *startvp,
+ int fd)
{
struct thread *td = curthread;
struct nameidata nd;
- int error;
+ int error, operation;
ASSERT(seg == UIO_SYSSPACE);
- ASSERT(filemode == (FWRITE | FCREAT | FTRUNC | FOFFMAX));
- ASSERT(crwhy == CRCREAT);
+ if ((filemode & FCREAT) != 0) {
+ ASSERT(filemode == (FWRITE | FCREAT | FTRUNC | FOFFMAX));
+ ASSERT(crwhy == CRCREAT);
+ operation = CREATE;
+ } else {
+ ASSERT(filemode == (FREAD | FWRITE | FOFFMAX));
+ ASSERT(crwhy == 0);
+ operation = LOOKUP;
+ }
ASSERT(umask == 0);
if (td->td_proc->p_fd->fd_rdir == NULL)
@@ -169,7 +169,10 @@ zfs_vn_open(char *pnamep, enum uio_seg seg, int filemode, int createmode,
if (td->td_proc->p_fd->fd_cdir == NULL)
td->td_proc->p_fd->fd_cdir = rootvnode;
- NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, pnamep, td);
+ if (startvp != NULL)
+ vref(startvp);
+ NDINIT_ATVP(&nd, operation, NOFOLLOW | MPSAFE, UIO_SYSSPACE, pnamep,
+ startvp, td);
error = vn_open_cred(&nd, &filemode, createmode, td->td_ucred, NULL);
NDFREE(&nd, NDF_ONLY_PNBUF);
if (error == 0) {
@@ -180,6 +183,15 @@ zfs_vn_open(char *pnamep, enum uio_seg seg, int filemode, int createmode,
}
return (error);
}
+
+static __inline int
+zfs_vn_open(char *pnamep, enum uio_seg seg, int filemode, int createmode,
+ vnode_t **vpp, enum create crwhy, mode_t umask)
+{
+
+ return (vn_openat(pnamep, seg, filemode, createmode, vpp, crwhy,
+ umask, NULL, -1));
+}
#define vn_open(pnamep, seg, filemode, createmode, vpp, crwhy, umask) \
zfs_vn_open((pnamep), (seg), (filemode), (createmode), (vpp), (crwhy), (umask))
@@ -192,14 +204,16 @@ zfs_vn_rdwr(enum uio_rw rw, vnode_t *vp, caddr_t base, ssize_t len,
struct thread *td = curthread;
int error, vfslocked, resid;
- ASSERT(rw == UIO_WRITE);
ASSERT(ioflag == 0);
ASSERT(ulimit == RLIM64_INFINITY);
- ioflag = IO_APPEND | IO_UNIT;
-
vfslocked = VFS_LOCK_GIANT(vp->v_mount);
- VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE);
+ if (rw == UIO_WRITE) {
+ ioflag = IO_SYNC;
+ VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE);
+ } else {
+ ioflag = IO_DIRECT;
+ }
error = vn_rdwr(rw, vp, base, len, offset, seg, ioflag, cr, NOCRED,
&resid, td);
VFS_UNLOCK_GIANT(vfslocked);
@@ -229,7 +243,7 @@ drop:
VFS_UNLOCK_GIANT(vfslocked);
return (error);
}
-#define VOP_FSYNC(vp, flag, cr) zfs_vop_fsync((vp), (flag), (cr))
+#define VOP_FSYNC(vp, flag, cr, ct) zfs_vop_fsync((vp), (flag), (cr))
static __inline int
zfs_vop_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr)
@@ -241,7 +255,7 @@ zfs_vop_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr)
return (vn_close(vp, flag, cr, curthread));
}
-#define VOP_CLOSE(vp, oflags, count, offset, cr) \
+#define VOP_CLOSE(vp, oflags, count, offset, cr, ct) \
zfs_vop_close((vp), (oflags), (count), (offset), (cr))
static __inline int
@@ -253,7 +267,6 @@ vn_rename(char *from, char *to, enum uio_seg seg)
return (kern_rename(curthread, from, to, seg));
}
-enum rm { RMFILE };
static __inline int
vn_remove(char *fnamep, enum uio_seg seg, enum rm dirflag)
{
diff --git a/sys/cddl/compat/opensolaris/sys/zone.h b/sys/cddl/compat/opensolaris/sys/zone.h
index 2e47eb1..d761310 100644
--- a/sys/cddl/compat/opensolaris/sys/zone.h
+++ b/sys/cddl/compat/opensolaris/sys/zone.h
@@ -38,9 +38,9 @@
*/
/*
- * Is process in the global zone?
+ * Is thread in the global zone?
*/
-#define INGLOBALZONE(p) (!jailed((p)->p_ucred))
+#define INGLOBALZONE(thread) (!jailed((thread)->td_ucred))
/*
* Attach the given dataset to the given jail.
@@ -61,8 +61,6 @@ extern int zone_dataset_visible(const char *, int *);
#define GLOBAL_ZONEID 0
-extern int getzoneid(void);
-
#endif /* _KERNEL */
#endif /* !_OPENSOLARIS_SYS_ZONE_H_ */
diff --git a/sys/cddl/contrib/opensolaris/common/acl/acl_common.c b/sys/cddl/contrib/opensolaris/common/acl/acl_common.c
index 2f32e7a..e6b6780 100644
--- a/sys/cddl/contrib/opensolaris/common/acl/acl_common.c
+++ b/sys/cddl/contrib/opensolaris/common/acl/acl_common.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,100 +19,245 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
-#include <sys/acl.h>
#include <sys/stat.h>
+#include <sys/avl.h>
+#include <sys/misc.h>
#if defined(_KERNEL)
+#include <sys/kmem.h>
#include <sys/systm.h>
+#include <sys/sysmacros.h>
+#include <acl/acl_common.h>
#include <sys/debug.h>
#else
#include <errno.h>
#include <stdlib.h>
+#include <stddef.h>
#include <strings.h>
+#include <unistd.h>
#include <assert.h>
+#include <grp.h>
+#include <pwd.h>
+#include <acl_common.h>
#define ASSERT assert
#endif
+#define ACE_POSIX_SUPPORTED_BITS (ACE_READ_DATA | \
+ ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_EXECUTE | \
+ ACE_READ_ATTRIBUTES | ACE_READ_ACL | ACE_WRITE_ACL)
+
+
+#define ACL_SYNCHRONIZE_SET_DENY 0x0000001
+#define ACL_SYNCHRONIZE_SET_ALLOW 0x0000002
+#define ACL_SYNCHRONIZE_ERR_DENY 0x0000004
+#define ACL_SYNCHRONIZE_ERR_ALLOW 0x0000008
+
+#define ACL_WRITE_OWNER_SET_DENY 0x0000010
+#define ACL_WRITE_OWNER_SET_ALLOW 0x0000020
+#define ACL_WRITE_OWNER_ERR_DENY 0x0000040
+#define ACL_WRITE_OWNER_ERR_ALLOW 0x0000080
+
+#define ACL_DELETE_SET_DENY 0x0000100
+#define ACL_DELETE_SET_ALLOW 0x0000200
+#define ACL_DELETE_ERR_DENY 0x0000400
+#define ACL_DELETE_ERR_ALLOW 0x0000800
+
+#define ACL_WRITE_ATTRS_OWNER_SET_DENY 0x0001000
+#define ACL_WRITE_ATTRS_OWNER_SET_ALLOW 0x0002000
+#define ACL_WRITE_ATTRS_OWNER_ERR_DENY 0x0004000
+#define ACL_WRITE_ATTRS_OWNER_ERR_ALLOW 0x0008000
+
+#define ACL_WRITE_ATTRS_WRITER_SET_DENY 0x0010000
+#define ACL_WRITE_ATTRS_WRITER_SET_ALLOW 0x0020000
+#define ACL_WRITE_ATTRS_WRITER_ERR_DENY 0x0040000
+#define ACL_WRITE_ATTRS_WRITER_ERR_ALLOW 0x0080000
+
+#define ACL_WRITE_NAMED_WRITER_SET_DENY 0x0100000
+#define ACL_WRITE_NAMED_WRITER_SET_ALLOW 0x0200000
+#define ACL_WRITE_NAMED_WRITER_ERR_DENY 0x0400000
+#define ACL_WRITE_NAMED_WRITER_ERR_ALLOW 0x0800000
+
+#define ACL_READ_NAMED_READER_SET_DENY 0x1000000
+#define ACL_READ_NAMED_READER_SET_ALLOW 0x2000000
+#define ACL_READ_NAMED_READER_ERR_DENY 0x4000000
+#define ACL_READ_NAMED_READER_ERR_ALLOW 0x8000000
+
+
+#define ACE_VALID_MASK_BITS (\
+ ACE_READ_DATA | \
+ ACE_LIST_DIRECTORY | \
+ ACE_WRITE_DATA | \
+ ACE_ADD_FILE | \
+ ACE_APPEND_DATA | \
+ ACE_ADD_SUBDIRECTORY | \
+ ACE_READ_NAMED_ATTRS | \
+ ACE_WRITE_NAMED_ATTRS | \
+ ACE_EXECUTE | \
+ ACE_DELETE_CHILD | \
+ ACE_READ_ATTRIBUTES | \
+ ACE_WRITE_ATTRIBUTES | \
+ ACE_DELETE | \
+ ACE_READ_ACL | \
+ ACE_WRITE_ACL | \
+ ACE_WRITE_OWNER | \
+ ACE_SYNCHRONIZE)
+
+#define ACE_MASK_UNDEFINED 0x80000000
+
+#define ACE_VALID_FLAG_BITS (ACE_FILE_INHERIT_ACE | \
+ ACE_DIRECTORY_INHERIT_ACE | \
+ ACE_NO_PROPAGATE_INHERIT_ACE | ACE_INHERIT_ONLY_ACE | \
+ ACE_SUCCESSFUL_ACCESS_ACE_FLAG | ACE_FAILED_ACCESS_ACE_FLAG | \
+ ACE_IDENTIFIER_GROUP | ACE_OWNER | ACE_GROUP | ACE_EVERYONE)
+
+/*
+ * ACL conversion helpers
+ */
+
+typedef enum {
+ ace_unused,
+ ace_user_obj,
+ ace_user,
+ ace_group, /* includes GROUP and GROUP_OBJ */
+ ace_other_obj
+} ace_to_aent_state_t;
+
+typedef struct acevals {
+ uid_t key;
+ avl_node_t avl;
+ uint32_t mask;
+ uint32_t allowed;
+ uint32_t denied;
+ int aent_type;
+} acevals_t;
+
+typedef struct ace_list {
+ acevals_t user_obj;
+ avl_tree_t user;
+ int numusers;
+ acevals_t group_obj;
+ avl_tree_t group;
+ int numgroups;
+ acevals_t other_obj;
+ uint32_t acl_mask;
+ int hasmask;
+ int dfacl_flag;
+ ace_to_aent_state_t state;
+ int seen; /* bitmask of all aclent_t a_type values seen */
+} ace_list_t;
ace_t trivial_acl[] = {
- {-1, 0, ACE_OWNER, ACE_ACCESS_DENIED_ACE_TYPE},
- {-1, ACE_WRITE_ACL|ACE_WRITE_OWNER|ACE_WRITE_ATTRIBUTES|
+ {(uid_t)-1, 0, ACE_OWNER, ACE_ACCESS_DENIED_ACE_TYPE},
+ {(uid_t)-1, ACE_WRITE_ACL|ACE_WRITE_OWNER|ACE_WRITE_ATTRIBUTES|
ACE_WRITE_NAMED_ATTRS, ACE_OWNER, ACE_ACCESS_ALLOWED_ACE_TYPE},
- {-1, 0, ACE_GROUP|ACE_IDENTIFIER_GROUP, ACE_ACCESS_DENIED_ACE_TYPE},
- {-1, 0, ACE_GROUP|ACE_IDENTIFIER_GROUP, ACE_ACCESS_ALLOWED_ACE_TYPE},
- {-1, ACE_WRITE_ACL|ACE_WRITE_OWNER| ACE_WRITE_ATTRIBUTES|
+ {(uid_t)-1, 0, ACE_GROUP|ACE_IDENTIFIER_GROUP,
+ ACE_ACCESS_DENIED_ACE_TYPE},
+ {(uid_t)-1, 0, ACE_GROUP|ACE_IDENTIFIER_GROUP,
+ ACE_ACCESS_ALLOWED_ACE_TYPE},
+ {(uid_t)-1, ACE_WRITE_ACL|ACE_WRITE_OWNER| ACE_WRITE_ATTRIBUTES|
ACE_WRITE_NAMED_ATTRS, ACE_EVERYONE, ACE_ACCESS_DENIED_ACE_TYPE},
- {-1, ACE_READ_ACL|ACE_READ_ATTRIBUTES|ACE_READ_NAMED_ATTRS|
+ {(uid_t)-1, ACE_READ_ACL|ACE_READ_ATTRIBUTES|ACE_READ_NAMED_ATTRS|
ACE_SYNCHRONIZE, ACE_EVERYONE, ACE_ACCESS_ALLOWED_ACE_TYPE}
};
void
-adjust_ace_pair(ace_t *pair, mode_t mode)
+adjust_ace_pair_common(void *pair, size_t access_off,
+ size_t pairsize, mode_t mode)
{
+ char *datap = (char *)pair;
+ uint32_t *amask0 = (uint32_t *)(uintptr_t)(datap + access_off);
+ uint32_t *amask1 = (uint32_t *)(uintptr_t)(datap + pairsize +
+ access_off);
if (mode & S_IROTH)
- pair[1].a_access_mask |= ACE_READ_DATA;
+ *amask1 |= ACE_READ_DATA;
else
- pair[0].a_access_mask |= ACE_READ_DATA;
+ *amask0 |= ACE_READ_DATA;
if (mode & S_IWOTH)
- pair[1].a_access_mask |=
- ACE_WRITE_DATA|ACE_APPEND_DATA;
+ *amask1 |= ACE_WRITE_DATA|ACE_APPEND_DATA;
else
- pair[0].a_access_mask |=
- ACE_WRITE_DATA|ACE_APPEND_DATA;
+ *amask0 |= ACE_WRITE_DATA|ACE_APPEND_DATA;
if (mode & S_IXOTH)
- pair[1].a_access_mask |= ACE_EXECUTE;
+ *amask1 |= ACE_EXECUTE;
else
- pair[0].a_access_mask |= ACE_EXECUTE;
+ *amask0 |= ACE_EXECUTE;
+}
+
+void
+adjust_ace_pair(ace_t *pair, mode_t mode)
+{
+ adjust_ace_pair_common(pair, offsetof(ace_t, a_access_mask),
+ sizeof (ace_t), mode);
+}
+
+static void
+ace_allow_deny_helper(uint16_t type, boolean_t *allow, boolean_t *deny)
+{
+ if (type == ACE_ACCESS_ALLOWED_ACE_TYPE)
+ *allow = B_TRUE;
+ else if (type == ACE_ACCESS_DENIED_ACE_TYPE)
+ *deny = B_TRUE;
}
/*
* ace_trivial:
* determine whether an ace_t acl is trivial
*
- * Trivialness implys that the acl is composed of only
+ * Trivialness implies that the acl is composed of only
* owner, group, everyone entries. ACL can't
* have read_acl denied, and write_owner/write_acl/write_attributes
* can only be owner@ entry.
*/
int
-ace_trivial(ace_t *acep, int aclcnt)
+ace_trivial_common(void *acep, int aclcnt,
+ uint64_t (*walk)(void *, uint64_t, int aclcnt,
+ uint16_t *, uint16_t *, uint32_t *))
{
- int i;
- int owner_seen = 0;
- int group_seen = 0;
- int everyone_seen = 0;
+ boolean_t owner_allow = B_FALSE;
+ boolean_t group_allow = B_FALSE;
+ boolean_t everyone_allow = B_FALSE;
+ boolean_t owner_deny = B_FALSE;
+ boolean_t group_deny = B_FALSE;
+ boolean_t everyone_deny = B_FALSE;
+ uint16_t flags;
+ uint32_t mask;
+ uint16_t type;
+ uint64_t cookie = 0;
- for (i = 0; i != aclcnt; i++) {
- switch (acep[i].a_flags & 0xf040) {
+ while (cookie = walk(acep, cookie, aclcnt, &flags, &type, &mask)) {
+ switch (flags & ACE_TYPE_FLAGS) {
case ACE_OWNER:
- if (group_seen || everyone_seen)
+ if (group_allow || group_deny || everyone_allow ||
+ everyone_deny)
return (1);
- owner_seen++;
+ ace_allow_deny_helper(type, &owner_allow, &owner_deny);
break;
case ACE_GROUP|ACE_IDENTIFIER_GROUP:
- if (everyone_seen || owner_seen == 0)
+ if (everyone_allow || everyone_deny &&
+ (!owner_allow && !owner_deny))
return (1);
- group_seen++;
+ ace_allow_deny_helper(type, &group_allow, &group_deny);
break;
case ACE_EVERYONE:
- if (owner_seen == 0 || group_seen == 0)
+ if (!owner_allow && !owner_deny &&
+ !group_allow && !group_deny)
return (1);
- everyone_seen++;
+ ace_allow_deny_helper(type,
+ &everyone_allow, &everyone_deny);
break;
default:
return (1);
}
- if (acep[i].a_flags & (ACE_FILE_INHERIT_ACE|
+ if (flags & (ACE_FILE_INHERIT_ACE|
ACE_DIRECTORY_INHERIT_ACE|ACE_NO_PROPAGATE_INHERIT_ACE|
ACE_INHERIT_ONLY_ACE))
return (1);
@@ -124,27 +268,49 @@ ace_trivial(ace_t *acep, int aclcnt)
* Don't allow anybody to deny reading basic
* attributes or a files ACL.
*/
- if ((acep[i].a_access_mask &
- (ACE_READ_ACL|ACE_READ_ATTRIBUTES)) &&
- (acep[i].a_type == ACE_ACCESS_DENIED_ACE_TYPE))
+ if ((mask & (ACE_READ_ACL|ACE_READ_ATTRIBUTES)) &&
+ (type == ACE_ACCESS_DENIED_ACE_TYPE))
return (1);
/*
* Allow on owner@ to allow
* write_acl/write_owner/write_attributes
*/
- if (acep[i].a_type == ACE_ACCESS_ALLOWED_ACE_TYPE &&
- (!(acep[i].a_flags & ACE_OWNER) && (acep[i].a_access_mask &
+ if (type == ACE_ACCESS_ALLOWED_ACE_TYPE &&
+ (!(flags & ACE_OWNER) && (mask &
(ACE_WRITE_OWNER|ACE_WRITE_ACL|ACE_WRITE_ATTRIBUTES))))
return (1);
+
}
- if ((owner_seen == 0) || (group_seen == 0) || (everyone_seen == 0))
- return (1);
+ if (!owner_allow || !owner_deny || !group_allow || !group_deny ||
+ !everyone_allow || !everyone_deny)
+ return (1);
return (0);
}
+uint64_t
+ace_walk(void *datap, uint64_t cookie, int aclcnt, uint16_t *flags,
+ uint16_t *type, uint32_t *mask)
+{
+ ace_t *acep = datap;
+
+ if (cookie >= aclcnt)
+ return (0);
+
+ *flags = acep[cookie].a_flags;
+ *type = acep[cookie].a_type;
+ *mask = acep[cookie++].a_access_mask;
+
+ return (cookie);
+}
+
+int
+ace_trivial(ace_t *acep, int aclcnt)
+{
+ return (ace_trivial_common(acep, aclcnt, ace_walk));
+}
/*
* Generic shellsort, from K&R (1st ed, p 58.), somewhat modified.
@@ -171,8 +337,8 @@ ksort(caddr_t v, int n, int s, int (*f)())
for (g = n / 2; g > 0; g /= 2) {
for (i = g; i < n; i++) {
for (j = i - g; j >= 0 &&
- (*f)(v + j * s, v + (j + g) * s) == 1;
- j -= g) {
+ (*f)(v + j * s, v + (j + g) * s) == 1;
+ j -= g) {
p1 = (void *)(v + j * s);
p2 = (void *)(v + (j + g) * s);
for (ii = 0; ii < s / 4; ii++) {
@@ -215,3 +381,1347 @@ cmp2acls(void *a, void *b)
/* Totally equal */
return (0);
}
+
+/*ARGSUSED*/
+static void *
+cacl_realloc(void *ptr, size_t size, size_t new_size)
+{
+#if defined(_KERNEL)
+ void *tmp;
+
+ tmp = kmem_alloc(new_size, KM_SLEEP);
+ (void) memcpy(tmp, ptr, (size < new_size) ? size : new_size);
+ kmem_free(ptr, size);
+ return (tmp);
+#else
+ return (realloc(ptr, new_size));
+#endif
+}
+
+static int
+cacl_malloc(void **ptr, size_t size)
+{
+#if defined(_KERNEL)
+ *ptr = kmem_zalloc(size, KM_SLEEP);
+ return (0);
+#else
+ *ptr = calloc(1, size);
+ if (*ptr == NULL)
+ return (errno);
+
+ return (0);
+#endif
+}
+
+/*ARGSUSED*/
+static void
+cacl_free(void *ptr, size_t size)
+{
+#if defined(_KERNEL)
+ kmem_free(ptr, size);
+#else
+ free(ptr);
+#endif
+}
+
+acl_t *
+acl_alloc(enum acl_type type)
+{
+ acl_t *aclp;
+
+ if (cacl_malloc((void **)&aclp, sizeof (acl_t)) != 0)
+ return (NULL);
+
+ aclp->acl_aclp = NULL;
+ aclp->acl_cnt = 0;
+
+ switch (type) {
+ case ACE_T:
+ aclp->acl_type = ACE_T;
+ aclp->acl_entry_size = sizeof (ace_t);
+ break;
+ case ACLENT_T:
+ aclp->acl_type = ACLENT_T;
+ aclp->acl_entry_size = sizeof (aclent_t);
+ break;
+ default:
+ acl_free(aclp);
+ aclp = NULL;
+ }
+ return (aclp);
+}
+
+/*
+ * Free acl_t structure
+ */
+void
+acl_free(acl_t *aclp)
+{
+ int acl_size;
+
+ if (aclp == NULL)
+ return;
+
+ if (aclp->acl_aclp) {
+ acl_size = aclp->acl_cnt * aclp->acl_entry_size;
+ cacl_free(aclp->acl_aclp, acl_size);
+ }
+
+ cacl_free(aclp, sizeof (acl_t));
+}
+
+static uint32_t
+access_mask_set(int haswriteperm, int hasreadperm, int isowner, int isallow)
+{
+ uint32_t access_mask = 0;
+ int acl_produce;
+ int synchronize_set = 0, write_owner_set = 0;
+ int delete_set = 0, write_attrs_set = 0;
+ int read_named_set = 0, write_named_set = 0;
+
+ acl_produce = (ACL_SYNCHRONIZE_SET_ALLOW |
+ ACL_WRITE_ATTRS_OWNER_SET_ALLOW |
+ ACL_WRITE_ATTRS_WRITER_SET_DENY);
+
+ if (isallow) {
+ synchronize_set = ACL_SYNCHRONIZE_SET_ALLOW;
+ write_owner_set = ACL_WRITE_OWNER_SET_ALLOW;
+ delete_set = ACL_DELETE_SET_ALLOW;
+ if (hasreadperm)
+ read_named_set = ACL_READ_NAMED_READER_SET_ALLOW;
+ if (haswriteperm)
+ write_named_set = ACL_WRITE_NAMED_WRITER_SET_ALLOW;
+ if (isowner)
+ write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_ALLOW;
+ else if (haswriteperm)
+ write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_ALLOW;
+ } else {
+
+ synchronize_set = ACL_SYNCHRONIZE_SET_DENY;
+ write_owner_set = ACL_WRITE_OWNER_SET_DENY;
+ delete_set = ACL_DELETE_SET_DENY;
+ if (hasreadperm)
+ read_named_set = ACL_READ_NAMED_READER_SET_DENY;
+ if (haswriteperm)
+ write_named_set = ACL_WRITE_NAMED_WRITER_SET_DENY;
+ if (isowner)
+ write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_DENY;
+ else if (haswriteperm)
+ write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_DENY;
+ else
+ /*
+ * If the entity is not the owner and does not
+ * have write permissions ACE_WRITE_ATTRIBUTES will
+ * always go in the DENY ACE.
+ */
+ access_mask |= ACE_WRITE_ATTRIBUTES;
+ }
+
+ if (acl_produce & synchronize_set)
+ access_mask |= ACE_SYNCHRONIZE;
+ if (acl_produce & write_owner_set)
+ access_mask |= ACE_WRITE_OWNER;
+ if (acl_produce & delete_set)
+ access_mask |= ACE_DELETE;
+ if (acl_produce & write_attrs_set)
+ access_mask |= ACE_WRITE_ATTRIBUTES;
+ if (acl_produce & read_named_set)
+ access_mask |= ACE_READ_NAMED_ATTRS;
+ if (acl_produce & write_named_set)
+ access_mask |= ACE_WRITE_NAMED_ATTRS;
+
+ return (access_mask);
+}
+
+/*
+ * Given an mode_t, convert it into an access_mask as used
+ * by nfsace, assuming aclent_t -> nfsace semantics.
+ */
+static uint32_t
+mode_to_ace_access(mode_t mode, int isdir, int isowner, int isallow)
+{
+ uint32_t access = 0;
+ int haswriteperm = 0;
+ int hasreadperm = 0;
+
+ if (isallow) {
+ haswriteperm = (mode & S_IWOTH);
+ hasreadperm = (mode & S_IROTH);
+ } else {
+ haswriteperm = !(mode & S_IWOTH);
+ hasreadperm = !(mode & S_IROTH);
+ }
+
+ /*
+ * The following call takes care of correctly setting the following
+ * mask bits in the access_mask:
+ * ACE_SYNCHRONIZE, ACE_WRITE_OWNER, ACE_DELETE,
+ * ACE_WRITE_ATTRIBUTES, ACE_WRITE_NAMED_ATTRS, ACE_READ_NAMED_ATTRS
+ */
+ access = access_mask_set(haswriteperm, hasreadperm, isowner, isallow);
+
+ if (isallow) {
+ access |= ACE_READ_ACL | ACE_READ_ATTRIBUTES;
+ if (isowner)
+ access |= ACE_WRITE_ACL;
+ } else {
+ if (! isowner)
+ access |= ACE_WRITE_ACL;
+ }
+
+ /* read */
+ if (mode & S_IROTH) {
+ access |= ACE_READ_DATA;
+ }
+ /* write */
+ if (mode & S_IWOTH) {
+ access |= ACE_WRITE_DATA |
+ ACE_APPEND_DATA;
+ if (isdir)
+ access |= ACE_DELETE_CHILD;
+ }
+ /* exec */
+ if (mode & 01) {
+ access |= ACE_EXECUTE;
+ }
+
+ return (access);
+}
+
+/*
+ * Given an nfsace (presumably an ALLOW entry), make a
+ * corresponding DENY entry at the address given.
+ */
+static void
+ace_make_deny(ace_t *allow, ace_t *deny, int isdir, int isowner)
+{
+ (void) memcpy(deny, allow, sizeof (ace_t));
+
+ deny->a_who = allow->a_who;
+
+ deny->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
+ deny->a_access_mask ^= ACE_POSIX_SUPPORTED_BITS;
+ if (isdir)
+ deny->a_access_mask ^= ACE_DELETE_CHILD;
+
+ deny->a_access_mask &= ~(ACE_SYNCHRONIZE | ACE_WRITE_OWNER |
+ ACE_DELETE | ACE_WRITE_ATTRIBUTES | ACE_READ_NAMED_ATTRS |
+ ACE_WRITE_NAMED_ATTRS);
+ deny->a_access_mask |= access_mask_set((allow->a_access_mask &
+ ACE_WRITE_DATA), (allow->a_access_mask & ACE_READ_DATA), isowner,
+ B_FALSE);
+}
+/*
+ * Make an initial pass over an array of aclent_t's. Gather
+ * information such as an ACL_MASK (if any), number of users,
+ * number of groups, and whether the array needs to be sorted.
+ */
+static int
+ln_aent_preprocess(aclent_t *aclent, int n,
+ int *hasmask, mode_t *mask,
+ int *numuser, int *numgroup, int *needsort)
+{
+ int error = 0;
+ int i;
+ int curtype = 0;
+
+ *hasmask = 0;
+ *mask = 07;
+ *needsort = 0;
+ *numuser = 0;
+ *numgroup = 0;
+
+ for (i = 0; i < n; i++) {
+ if (aclent[i].a_type < curtype)
+ *needsort = 1;
+ else if (aclent[i].a_type > curtype)
+ curtype = aclent[i].a_type;
+ if (aclent[i].a_type & USER)
+ (*numuser)++;
+ if (aclent[i].a_type & (GROUP | GROUP_OBJ))
+ (*numgroup)++;
+ if (aclent[i].a_type & CLASS_OBJ) {
+ if (*hasmask) {
+ error = EINVAL;
+ goto out;
+ } else {
+ *hasmask = 1;
+ *mask = aclent[i].a_perm;
+ }
+ }
+ }
+
+ if ((! *hasmask) && (*numuser + *numgroup > 1)) {
+ error = EINVAL;
+ goto out;
+ }
+
+out:
+ return (error);
+}
+
+/*
+ * Convert an array of aclent_t into an array of nfsace entries,
+ * following POSIX draft -> nfsv4 conversion semantics as outlined in
+ * the IETF draft.
+ */
+static int
+ln_aent_to_ace(aclent_t *aclent, int n, ace_t **acepp, int *rescount, int isdir)
+{
+ int error = 0;
+ mode_t mask;
+ int numuser, numgroup, needsort;
+ int resultsize = 0;
+ int i, groupi = 0, skip;
+ ace_t *acep, *result = NULL;
+ int hasmask;
+
+ error = ln_aent_preprocess(aclent, n, &hasmask, &mask,
+ &numuser, &numgroup, &needsort);
+ if (error != 0)
+ goto out;
+
+ /* allow + deny for each aclent */
+ resultsize = n * 2;
+ if (hasmask) {
+ /*
+ * stick extra deny on the group_obj and on each
+ * user|group for the mask (the group_obj was added
+ * into the count for numgroup)
+ */
+ resultsize += numuser + numgroup;
+ /* ... and don't count the mask itself */
+ resultsize -= 2;
+ }
+
+ /* sort the source if necessary */
+ if (needsort)
+ ksort((caddr_t)aclent, n, sizeof (aclent_t), cmp2acls);
+
+ if (cacl_malloc((void **)&result, resultsize * sizeof (ace_t)) != 0)
+ goto out;
+
+ acep = result;
+
+ for (i = 0; i < n; i++) {
+ /*
+ * don't process CLASS_OBJ (mask); mask was grabbed in
+ * ln_aent_preprocess()
+ */
+ if (aclent[i].a_type & CLASS_OBJ)
+ continue;
+
+ /* If we need an ACL_MASK emulator, prepend it now */
+ if ((hasmask) &&
+ (aclent[i].a_type & (USER | GROUP | GROUP_OBJ))) {
+ acep->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
+ acep->a_flags = 0;
+ if (aclent[i].a_type & GROUP_OBJ) {
+ acep->a_who = (uid_t)-1;
+ acep->a_flags |=
+ (ACE_IDENTIFIER_GROUP|ACE_GROUP);
+ } else if (aclent[i].a_type & USER) {
+ acep->a_who = aclent[i].a_id;
+ } else {
+ acep->a_who = aclent[i].a_id;
+ acep->a_flags |= ACE_IDENTIFIER_GROUP;
+ }
+ if (aclent[i].a_type & ACL_DEFAULT) {
+ acep->a_flags |= ACE_INHERIT_ONLY_ACE |
+ ACE_FILE_INHERIT_ACE |
+ ACE_DIRECTORY_INHERIT_ACE;
+ }
+ /*
+ * Set the access mask for the prepended deny
+ * ace. To do this, we invert the mask (found
+ * in ln_aent_preprocess()) then convert it to an
+ * DENY ace access_mask.
+ */
+ acep->a_access_mask = mode_to_ace_access((mask ^ 07),
+ isdir, 0, 0);
+ acep += 1;
+ }
+
+ /* handle a_perm -> access_mask */
+ acep->a_access_mask = mode_to_ace_access(aclent[i].a_perm,
+ isdir, aclent[i].a_type & USER_OBJ, 1);
+
+ /* emulate a default aclent */
+ if (aclent[i].a_type & ACL_DEFAULT) {
+ acep->a_flags |= ACE_INHERIT_ONLY_ACE |
+ ACE_FILE_INHERIT_ACE |
+ ACE_DIRECTORY_INHERIT_ACE;
+ }
+
+ /*
+ * handle a_perm and a_id
+ *
+ * this must be done last, since it involves the
+ * corresponding deny aces, which are handled
+ * differently for each different a_type.
+ */
+ if (aclent[i].a_type & USER_OBJ) {
+ acep->a_who = (uid_t)-1;
+ acep->a_flags |= ACE_OWNER;
+ ace_make_deny(acep, acep + 1, isdir, B_TRUE);
+ acep += 2;
+ } else if (aclent[i].a_type & USER) {
+ acep->a_who = aclent[i].a_id;
+ ace_make_deny(acep, acep + 1, isdir, B_FALSE);
+ acep += 2;
+ } else if (aclent[i].a_type & (GROUP_OBJ | GROUP)) {
+ if (aclent[i].a_type & GROUP_OBJ) {
+ acep->a_who = (uid_t)-1;
+ acep->a_flags |= ACE_GROUP;
+ } else {
+ acep->a_who = aclent[i].a_id;
+ }
+ acep->a_flags |= ACE_IDENTIFIER_GROUP;
+ /*
+ * Set the corresponding deny for the group ace.
+ *
+ * The deny aces go after all of the groups, unlike
+ * everything else, where they immediately follow
+ * the allow ace.
+ *
+ * We calculate "skip", the number of slots to
+ * skip ahead for the deny ace, here.
+ *
+ * The pattern is:
+ * MD1 A1 MD2 A2 MD3 A3 D1 D2 D3
+ * thus, skip is
+ * (2 * numgroup) - 1 - groupi
+ * (2 * numgroup) to account for MD + A
+ * - 1 to account for the fact that we're on the
+ * access (A), not the mask (MD)
+ * - groupi to account for the fact that we have
+ * passed up groupi number of MD's.
+ */
+ skip = (2 * numgroup) - 1 - groupi;
+ ace_make_deny(acep, acep + skip, isdir, B_FALSE);
+ /*
+ * If we just did the last group, skip acep past
+ * all of the denies; else, just move ahead one.
+ */
+ if (++groupi >= numgroup)
+ acep += numgroup + 1;
+ else
+ acep += 1;
+ } else if (aclent[i].a_type & OTHER_OBJ) {
+ acep->a_who = (uid_t)-1;
+ acep->a_flags |= ACE_EVERYONE;
+ ace_make_deny(acep, acep + 1, isdir, B_FALSE);
+ acep += 2;
+ } else {
+ error = EINVAL;
+ goto out;
+ }
+ }
+
+ *acepp = result;
+ *rescount = resultsize;
+
+out:
+ if (error != 0) {
+ if ((result != NULL) && (resultsize > 0)) {
+ cacl_free(result, resultsize * sizeof (ace_t));
+ }
+ }
+
+ return (error);
+}
+
+static int
+convert_aent_to_ace(aclent_t *aclentp, int aclcnt, int isdir,
+ ace_t **retacep, int *retacecnt)
+{
+ ace_t *acep;
+ ace_t *dfacep;
+ int acecnt = 0;
+ int dfacecnt = 0;
+ int dfaclstart = 0;
+ int dfaclcnt = 0;
+ aclent_t *aclp;
+ int i;
+ int error;
+ int acesz, dfacesz;
+
+ ksort((caddr_t)aclentp, aclcnt, sizeof (aclent_t), cmp2acls);
+
+ for (i = 0, aclp = aclentp; i < aclcnt; aclp++, i++) {
+ if (aclp->a_type & ACL_DEFAULT)
+ break;
+ }
+
+ if (i < aclcnt) {
+ dfaclstart = i;
+ dfaclcnt = aclcnt - i;
+ }
+
+ if (dfaclcnt && isdir == 0) {
+ return (EINVAL);
+ }
+
+ error = ln_aent_to_ace(aclentp, i, &acep, &acecnt, isdir);
+ if (error)
+ return (error);
+
+ if (dfaclcnt) {
+ error = ln_aent_to_ace(&aclentp[dfaclstart], dfaclcnt,
+ &dfacep, &dfacecnt, isdir);
+ if (error) {
+ if (acep) {
+ cacl_free(acep, acecnt * sizeof (ace_t));
+ }
+ return (error);
+ }
+ }
+
+ if (dfacecnt != 0) {
+ acesz = sizeof (ace_t) * acecnt;
+ dfacesz = sizeof (ace_t) * dfacecnt;
+ acep = cacl_realloc(acep, acesz, acesz + dfacesz);
+ if (acep == NULL)
+ return (ENOMEM);
+ if (dfaclcnt) {
+ (void) memcpy(acep + acecnt, dfacep, dfacesz);
+ }
+ }
+ if (dfaclcnt)
+ cacl_free(dfacep, dfacecnt * sizeof (ace_t));
+
+ *retacecnt = acecnt + dfacecnt;
+ *retacep = acep;
+ return (0);
+}
+
+static int
+ace_mask_to_mode(uint32_t mask, o_mode_t *modep, int isdir)
+{
+ int error = 0;
+ o_mode_t mode = 0;
+ uint32_t bits, wantbits;
+
+ /* read */
+ if (mask & ACE_READ_DATA)
+ mode |= S_IROTH;
+
+ /* write */
+ wantbits = (ACE_WRITE_DATA | ACE_APPEND_DATA);
+ if (isdir)
+ wantbits |= ACE_DELETE_CHILD;
+ bits = mask & wantbits;
+ if (bits != 0) {
+ if (bits != wantbits) {
+ error = ENOTSUP;
+ goto out;
+ }
+ mode |= S_IWOTH;
+ }
+
+ /* exec */
+ if (mask & ACE_EXECUTE) {
+ mode |= S_IXOTH;
+ }
+
+ *modep = mode;
+
+out:
+ return (error);
+}
+
+static void
+acevals_init(acevals_t *vals, uid_t key)
+{
+ bzero(vals, sizeof (*vals));
+ vals->allowed = ACE_MASK_UNDEFINED;
+ vals->denied = ACE_MASK_UNDEFINED;
+ vals->mask = ACE_MASK_UNDEFINED;
+ vals->key = key;
+}
+
+static void
+ace_list_init(ace_list_t *al, int dfacl_flag)
+{
+ acevals_init(&al->user_obj, 0);
+ acevals_init(&al->group_obj, 0);
+ acevals_init(&al->other_obj, 0);
+ al->numusers = 0;
+ al->numgroups = 0;
+ al->acl_mask = 0;
+ al->hasmask = 0;
+ al->state = ace_unused;
+ al->seen = 0;
+ al->dfacl_flag = dfacl_flag;
+}
+
+/*
+ * Find or create an acevals holder for a given id and avl tree.
+ *
+ * Note that only one thread will ever touch these avl trees, so
+ * there is no need for locking.
+ */
+static acevals_t *
+acevals_find(ace_t *ace, avl_tree_t *avl, int *num)
+{
+ acevals_t key, *rc;
+ avl_index_t where;
+
+ key.key = ace->a_who;
+ rc = avl_find(avl, &key, &where);
+ if (rc != NULL)
+ return (rc);
+
+ /* this memory is freed by ln_ace_to_aent()->ace_list_free() */
+ if (cacl_malloc((void **)&rc, sizeof (acevals_t)) != 0)
+ return (NULL);
+
+ acevals_init(rc, ace->a_who);
+ avl_insert(avl, rc, where);
+ (*num)++;
+
+ return (rc);
+}
+
+static int
+access_mask_check(ace_t *acep, int mask_bit, int isowner)
+{
+ int set_deny, err_deny;
+ int set_allow, err_allow;
+ int acl_consume;
+ int haswriteperm, hasreadperm;
+
+ if (acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) {
+ haswriteperm = (acep->a_access_mask & ACE_WRITE_DATA) ? 0 : 1;
+ hasreadperm = (acep->a_access_mask & ACE_READ_DATA) ? 0 : 1;
+ } else {
+ haswriteperm = (acep->a_access_mask & ACE_WRITE_DATA) ? 1 : 0;
+ hasreadperm = (acep->a_access_mask & ACE_READ_DATA) ? 1 : 0;
+ }
+
+ acl_consume = (ACL_SYNCHRONIZE_ERR_DENY |
+ ACL_DELETE_ERR_DENY |
+ ACL_WRITE_OWNER_ERR_DENY |
+ ACL_WRITE_OWNER_ERR_ALLOW |
+ ACL_WRITE_ATTRS_OWNER_SET_ALLOW |
+ ACL_WRITE_ATTRS_OWNER_ERR_DENY |
+ ACL_WRITE_ATTRS_WRITER_SET_DENY |
+ ACL_WRITE_ATTRS_WRITER_ERR_ALLOW |
+ ACL_WRITE_NAMED_WRITER_ERR_DENY |
+ ACL_READ_NAMED_READER_ERR_DENY);
+
+ if (mask_bit == ACE_SYNCHRONIZE) {
+ set_deny = ACL_SYNCHRONIZE_SET_DENY;
+ err_deny = ACL_SYNCHRONIZE_ERR_DENY;
+ set_allow = ACL_SYNCHRONIZE_SET_ALLOW;
+ err_allow = ACL_SYNCHRONIZE_ERR_ALLOW;
+ } else if (mask_bit == ACE_WRITE_OWNER) {
+ set_deny = ACL_WRITE_OWNER_SET_DENY;
+ err_deny = ACL_WRITE_OWNER_ERR_DENY;
+ set_allow = ACL_WRITE_OWNER_SET_ALLOW;
+ err_allow = ACL_WRITE_OWNER_ERR_ALLOW;
+ } else if (mask_bit == ACE_DELETE) {
+ set_deny = ACL_DELETE_SET_DENY;
+ err_deny = ACL_DELETE_ERR_DENY;
+ set_allow = ACL_DELETE_SET_ALLOW;
+ err_allow = ACL_DELETE_ERR_ALLOW;
+ } else if (mask_bit == ACE_WRITE_ATTRIBUTES) {
+ if (isowner) {
+ set_deny = ACL_WRITE_ATTRS_OWNER_SET_DENY;
+ err_deny = ACL_WRITE_ATTRS_OWNER_ERR_DENY;
+ set_allow = ACL_WRITE_ATTRS_OWNER_SET_ALLOW;
+ err_allow = ACL_WRITE_ATTRS_OWNER_ERR_ALLOW;
+ } else if (haswriteperm) {
+ set_deny = ACL_WRITE_ATTRS_WRITER_SET_DENY;
+ err_deny = ACL_WRITE_ATTRS_WRITER_ERR_DENY;
+ set_allow = ACL_WRITE_ATTRS_WRITER_SET_ALLOW;
+ err_allow = ACL_WRITE_ATTRS_WRITER_ERR_ALLOW;
+ } else {
+ if ((acep->a_access_mask & mask_bit) &&
+ (acep->a_type & ACE_ACCESS_ALLOWED_ACE_TYPE)) {
+ return (ENOTSUP);
+ }
+ return (0);
+ }
+ } else if (mask_bit == ACE_READ_NAMED_ATTRS) {
+ if (!hasreadperm)
+ return (0);
+
+ set_deny = ACL_READ_NAMED_READER_SET_DENY;
+ err_deny = ACL_READ_NAMED_READER_ERR_DENY;
+ set_allow = ACL_READ_NAMED_READER_SET_ALLOW;
+ err_allow = ACL_READ_NAMED_READER_ERR_ALLOW;
+ } else if (mask_bit == ACE_WRITE_NAMED_ATTRS) {
+ if (!haswriteperm)
+ return (0);
+
+ set_deny = ACL_WRITE_NAMED_WRITER_SET_DENY;
+ err_deny = ACL_WRITE_NAMED_WRITER_ERR_DENY;
+ set_allow = ACL_WRITE_NAMED_WRITER_SET_ALLOW;
+ err_allow = ACL_WRITE_NAMED_WRITER_ERR_ALLOW;
+ } else {
+ return (EINVAL);
+ }
+
+ if (acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) {
+ if (acl_consume & set_deny) {
+ if (!(acep->a_access_mask & mask_bit)) {
+ return (ENOTSUP);
+ }
+ } else if (acl_consume & err_deny) {
+ if (acep->a_access_mask & mask_bit) {
+ return (ENOTSUP);
+ }
+ }
+ } else {
+ /* ACE_ACCESS_ALLOWED_ACE_TYPE */
+ if (acl_consume & set_allow) {
+ if (!(acep->a_access_mask & mask_bit)) {
+ return (ENOTSUP);
+ }
+ } else if (acl_consume & err_allow) {
+ if (acep->a_access_mask & mask_bit) {
+ return (ENOTSUP);
+ }
+ }
+ }
+ return (0);
+}
+
+static int
+ace_to_aent_legal(ace_t *acep)
+{
+ int error = 0;
+ int isowner;
+
+ /* only ALLOW or DENY */
+ if ((acep->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE) &&
+ (acep->a_type != ACE_ACCESS_DENIED_ACE_TYPE)) {
+ error = ENOTSUP;
+ goto out;
+ }
+
+ /* check for invalid flags */
+ if (acep->a_flags & ~(ACE_VALID_FLAG_BITS)) {
+ error = EINVAL;
+ goto out;
+ }
+
+ /* some flags are illegal */
+ if (acep->a_flags & (ACE_SUCCESSFUL_ACCESS_ACE_FLAG |
+ ACE_FAILED_ACCESS_ACE_FLAG |
+ ACE_NO_PROPAGATE_INHERIT_ACE)) {
+ error = ENOTSUP;
+ goto out;
+ }
+
+ /* check for invalid masks */
+ if (acep->a_access_mask & ~(ACE_VALID_MASK_BITS)) {
+ error = EINVAL;
+ goto out;
+ }
+
+ if ((acep->a_flags & ACE_OWNER)) {
+ isowner = 1;
+ } else {
+ isowner = 0;
+ }
+
+ error = access_mask_check(acep, ACE_SYNCHRONIZE, isowner);
+ if (error)
+ goto out;
+
+ error = access_mask_check(acep, ACE_WRITE_OWNER, isowner);
+ if (error)
+ goto out;
+
+ error = access_mask_check(acep, ACE_DELETE, isowner);
+ if (error)
+ goto out;
+
+ error = access_mask_check(acep, ACE_WRITE_ATTRIBUTES, isowner);
+ if (error)
+ goto out;
+
+ error = access_mask_check(acep, ACE_READ_NAMED_ATTRS, isowner);
+ if (error)
+ goto out;
+
+ error = access_mask_check(acep, ACE_WRITE_NAMED_ATTRS, isowner);
+ if (error)
+ goto out;
+
+ /* more detailed checking of masks */
+ if (acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) {
+ if (! (acep->a_access_mask & ACE_READ_ATTRIBUTES)) {
+ error = ENOTSUP;
+ goto out;
+ }
+ if ((acep->a_access_mask & ACE_WRITE_DATA) &&
+ (! (acep->a_access_mask & ACE_APPEND_DATA))) {
+ error = ENOTSUP;
+ goto out;
+ }
+ if ((! (acep->a_access_mask & ACE_WRITE_DATA)) &&
+ (acep->a_access_mask & ACE_APPEND_DATA)) {
+ error = ENOTSUP;
+ goto out;
+ }
+ }
+
+ /* ACL enforcement */
+ if ((acep->a_access_mask & ACE_READ_ACL) &&
+ (acep->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE)) {
+ error = ENOTSUP;
+ goto out;
+ }
+ if (acep->a_access_mask & ACE_WRITE_ACL) {
+ if ((acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) &&
+ (isowner)) {
+ error = ENOTSUP;
+ goto out;
+ }
+ if ((acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) &&
+ (! isowner)) {
+ error = ENOTSUP;
+ goto out;
+ }
+ }
+
+out:
+ return (error);
+}
+
+static int
+ace_allow_to_mode(uint32_t mask, o_mode_t *modep, int isdir)
+{
+ /* ACE_READ_ACL and ACE_READ_ATTRIBUTES must both be set */
+ if ((mask & (ACE_READ_ACL | ACE_READ_ATTRIBUTES)) !=
+ (ACE_READ_ACL | ACE_READ_ATTRIBUTES)) {
+ return (ENOTSUP);
+ }
+
+ return (ace_mask_to_mode(mask, modep, isdir));
+}
+
+static int
+acevals_to_aent(acevals_t *vals, aclent_t *dest, ace_list_t *list,
+ uid_t owner, gid_t group, int isdir)
+{
+ int error;
+ uint32_t flips = ACE_POSIX_SUPPORTED_BITS;
+
+ if (isdir)
+ flips |= ACE_DELETE_CHILD;
+ if (vals->allowed != (vals->denied ^ flips)) {
+ error = ENOTSUP;
+ goto out;
+ }
+ if ((list->hasmask) && (list->acl_mask != vals->mask) &&
+ (vals->aent_type & (USER | GROUP | GROUP_OBJ))) {
+ error = ENOTSUP;
+ goto out;
+ }
+ error = ace_allow_to_mode(vals->allowed, &dest->a_perm, isdir);
+ if (error != 0)
+ goto out;
+ dest->a_type = vals->aent_type;
+ if (dest->a_type & (USER | GROUP)) {
+ dest->a_id = vals->key;
+ } else if (dest->a_type & USER_OBJ) {
+ dest->a_id = owner;
+ } else if (dest->a_type & GROUP_OBJ) {
+ dest->a_id = group;
+ } else if (dest->a_type & OTHER_OBJ) {
+ dest->a_id = 0;
+ } else {
+ error = EINVAL;
+ goto out;
+ }
+
+out:
+ return (error);
+}
+
+
+static int
+ace_list_to_aent(ace_list_t *list, aclent_t **aclentp, int *aclcnt,
+ uid_t owner, gid_t group, int isdir)
+{
+ int error = 0;
+ aclent_t *aent, *result = NULL;
+ acevals_t *vals;
+ int resultcount;
+
+ if ((list->seen & (USER_OBJ | GROUP_OBJ | OTHER_OBJ)) !=
+ (USER_OBJ | GROUP_OBJ | OTHER_OBJ)) {
+ error = ENOTSUP;
+ goto out;
+ }
+ if ((! list->hasmask) && (list->numusers + list->numgroups > 0)) {
+ error = ENOTSUP;
+ goto out;
+ }
+
+ resultcount = 3 + list->numusers + list->numgroups;
+ /*
+ * This must be the same condition as below, when we add the CLASS_OBJ
+ * (aka ACL mask)
+ */
+ if ((list->hasmask) || (! list->dfacl_flag))
+ resultcount += 1;
+
+ if (cacl_malloc((void **)&result,
+ resultcount * sizeof (aclent_t)) != 0) {
+ error = ENOMEM;
+ goto out;
+ }
+ aent = result;
+
+ /* USER_OBJ */
+ if (!(list->user_obj.aent_type & USER_OBJ)) {
+ error = EINVAL;
+ goto out;
+ }
+
+ error = acevals_to_aent(&list->user_obj, aent, list, owner, group,
+ isdir);
+
+ if (error != 0)
+ goto out;
+ ++aent;
+ /* USER */
+ vals = NULL;
+ for (vals = avl_first(&list->user); vals != NULL;
+ vals = AVL_NEXT(&list->user, vals)) {
+ if (!(vals->aent_type & USER)) {
+ error = EINVAL;
+ goto out;
+ }
+ error = acevals_to_aent(vals, aent, list, owner, group,
+ isdir);
+ if (error != 0)
+ goto out;
+ ++aent;
+ }
+ /* GROUP_OBJ */
+ if (!(list->group_obj.aent_type & GROUP_OBJ)) {
+ error = EINVAL;
+ goto out;
+ }
+ error = acevals_to_aent(&list->group_obj, aent, list, owner, group,
+ isdir);
+ if (error != 0)
+ goto out;
+ ++aent;
+ /* GROUP */
+ vals = NULL;
+ for (vals = avl_first(&list->group); vals != NULL;
+ vals = AVL_NEXT(&list->group, vals)) {
+ if (!(vals->aent_type & GROUP)) {
+ error = EINVAL;
+ goto out;
+ }
+ error = acevals_to_aent(vals, aent, list, owner, group,
+ isdir);
+ if (error != 0)
+ goto out;
+ ++aent;
+ }
+ /*
+ * CLASS_OBJ (aka ACL_MASK)
+ *
+ * An ACL_MASK is not fabricated if the ACL is a default ACL.
+ * This is to follow UFS's behavior.
+ */
+ if ((list->hasmask) || (! list->dfacl_flag)) {
+ if (list->hasmask) {
+ uint32_t flips = ACE_POSIX_SUPPORTED_BITS;
+ if (isdir)
+ flips |= ACE_DELETE_CHILD;
+ error = ace_mask_to_mode(list->acl_mask ^ flips,
+ &aent->a_perm, isdir);
+ if (error != 0)
+ goto out;
+ } else {
+ /* fabricate the ACL_MASK from the group permissions */
+ error = ace_mask_to_mode(list->group_obj.allowed,
+ &aent->a_perm, isdir);
+ if (error != 0)
+ goto out;
+ }
+ aent->a_id = 0;
+ aent->a_type = CLASS_OBJ | list->dfacl_flag;
+ ++aent;
+ }
+ /* OTHER_OBJ */
+ if (!(list->other_obj.aent_type & OTHER_OBJ)) {
+ error = EINVAL;
+ goto out;
+ }
+ error = acevals_to_aent(&list->other_obj, aent, list, owner, group,
+ isdir);
+ if (error != 0)
+ goto out;
+ ++aent;
+
+ *aclentp = result;
+ *aclcnt = resultcount;
+
+out:
+ if (error != 0) {
+ if (result != NULL)
+ cacl_free(result, resultcount * sizeof (aclent_t));
+ }
+
+ return (error);
+}
+
+
+/*
+ * free all data associated with an ace_list
+ */
+static void
+ace_list_free(ace_list_t *al)
+{
+ acevals_t *node;
+ void *cookie;
+
+ if (al == NULL)
+ return;
+
+ cookie = NULL;
+ while ((node = avl_destroy_nodes(&al->user, &cookie)) != NULL)
+ cacl_free(node, sizeof (acevals_t));
+ cookie = NULL;
+ while ((node = avl_destroy_nodes(&al->group, &cookie)) != NULL)
+ cacl_free(node, sizeof (acevals_t));
+
+ avl_destroy(&al->user);
+ avl_destroy(&al->group);
+
+ /* free the container itself */
+ cacl_free(al, sizeof (ace_list_t));
+}
+
+static int
+acevals_compare(const void *va, const void *vb)
+{
+ const acevals_t *a = va, *b = vb;
+
+ if (a->key == b->key)
+ return (0);
+
+ if (a->key > b->key)
+ return (1);
+
+ else
+ return (-1);
+}
+
+/*
+ * Convert a list of ace_t entries to equivalent regular and default
+ * aclent_t lists. Return error (ENOTSUP) when conversion is not possible.
+ */
+static int
+ln_ace_to_aent(ace_t *ace, int n, uid_t owner, gid_t group,
+ aclent_t **aclentp, int *aclcnt, aclent_t **dfaclentp, int *dfaclcnt,
+ int isdir)
+{
+ int error = 0;
+ ace_t *acep;
+ uint32_t bits;
+ int i;
+ ace_list_t *normacl = NULL, *dfacl = NULL, *acl;
+ acevals_t *vals;
+
+ *aclentp = NULL;
+ *aclcnt = 0;
+ *dfaclentp = NULL;
+ *dfaclcnt = 0;
+
+ /* we need at least user_obj, group_obj, and other_obj */
+ if (n < 6) {
+ error = ENOTSUP;
+ goto out;
+ }
+ if (ace == NULL) {
+ error = EINVAL;
+ goto out;
+ }
+
+ error = cacl_malloc((void **)&normacl, sizeof (ace_list_t));
+ if (error != 0)
+ goto out;
+
+ avl_create(&normacl->user, acevals_compare, sizeof (acevals_t),
+ offsetof(acevals_t, avl));
+ avl_create(&normacl->group, acevals_compare, sizeof (acevals_t),
+ offsetof(acevals_t, avl));
+
+ ace_list_init(normacl, 0);
+
+ error = cacl_malloc((void **)&dfacl, sizeof (ace_list_t));
+ if (error != 0)
+ goto out;
+
+ avl_create(&dfacl->user, acevals_compare, sizeof (acevals_t),
+ offsetof(acevals_t, avl));
+ avl_create(&dfacl->group, acevals_compare, sizeof (acevals_t),
+ offsetof(acevals_t, avl));
+ ace_list_init(dfacl, ACL_DEFAULT);
+
+ /* process every ace_t... */
+ for (i = 0; i < n; i++) {
+ acep = &ace[i];
+
+ /* rule out certain cases quickly */
+ error = ace_to_aent_legal(acep);
+ if (error != 0)
+ goto out;
+
+ /*
+ * Turn off these bits in order to not have to worry about
+ * them when doing the checks for compliments.
+ */
+ acep->a_access_mask &= ~(ACE_WRITE_OWNER | ACE_DELETE |
+ ACE_SYNCHRONIZE | ACE_WRITE_ATTRIBUTES |
+ ACE_READ_NAMED_ATTRS | ACE_WRITE_NAMED_ATTRS);
+
+ /* see if this should be a regular or default acl */
+ bits = acep->a_flags &
+ (ACE_INHERIT_ONLY_ACE |
+ ACE_FILE_INHERIT_ACE |
+ ACE_DIRECTORY_INHERIT_ACE);
+ if (bits != 0) {
+ /* all or nothing on these inherit bits */
+ if (bits != (ACE_INHERIT_ONLY_ACE |
+ ACE_FILE_INHERIT_ACE |
+ ACE_DIRECTORY_INHERIT_ACE)) {
+ error = ENOTSUP;
+ goto out;
+ }
+ acl = dfacl;
+ } else {
+ acl = normacl;
+ }
+
+ if ((acep->a_flags & ACE_OWNER)) {
+ if (acl->state > ace_user_obj) {
+ error = ENOTSUP;
+ goto out;
+ }
+ acl->state = ace_user_obj;
+ acl->seen |= USER_OBJ;
+ vals = &acl->user_obj;
+ vals->aent_type = USER_OBJ | acl->dfacl_flag;
+ } else if ((acep->a_flags & ACE_EVERYONE)) {
+ acl->state = ace_other_obj;
+ acl->seen |= OTHER_OBJ;
+ vals = &acl->other_obj;
+ vals->aent_type = OTHER_OBJ | acl->dfacl_flag;
+ } else if (acep->a_flags & ACE_IDENTIFIER_GROUP) {
+ if (acl->state > ace_group) {
+ error = ENOTSUP;
+ goto out;
+ }
+ if ((acep->a_flags & ACE_GROUP)) {
+ acl->seen |= GROUP_OBJ;
+ vals = &acl->group_obj;
+ vals->aent_type = GROUP_OBJ | acl->dfacl_flag;
+ } else {
+ acl->seen |= GROUP;
+ vals = acevals_find(acep, &acl->group,
+ &acl->numgroups);
+ if (vals == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ vals->aent_type = GROUP | acl->dfacl_flag;
+ }
+ acl->state = ace_group;
+ } else {
+ if (acl->state > ace_user) {
+ error = ENOTSUP;
+ goto out;
+ }
+ acl->state = ace_user;
+ acl->seen |= USER;
+ vals = acevals_find(acep, &acl->user,
+ &acl->numusers);
+ if (vals == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ vals->aent_type = USER | acl->dfacl_flag;
+ }
+
+ if (!(acl->state > ace_unused)) {
+ error = EINVAL;
+ goto out;
+ }
+
+ if (acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) {
+ /* no more than one allowed per aclent_t */
+ if (vals->allowed != ACE_MASK_UNDEFINED) {
+ error = ENOTSUP;
+ goto out;
+ }
+ vals->allowed = acep->a_access_mask;
+ } else {
+ /*
+ * it's a DENY; if there was a previous DENY, it
+ * must have been an ACL_MASK.
+ */
+ if (vals->denied != ACE_MASK_UNDEFINED) {
+ /* ACL_MASK is for USER and GROUP only */
+ if ((acl->state != ace_user) &&
+ (acl->state != ace_group)) {
+ error = ENOTSUP;
+ goto out;
+ }
+
+ if (! acl->hasmask) {
+ acl->hasmask = 1;
+ acl->acl_mask = vals->denied;
+ /* check for mismatched ACL_MASK emulations */
+ } else if (acl->acl_mask != vals->denied) {
+ error = ENOTSUP;
+ goto out;
+ }
+ vals->mask = vals->denied;
+ }
+ vals->denied = acep->a_access_mask;
+ }
+ }
+
+ /* done collating; produce the aclent_t lists */
+ if (normacl->state != ace_unused) {
+ error = ace_list_to_aent(normacl, aclentp, aclcnt,
+ owner, group, isdir);
+ if (error != 0) {
+ goto out;
+ }
+ }
+ if (dfacl->state != ace_unused) {
+ error = ace_list_to_aent(dfacl, dfaclentp, dfaclcnt,
+ owner, group, isdir);
+ if (error != 0) {
+ goto out;
+ }
+ }
+
+out:
+ if (normacl != NULL)
+ ace_list_free(normacl);
+ if (dfacl != NULL)
+ ace_list_free(dfacl);
+
+ return (error);
+}
+
+static int
+convert_ace_to_aent(ace_t *acebufp, int acecnt, int isdir,
+ uid_t owner, gid_t group, aclent_t **retaclentp, int *retaclcnt)
+{
+ int error = 0;
+ aclent_t *aclentp, *dfaclentp;
+ int aclcnt, dfaclcnt;
+ int aclsz, dfaclsz;
+
+ error = ln_ace_to_aent(acebufp, acecnt, owner, group,
+ &aclentp, &aclcnt, &dfaclentp, &dfaclcnt, isdir);
+
+ if (error)
+ return (error);
+
+
+ if (dfaclcnt != 0) {
+ /*
+ * Slap aclentp and dfaclentp into a single array.
+ */
+ aclsz = sizeof (aclent_t) * aclcnt;
+ dfaclsz = sizeof (aclent_t) * dfaclcnt;
+ aclentp = cacl_realloc(aclentp, aclsz, aclsz + dfaclsz);
+ if (aclentp != NULL) {
+ (void) memcpy(aclentp + aclcnt, dfaclentp, dfaclsz);
+ } else {
+ error = ENOMEM;
+ }
+ }
+
+ if (aclentp) {
+ *retaclentp = aclentp;
+ *retaclcnt = aclcnt + dfaclcnt;
+ }
+
+ if (dfaclentp)
+ cacl_free(dfaclentp, dfaclsz);
+
+ return (error);
+}
+
+
+int
+acl_translate(acl_t *aclp, int target_flavor, int isdir, uid_t owner,
+ gid_t group)
+{
+ int aclcnt;
+ void *acldata;
+ int error;
+
+ /*
+ * See if we need to translate
+ */
+ if ((target_flavor == _ACL_ACE_ENABLED && aclp->acl_type == ACE_T) ||
+ (target_flavor == _ACL_ACLENT_ENABLED &&
+ aclp->acl_type == ACLENT_T))
+ return (0);
+
+ if (target_flavor == -1) {
+ error = EINVAL;
+ goto out;
+ }
+
+ if (target_flavor == _ACL_ACE_ENABLED &&
+ aclp->acl_type == ACLENT_T) {
+ error = convert_aent_to_ace(aclp->acl_aclp,
+ aclp->acl_cnt, isdir, (ace_t **)&acldata, &aclcnt);
+ if (error)
+ goto out;
+
+ } else if (target_flavor == _ACL_ACLENT_ENABLED &&
+ aclp->acl_type == ACE_T) {
+ error = convert_ace_to_aent(aclp->acl_aclp, aclp->acl_cnt,
+ isdir, owner, group, (aclent_t **)&acldata, &aclcnt);
+ if (error)
+ goto out;
+ } else {
+ error = ENOTSUP;
+ goto out;
+ }
+
+ /*
+ * replace old acl with newly translated acl
+ */
+ cacl_free(aclp->acl_aclp, aclp->acl_cnt * aclp->acl_entry_size);
+ aclp->acl_aclp = acldata;
+ aclp->acl_cnt = aclcnt;
+ if (target_flavor == _ACL_ACE_ENABLED) {
+ aclp->acl_type = ACE_T;
+ aclp->acl_entry_size = sizeof (ace_t);
+ } else {
+ aclp->acl_type = ACLENT_T;
+ aclp->acl_entry_size = sizeof (aclent_t);
+ }
+ return (0);
+
+out:
+
+#if !defined(_KERNEL)
+ errno = error;
+ return (-1);
+#else
+ return (error);
+#endif
+}
diff --git a/sys/cddl/contrib/opensolaris/common/acl/acl_common.h b/sys/cddl/contrib/opensolaris/common/acl/acl_common.h
index 2227ad7..84bd04f 100644
--- a/sys/cddl/contrib/opensolaris/common/acl/acl_common.h
+++ b/sys/cddl/contrib/opensolaris/common/acl/acl_common.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,12 +19,12 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#ifndef _ACL_ACL_UTILS_H
-#define _ACL_ACL_UTILS_H
+#ifndef _ACL_COMMON_H
+#define _ACL_COMMON_H
#pragma ident "%Z%%M% %I% %E% SMI"
@@ -34,7 +33,7 @@
#include <sys/acl.h>
#include <sys/stat.h>
-#ifdef __cplusplus
+#ifdef __cplusplus
extern "C" {
#endif
@@ -42,15 +41,21 @@ extern ace_t trivial_acl[6];
extern int acltrivial(const char *);
extern void adjust_ace_pair(ace_t *pair, mode_t mode);
+extern void adjust_ace_pair_common(void *, size_t, size_t, mode_t);
extern int ace_trivial(ace_t *acep, int aclcnt);
+extern int ace_trivial_common(void *, int,
+ uint64_t (*walk)(void *, uint64_t, int aclcnt, uint16_t *, uint16_t *,
+ uint32_t *mask));
+extern acl_t *acl_alloc(acl_type_t);
+extern void acl_free(acl_t *aclp);
+extern int acl_translate(acl_t *aclp, int target_flavor,
+ int isdir, uid_t owner, gid_t group);
void ksort(caddr_t v, int n, int s, int (*f)());
int cmp2acls(void *a, void *b);
-
-
-#ifdef __cplusplus
+#ifdef __cplusplus
}
#endif
-#endif /* _ACL_ACL_UTILS_H */
+#endif /* _ACL_COMMON_H */
diff --git a/sys/cddl/contrib/opensolaris/common/atomic/amd64/atomic.S b/sys/cddl/contrib/opensolaris/common/atomic/amd64/atomic.S
index 2e62aa4..6851086 100644
--- a/sys/cddl/contrib/opensolaris/common/atomic/amd64/atomic.S
+++ b/sys/cddl/contrib/opensolaris/common/atomic/amd64/atomic.S
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,14 +18,13 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
- .ident "%Z%%M% %I% %E% SMI"
-
- .file "%M%"
+ .file "atomic.s"
#define _ASM
#include <sys/asm_linkage.h>
diff --git a/sys/cddl/contrib/opensolaris/common/atomic/i386/atomic.S b/sys/cddl/contrib/opensolaris/common/atomic/i386/atomic.S
index bc7f22a..57f7d0a 100644
--- a/sys/cddl/contrib/opensolaris/common/atomic/i386/atomic.S
+++ b/sys/cddl/contrib/opensolaris/common/atomic/i386/atomic.S
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,18 +18,54 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
- .ident "%Z%%M% %I% %E% SMI"
-
- .file "%M%"
+ .file "atomic.s"
#define _ASM
#include <sys/asm_linkage.h>
+ /*
+ * NOTE: If atomic_dec_64 and atomic_dec_64_nv are ever
+ * separated, it is important to edit the libc i386 platform
+ * specific mapfile and remove the NODYNSORT attribute
+ * from atomic_dec_64_nv.
+ */
+ ENTRY(atomic_dec_64)
+ ALTENTRY(atomic_dec_64_nv)
+ pushl %edi
+ pushl %ebx
+ movl 12(%esp), %edi // %edi = target address
+ movl (%edi), %eax
+ movl 4(%edi), %edx // %edx:%eax = old value
+1:
+ xorl %ebx, %ebx
+ xorl %ecx, %ecx
+ not %ecx
+ not %ebx // %ecx:%ebx = -1
+ addl %eax, %ebx
+ adcl %edx, %ecx // add in the carry from inc
+ lock
+ cmpxchg8b (%edi) // try to stick it in
+ jne 1b
+ movl %ebx, %eax
+ movl %ecx, %edx // return new value
+ popl %ebx
+ popl %edi
+ ret
+ SET_SIZE(atomic_dec_64_nv)
+ SET_SIZE(atomic_dec_64)
+
+ /*
+ * NOTE: If atomic_add_64 and atomic_add_64_nv are ever
+ * separated, it is important to edit the libc i386 platform
+ * specific mapfile and remove the NODYNSORT attribute
+ * from atomic_add_64_nv.
+ */
ENTRY(atomic_add_64)
ALTENTRY(atomic_add_64_nv)
pushl %edi
diff --git a/sys/cddl/contrib/opensolaris/common/avl/avl.c b/sys/cddl/contrib/opensolaris/common/avl/avl.c
index 1fa2236..01aa3cb 100644
--- a/sys/cddl/contrib/opensolaris/common/avl/avl.c
+++ b/sys/cddl/contrib/opensolaris/common/avl/avl.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -808,6 +808,64 @@ avl_remove(avl_tree_t *tree, void *data)
} while (parent != NULL);
}
+#define AVL_REINSERT(tree, obj) \
+ avl_remove((tree), (obj)); \
+ avl_add((tree), (obj))
+
+boolean_t
+avl_update_lt(avl_tree_t *t, void *obj)
+{
+ void *neighbor;
+
+ ASSERT(((neighbor = AVL_NEXT(t, obj)) == NULL) ||
+ (t->avl_compar(obj, neighbor) <= 0));
+
+ neighbor = AVL_PREV(t, obj);
+ if ((neighbor != NULL) && (t->avl_compar(obj, neighbor) < 0)) {
+ AVL_REINSERT(t, obj);
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+boolean_t
+avl_update_gt(avl_tree_t *t, void *obj)
+{
+ void *neighbor;
+
+ ASSERT(((neighbor = AVL_PREV(t, obj)) == NULL) ||
+ (t->avl_compar(obj, neighbor) >= 0));
+
+ neighbor = AVL_NEXT(t, obj);
+ if ((neighbor != NULL) && (t->avl_compar(obj, neighbor) > 0)) {
+ AVL_REINSERT(t, obj);
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+boolean_t
+avl_update(avl_tree_t *t, void *obj)
+{
+ void *neighbor;
+
+ neighbor = AVL_PREV(t, obj);
+ if ((neighbor != NULL) && (t->avl_compar(obj, neighbor) < 0)) {
+ AVL_REINSERT(t, obj);
+ return (B_TRUE);
+ }
+
+ neighbor = AVL_NEXT(t, obj);
+ if ((neighbor != NULL) && (t->avl_compar(obj, neighbor) > 0)) {
+ AVL_REINSERT(t, obj);
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
/*
* initialize a new AVL tree
*/
@@ -853,6 +911,12 @@ avl_numnodes(avl_tree_t *tree)
return (tree->avl_numnodes);
}
+boolean_t
+avl_is_empty(avl_tree_t *tree)
+{
+ ASSERT(tree);
+ return (tree->avl_numnodes == 0);
+}
#define CHILDBIT (1L)
diff --git a/sys/cddl/contrib/opensolaris/common/nvpair/nvpair.c b/sys/cddl/contrib/opensolaris/common/nvpair/nvpair.c
index d3d5bed..9cdc534 100644
--- a/sys/cddl/contrib/opensolaris/common/nvpair/nvpair.c
+++ b/sys/cddl/contrib/opensolaris/common/nvpair/nvpair.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -34,15 +34,17 @@
#if defined(_KERNEL) && !defined(_BOOT)
#include <sys/varargs.h>
+#include <sys/sunddi.h>
#else
#include <stdarg.h>
-#include <strings.h>
+#include <stdlib.h>
+#include <string.h>
#endif
#ifndef offsetof
-#define offsetof(s, m) ((size_t)(&(((s *)0)->m)))
+#define offsetof(s, m) ((size_t)(&(((s *)0)->m)))
#endif
-
+#define skip_whitespace(p) while ((*(p) == ' ') || (*(p) == '\t')) p++
/*
* nvpair.c - Provides kernel & userland interfaces for manipulating
@@ -201,7 +203,7 @@ nv_mem_free(nvpriv_t *nvp, void *buf, size_t size)
static void
nv_priv_init(nvpriv_t *priv, nv_alloc_t *nva, uint32_t stat)
{
- bzero(priv, sizeof (priv));
+ bzero(priv, sizeof (nvpriv_t));
priv->nvp_nva = nva;
priv->nvp_stat = stat;
@@ -395,6 +397,9 @@ i_validate_type_nelem(data_type_t type, uint_t nelem)
case DATA_TYPE_STRING:
case DATA_TYPE_HRTIME:
case DATA_TYPE_NVLIST:
+#if !defined(_KERNEL)
+ case DATA_TYPE_DOUBLE:
+#endif
if (nelem != 1)
return (EINVAL);
break;
@@ -733,6 +738,11 @@ i_get_value_size(data_type_t type, const void *data, uint_t nelem)
case DATA_TYPE_UINT64:
value_sz = sizeof (uint64_t);
break;
+#if !defined(_KERNEL)
+ case DATA_TYPE_DOUBLE:
+ value_sz = sizeof (double);
+ break;
+#endif
case DATA_TYPE_STRING:
if (data == NULL)
value_sz = 0;
@@ -1017,6 +1027,14 @@ nvlist_add_uint64(nvlist_t *nvl, const char *name, uint64_t val)
return (nvlist_add_common(nvl, name, DATA_TYPE_UINT64, 1, &val));
}
+#if !defined(_KERNEL)
+int
+nvlist_add_double(nvlist_t *nvl, const char *name, double val)
+{
+ return (nvlist_add_common(nvl, name, DATA_TYPE_DOUBLE, 1, &val));
+}
+#endif
+
int
nvlist_add_string(nvlist_t *nvl, const char *name, const char *val)
{
@@ -1123,13 +1141,15 @@ nvlist_next_nvpair(nvlist_t *nvl, nvpair_t *nvp)
curr = NVPAIR2I_NVP(nvp);
/*
- * Ensure that nvp is an valid pointer.
+ * Ensure that nvp is a valid nvpair on this nvlist.
+ * NB: nvp_curr is used only as a hint so that we don't always
+ * have to walk the list to determine if nvp is still on the list.
*/
if (nvp == NULL)
curr = priv->nvp_list;
- else if (priv->nvp_curr == curr)
+ else if (priv->nvp_curr == curr || nvlist_contains_nvp(nvl, nvp))
curr = curr->nvi_next;
- else if (nvlist_contains_nvp(nvl, nvp) == 0)
+ else
curr = NULL;
priv->nvp_curr = curr;
@@ -1149,6 +1169,27 @@ nvpair_type(nvpair_t *nvp)
return (NVP_TYPE(nvp));
}
+int
+nvpair_type_is_array(nvpair_t *nvp)
+{
+ data_type_t type = NVP_TYPE(nvp);
+
+ if ((type == DATA_TYPE_BYTE_ARRAY) ||
+ (type == DATA_TYPE_UINT8_ARRAY) ||
+ (type == DATA_TYPE_INT16_ARRAY) ||
+ (type == DATA_TYPE_UINT16_ARRAY) ||
+ (type == DATA_TYPE_INT32_ARRAY) ||
+ (type == DATA_TYPE_UINT32_ARRAY) ||
+ (type == DATA_TYPE_INT64_ARRAY) ||
+ (type == DATA_TYPE_UINT64_ARRAY) ||
+ (type == DATA_TYPE_BOOLEAN_ARRAY) ||
+ (type == DATA_TYPE_STRING_ARRAY) ||
+ (type == DATA_TYPE_NVLIST_ARRAY))
+ return (1);
+ return (0);
+
+}
+
static int
nvpair_value_common(nvpair_t *nvp, data_type_t type, uint_t *nelem, void *data)
{
@@ -1176,6 +1217,9 @@ nvpair_value_common(nvpair_t *nvp, data_type_t type, uint_t *nelem, void *data)
case DATA_TYPE_INT64:
case DATA_TYPE_UINT64:
case DATA_TYPE_HRTIME:
+#if !defined(_KERNEL)
+ case DATA_TYPE_DOUBLE:
+#endif
if (data == NULL)
return (EINVAL);
bcopy(NVP_VALUE(nvp), data,
@@ -1312,6 +1356,14 @@ nvlist_lookup_uint64(nvlist_t *nvl, const char *name, uint64_t *val)
return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT64, NULL, val));
}
+#if !defined(_KERNEL)
+int
+nvlist_lookup_double(nvlist_t *nvl, const char *name, double *val)
+{
+ return (nvlist_lookup_common(nvl, name, DATA_TYPE_DOUBLE, NULL, val));
+}
+#endif
+
int
nvlist_lookup_string(nvlist_t *nvl, const char *name, char **val)
{
@@ -1446,6 +1498,9 @@ nvlist_lookup_pairs(nvlist_t *nvl, int flag, ...)
case DATA_TYPE_HRTIME:
case DATA_TYPE_STRING:
case DATA_TYPE_NVLIST:
+#if !defined(_KERNEL)
+ case DATA_TYPE_DOUBLE:
+#endif
val = va_arg(ap, void *);
ret = nvlist_lookup_common(nvl, name, type, NULL, val);
break;
@@ -1479,6 +1534,224 @@ nvlist_lookup_pairs(nvlist_t *nvl, int flag, ...)
return (ret);
}
+/*
+ * Find the 'name'ed nvpair in the nvlist 'nvl'. If 'name' found, the function
+ * returns zero and a pointer to the matching nvpair is returned in '*ret'
+ * (given 'ret' is non-NULL). If 'sep' is specified then 'name' will penitrate
+ * multiple levels of embedded nvlists, with 'sep' as the separator. As an
+ * example, if sep is '.', name might look like: "a" or "a.b" or "a.c[3]" or
+ * "a.d[3].e[1]". This matches the C syntax for array embed (for convience,
+ * code also supports "a.d[3]e[1]" syntax).
+ *
+ * If 'ip' is non-NULL and the last name component is an array, return the
+ * value of the "...[index]" array index in *ip. For an array reference that
+ * is not indexed, *ip will be returned as -1. If there is a syntax error in
+ * 'name', and 'ep' is non-NULL then *ep will be set to point to the location
+ * inside the 'name' string where the syntax error was detected.
+ */
+static int
+nvlist_lookup_nvpair_ei_sep(nvlist_t *nvl, const char *name, const char sep,
+ nvpair_t **ret, int *ip, char **ep)
+{
+ nvpair_t *nvp;
+ const char *np;
+ char *sepp;
+ char *idxp, *idxep;
+ nvlist_t **nva;
+ long idx;
+ int n;
+
+ if (ip)
+ *ip = -1; /* not indexed */
+ if (ep)
+ *ep = NULL;
+
+ if ((nvl == NULL) || (name == NULL))
+ return (EINVAL);
+
+ /* step through components of name */
+ for (np = name; np && *np; np = sepp) {
+ /* ensure unique names */
+ if (!(nvl->nvl_nvflag & NV_UNIQUE_NAME))
+ return (ENOTSUP);
+
+ /* skip white space */
+ skip_whitespace(np);
+ if (*np == 0)
+ break;
+
+ /* set 'sepp' to end of current component 'np' */
+ if (sep)
+ sepp = strchr(np, sep);
+ else
+ sepp = NULL;
+
+ /* find start of next "[ index ]..." */
+ idxp = strchr(np, '[');
+
+ /* if sepp comes first, set idxp to NULL */
+ if (sepp && idxp && (sepp < idxp))
+ idxp = NULL;
+
+ /*
+ * At this point 'idxp' is set if there is an index
+ * expected for the current component.
+ */
+ if (idxp) {
+ /* set 'n' to length of current 'np' name component */
+ n = idxp++ - np;
+
+ /* keep sepp up to date for *ep use as we advance */
+ skip_whitespace(idxp);
+ sepp = idxp;
+
+ /* determine the index value */
+#if defined(_KERNEL) && !defined(_BOOT)
+ if (ddi_strtol(idxp, &idxep, 0, &idx))
+ goto fail;
+#else
+ idx = strtol(idxp, &idxep, 0);
+#endif
+ if (idxep == idxp)
+ goto fail;
+
+ /* keep sepp up to date for *ep use as we advance */
+ sepp = idxep;
+
+ /* skip white space index value and check for ']' */
+ skip_whitespace(sepp);
+ if (*sepp++ != ']')
+ goto fail;
+
+ /* for embedded arrays, support C syntax: "a[1].b" */
+ skip_whitespace(sepp);
+ if (sep && (*sepp == sep))
+ sepp++;
+ } else if (sepp) {
+ n = sepp++ - np;
+ } else {
+ n = strlen(np);
+ }
+
+ /* trim trailing whitespace by reducing length of 'np' */
+ if (n == 0)
+ goto fail;
+ for (n--; (np[n] == ' ') || (np[n] == '\t'); n--)
+ ;
+ n++;
+
+ /* skip whitespace, and set sepp to NULL if complete */
+ if (sepp) {
+ skip_whitespace(sepp);
+ if (*sepp == 0)
+ sepp = NULL;
+ }
+
+ /*
+ * At this point:
+ * o 'n' is the length of current 'np' component.
+ * o 'idxp' is set if there was an index, and value 'idx'.
+ * o 'sepp' is set to the beginning of the next component,
+ * and set to NULL if we have no more components.
+ *
+ * Search for nvpair with matching component name.
+ */
+ for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(nvl, nvp)) {
+
+ /* continue if no match on name */
+ if (strncmp(np, nvpair_name(nvp), n) ||
+ (strlen(nvpair_name(nvp)) != n))
+ continue;
+
+ /* if indexed, verify type is array oriented */
+ if (idxp && !nvpair_type_is_array(nvp))
+ goto fail;
+
+ /*
+ * Full match found, return nvp and idx if this
+ * was the last component.
+ */
+ if (sepp == NULL) {
+ if (ret)
+ *ret = nvp;
+ if (ip && idxp)
+ *ip = (int)idx; /* return index */
+ return (0); /* found */
+ }
+
+ /*
+ * More components: current match must be
+ * of DATA_TYPE_NVLIST or DATA_TYPE_NVLIST_ARRAY
+ * to support going deeper.
+ */
+ if (nvpair_type(nvp) == DATA_TYPE_NVLIST) {
+ nvl = EMBEDDED_NVL(nvp);
+ break;
+ } else if (nvpair_type(nvp) == DATA_TYPE_NVLIST_ARRAY) {
+ (void) nvpair_value_nvlist_array(nvp,
+ &nva, (uint_t *)&n);
+ if ((n < 0) || (idx >= n))
+ goto fail;
+ nvl = nva[idx];
+ break;
+ }
+
+ /* type does not support more levels */
+ goto fail;
+ }
+ if (nvp == NULL)
+ goto fail; /* 'name' not found */
+
+ /* search for match of next component in embedded 'nvl' list */
+ }
+
+fail: if (ep && sepp)
+ *ep = sepp;
+ return (EINVAL);
+}
+
+/*
+ * Return pointer to nvpair with specified 'name'.
+ */
+int
+nvlist_lookup_nvpair(nvlist_t *nvl, const char *name, nvpair_t **ret)
+{
+ return (nvlist_lookup_nvpair_ei_sep(nvl, name, 0, ret, NULL, NULL));
+}
+
+/*
+ * Determine if named nvpair exists in nvlist (use embedded separator of '.'
+ * and return array index). See nvlist_lookup_nvpair_ei_sep for more detailed
+ * description.
+ */
+int nvlist_lookup_nvpair_embedded_index(nvlist_t *nvl,
+ const char *name, nvpair_t **ret, int *ip, char **ep)
+{
+ return (nvlist_lookup_nvpair_ei_sep(nvl, name, '.', ret, ip, ep));
+}
+
+boolean_t
+nvlist_exists(nvlist_t *nvl, const char *name)
+{
+ nvpriv_t *priv;
+ nvpair_t *nvp;
+ i_nvp_t *curr;
+
+ if (name == NULL || nvl == NULL ||
+ (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL)
+ return (B_FALSE);
+
+ for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) {
+ nvp = &curr->nvi_nvp;
+
+ if (strcmp(name, NVP_NAME(nvp)) == 0)
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
int
nvpair_value_boolean_value(nvpair_t *nvp, boolean_t *val)
{
@@ -1539,6 +1812,14 @@ nvpair_value_uint64(nvpair_t *nvp, uint64_t *val)
return (nvpair_value_common(nvp, DATA_TYPE_UINT64, NULL, val));
}
+#if !defined(_KERNEL)
+int
+nvpair_value_double(nvpair_t *nvp, double *val)
+{
+ return (nvpair_value_common(nvp, DATA_TYPE_DOUBLE, NULL, val));
+}
+#endif
+
int
nvpair_value_string(nvpair_t *nvp, char **val)
{
@@ -2676,7 +2957,11 @@ nvs_xdr_nvp_op(nvstream_t *nvs, nvpair_t *nvp)
*/
ret = xdr_longlong_t(xdr, (void *)buf);
break;
-
+#if !defined(_KERNEL)
+ case DATA_TYPE_DOUBLE:
+ ret = xdr_double(xdr, (void *)buf);
+ break;
+#endif
case DATA_TYPE_STRING:
ret = xdr_string(xdr, &buf, buflen - 1);
break;
@@ -2782,6 +3067,9 @@ nvs_xdr_nvp_size(nvstream_t *nvs, nvpair_t *nvp, size_t *size)
case DATA_TYPE_INT64:
case DATA_TYPE_UINT64:
case DATA_TYPE_HRTIME:
+#if !defined(_KERNEL)
+ case DATA_TYPE_DOUBLE:
+#endif
nvp_sz += 8;
break;
diff --git a/sys/cddl/contrib/opensolaris/common/unicode/u8_textprep.c b/sys/cddl/contrib/opensolaris/common/unicode/u8_textprep.c
new file mode 100644
index 0000000..73cf74a
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/common/unicode/u8_textprep.c
@@ -0,0 +1,2130 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL 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.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+
+/*
+ * UTF-8 text preparation functions (PSARC/2007/149, PSARC/2007/458).
+ *
+ * Man pages: u8_textprep_open(9F), u8_textprep_buf(9F), u8_textprep_close(9F),
+ * u8_textprep_str(9F), u8_strcmp(9F), and u8_validate(9F). See also
+ * the section 3C man pages.
+ * Interface stability: Committed.
+ */
+
+#include <sys/types.h>
+#ifdef _KERNEL
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/debug.h>
+#include <sys/kmem.h>
+#else
+#include <strings.h>
+#endif /* _KERNEL */
+#include <sys/byteorder.h>
+#include <sys/errno.h>
+#include <sys/u8_textprep.h>
+#include <sys/u8_textprep_data.h>
+
+
+/* The maximum possible number of bytes in a UTF-8 character. */
+#define U8_MB_CUR_MAX (4)
+
+/*
+ * The maximum number of bytes needed for a UTF-8 character to cover
+ * U+0000 - U+FFFF, i.e., the coding space of now deprecated UCS-2.
+ */
+#define U8_MAX_BYTES_UCS2 (3)
+
+/* The maximum possible number of bytes in a Stream-Safe Text. */
+#define U8_STREAM_SAFE_TEXT_MAX (128)
+
+/*
+ * The maximum number of characters in a combining/conjoining sequence and
+ * the actual upperbound limit of a combining/conjoining sequence.
+ */
+#define U8_MAX_CHARS_A_SEQ (32)
+#define U8_UPPER_LIMIT_IN_A_SEQ (31)
+
+/* The combining class value for Starter. */
+#define U8_COMBINING_CLASS_STARTER (0)
+
+/*
+ * Some Hangul related macros at below.
+ *
+ * The first and the last of Hangul syllables, Hangul Jamo Leading consonants,
+ * Vowels, and optional Trailing consonants in Unicode scalar values.
+ *
+ * Please be noted that the U8_HANGUL_JAMO_T_FIRST is 0x11A7 at below not
+ * the actual U+11A8. This is due to that the trailing consonant is optional
+ * and thus we are doing a pre-calculation of subtracting one.
+ *
+ * Each of 19 modern leading consonants has total 588 possible syllables since
+ * Hangul has 21 modern vowels and 27 modern trailing consonants plus 1 for
+ * no trailing consonant case, i.e., 21 x 28 = 588.
+ *
+ * We also have bunch of Hangul related macros at below. Please bear in mind
+ * that the U8_HANGUL_JAMO_1ST_BYTE can be used to check whether it is
+ * a Hangul Jamo or not but the value does not guarantee that it is a Hangul
+ * Jamo; it just guarantee that it will be most likely.
+ */
+#define U8_HANGUL_SYL_FIRST (0xAC00U)
+#define U8_HANGUL_SYL_LAST (0xD7A3U)
+
+#define U8_HANGUL_JAMO_L_FIRST (0x1100U)
+#define U8_HANGUL_JAMO_L_LAST (0x1112U)
+#define U8_HANGUL_JAMO_V_FIRST (0x1161U)
+#define U8_HANGUL_JAMO_V_LAST (0x1175U)
+#define U8_HANGUL_JAMO_T_FIRST (0x11A7U)
+#define U8_HANGUL_JAMO_T_LAST (0x11C2U)
+
+#define U8_HANGUL_V_COUNT (21)
+#define U8_HANGUL_VT_COUNT (588)
+#define U8_HANGUL_T_COUNT (28)
+
+#define U8_HANGUL_JAMO_1ST_BYTE (0xE1U)
+
+#define U8_SAVE_HANGUL_AS_UTF8(s, i, j, k, b) \
+ (s)[(i)] = (uchar_t)(0xE0U | ((uint32_t)(b) & 0xF000U) >> 12); \
+ (s)[(j)] = (uchar_t)(0x80U | ((uint32_t)(b) & 0x0FC0U) >> 6); \
+ (s)[(k)] = (uchar_t)(0x80U | ((uint32_t)(b) & 0x003FU));
+
+#define U8_HANGUL_JAMO_L(u) \
+ ((u) >= U8_HANGUL_JAMO_L_FIRST && (u) <= U8_HANGUL_JAMO_L_LAST)
+
+#define U8_HANGUL_JAMO_V(u) \
+ ((u) >= U8_HANGUL_JAMO_V_FIRST && (u) <= U8_HANGUL_JAMO_V_LAST)
+
+#define U8_HANGUL_JAMO_T(u) \
+ ((u) > U8_HANGUL_JAMO_T_FIRST && (u) <= U8_HANGUL_JAMO_T_LAST)
+
+#define U8_HANGUL_JAMO(u) \
+ ((u) >= U8_HANGUL_JAMO_L_FIRST && (u) <= U8_HANGUL_JAMO_T_LAST)
+
+#define U8_HANGUL_SYLLABLE(u) \
+ ((u) >= U8_HANGUL_SYL_FIRST && (u) <= U8_HANGUL_SYL_LAST)
+
+#define U8_HANGUL_COMPOSABLE_L_V(s, u) \
+ ((s) == U8_STATE_HANGUL_L && U8_HANGUL_JAMO_V((u)))
+
+#define U8_HANGUL_COMPOSABLE_LV_T(s, u) \
+ ((s) == U8_STATE_HANGUL_LV && U8_HANGUL_JAMO_T((u)))
+
+/* The types of decomposition mappings. */
+#define U8_DECOMP_BOTH (0xF5U)
+#define U8_DECOMP_CANONICAL (0xF6U)
+
+/* The indicator for 16-bit table. */
+#define U8_16BIT_TABLE_INDICATOR (0x8000U)
+
+/* The following are some convenience macros. */
+#define U8_PUT_3BYTES_INTO_UTF32(u, b1, b2, b3) \
+ (u) = ((uint32_t)(b1) & 0x0F) << 12 | ((uint32_t)(b2) & 0x3F) << 6 | \
+ (uint32_t)(b3) & 0x3F;
+
+#define U8_SIMPLE_SWAP(a, b, t) \
+ (t) = (a); \
+ (a) = (b); \
+ (b) = (t);
+
+#define U8_ASCII_TOUPPER(c) \
+ (((c) >= 'a' && (c) <= 'z') ? (c) - 'a' + 'A' : (c))
+
+#define U8_ASCII_TOLOWER(c) \
+ (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c))
+
+#define U8_ISASCII(c) (((uchar_t)(c)) < 0x80U)
+/*
+ * The following macro assumes that the two characters that are to be
+ * swapped are adjacent to each other and 'a' comes before 'b'.
+ *
+ * If the assumptions are not met, then, the macro will fail.
+ */
+#define U8_SWAP_COMB_MARKS(a, b) \
+ for (k = 0; k < disp[(a)]; k++) \
+ u8t[k] = u8s[start[(a)] + k]; \
+ for (k = 0; k < disp[(b)]; k++) \
+ u8s[start[(a)] + k] = u8s[start[(b)] + k]; \
+ start[(b)] = start[(a)] + disp[(b)]; \
+ for (k = 0; k < disp[(a)]; k++) \
+ u8s[start[(b)] + k] = u8t[k]; \
+ U8_SIMPLE_SWAP(comb_class[(a)], comb_class[(b)], tc); \
+ U8_SIMPLE_SWAP(disp[(a)], disp[(b)], tc);
+
+/* The possible states during normalization. */
+typedef enum {
+ U8_STATE_START = 0,
+ U8_STATE_HANGUL_L = 1,
+ U8_STATE_HANGUL_LV = 2,
+ U8_STATE_HANGUL_LVT = 3,
+ U8_STATE_HANGUL_V = 4,
+ U8_STATE_HANGUL_T = 5,
+ U8_STATE_COMBINING_MARK = 6
+} u8_normalization_states_t;
+
+/*
+ * The three vectors at below are used to check bytes of a given UTF-8
+ * character are valid and not containing any malformed byte values.
+ *
+ * We used to have a quite relaxed UTF-8 binary representation but then there
+ * was some security related issues and so the Unicode Consortium defined
+ * and announced the UTF-8 Corrigendum at Unicode 3.1 and then refined it
+ * one more time at the Unicode 3.2. The following three tables are based on
+ * that.
+ */
+
+#define U8_ILLEGAL_NEXT_BYTE_COMMON(c) ((c) < 0x80 || (c) > 0xBF)
+
+#define I_ U8_ILLEGAL_CHAR
+#define O_ U8_OUT_OF_RANGE_CHAR
+
+const int8_t u8_number_of_bytes[0x100] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+/* 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F */
+ I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_,
+
+/* 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F */
+ I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_,
+
+/* A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF */
+ I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_,
+
+/* B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF */
+ I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_, I_,
+
+/* C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF */
+ I_, I_, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+
+/* D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF */
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+
+/* E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF */
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+
+/* F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF */
+ 4, 4, 4, 4, 4, O_, O_, O_, O_, O_, O_, O_, O_, O_, O_, O_,
+};
+
+#undef I_
+#undef O_
+
+const uint8_t u8_valid_min_2nd_byte[0x100] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* C0 C1 C2 C3 C4 C5 C6 C7 */
+ 0, 0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+/* C8 C9 CA CB CC CD CE CF */
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+/* D0 D1 D2 D3 D4 D5 D6 D7 */
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+/* D8 D9 DA DB DC DD DE DF */
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+/* E0 E1 E2 E3 E4 E5 E6 E7 */
+ 0xa0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+/* E8 E9 EA EB EC ED EE EF */
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+/* F0 F1 F2 F3 F4 F5 F6 F7 */
+ 0x90, 0x80, 0x80, 0x80, 0x80, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+const uint8_t u8_valid_max_2nd_byte[0x100] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* C0 C1 C2 C3 C4 C5 C6 C7 */
+ 0, 0, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+/* C8 C9 CA CB CC CD CE CF */
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+/* D0 D1 D2 D3 D4 D5 D6 D7 */
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+/* D8 D9 DA DB DC DD DE DF */
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+/* E0 E1 E2 E3 E4 E5 E6 E7 */
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+/* E8 E9 EA EB EC ED EE EF */
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0x9f, 0xbf, 0xbf,
+/* F0 F1 F2 F3 F4 F5 F6 F7 */
+ 0xbf, 0xbf, 0xbf, 0xbf, 0x8f, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+
+/*
+ * The u8_validate() validates on the given UTF-8 character string and
+ * calculate the byte length. It is quite similar to mblen(3C) except that
+ * this will validate against the list of characters if required and
+ * specific to UTF-8 and Unicode.
+ */
+int
+u8_validate(char *u8str, size_t n, char **list, int flag, int *errnum)
+{
+ uchar_t *ib;
+ uchar_t *ibtail;
+ uchar_t **p;
+ uchar_t *s1;
+ uchar_t *s2;
+ uchar_t f;
+ int sz;
+ size_t i;
+ int ret_val;
+ boolean_t second;
+ boolean_t no_need_to_validate_entire;
+ boolean_t check_additional;
+ boolean_t validate_ucs2_range_only;
+
+ if (! u8str)
+ return (0);
+
+ ib = (uchar_t *)u8str;
+ ibtail = ib + n;
+
+ ret_val = 0;
+
+ no_need_to_validate_entire = ! (flag & U8_VALIDATE_ENTIRE);
+ check_additional = flag & U8_VALIDATE_CHECK_ADDITIONAL;
+ validate_ucs2_range_only = flag & U8_VALIDATE_UCS2_RANGE;
+
+ while (ib < ibtail) {
+ /*
+ * The first byte of a UTF-8 character tells how many
+ * bytes will follow for the character. If the first byte
+ * is an illegal byte value or out of range value, we just
+ * return -1 with an appropriate error number.
+ */
+ sz = u8_number_of_bytes[*ib];
+ if (sz == U8_ILLEGAL_CHAR) {
+ *errnum = EILSEQ;
+ return (-1);
+ }
+
+ if (sz == U8_OUT_OF_RANGE_CHAR ||
+ (validate_ucs2_range_only && sz > U8_MAX_BYTES_UCS2)) {
+ *errnum = ERANGE;
+ return (-1);
+ }
+
+ /*
+ * If we don't have enough bytes to check on, that's also
+ * an error. As you can see, we give illegal byte sequence
+ * checking higher priority then EINVAL cases.
+ */
+ if ((ibtail - ib) < sz) {
+ *errnum = EINVAL;
+ return (-1);
+ }
+
+ if (sz == 1) {
+ ib++;
+ ret_val++;
+ } else {
+ /*
+ * Check on the multi-byte UTF-8 character. For more
+ * details on this, see comment added for the used
+ * data structures at the beginning of the file.
+ */
+ f = *ib++;
+ ret_val++;
+ second = B_TRUE;
+ for (i = 1; i < sz; i++) {
+ if (second) {
+ if (*ib < u8_valid_min_2nd_byte[f] ||
+ *ib > u8_valid_max_2nd_byte[f]) {
+ *errnum = EILSEQ;
+ return (-1);
+ }
+ second = B_FALSE;
+ } else if (U8_ILLEGAL_NEXT_BYTE_COMMON(*ib)) {
+ *errnum = EILSEQ;
+ return (-1);
+ }
+ ib++;
+ ret_val++;
+ }
+ }
+
+ if (check_additional) {
+ for (p = (uchar_t **)list, i = 0; p[i]; i++) {
+ s1 = ib - sz;
+ s2 = p[i];
+ while (s1 < ib) {
+ if (*s1 != *s2 || *s2 == '\0')
+ break;
+ s1++;
+ s2++;
+ }
+
+ if (s1 >= ib && *s2 == '\0') {
+ *errnum = EBADF;
+ return (-1);
+ }
+ }
+ }
+
+ if (no_need_to_validate_entire)
+ break;
+ }
+
+ return (ret_val);
+}
+
+/*
+ * The do_case_conv() looks at the mapping tables and returns found
+ * bytes if any. If not found, the input bytes are returned. The function
+ * always terminate the return bytes with a null character assuming that
+ * there are plenty of room to do so.
+ *
+ * The case conversions are simple case conversions mapping a character to
+ * another character as specified in the Unicode data. The byte size of
+ * the mapped character could be different from that of the input character.
+ *
+ * The return value is the byte length of the returned character excluding
+ * the terminating null byte.
+ */
+static size_t
+do_case_conv(int uv, uchar_t *u8s, uchar_t *s, int sz, boolean_t is_it_toupper)
+{
+ size_t i;
+ uint16_t b1 = 0;
+ uint16_t b2 = 0;
+ uint16_t b3 = 0;
+ uint16_t b3_tbl;
+ uint16_t b3_base;
+ uint16_t b4 = 0;
+ size_t start_id;
+ size_t end_id;
+
+ /*
+ * At this point, the only possible values for sz are 2, 3, and 4.
+ * The u8s should point to a vector that is well beyond the size of
+ * 5 bytes.
+ */
+ if (sz == 2) {
+ b3 = u8s[0] = s[0];
+ b4 = u8s[1] = s[1];
+ } else if (sz == 3) {
+ b2 = u8s[0] = s[0];
+ b3 = u8s[1] = s[1];
+ b4 = u8s[2] = s[2];
+ } else if (sz == 4) {
+ b1 = u8s[0] = s[0];
+ b2 = u8s[1] = s[1];
+ b3 = u8s[2] = s[2];
+ b4 = u8s[3] = s[3];
+ } else {
+ /* This is not possible but just in case as a fallback. */
+ if (is_it_toupper)
+ *u8s = U8_ASCII_TOUPPER(*s);
+ else
+ *u8s = U8_ASCII_TOLOWER(*s);
+ u8s[1] = '\0';
+
+ return (1);
+ }
+ u8s[sz] = '\0';
+
+ /*
+ * Let's find out if we have a corresponding character.
+ */
+ b1 = u8_common_b1_tbl[uv][b1];
+ if (b1 == U8_TBL_ELEMENT_NOT_DEF)
+ return ((size_t)sz);
+
+ b2 = u8_case_common_b2_tbl[uv][b1][b2];
+ if (b2 == U8_TBL_ELEMENT_NOT_DEF)
+ return ((size_t)sz);
+
+ if (is_it_toupper) {
+ b3_tbl = u8_toupper_b3_tbl[uv][b2][b3].tbl_id;
+ if (b3_tbl == U8_TBL_ELEMENT_NOT_DEF)
+ return ((size_t)sz);
+
+ start_id = u8_toupper_b4_tbl[uv][b3_tbl][b4];
+ end_id = u8_toupper_b4_tbl[uv][b3_tbl][b4 + 1];
+
+ /* Either there is no match or an error at the table. */
+ if (start_id >= end_id || (end_id - start_id) > U8_MB_CUR_MAX)
+ return ((size_t)sz);
+
+ b3_base = u8_toupper_b3_tbl[uv][b2][b3].base;
+
+ for (i = 0; start_id < end_id; start_id++)
+ u8s[i++] = u8_toupper_final_tbl[uv][b3_base + start_id];
+ } else {
+ b3_tbl = u8_tolower_b3_tbl[uv][b2][b3].tbl_id;
+ if (b3_tbl == U8_TBL_ELEMENT_NOT_DEF)
+ return ((size_t)sz);
+
+ start_id = u8_tolower_b4_tbl[uv][b3_tbl][b4];
+ end_id = u8_tolower_b4_tbl[uv][b3_tbl][b4 + 1];
+
+ if (start_id >= end_id || (end_id - start_id) > U8_MB_CUR_MAX)
+ return ((size_t)sz);
+
+ b3_base = u8_tolower_b3_tbl[uv][b2][b3].base;
+
+ for (i = 0; start_id < end_id; start_id++)
+ u8s[i++] = u8_tolower_final_tbl[uv][b3_base + start_id];
+ }
+
+ /*
+ * If i is still zero, that means there is no corresponding character.
+ */
+ if (i == 0)
+ return ((size_t)sz);
+
+ u8s[i] = '\0';
+
+ return (i);
+}
+
+/*
+ * The do_case_compare() function compares the two input strings, s1 and s2,
+ * one character at a time doing case conversions if applicable and return
+ * the comparison result as like strcmp().
+ *
+ * Since, in empirical sense, most of text data are 7-bit ASCII characters,
+ * we treat the 7-bit ASCII characters as a special case trying to yield
+ * faster processing time.
+ */
+static int
+do_case_compare(size_t uv, uchar_t *s1, uchar_t *s2, size_t n1,
+ size_t n2, boolean_t is_it_toupper, int *errnum)
+{
+ int f;
+ int sz1;
+ int sz2;
+ size_t j;
+ size_t i1;
+ size_t i2;
+ uchar_t u8s1[U8_MB_CUR_MAX + 1];
+ uchar_t u8s2[U8_MB_CUR_MAX + 1];
+
+ i1 = i2 = 0;
+ while (i1 < n1 && i2 < n2) {
+ /*
+ * Find out what would be the byte length for this UTF-8
+ * character at string s1 and also find out if this is
+ * an illegal start byte or not and if so, issue a proper
+ * error number and yet treat this byte as a character.
+ */
+ sz1 = u8_number_of_bytes[*s1];
+ if (sz1 < 0) {
+ *errnum = EILSEQ;
+ sz1 = 1;
+ }
+
+ /*
+ * For 7-bit ASCII characters mainly, we do a quick case
+ * conversion right at here.
+ *
+ * If we don't have enough bytes for this character, issue
+ * an EINVAL error and use what are available.
+ *
+ * If we have enough bytes, find out if there is
+ * a corresponding uppercase character and if so, copy over
+ * the bytes for a comparison later. If there is no
+ * corresponding uppercase character, then, use what we have
+ * for the comparison.
+ */
+ if (sz1 == 1) {
+ if (is_it_toupper)
+ u8s1[0] = U8_ASCII_TOUPPER(*s1);
+ else
+ u8s1[0] = U8_ASCII_TOLOWER(*s1);
+ s1++;
+ u8s1[1] = '\0';
+ } else if ((i1 + sz1) > n1) {
+ *errnum = EINVAL;
+ for (j = 0; (i1 + j) < n1; )
+ u8s1[j++] = *s1++;
+ u8s1[j] = '\0';
+ } else {
+ (void) do_case_conv(uv, u8s1, s1, sz1, is_it_toupper);
+ s1 += sz1;
+ }
+
+ /* Do the same for the string s2. */
+ sz2 = u8_number_of_bytes[*s2];
+ if (sz2 < 0) {
+ *errnum = EILSEQ;
+ sz2 = 1;
+ }
+
+ if (sz2 == 1) {
+ if (is_it_toupper)
+ u8s2[0] = U8_ASCII_TOUPPER(*s2);
+ else
+ u8s2[0] = U8_ASCII_TOLOWER(*s2);
+ s2++;
+ u8s2[1] = '\0';
+ } else if ((i2 + sz2) > n2) {
+ *errnum = EINVAL;
+ for (j = 0; (i2 + j) < n2; )
+ u8s2[j++] = *s2++;
+ u8s2[j] = '\0';
+ } else {
+ (void) do_case_conv(uv, u8s2, s2, sz2, is_it_toupper);
+ s2 += sz2;
+ }
+
+ /* Now compare the two characters. */
+ if (sz1 == 1 && sz2 == 1) {
+ if (*u8s1 > *u8s2)
+ return (1);
+ if (*u8s1 < *u8s2)
+ return (-1);
+ } else {
+ f = strcmp((const char *)u8s1, (const char *)u8s2);
+ if (f != 0)
+ return (f);
+ }
+
+ /*
+ * They were the same. Let's move on to the next
+ * characters then.
+ */
+ i1 += sz1;
+ i2 += sz2;
+ }
+
+ /*
+ * We compared until the end of either or both strings.
+ *
+ * If we reached to or went over the ends for the both, that means
+ * they are the same.
+ *
+ * If we reached only one of the two ends, that means the other string
+ * has something which then the fact can be used to determine
+ * the return value.
+ */
+ if (i1 >= n1) {
+ if (i2 >= n2)
+ return (0);
+ return (-1);
+ }
+ return (1);
+}
+
+/*
+ * The combining_class() function checks on the given bytes and find out
+ * the corresponding Unicode combining class value. The return value 0 means
+ * it is a Starter. Any illegal UTF-8 character will also be treated as
+ * a Starter.
+ */
+static uchar_t
+combining_class(size_t uv, uchar_t *s, size_t sz)
+{
+ uint16_t b1 = 0;
+ uint16_t b2 = 0;
+ uint16_t b3 = 0;
+ uint16_t b4 = 0;
+
+ if (sz == 1 || sz > 4)
+ return (0);
+
+ if (sz == 2) {
+ b3 = s[0];
+ b4 = s[1];
+ } else if (sz == 3) {
+ b2 = s[0];
+ b3 = s[1];
+ b4 = s[2];
+ } else if (sz == 4) {
+ b1 = s[0];
+ b2 = s[1];
+ b3 = s[2];
+ b4 = s[3];
+ }
+
+ b1 = u8_common_b1_tbl[uv][b1];
+ if (b1 == U8_TBL_ELEMENT_NOT_DEF)
+ return (0);
+
+ b2 = u8_combining_class_b2_tbl[uv][b1][b2];
+ if (b2 == U8_TBL_ELEMENT_NOT_DEF)
+ return (0);
+
+ b3 = u8_combining_class_b3_tbl[uv][b2][b3];
+ if (b3 == U8_TBL_ELEMENT_NOT_DEF)
+ return (0);
+
+ return (u8_combining_class_b4_tbl[uv][b3][b4]);
+}
+
+/*
+ * The do_decomp() function finds out a matching decomposition if any
+ * and return. If there is no match, the input bytes are copied and returned.
+ * The function also checks if there is a Hangul, decomposes it if necessary
+ * and returns.
+ *
+ * To save time, a single byte 7-bit ASCII character should be handled by
+ * the caller.
+ *
+ * The function returns the number of bytes returned sans always terminating
+ * the null byte. It will also return a state that will tell if there was
+ * a Hangul character decomposed which then will be used by the caller.
+ */
+static size_t
+do_decomp(size_t uv, uchar_t *u8s, uchar_t *s, int sz,
+ boolean_t canonical_decomposition, u8_normalization_states_t *state)
+{
+ uint16_t b1 = 0;
+ uint16_t b2 = 0;
+ uint16_t b3 = 0;
+ uint16_t b3_tbl;
+ uint16_t b3_base;
+ uint16_t b4 = 0;
+ size_t start_id;
+ size_t end_id;
+ size_t i;
+ uint32_t u1;
+
+ if (sz == 2) {
+ b3 = u8s[0] = s[0];
+ b4 = u8s[1] = s[1];
+ u8s[2] = '\0';
+ } else if (sz == 3) {
+ /* Convert it to a Unicode scalar value. */
+ U8_PUT_3BYTES_INTO_UTF32(u1, s[0], s[1], s[2]);
+
+ /*
+ * If this is a Hangul syllable, we decompose it into
+ * a leading consonant, a vowel, and an optional trailing
+ * consonant and then return.
+ */
+ if (U8_HANGUL_SYLLABLE(u1)) {
+ u1 -= U8_HANGUL_SYL_FIRST;
+
+ b1 = U8_HANGUL_JAMO_L_FIRST + u1 / U8_HANGUL_VT_COUNT;
+ b2 = U8_HANGUL_JAMO_V_FIRST + (u1 % U8_HANGUL_VT_COUNT)
+ / U8_HANGUL_T_COUNT;
+ b3 = u1 % U8_HANGUL_T_COUNT;
+
+ U8_SAVE_HANGUL_AS_UTF8(u8s, 0, 1, 2, b1);
+ U8_SAVE_HANGUL_AS_UTF8(u8s, 3, 4, 5, b2);
+ if (b3) {
+ b3 += U8_HANGUL_JAMO_T_FIRST;
+ U8_SAVE_HANGUL_AS_UTF8(u8s, 6, 7, 8, b3);
+
+ u8s[9] = '\0';
+ *state = U8_STATE_HANGUL_LVT;
+ return (9);
+ }
+
+ u8s[6] = '\0';
+ *state = U8_STATE_HANGUL_LV;
+ return (6);
+ }
+
+ b2 = u8s[0] = s[0];
+ b3 = u8s[1] = s[1];
+ b4 = u8s[2] = s[2];
+ u8s[3] = '\0';
+
+ /*
+ * If this is a Hangul Jamo, we know there is nothing
+ * further that we can decompose.
+ */
+ if (U8_HANGUL_JAMO_L(u1)) {
+ *state = U8_STATE_HANGUL_L;
+ return (3);
+ }
+
+ if (U8_HANGUL_JAMO_V(u1)) {
+ if (*state == U8_STATE_HANGUL_L)
+ *state = U8_STATE_HANGUL_LV;
+ else
+ *state = U8_STATE_HANGUL_V;
+ return (3);
+ }
+
+ if (U8_HANGUL_JAMO_T(u1)) {
+ if (*state == U8_STATE_HANGUL_LV)
+ *state = U8_STATE_HANGUL_LVT;
+ else
+ *state = U8_STATE_HANGUL_T;
+ return (3);
+ }
+ } else if (sz == 4) {
+ b1 = u8s[0] = s[0];
+ b2 = u8s[1] = s[1];
+ b3 = u8s[2] = s[2];
+ b4 = u8s[3] = s[3];
+ u8s[4] = '\0';
+ } else {
+ /*
+ * This is a fallback and should not happen if the function
+ * was called properly.
+ */
+ u8s[0] = s[0];
+ u8s[1] = '\0';
+ *state = U8_STATE_START;
+ return (1);
+ }
+
+ /*
+ * At this point, this rountine does not know what it would get.
+ * The caller should sort it out if the state isn't a Hangul one.
+ */
+ *state = U8_STATE_START;
+
+ /* Try to find matching decomposition mapping byte sequence. */
+ b1 = u8_common_b1_tbl[uv][b1];
+ if (b1 == U8_TBL_ELEMENT_NOT_DEF)
+ return ((size_t)sz);
+
+ b2 = u8_decomp_b2_tbl[uv][b1][b2];
+ if (b2 == U8_TBL_ELEMENT_NOT_DEF)
+ return ((size_t)sz);
+
+ b3_tbl = u8_decomp_b3_tbl[uv][b2][b3].tbl_id;
+ if (b3_tbl == U8_TBL_ELEMENT_NOT_DEF)
+ return ((size_t)sz);
+
+ /*
+ * If b3_tbl is bigger than or equal to U8_16BIT_TABLE_INDICATOR
+ * which is 0x8000, this means we couldn't fit the mappings into
+ * the cardinality of a unsigned byte.
+ */
+ if (b3_tbl >= U8_16BIT_TABLE_INDICATOR) {
+ b3_tbl -= U8_16BIT_TABLE_INDICATOR;
+ start_id = u8_decomp_b4_16bit_tbl[uv][b3_tbl][b4];
+ end_id = u8_decomp_b4_16bit_tbl[uv][b3_tbl][b4 + 1];
+ } else {
+ start_id = u8_decomp_b4_tbl[uv][b3_tbl][b4];
+ end_id = u8_decomp_b4_tbl[uv][b3_tbl][b4 + 1];
+ }
+
+ /* This also means there wasn't any matching decomposition. */
+ if (start_id >= end_id)
+ return ((size_t)sz);
+
+ /*
+ * The final table for decomposition mappings has three types of
+ * byte sequences depending on whether a mapping is for compatibility
+ * decomposition, canonical decomposition, or both like the following:
+ *
+ * (1) Compatibility decomposition mappings:
+ *
+ * +---+---+-...-+---+
+ * | B0| B1| ... | Bm|
+ * +---+---+-...-+---+
+ *
+ * The first byte, B0, is always less then 0xF5 (U8_DECOMP_BOTH).
+ *
+ * (2) Canonical decomposition mappings:
+ *
+ * +---+---+---+-...-+---+
+ * | T | b0| b1| ... | bn|
+ * +---+---+---+-...-+---+
+ *
+ * where the first byte, T, is 0xF6 (U8_DECOMP_CANONICAL).
+ *
+ * (3) Both mappings:
+ *
+ * +---+---+---+---+-...-+---+---+---+-...-+---+
+ * | T | D | b0| b1| ... | bn| B0| B1| ... | Bm|
+ * +---+---+---+---+-...-+---+---+---+-...-+---+
+ *
+ * where T is 0xF5 (U8_DECOMP_BOTH) and D is a displacement
+ * byte, b0 to bn are canonical mapping bytes and B0 to Bm are
+ * compatibility mapping bytes.
+ *
+ * Note that compatibility decomposition means doing recursive
+ * decompositions using both compatibility decomposition mappings and
+ * canonical decomposition mappings. On the other hand, canonical
+ * decomposition means doing recursive decompositions using only
+ * canonical decomposition mappings. Since the table we have has gone
+ * through the recursions already, we do not need to do so during
+ * runtime, i.e., the table has been completely flattened out
+ * already.
+ */
+
+ b3_base = u8_decomp_b3_tbl[uv][b2][b3].base;
+
+ /* Get the type, T, of the byte sequence. */
+ b1 = u8_decomp_final_tbl[uv][b3_base + start_id];
+
+ /*
+ * If necessary, adjust start_id, end_id, or both. Note that if
+ * this is compatibility decomposition mapping, there is no
+ * adjustment.
+ */
+ if (canonical_decomposition) {
+ /* Is the mapping only for compatibility decomposition? */
+ if (b1 < U8_DECOMP_BOTH)
+ return ((size_t)sz);
+
+ start_id++;
+
+ if (b1 == U8_DECOMP_BOTH) {
+ end_id = start_id +
+ u8_decomp_final_tbl[uv][b3_base + start_id];
+ start_id++;
+ }
+ } else {
+ /*
+ * Unless this is a compatibility decomposition mapping,
+ * we adjust the start_id.
+ */
+ if (b1 == U8_DECOMP_BOTH) {
+ start_id++;
+ start_id += u8_decomp_final_tbl[uv][b3_base + start_id];
+ } else if (b1 == U8_DECOMP_CANONICAL) {
+ start_id++;
+ }
+ }
+
+ for (i = 0; start_id < end_id; start_id++)
+ u8s[i++] = u8_decomp_final_tbl[uv][b3_base + start_id];
+ u8s[i] = '\0';
+
+ return (i);
+}
+
+/*
+ * The find_composition_start() function uses the character bytes given and
+ * find out the matching composition mappings if any and return the address
+ * to the composition mappings as explained in the do_composition().
+ */
+static uchar_t *
+find_composition_start(size_t uv, uchar_t *s, size_t sz)
+{
+ uint16_t b1 = 0;
+ uint16_t b2 = 0;
+ uint16_t b3 = 0;
+ uint16_t b3_tbl;
+ uint16_t b3_base;
+ uint16_t b4 = 0;
+ size_t start_id;
+ size_t end_id;
+
+ if (sz == 1) {
+ b4 = s[0];
+ } else if (sz == 2) {
+ b3 = s[0];
+ b4 = s[1];
+ } else if (sz == 3) {
+ b2 = s[0];
+ b3 = s[1];
+ b4 = s[2];
+ } else if (sz == 4) {
+ b1 = s[0];
+ b2 = s[1];
+ b3 = s[2];
+ b4 = s[3];
+ } else {
+ /*
+ * This is a fallback and should not happen if the function
+ * was called properly.
+ */
+ return (NULL);
+ }
+
+ b1 = u8_composition_b1_tbl[uv][b1];
+ if (b1 == U8_TBL_ELEMENT_NOT_DEF)
+ return (NULL);
+
+ b2 = u8_composition_b2_tbl[uv][b1][b2];
+ if (b2 == U8_TBL_ELEMENT_NOT_DEF)
+ return (NULL);
+
+ b3_tbl = u8_composition_b3_tbl[uv][b2][b3].tbl_id;
+ if (b3_tbl == U8_TBL_ELEMENT_NOT_DEF)
+ return (NULL);
+
+ if (b3_tbl >= U8_16BIT_TABLE_INDICATOR) {
+ b3_tbl -= U8_16BIT_TABLE_INDICATOR;
+ start_id = u8_composition_b4_16bit_tbl[uv][b3_tbl][b4];
+ end_id = u8_composition_b4_16bit_tbl[uv][b3_tbl][b4 + 1];
+ } else {
+ start_id = u8_composition_b4_tbl[uv][b3_tbl][b4];
+ end_id = u8_composition_b4_tbl[uv][b3_tbl][b4 + 1];
+ }
+
+ if (start_id >= end_id)
+ return (NULL);
+
+ b3_base = u8_composition_b3_tbl[uv][b2][b3].base;
+
+ return ((uchar_t *)&(u8_composition_final_tbl[uv][b3_base + start_id]));
+}
+
+/*
+ * The blocked() function checks on the combining class values of previous
+ * characters in this sequence and return whether it is blocked or not.
+ */
+static boolean_t
+blocked(uchar_t *comb_class, size_t last)
+{
+ uchar_t my_comb_class;
+ size_t i;
+
+ my_comb_class = comb_class[last];
+ for (i = 1; i < last; i++)
+ if (comb_class[i] >= my_comb_class ||
+ comb_class[i] == U8_COMBINING_CLASS_STARTER)
+ return (B_TRUE);
+
+ return (B_FALSE);
+}
+
+/*
+ * The do_composition() reads the character string pointed by 's' and
+ * do necessary canonical composition and then copy over the result back to
+ * the 's'.
+ *
+ * The input argument 's' cannot contain more than 32 characters.
+ */
+static size_t
+do_composition(size_t uv, uchar_t *s, uchar_t *comb_class, uchar_t *start,
+ uchar_t *disp, size_t last, uchar_t **os, uchar_t *oslast)
+{
+ uchar_t t[U8_STREAM_SAFE_TEXT_MAX + 1];
+ uchar_t tc[U8_MB_CUR_MAX];
+ uint8_t saved_marks[U8_MAX_CHARS_A_SEQ];
+ size_t saved_marks_count;
+ uchar_t *p;
+ uchar_t *saved_p;
+ uchar_t *q;
+ size_t i;
+ size_t saved_i;
+ size_t j;
+ size_t k;
+ size_t l;
+ size_t C;
+ size_t saved_l;
+ size_t size;
+ uint32_t u1;
+ uint32_t u2;
+ boolean_t match_not_found = B_TRUE;
+
+ /*
+ * This should never happen unless the callers are doing some strange
+ * and unexpected things.
+ *
+ * The "last" is the index pointing to the last character not last + 1.
+ */
+ if (last >= U8_MAX_CHARS_A_SEQ)
+ last = U8_UPPER_LIMIT_IN_A_SEQ;
+
+ for (i = l = 0; i <= last; i++) {
+ /*
+ * The last or any non-Starters at the beginning, we don't
+ * have any chance to do composition and so we just copy them
+ * to the temporary buffer.
+ */
+ if (i >= last || comb_class[i] != U8_COMBINING_CLASS_STARTER) {
+SAVE_THE_CHAR:
+ p = s + start[i];
+ size = disp[i];
+ for (k = 0; k < size; k++)
+ t[l++] = *p++;
+ continue;
+ }
+
+ /*
+ * If this could be a start of Hangul Jamos, then, we try to
+ * conjoin them.
+ */
+ if (s[start[i]] == U8_HANGUL_JAMO_1ST_BYTE) {
+ U8_PUT_3BYTES_INTO_UTF32(u1, s[start[i]],
+ s[start[i] + 1], s[start[i] + 2]);
+ U8_PUT_3BYTES_INTO_UTF32(u2, s[start[i] + 3],
+ s[start[i] + 4], s[start[i] + 5]);
+
+ if (U8_HANGUL_JAMO_L(u1) && U8_HANGUL_JAMO_V(u2)) {
+ u1 -= U8_HANGUL_JAMO_L_FIRST;
+ u2 -= U8_HANGUL_JAMO_V_FIRST;
+ u1 = U8_HANGUL_SYL_FIRST +
+ (u1 * U8_HANGUL_V_COUNT + u2) *
+ U8_HANGUL_T_COUNT;
+
+ i += 2;
+ if (i <= last) {
+ U8_PUT_3BYTES_INTO_UTF32(u2,
+ s[start[i]], s[start[i] + 1],
+ s[start[i] + 2]);
+
+ if (U8_HANGUL_JAMO_T(u2)) {
+ u1 += u2 -
+ U8_HANGUL_JAMO_T_FIRST;
+ i++;
+ }
+ }
+
+ U8_SAVE_HANGUL_AS_UTF8(t + l, 0, 1, 2, u1);
+ i--;
+ l += 3;
+ continue;
+ }
+ }
+
+ /*
+ * Let's then find out if this Starter has composition
+ * mapping.
+ */
+ p = find_composition_start(uv, s + start[i], disp[i]);
+ if (p == NULL)
+ goto SAVE_THE_CHAR;
+
+ /*
+ * We have a Starter with composition mapping and the next
+ * character is a non-Starter. Let's try to find out if
+ * we can do composition.
+ */
+
+ saved_p = p;
+ saved_i = i;
+ saved_l = l;
+ saved_marks_count = 0;
+
+TRY_THE_NEXT_MARK:
+ q = s + start[++i];
+ size = disp[i];
+
+ /*
+ * The next for() loop compares the non-Starter pointed by
+ * 'q' with the possible (joinable) characters pointed by 'p'.
+ *
+ * The composition final table entry pointed by the 'p'
+ * looks like the following:
+ *
+ * +---+---+---+-...-+---+---+---+---+-...-+---+---+
+ * | C | b0| b2| ... | bn| F | B0| B1| ... | Bm| F |
+ * +---+---+---+-...-+---+---+---+---+-...-+---+---+
+ *
+ * where C is the count byte indicating the number of
+ * mapping pairs where each pair would be look like
+ * (b0-bn F, B0-Bm F). The b0-bn are the bytes of the second
+ * character of a canonical decomposition and the B0-Bm are
+ * the bytes of a matching composite character. The F is
+ * a filler byte after each character as the separator.
+ */
+
+ match_not_found = B_TRUE;
+
+ for (C = *p++; C > 0; C--) {
+ for (k = 0; k < size; p++, k++)
+ if (*p != q[k])
+ break;
+
+ /* Have we found it? */
+ if (k >= size && *p == U8_TBL_ELEMENT_FILLER) {
+ match_not_found = B_FALSE;
+
+ l = saved_l;
+
+ while (*++p != U8_TBL_ELEMENT_FILLER)
+ t[l++] = *p;
+
+ break;
+ }
+
+ /* We didn't find; skip to the next pair. */
+ if (*p != U8_TBL_ELEMENT_FILLER)
+ while (*++p != U8_TBL_ELEMENT_FILLER)
+ ;
+ while (*++p != U8_TBL_ELEMENT_FILLER)
+ ;
+ p++;
+ }
+
+ /*
+ * If there was no match, we will need to save the combining
+ * mark for later appending. After that, if the next one
+ * is a non-Starter and not blocked, then, we try once
+ * again to do composition with the next non-Starter.
+ *
+ * If there was no match and this was a Starter, then,
+ * this is a new start.
+ *
+ * If there was a match and a composition done and we have
+ * more to check on, then, we retrieve a new composition final
+ * table entry for the composite and then try to do the
+ * composition again.
+ */
+
+ if (match_not_found) {
+ if (comb_class[i] == U8_COMBINING_CLASS_STARTER) {
+ i--;
+ goto SAVE_THE_CHAR;
+ }
+
+ saved_marks[saved_marks_count++] = i;
+ }
+
+ if (saved_l == l) {
+ while (i < last) {
+ if (blocked(comb_class, i + 1))
+ saved_marks[saved_marks_count++] = ++i;
+ else
+ break;
+ }
+ if (i < last) {
+ p = saved_p;
+ goto TRY_THE_NEXT_MARK;
+ }
+ } else if (i < last) {
+ p = find_composition_start(uv, t + saved_l,
+ l - saved_l);
+ if (p != NULL) {
+ saved_p = p;
+ goto TRY_THE_NEXT_MARK;
+ }
+ }
+
+ /*
+ * There is no more composition possible.
+ *
+ * If there was no composition what so ever then we copy
+ * over the original Starter and then append any non-Starters
+ * remaining at the target string sequentially after that.
+ */
+
+ if (saved_l == l) {
+ p = s + start[saved_i];
+ size = disp[saved_i];
+ for (j = 0; j < size; j++)
+ t[l++] = *p++;
+ }
+
+ for (k = 0; k < saved_marks_count; k++) {
+ p = s + start[saved_marks[k]];
+ size = disp[saved_marks[k]];
+ for (j = 0; j < size; j++)
+ t[l++] = *p++;
+ }
+ }
+
+ /*
+ * If the last character is a Starter and if we have a character
+ * (possibly another Starter) that can be turned into a composite,
+ * we do so and we do so until there is no more of composition
+ * possible.
+ */
+ if (comb_class[last] == U8_COMBINING_CLASS_STARTER) {
+ p = *os;
+ saved_l = l - disp[last];
+
+ while (p < oslast) {
+ size = u8_number_of_bytes[*p];
+ if (size <= 1 || (p + size) > oslast)
+ break;
+
+ saved_p = p;
+
+ for (i = 0; i < size; i++)
+ tc[i] = *p++;
+
+ q = find_composition_start(uv, t + saved_l,
+ l - saved_l);
+ if (q == NULL) {
+ p = saved_p;
+ break;
+ }
+
+ match_not_found = B_TRUE;
+
+ for (C = *q++; C > 0; C--) {
+ for (k = 0; k < size; q++, k++)
+ if (*q != tc[k])
+ break;
+
+ if (k >= size && *q == U8_TBL_ELEMENT_FILLER) {
+ match_not_found = B_FALSE;
+
+ l = saved_l;
+
+ while (*++q != U8_TBL_ELEMENT_FILLER) {
+ /*
+ * This is practically
+ * impossible but we don't
+ * want to take any chances.
+ */
+ if (l >=
+ U8_STREAM_SAFE_TEXT_MAX) {
+ p = saved_p;
+ goto SAFE_RETURN;
+ }
+ t[l++] = *q;
+ }
+
+ break;
+ }
+
+ if (*q != U8_TBL_ELEMENT_FILLER)
+ while (*++q != U8_TBL_ELEMENT_FILLER)
+ ;
+ while (*++q != U8_TBL_ELEMENT_FILLER)
+ ;
+ q++;
+ }
+
+ if (match_not_found) {
+ p = saved_p;
+ break;
+ }
+ }
+SAFE_RETURN:
+ *os = p;
+ }
+
+ /*
+ * Now we copy over the temporary string to the target string.
+ * Since composition always reduces the number of characters or
+ * the number of characters stay, we don't need to worry about
+ * the buffer overflow here.
+ */
+ for (i = 0; i < l; i++)
+ s[i] = t[i];
+ s[l] = '\0';
+
+ return (l);
+}
+
+/*
+ * The collect_a_seq() function checks on the given string s, collect
+ * a sequence of characters at u8s, and return the sequence. While it collects
+ * a sequence, it also applies case conversion, canonical or compatibility
+ * decomposition, canonical decomposition, or some or all of them and
+ * in that order.
+ *
+ * The collected sequence cannot be bigger than 32 characters since if
+ * it is having more than 31 characters, the sequence will be terminated
+ * with a U+034F COMBINING GRAPHEME JOINER (CGJ) character and turned into
+ * a Stream-Safe Text. The collected sequence is always terminated with
+ * a null byte and the return value is the byte length of the sequence
+ * including 0. The return value does not include the terminating
+ * null byte.
+ */
+static size_t
+collect_a_seq(size_t uv, uchar_t *u8s, uchar_t **source, uchar_t *slast,
+ boolean_t is_it_toupper,
+ boolean_t is_it_tolower,
+ boolean_t canonical_decomposition,
+ boolean_t compatibility_decomposition,
+ boolean_t canonical_composition,
+ int *errnum, u8_normalization_states_t *state)
+{
+ uchar_t *s;
+ int sz;
+ int saved_sz;
+ size_t i;
+ size_t j;
+ size_t k;
+ size_t l;
+ uchar_t comb_class[U8_MAX_CHARS_A_SEQ];
+ uchar_t disp[U8_MAX_CHARS_A_SEQ];
+ uchar_t start[U8_MAX_CHARS_A_SEQ];
+ uchar_t u8t[U8_MB_CUR_MAX];
+ uchar_t uts[U8_STREAM_SAFE_TEXT_MAX + 1];
+ uchar_t tc;
+ size_t last;
+ size_t saved_last;
+ uint32_t u1;
+
+ /*
+ * Save the source string pointer which we will return a changed
+ * pointer if we do processing.
+ */
+ s = *source;
+
+ /*
+ * The following is a fallback for just in case callers are not
+ * checking the string boundaries before the calling.
+ */
+ if (s >= slast) {
+ u8s[0] = '\0';
+
+ return (0);
+ }
+
+ /*
+ * As the first thing, let's collect a character and do case
+ * conversion if necessary.
+ */
+
+ sz = u8_number_of_bytes[*s];
+
+ if (sz < 0) {
+ *errnum = EILSEQ;
+
+ u8s[0] = *s++;
+ u8s[1] = '\0';
+
+ *source = s;
+
+ return (1);
+ }
+
+ if (sz == 1) {
+ if (is_it_toupper)
+ u8s[0] = U8_ASCII_TOUPPER(*s);
+ else if (is_it_tolower)
+ u8s[0] = U8_ASCII_TOLOWER(*s);
+ else
+ u8s[0] = *s;
+ s++;
+ u8s[1] = '\0';
+ } else if ((s + sz) > slast) {
+ *errnum = EINVAL;
+
+ for (i = 0; s < slast; )
+ u8s[i++] = *s++;
+ u8s[i] = '\0';
+
+ *source = s;
+
+ return (i);
+ } else {
+ if (is_it_toupper || is_it_tolower) {
+ i = do_case_conv(uv, u8s, s, sz, is_it_toupper);
+ s += sz;
+ sz = i;
+ } else {
+ for (i = 0; i < sz; )
+ u8s[i++] = *s++;
+ u8s[i] = '\0';
+ }
+ }
+
+ /*
+ * And then canonical/compatibility decomposition followed by
+ * an optional canonical composition. Please be noted that
+ * canonical composition is done only when a decomposition is
+ * done.
+ */
+ if (canonical_decomposition || compatibility_decomposition) {
+ if (sz == 1) {
+ *state = U8_STATE_START;
+
+ saved_sz = 1;
+
+ comb_class[0] = 0;
+ start[0] = 0;
+ disp[0] = 1;
+
+ last = 1;
+ } else {
+ saved_sz = do_decomp(uv, u8s, u8s, sz,
+ canonical_decomposition, state);
+
+ last = 0;
+
+ for (i = 0; i < saved_sz; ) {
+ sz = u8_number_of_bytes[u8s[i]];
+
+ comb_class[last] = combining_class(uv,
+ u8s + i, sz);
+ start[last] = i;
+ disp[last] = sz;
+
+ last++;
+ i += sz;
+ }
+
+ /*
+ * Decomposition yields various Hangul related
+ * states but not on combining marks. We need to
+ * find out at here by checking on the last
+ * character.
+ */
+ if (*state == U8_STATE_START) {
+ if (comb_class[last - 1])
+ *state = U8_STATE_COMBINING_MARK;
+ }
+ }
+
+ saved_last = last;
+
+ while (s < slast) {
+ sz = u8_number_of_bytes[*s];
+
+ /*
+ * If this is an illegal character, an incomplete
+ * character, or an 7-bit ASCII Starter character,
+ * then we have collected a sequence; break and let
+ * the next call deal with the two cases.
+ *
+ * Note that this is okay only if you are using this
+ * function with a fixed length string, not on
+ * a buffer with multiple calls of one chunk at a time.
+ */
+ if (sz <= 1) {
+ break;
+ } else if ((s + sz) > slast) {
+ break;
+ } else {
+ /*
+ * If the previous character was a Hangul Jamo
+ * and this character is a Hangul Jamo that
+ * can be conjoined, we collect the Jamo.
+ */
+ if (*s == U8_HANGUL_JAMO_1ST_BYTE) {
+ U8_PUT_3BYTES_INTO_UTF32(u1,
+ *s, *(s + 1), *(s + 2));
+
+ if (U8_HANGUL_COMPOSABLE_L_V(*state,
+ u1)) {
+ i = 0;
+ *state = U8_STATE_HANGUL_LV;
+ goto COLLECT_A_HANGUL;
+ }
+
+ if (U8_HANGUL_COMPOSABLE_LV_T(*state,
+ u1)) {
+ i = 0;
+ *state = U8_STATE_HANGUL_LVT;
+ goto COLLECT_A_HANGUL;
+ }
+ }
+
+ /*
+ * Regardless of whatever it was, if this is
+ * a Starter, we don't collect the character
+ * since that's a new start and we will deal
+ * with it at the next time.
+ */
+ i = combining_class(uv, s, sz);
+ if (i == U8_COMBINING_CLASS_STARTER)
+ break;
+
+ /*
+ * We know the current character is a combining
+ * mark. If the previous character wasn't
+ * a Starter (not Hangul) or a combining mark,
+ * then, we don't collect this combining mark.
+ */
+ if (*state != U8_STATE_START &&
+ *state != U8_STATE_COMBINING_MARK)
+ break;
+
+ *state = U8_STATE_COMBINING_MARK;
+COLLECT_A_HANGUL:
+ /*
+ * If we collected a Starter and combining
+ * marks up to 30, i.e., total 31 characters,
+ * then, we terminate this degenerately long
+ * combining sequence with a U+034F COMBINING
+ * GRAPHEME JOINER (CGJ) which is 0xCD 0x8F in
+ * UTF-8 and turn this into a Stream-Safe
+ * Text. This will be extremely rare but
+ * possible.
+ *
+ * The following will also guarantee that
+ * we are not writing more than 32 characters
+ * plus a NULL at u8s[].
+ */
+ if (last >= U8_UPPER_LIMIT_IN_A_SEQ) {
+TURN_STREAM_SAFE:
+ *state = U8_STATE_START;
+ comb_class[last] = 0;
+ start[last] = saved_sz;
+ disp[last] = 2;
+ last++;
+
+ u8s[saved_sz++] = 0xCD;
+ u8s[saved_sz++] = 0x8F;
+
+ break;
+ }
+
+ /*
+ * Some combining marks also do decompose into
+ * another combining mark or marks.
+ */
+ if (*state == U8_STATE_COMBINING_MARK) {
+ k = last;
+ l = sz;
+ i = do_decomp(uv, uts, s, sz,
+ canonical_decomposition, state);
+ for (j = 0; j < i; ) {
+ sz = u8_number_of_bytes[uts[j]];
+
+ comb_class[last] =
+ combining_class(uv,
+ uts + j, sz);
+ start[last] = saved_sz + j;
+ disp[last] = sz;
+
+ last++;
+ if (last >=
+ U8_UPPER_LIMIT_IN_A_SEQ) {
+ last = k;
+ goto TURN_STREAM_SAFE;
+ }
+ j += sz;
+ }
+
+ *state = U8_STATE_COMBINING_MARK;
+ sz = i;
+ s += l;
+
+ for (i = 0; i < sz; i++)
+ u8s[saved_sz++] = uts[i];
+ } else {
+ comb_class[last] = i;
+ start[last] = saved_sz;
+ disp[last] = sz;
+ last++;
+
+ for (i = 0; i < sz; i++)
+ u8s[saved_sz++] = *s++;
+ }
+
+ /*
+ * If this is U+0345 COMBINING GREEK
+ * YPOGEGRAMMENI (0xCD 0x85 in UTF-8), a.k.a.,
+ * iota subscript, and need to be converted to
+ * uppercase letter, convert it to U+0399 GREEK
+ * CAPITAL LETTER IOTA (0xCE 0x99 in UTF-8),
+ * i.e., convert to capital adscript form as
+ * specified in the Unicode standard.
+ *
+ * This is the only special case of (ambiguous)
+ * case conversion at combining marks and
+ * probably the standard will never have
+ * anything similar like this in future.
+ */
+ if (is_it_toupper && sz >= 2 &&
+ u8s[saved_sz - 2] == 0xCD &&
+ u8s[saved_sz - 1] == 0x85) {
+ u8s[saved_sz - 2] = 0xCE;
+ u8s[saved_sz - 1] = 0x99;
+ }
+ }
+ }
+
+ /*
+ * Let's try to ensure a canonical ordering for the collected
+ * combining marks. We do this only if we have collected
+ * at least one more non-Starter. (The decomposition mapping
+ * data tables have fully (and recursively) expanded and
+ * canonically ordered decompositions.)
+ *
+ * The U8_SWAP_COMB_MARKS() convenience macro has some
+ * assumptions and we are meeting the assumptions.
+ */
+ last--;
+ if (last >= saved_last) {
+ for (i = 0; i < last; i++)
+ for (j = last; j > i; j--)
+ if (comb_class[j] &&
+ comb_class[j - 1] > comb_class[j]) {
+ U8_SWAP_COMB_MARKS(j - 1, j);
+ }
+ }
+
+ *source = s;
+
+ if (! canonical_composition) {
+ u8s[saved_sz] = '\0';
+ return (saved_sz);
+ }
+
+ /*
+ * Now do the canonical composition. Note that we do this
+ * only after a canonical or compatibility decomposition to
+ * finish up NFC or NFKC.
+ */
+ sz = do_composition(uv, u8s, comb_class, start, disp, last,
+ &s, slast);
+ }
+
+ *source = s;
+
+ return ((size_t)sz);
+}
+
+/*
+ * The do_norm_compare() function does string comparion based on Unicode
+ * simple case mappings and Unicode Normalization definitions.
+ *
+ * It does so by collecting a sequence of character at a time and comparing
+ * the collected sequences from the strings.
+ *
+ * The meanings on the return values are the same as the usual strcmp().
+ */
+static int
+do_norm_compare(size_t uv, uchar_t *s1, uchar_t *s2, size_t n1, size_t n2,
+ int flag, int *errnum)
+{
+ int result;
+ size_t sz1;
+ size_t sz2;
+ uchar_t u8s1[U8_STREAM_SAFE_TEXT_MAX + 1];
+ uchar_t u8s2[U8_STREAM_SAFE_TEXT_MAX + 1];
+ uchar_t *s1last;
+ uchar_t *s2last;
+ boolean_t is_it_toupper;
+ boolean_t is_it_tolower;
+ boolean_t canonical_decomposition;
+ boolean_t compatibility_decomposition;
+ boolean_t canonical_composition;
+ u8_normalization_states_t state;
+
+ s1last = s1 + n1;
+ s2last = s2 + n2;
+
+ is_it_toupper = flag & U8_TEXTPREP_TOUPPER;
+ is_it_tolower = flag & U8_TEXTPREP_TOLOWER;
+ canonical_decomposition = flag & U8_CANON_DECOMP;
+ compatibility_decomposition = flag & U8_COMPAT_DECOMP;
+ canonical_composition = flag & U8_CANON_COMP;
+
+ while (s1 < s1last && s2 < s2last) {
+ /*
+ * If the current character is a 7-bit ASCII and the last
+ * character, or, if the current character and the next
+ * character are both some 7-bit ASCII characters then
+ * we treat the current character as a sequence.
+ *
+ * In any other cases, we need to call collect_a_seq().
+ */
+
+ if (U8_ISASCII(*s1) && ((s1 + 1) >= s1last ||
+ ((s1 + 1) < s1last && U8_ISASCII(*(s1 + 1))))) {
+ if (is_it_toupper)
+ u8s1[0] = U8_ASCII_TOUPPER(*s1);
+ else if (is_it_tolower)
+ u8s1[0] = U8_ASCII_TOLOWER(*s1);
+ else
+ u8s1[0] = *s1;
+ u8s1[1] = '\0';
+ sz1 = 1;
+ s1++;
+ } else {
+ state = U8_STATE_START;
+ sz1 = collect_a_seq(uv, u8s1, &s1, s1last,
+ is_it_toupper, is_it_tolower,
+ canonical_decomposition,
+ compatibility_decomposition,
+ canonical_composition, errnum, &state);
+ }
+
+ if (U8_ISASCII(*s2) && ((s2 + 1) >= s2last ||
+ ((s2 + 1) < s2last && U8_ISASCII(*(s2 + 1))))) {
+ if (is_it_toupper)
+ u8s2[0] = U8_ASCII_TOUPPER(*s2);
+ else if (is_it_tolower)
+ u8s2[0] = U8_ASCII_TOLOWER(*s2);
+ else
+ u8s2[0] = *s2;
+ u8s2[1] = '\0';
+ sz2 = 1;
+ s2++;
+ } else {
+ state = U8_STATE_START;
+ sz2 = collect_a_seq(uv, u8s2, &s2, s2last,
+ is_it_toupper, is_it_tolower,
+ canonical_decomposition,
+ compatibility_decomposition,
+ canonical_composition, errnum, &state);
+ }
+
+ /*
+ * Now compare the two characters. If they are the same,
+ * we move on to the next character sequences.
+ */
+ if (sz1 == 1 && sz2 == 1) {
+ if (*u8s1 > *u8s2)
+ return (1);
+ if (*u8s1 < *u8s2)
+ return (-1);
+ } else {
+ result = strcmp((const char *)u8s1, (const char *)u8s2);
+ if (result != 0)
+ return (result);
+ }
+ }
+
+ /*
+ * We compared until the end of either or both strings.
+ *
+ * If we reached to or went over the ends for the both, that means
+ * they are the same.
+ *
+ * If we reached only one end, that means the other string has
+ * something which then can be used to determine the return value.
+ */
+ if (s1 >= s1last) {
+ if (s2 >= s2last)
+ return (0);
+ return (-1);
+ }
+ return (1);
+}
+
+/*
+ * The u8_strcmp() function compares two UTF-8 strings quite similar to
+ * the strcmp(). For the comparison, however, Unicode Normalization specific
+ * equivalency and Unicode simple case conversion mappings based equivalency
+ * can be requested and checked against.
+ */
+int
+u8_strcmp(const char *s1, const char *s2, size_t n, int flag, size_t uv,
+ int *errnum)
+{
+ int f;
+ size_t n1;
+ size_t n2;
+
+ *errnum = 0;
+
+ /*
+ * Check on the requested Unicode version, case conversion, and
+ * normalization flag values.
+ */
+
+ if (uv > U8_UNICODE_LATEST) {
+ *errnum = ERANGE;
+ uv = U8_UNICODE_LATEST;
+ }
+
+ if (flag == 0) {
+ flag = U8_STRCMP_CS;
+ } else {
+ f = flag & (U8_STRCMP_CS | U8_STRCMP_CI_UPPER |
+ U8_STRCMP_CI_LOWER);
+ if (f == 0) {
+ flag |= U8_STRCMP_CS;
+ } else if (f != U8_STRCMP_CS && f != U8_STRCMP_CI_UPPER &&
+ f != U8_STRCMP_CI_LOWER) {
+ *errnum = EBADF;
+ flag = U8_STRCMP_CS;
+ }
+
+ f = flag & (U8_CANON_DECOMP | U8_COMPAT_DECOMP | U8_CANON_COMP);
+ if (f && f != U8_STRCMP_NFD && f != U8_STRCMP_NFC &&
+ f != U8_STRCMP_NFKD && f != U8_STRCMP_NFKC) {
+ *errnum = EBADF;
+ flag = U8_STRCMP_CS;
+ }
+ }
+
+ if (flag == U8_STRCMP_CS) {
+ return (n == 0 ? strcmp(s1, s2) : strncmp(s1, s2, n));
+ }
+
+ n1 = strlen(s1);
+ n2 = strlen(s2);
+ if (n != 0) {
+ if (n < n1)
+ n1 = n;
+ if (n < n2)
+ n2 = n;
+ }
+
+ /*
+ * Simple case conversion can be done much faster and so we do
+ * them separately here.
+ */
+ if (flag == U8_STRCMP_CI_UPPER) {
+ return (do_case_compare(uv, (uchar_t *)s1, (uchar_t *)s2,
+ n1, n2, B_TRUE, errnum));
+ } else if (flag == U8_STRCMP_CI_LOWER) {
+ return (do_case_compare(uv, (uchar_t *)s1, (uchar_t *)s2,
+ n1, n2, B_FALSE, errnum));
+ }
+
+ return (do_norm_compare(uv, (uchar_t *)s1, (uchar_t *)s2, n1, n2,
+ flag, errnum));
+}
+
+size_t
+u8_textprep_str(char *inarray, size_t *inlen, char *outarray, size_t *outlen,
+ int flag, size_t unicode_version, int *errnum)
+{
+ int f;
+ int sz;
+ uchar_t *ib;
+ uchar_t *ibtail;
+ uchar_t *ob;
+ uchar_t *obtail;
+ boolean_t do_not_ignore_null;
+ boolean_t do_not_ignore_invalid;
+ boolean_t is_it_toupper;
+ boolean_t is_it_tolower;
+ boolean_t canonical_decomposition;
+ boolean_t compatibility_decomposition;
+ boolean_t canonical_composition;
+ size_t ret_val;
+ size_t i;
+ size_t j;
+ uchar_t u8s[U8_STREAM_SAFE_TEXT_MAX + 1];
+ u8_normalization_states_t state;
+
+ if (unicode_version > U8_UNICODE_LATEST) {
+ *errnum = ERANGE;
+ return ((size_t)-1);
+ }
+
+ f = flag & (U8_TEXTPREP_TOUPPER | U8_TEXTPREP_TOLOWER);
+ if (f == (U8_TEXTPREP_TOUPPER | U8_TEXTPREP_TOLOWER)) {
+ *errnum = EBADF;
+ return ((size_t)-1);
+ }
+
+ f = flag & (U8_CANON_DECOMP | U8_COMPAT_DECOMP | U8_CANON_COMP);
+ if (f && f != U8_TEXTPREP_NFD && f != U8_TEXTPREP_NFC &&
+ f != U8_TEXTPREP_NFKD && f != U8_TEXTPREP_NFKC) {
+ *errnum = EBADF;
+ return ((size_t)-1);
+ }
+
+ if (inarray == NULL || *inlen == 0)
+ return (0);
+
+ if (outarray == NULL) {
+ *errnum = E2BIG;
+ return ((size_t)-1);
+ }
+
+ ib = (uchar_t *)inarray;
+ ob = (uchar_t *)outarray;
+ ibtail = ib + *inlen;
+ obtail = ob + *outlen;
+
+ do_not_ignore_null = !(flag & U8_TEXTPREP_IGNORE_NULL);
+ do_not_ignore_invalid = !(flag & U8_TEXTPREP_IGNORE_INVALID);
+ is_it_toupper = flag & U8_TEXTPREP_TOUPPER;
+ is_it_tolower = flag & U8_TEXTPREP_TOLOWER;
+
+ ret_val = 0;
+
+ /*
+ * If we don't have a normalization flag set, we do the simple case
+ * conversion based text preparation separately below. Text
+ * preparation involving Normalization will be done in the false task
+ * block, again, separately since it will take much more time and
+ * resource than doing simple case conversions.
+ */
+ if (f == 0) {
+ while (ib < ibtail) {
+ if (*ib == '\0' && do_not_ignore_null)
+ break;
+
+ sz = u8_number_of_bytes[*ib];
+
+ if (sz < 0) {
+ if (do_not_ignore_invalid) {
+ *errnum = EILSEQ;
+ ret_val = (size_t)-1;
+ break;
+ }
+
+ sz = 1;
+ ret_val++;
+ }
+
+ if (sz == 1) {
+ if (ob >= obtail) {
+ *errnum = E2BIG;
+ ret_val = (size_t)-1;
+ break;
+ }
+
+ if (is_it_toupper)
+ *ob = U8_ASCII_TOUPPER(*ib);
+ else if (is_it_tolower)
+ *ob = U8_ASCII_TOLOWER(*ib);
+ else
+ *ob = *ib;
+ ib++;
+ ob++;
+ } else if ((ib + sz) > ibtail) {
+ if (do_not_ignore_invalid) {
+ *errnum = EINVAL;
+ ret_val = (size_t)-1;
+ break;
+ }
+
+ if ((obtail - ob) < (ibtail - ib)) {
+ *errnum = E2BIG;
+ ret_val = (size_t)-1;
+ break;
+ }
+
+ /*
+ * We treat the remaining incomplete character
+ * bytes as a character.
+ */
+ ret_val++;
+
+ while (ib < ibtail)
+ *ob++ = *ib++;
+ } else {
+ if (is_it_toupper || is_it_tolower) {
+ i = do_case_conv(unicode_version, u8s,
+ ib, sz, is_it_toupper);
+
+ if ((obtail - ob) < i) {
+ *errnum = E2BIG;
+ ret_val = (size_t)-1;
+ break;
+ }
+
+ ib += sz;
+
+ for (sz = 0; sz < i; sz++)
+ *ob++ = u8s[sz];
+ } else {
+ if ((obtail - ob) < sz) {
+ *errnum = E2BIG;
+ ret_val = (size_t)-1;
+ break;
+ }
+
+ for (i = 0; i < sz; i++)
+ *ob++ = *ib++;
+ }
+ }
+ }
+ } else {
+ canonical_decomposition = flag & U8_CANON_DECOMP;
+ compatibility_decomposition = flag & U8_COMPAT_DECOMP;
+ canonical_composition = flag & U8_CANON_COMP;
+
+ while (ib < ibtail) {
+ if (*ib == '\0' && do_not_ignore_null)
+ break;
+
+ /*
+ * If the current character is a 7-bit ASCII
+ * character and it is the last character, or,
+ * if the current character is a 7-bit ASCII
+ * character and the next character is also a 7-bit
+ * ASCII character, then, we copy over this
+ * character without going through collect_a_seq().
+ *
+ * In any other cases, we need to look further with
+ * the collect_a_seq() function.
+ */
+ if (U8_ISASCII(*ib) && ((ib + 1) >= ibtail ||
+ ((ib + 1) < ibtail && U8_ISASCII(*(ib + 1))))) {
+ if (ob >= obtail) {
+ *errnum = E2BIG;
+ ret_val = (size_t)-1;
+ break;
+ }
+
+ if (is_it_toupper)
+ *ob = U8_ASCII_TOUPPER(*ib);
+ else if (is_it_tolower)
+ *ob = U8_ASCII_TOLOWER(*ib);
+ else
+ *ob = *ib;
+ ib++;
+ ob++;
+ } else {
+ *errnum = 0;
+ state = U8_STATE_START;
+
+ j = collect_a_seq(unicode_version, u8s,
+ &ib, ibtail,
+ is_it_toupper,
+ is_it_tolower,
+ canonical_decomposition,
+ compatibility_decomposition,
+ canonical_composition,
+ errnum, &state);
+
+ if (*errnum && do_not_ignore_invalid) {
+ ret_val = (size_t)-1;
+ break;
+ }
+
+ if ((obtail - ob) < j) {
+ *errnum = E2BIG;
+ ret_val = (size_t)-1;
+ break;
+ }
+
+ for (i = 0; i < j; i++)
+ *ob++ = u8s[i];
+ }
+ }
+ }
+
+ *inlen = ibtail - ib;
+ *outlen = obtail - ob;
+
+ return (ret_val);
+}
diff --git a/sys/cddl/contrib/opensolaris/common/zfs/zfs_comutil.c b/sys/cddl/contrib/opensolaris/common/zfs/zfs_comutil.c
new file mode 100644
index 0000000..74517a3
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/common/zfs/zfs_comutil.c
@@ -0,0 +1,65 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL 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.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file is intended for functions that ought to be common between user
+ * land (libzfs) and the kernel. When many common routines need to be shared
+ * then a separate file should to be created.
+ */
+
+#if defined(_KERNEL)
+#include <sys/systm.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/fs/zfs.h>
+#include <sys/nvpair.h>
+
+/*
+ * Are there allocatable vdevs?
+ */
+boolean_t
+zfs_allocatable_devs(nvlist_t *nv)
+{
+ uint64_t is_log;
+ uint_t c;
+ nvlist_t **child;
+ uint_t children;
+
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
+ &child, &children) != 0) {
+ return (B_FALSE);
+ }
+ for (c = 0; c < children; c++) {
+ is_log = 0;
+ (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG,
+ &is_log);
+ if (!is_log)
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
diff --git a/sys/cddl/contrib/opensolaris/common/zfs/zfs_comutil.h b/sys/cddl/contrib/opensolaris/common/zfs/zfs_comutil.h
new file mode 100644
index 0000000..f517044
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/common/zfs/zfs_comutil.h
@@ -0,0 +1,44 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL 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.
+ */
+
+#ifndef _ZFS_COMUTIL_H
+#define _ZFS_COMUTIL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/fs/zfs.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern boolean_t zfs_allocatable_devs(nvlist_t *nv);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZFS_COMUTIL_H */
diff --git a/sys/cddl/contrib/opensolaris/common/zfs/zfs_deleg.c b/sys/cddl/contrib/opensolaris/common/zfs/zfs_deleg.c
new file mode 100644
index 0000000..0fd5800
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/common/zfs/zfs_deleg.c
@@ -0,0 +1,234 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL 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.
+ */
+
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#if defined(_KERNEL)
+#include <sys/systm.h>
+#include <sys/sunddi.h>
+#include <sys/ctype.h>
+#else
+#include <stdio.h>
+#include <unistd.h>
+#include <strings.h>
+#include <libnvpair.h>
+#include <ctype.h>
+#endif
+/* XXX includes zfs_context.h, so why bother with the above? */
+#include <sys/dsl_deleg.h>
+#include "zfs_prop.h"
+#include "zfs_deleg.h"
+#include "zfs_namecheck.h"
+
+/*
+ * permission table
+ *
+ * Keep this table in sorted order
+ *
+ * This table is used for displaying all permissions for
+ * zfs allow
+ */
+
+zfs_deleg_perm_tab_t zfs_deleg_perm_tab[] = {
+ {ZFS_DELEG_PERM_ALLOW, ZFS_DELEG_NOTE_ALLOW},
+ {ZFS_DELEG_PERM_CLONE, ZFS_DELEG_NOTE_CLONE },
+ {ZFS_DELEG_PERM_CREATE, ZFS_DELEG_NOTE_CREATE },
+ {ZFS_DELEG_PERM_DESTROY, ZFS_DELEG_NOTE_DESTROY },
+ {ZFS_DELEG_PERM_MOUNT, ZFS_DELEG_NOTE_MOUNT },
+ {ZFS_DELEG_PERM_PROMOTE, ZFS_DELEG_NOTE_PROMOTE },
+ {ZFS_DELEG_PERM_RECEIVE, ZFS_DELEG_NOTE_RECEIVE },
+ {ZFS_DELEG_PERM_RENAME, ZFS_DELEG_NOTE_RENAME },
+ {ZFS_DELEG_PERM_ROLLBACK, ZFS_DELEG_NOTE_ROLLBACK },
+ {ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT },
+ {ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE },
+ {ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_NONE },
+ {ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP },
+ {NULL, ZFS_DELEG_NOTE_NONE }
+};
+
+static int
+zfs_valid_permission_name(const char *perm)
+{
+ if (zfs_deleg_canonicalize_perm(perm))
+ return (0);
+
+ return (permset_namecheck(perm, NULL, NULL));
+}
+
+const char *
+zfs_deleg_canonicalize_perm(const char *perm)
+{
+ int i;
+ zfs_prop_t prop;
+
+ for (i = 0; zfs_deleg_perm_tab[i].z_perm != NULL; i++) {
+ if (strcmp(perm, zfs_deleg_perm_tab[i].z_perm) == 0)
+ return (perm);
+ }
+
+ prop = zfs_name_to_prop(perm);
+ if (prop != ZPROP_INVAL && zfs_prop_delegatable(prop))
+ return (zfs_prop_to_name(prop));
+ return (NULL);
+
+}
+
+static int
+zfs_validate_who(char *who)
+{
+ char *p;
+
+ if (who[2] != ZFS_DELEG_FIELD_SEP_CHR)
+ return (-1);
+
+ switch (who[0]) {
+ case ZFS_DELEG_USER:
+ case ZFS_DELEG_GROUP:
+ case ZFS_DELEG_USER_SETS:
+ case ZFS_DELEG_GROUP_SETS:
+ if (who[1] != ZFS_DELEG_LOCAL && who[1] != ZFS_DELEG_DESCENDENT)
+ return (-1);
+ for (p = &who[3]; *p; p++)
+ if (!isdigit(*p))
+ return (-1);
+ break;
+
+ case ZFS_DELEG_NAMED_SET:
+ case ZFS_DELEG_NAMED_SET_SETS:
+ if (who[1] != ZFS_DELEG_NA)
+ return (-1);
+ return (permset_namecheck(&who[3], NULL, NULL));
+
+ case ZFS_DELEG_CREATE:
+ case ZFS_DELEG_CREATE_SETS:
+ if (who[1] != ZFS_DELEG_NA)
+ return (-1);
+ if (who[3] != '\0')
+ return (-1);
+ break;
+
+ case ZFS_DELEG_EVERYONE:
+ case ZFS_DELEG_EVERYONE_SETS:
+ if (who[1] != ZFS_DELEG_LOCAL && who[1] != ZFS_DELEG_DESCENDENT)
+ return (-1);
+ if (who[3] != '\0')
+ return (-1);
+ break;
+
+ default:
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+zfs_deleg_verify_nvlist(nvlist_t *nvp)
+{
+ nvpair_t *who, *perm_name;
+ nvlist_t *perms;
+ int error;
+
+ if (nvp == NULL)
+ return (-1);
+
+ who = nvlist_next_nvpair(nvp, NULL);
+ if (who == NULL)
+ return (-1);
+
+ do {
+ if (zfs_validate_who(nvpair_name(who)))
+ return (-1);
+
+ error = nvlist_lookup_nvlist(nvp, nvpair_name(who), &perms);
+
+ if (error && error != ENOENT)
+ return (-1);
+ if (error == ENOENT)
+ continue;
+
+ perm_name = nvlist_next_nvpair(perms, NULL);
+ if (perm_name == NULL) {
+ return (-1);
+ }
+ do {
+ error = zfs_valid_permission_name(
+ nvpair_name(perm_name));
+ if (error)
+ return (-1);
+ } while (perm_name = nvlist_next_nvpair(perms, perm_name));
+ } while (who = nvlist_next_nvpair(nvp, who));
+ return (0);
+}
+
+/*
+ * Construct the base attribute name. The base attribute names
+ * are the "key" to locate the jump objects which contain the actual
+ * permissions. The base attribute names are encoded based on
+ * type of entry and whether it is a local or descendent permission.
+ *
+ * Arguments:
+ * attr - attribute name return string, attribute is assumed to be
+ * ZFS_MAX_DELEG_NAME long.
+ * type - type of entry to construct
+ * inheritchr - inheritance type (local,descendent, or NA for create and
+ * permission set definitions
+ * data - is either a permission set name or a 64 bit uid/gid.
+ */
+void
+zfs_deleg_whokey(char *attr, zfs_deleg_who_type_t type,
+ char inheritchr, void *data)
+{
+ int len = ZFS_MAX_DELEG_NAME;
+ uint64_t *id = data;
+
+ switch (type) {
+ case ZFS_DELEG_USER:
+ case ZFS_DELEG_GROUP:
+ case ZFS_DELEG_USER_SETS:
+ case ZFS_DELEG_GROUP_SETS:
+ (void) snprintf(attr, len, "%c%c%c%lld", type, inheritchr,
+ ZFS_DELEG_FIELD_SEP_CHR, (longlong_t)*id);
+ break;
+ case ZFS_DELEG_NAMED_SET_SETS:
+ case ZFS_DELEG_NAMED_SET:
+ (void) snprintf(attr, len, "%c-%c%s", type,
+ ZFS_DELEG_FIELD_SEP_CHR, (char *)data);
+ break;
+ case ZFS_DELEG_CREATE:
+ case ZFS_DELEG_CREATE_SETS:
+ (void) snprintf(attr, len, "%c-%c", type,
+ ZFS_DELEG_FIELD_SEP_CHR);
+ break;
+ case ZFS_DELEG_EVERYONE:
+ case ZFS_DELEG_EVERYONE_SETS:
+ (void) snprintf(attr, len, "%c%c%c", type, inheritchr,
+ ZFS_DELEG_FIELD_SEP_CHR);
+ break;
+ default:
+ ASSERT(!"bad zfs_deleg_who_type_t");
+ }
+}
diff --git a/sys/cddl/contrib/opensolaris/common/zfs/zfs_deleg.h b/sys/cddl/contrib/opensolaris/common/zfs/zfs_deleg.h
new file mode 100644
index 0000000..561b73e
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/common/zfs/zfs_deleg.h
@@ -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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _ZFS_DELEG_H
+#define _ZFS_DELEG_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/fs/zfs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZFS_DELEG_SET_NAME_CHR '@' /* set name lead char */
+#define ZFS_DELEG_FIELD_SEP_CHR '$' /* field separator */
+
+/*
+ * Max name length for a delegation attribute
+ */
+#define ZFS_MAX_DELEG_NAME 128
+
+#define ZFS_DELEG_LOCAL 'l'
+#define ZFS_DELEG_DESCENDENT 'd'
+#define ZFS_DELEG_NA '-'
+
+typedef enum {
+ ZFS_DELEG_NOTE_CREATE,
+ ZFS_DELEG_NOTE_DESTROY,
+ ZFS_DELEG_NOTE_SNAPSHOT,
+ ZFS_DELEG_NOTE_ROLLBACK,
+ ZFS_DELEG_NOTE_CLONE,
+ ZFS_DELEG_NOTE_PROMOTE,
+ ZFS_DELEG_NOTE_RENAME,
+ ZFS_DELEG_NOTE_RECEIVE,
+ ZFS_DELEG_NOTE_ALLOW,
+ ZFS_DELEG_NOTE_USERPROP,
+ ZFS_DELEG_NOTE_MOUNT,
+ ZFS_DELEG_NOTE_SHARE,
+ ZFS_DELEG_NOTE_NONE
+} zfs_deleg_note_t;
+
+typedef struct zfs_deleg_perm_tab {
+ char *z_perm;
+ zfs_deleg_note_t z_note;
+} zfs_deleg_perm_tab_t;
+
+extern zfs_deleg_perm_tab_t zfs_deleg_perm_tab[];
+
+int zfs_deleg_verify_nvlist(nvlist_t *nvlist);
+void zfs_deleg_whokey(char *attr, zfs_deleg_who_type_t type,
+ char checkflag, void *data);
+const char *zfs_deleg_canonicalize_perm(const char *perm);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZFS_DELEG_H */
diff --git a/sys/cddl/contrib/opensolaris/common/zfs/zfs_namecheck.c b/sys/cddl/contrib/opensolaris/common/zfs/zfs_namecheck.c
index 2004d86..a9d109b 100644
--- a/sys/cddl/contrib/opensolaris/common/zfs/zfs_namecheck.c
+++ b/sys/cddl/contrib/opensolaris/common/zfs/zfs_namecheck.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -44,7 +44,9 @@
#endif
#include <sys/param.h>
+#include <sys/nvpair.h>
#include "zfs_namecheck.h"
+#include "zfs_deleg.h"
static int
valid_char(char c)
@@ -52,7 +54,7 @@ valid_char(char c)
return ((c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') ||
- c == '-' || c == '_' || c == '.' || c == ':');
+ c == '-' || c == '_' || c == '.' || c == ':' || c == ' ');
}
/*
@@ -90,6 +92,32 @@ snapshot_namecheck(const char *path, namecheck_err_t *why, char *what)
return (0);
}
+
+/*
+ * Permissions set name must start with the letter '@' followed by the
+ * same character restrictions as snapshot names, except that the name
+ * cannot exceed 64 characters.
+ */
+int
+permset_namecheck(const char *path, namecheck_err_t *why, char *what)
+{
+ if (strlen(path) >= ZFS_PERMSET_MAXLEN) {
+ if (why)
+ *why = NAME_ERR_TOOLONG;
+ return (-1);
+ }
+
+ if (path[0] != '@') {
+ if (why) {
+ *why = NAME_ERR_NO_AT;
+ *what = path[0];
+ }
+ return (-1);
+ }
+
+ return (snapshot_namecheck(&path[1], why, what));
+}
+
/*
* Dataset names must be of the following form:
*
@@ -98,7 +126,10 @@ snapshot_namecheck(const char *path, namecheck_err_t *why, char *what)
* Where each component is made up of alphanumeric characters plus the following
* characters:
*
- * [-_.:]
+ * [-_.:%]
+ *
+ * We allow '%' here as we use that character internally to create unique
+ * names for temporary clones (for online recv).
*/
int
dataset_namecheck(const char *path, namecheck_err_t *why, char *what)
@@ -114,6 +145,7 @@ dataset_namecheck(const char *path, namecheck_err_t *why, char *what)
* If ZFS_MAXNAMELEN value is changed, make sure to cleanup all
* places using MAXNAMELEN.
*/
+
if (strlen(path) >= MAXNAMELEN) {
if (why)
*why = NAME_ERR_TOOLONG;
@@ -167,7 +199,7 @@ dataset_namecheck(const char *path, namecheck_err_t *why, char *what)
/* Validate the contents of this component */
while (loc != end) {
- if (!valid_char(*loc)) {
+ if (!valid_char(*loc) && *loc != '%') {
if (why) {
*why = NAME_ERR_INVALCHAR;
*what = *loc;
@@ -211,6 +243,50 @@ dataset_namecheck(const char *path, namecheck_err_t *why, char *what)
}
}
+
+/*
+ * mountpoint names must be of the following form:
+ *
+ * /[component][/]*[component][/]
+ */
+int
+mountpoint_namecheck(const char *path, namecheck_err_t *why)
+{
+ const char *start, *end;
+
+ /*
+ * Make sure none of the mountpoint component names are too long.
+ * If a component name is too long then the mkdir of the mountpoint
+ * will fail but then the mountpoint property will be set to a value
+ * that can never be mounted. Better to fail before setting the prop.
+ * Extra slashes are OK, they will be tossed by the mountpoint mkdir.
+ */
+
+ if (path == NULL || *path != '/') {
+ if (why)
+ *why = NAME_ERR_LEADING_SLASH;
+ return (-1);
+ }
+
+ /* Skip leading slash */
+ start = &path[1];
+ do {
+ end = start;
+ while (*end != '/' && *end != '\0')
+ end++;
+
+ if (end - start >= MAXNAMELEN) {
+ if (why)
+ *why = NAME_ERR_TOOLONG;
+ return (-1);
+ }
+ start = end + 1;
+
+ } while (*end != '\0');
+
+ return (0);
+}
+
/*
* For pool names, we have the same set of valid characters as described in
* dataset names, with the additional restriction that the pool name must begin
diff --git a/sys/cddl/contrib/opensolaris/common/zfs/zfs_namecheck.h b/sys/cddl/contrib/opensolaris/common/zfs/zfs_namecheck.h
index 7e0cda9..ec85e62 100644
--- a/sys/cddl/contrib/opensolaris/common/zfs/zfs_namecheck.h
+++ b/sys/cddl/contrib/opensolaris/common/zfs/zfs_namecheck.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -42,12 +42,17 @@ typedef enum {
NAME_ERR_RESERVED, /* entire name is reserved */
NAME_ERR_DISKLIKE, /* reserved disk name (c[0-9].*) */
NAME_ERR_TOOLONG, /* name is too long */
+ NAME_ERR_NO_AT, /* permission set is missing '@' */
} namecheck_err_t;
+#define ZFS_PERMSET_MAXLEN 64
+
int pool_namecheck(const char *, namecheck_err_t *, char *);
int dataset_namecheck(const char *, namecheck_err_t *, char *);
+int mountpoint_namecheck(const char *, namecheck_err_t *);
int dataset_name_hidden(const char *);
int snapshot_namecheck(const char *, namecheck_err_t *, char *);
+int permset_namecheck(const char *, namecheck_err_t *, char *);
#ifdef __cplusplus
}
diff --git a/sys/cddl/contrib/opensolaris/common/zfs/zfs_prop.c b/sys/cddl/contrib/opensolaris/common/zfs/zfs_prop.c
index 7125619..27a6f2e 100644
--- a/sys/cddl/contrib/opensolaris/common/zfs/zfs_prop.c
+++ b/sys/cddl/contrib/opensolaris/common/zfs/zfs_prop.c
@@ -19,40 +19,19 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-/*
- * Master property table.
- *
- * This table keeps track of all the properties supported by ZFS, and their
- * various attributes. Not all of these are needed by the kernel, and several
- * are only used by a single libzfs client. But having them here centralizes
- * all property information in one location.
- *
- * name The human-readable string representing this property
- * proptype Basic type (string, boolean, number)
- * default Default value for the property. Sadly, C only allows
- * you to initialize the first member of a union, so we
- * have two default members for each property.
- * attr Attributes (readonly, inheritable) for the property
- * types Valid dataset types to which this applies
- * values String describing acceptable values for the property
- * colname The column header for 'zfs list'
- * colfmt The column formatting for 'zfs list'
- *
- * This table must match the order of property types in libzfs.h.
- */
-
#include <sys/zio.h>
#include <sys/spa.h>
+#include <sys/u8_textprep.h>
#include <sys/zfs_acl.h>
#include <sys/zfs_ioctl.h>
+#include <sys/zfs_znode.h>
#include "zfs_prop.h"
+#include "zfs_deleg.h"
#if defined(_KERNEL)
#include <sys/systm.h>
@@ -62,244 +41,283 @@
#include <ctype.h>
#endif
-typedef enum {
- prop_default,
- prop_readonly,
- prop_inherit
-} prop_attr_t;
-
-typedef struct {
- const char *pd_name;
- zfs_proptype_t pd_proptype;
- uint64_t pd_numdefault;
- const char *pd_strdefault;
- prop_attr_t pd_attr;
- int pd_types;
- const char *pd_values;
- const char *pd_colname;
- boolean_t pd_rightalign;
- boolean_t pd_visible;
-} prop_desc_t;
-
-static prop_desc_t zfs_prop_table[] = {
- { "type", prop_type_string, 0, NULL, prop_readonly,
- ZFS_TYPE_ANY, "filesystem | volume | snapshot", "TYPE", B_TRUE,
- B_TRUE },
- { "creation", prop_type_number, 0, NULL, prop_readonly,
- ZFS_TYPE_ANY, "<date>", "CREATION", B_FALSE, B_TRUE },
- { "used", prop_type_number, 0, NULL, prop_readonly,
- ZFS_TYPE_ANY, "<size>", "USED", B_TRUE, B_TRUE },
- { "available", prop_type_number, 0, NULL, prop_readonly,
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<size>", "AVAIL", B_TRUE,
- B_TRUE },
- { "referenced", prop_type_number, 0, NULL, prop_readonly,
- ZFS_TYPE_ANY,
- "<size>", "REFER", B_TRUE, B_TRUE },
- { "compressratio", prop_type_number, 0, NULL, prop_readonly,
- ZFS_TYPE_ANY, "<1.00x or higher if compressed>", "RATIO", B_TRUE,
- B_TRUE },
- { "mounted", prop_type_boolean, 0, NULL, prop_readonly,
- ZFS_TYPE_FILESYSTEM, "yes | no | -", "MOUNTED", B_TRUE, B_TRUE },
- { "origin", prop_type_string, 0, NULL, prop_readonly,
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<snapshot>", "ORIGIN",
- B_FALSE, B_TRUE },
- { "quota", prop_type_number, 0, NULL, prop_default,
- ZFS_TYPE_FILESYSTEM, "<size> | none", "QUOTA", B_TRUE, B_TRUE },
- { "reservation", prop_type_number, 0, NULL, prop_default,
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
- "<size> | none", "RESERV", B_TRUE, B_TRUE },
- { "volsize", prop_type_number, 0, NULL, prop_default,
- ZFS_TYPE_VOLUME, "<size>", "VOLSIZE", B_TRUE, B_TRUE },
- { "volblocksize", prop_type_number, 8192, NULL, prop_readonly,
- ZFS_TYPE_VOLUME, "512 to 128k, power of 2", "VOLBLOCK", B_TRUE,
- B_TRUE },
- { "recordsize", prop_type_number, SPA_MAXBLOCKSIZE, NULL,
- prop_inherit,
- ZFS_TYPE_FILESYSTEM,
- "512 to 128k, power of 2", "RECSIZE", B_TRUE, B_TRUE },
- { "mountpoint", prop_type_string, 0, "/", prop_inherit,
- ZFS_TYPE_FILESYSTEM,
- "<path> | legacy | none", "MOUNTPOINT", B_FALSE, B_TRUE },
- { "sharenfs", prop_type_string, 0, "off", prop_inherit,
- ZFS_TYPE_FILESYSTEM,
- "on | off | exports(5) options", "SHARENFS", B_FALSE, B_TRUE },
- { "checksum", prop_type_index, ZIO_CHECKSUM_DEFAULT, "on",
- prop_inherit, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
- "on | off | fletcher2 | fletcher4 | sha256", "CHECKSUM", B_TRUE,
- B_TRUE },
- { "compression", prop_type_index, ZIO_COMPRESS_DEFAULT, "off",
- prop_inherit, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
- "on | off | lzjb | gzip | gzip-[1-9]", "COMPRESS", B_TRUE, B_TRUE },
- { "atime", prop_type_boolean, 1, NULL, prop_inherit,
- ZFS_TYPE_FILESYSTEM,
- "on | off", "ATIME", B_TRUE, B_TRUE },
- { "devices", prop_type_boolean, 1, NULL, prop_inherit,
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT,
- "on | off", "DEVICES", B_TRUE, B_TRUE },
- { "exec", prop_type_boolean, 1, NULL, prop_inherit,
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT,
- "on | off", "EXEC", B_TRUE, B_TRUE },
- { "setuid", prop_type_boolean, 1, NULL, prop_inherit,
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, "on | off", "SETUID",
- B_TRUE, B_TRUE },
- { "readonly", prop_type_boolean, 0, NULL, prop_inherit,
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
- "on | off", "RDONLY", B_TRUE, B_TRUE },
- { "jailed", prop_type_boolean, 0, NULL, prop_inherit,
- ZFS_TYPE_FILESYSTEM,
- "on | off", "JAILED", B_TRUE, B_TRUE },
- { "snapdir", prop_type_index, ZFS_SNAPDIR_HIDDEN, "hidden",
- prop_inherit,
- ZFS_TYPE_FILESYSTEM,
- "hidden | visible", "SNAPDIR", B_TRUE, B_TRUE },
- { "aclmode", prop_type_index, ZFS_ACL_GROUPMASK, "groupmask",
- prop_inherit, ZFS_TYPE_FILESYSTEM,
- "discard | groupmask | passthrough", "ACLMODE", B_TRUE, B_TRUE },
- { "aclinherit", prop_type_index, ZFS_ACL_SECURE, "secure",
- prop_inherit, ZFS_TYPE_FILESYSTEM,
- "discard | noallow | secure | passthrough", "ACLINHERIT", B_TRUE,
- B_TRUE },
- { "createtxg", prop_type_number, 0, NULL, prop_readonly,
- ZFS_TYPE_ANY, NULL, NULL, B_FALSE, B_FALSE },
- { "name", prop_type_string, 0, NULL, prop_readonly,
- ZFS_TYPE_ANY, NULL, "NAME", B_FALSE, B_FALSE },
- { "canmount", prop_type_boolean, 1, NULL, prop_default,
- ZFS_TYPE_FILESYSTEM,
- "on | off", "CANMOUNT", B_TRUE, B_TRUE },
- { "shareiscsi", prop_type_string, 0, "off", prop_inherit,
- ZFS_TYPE_ANY,
- "on | off | type=<type>", "SHAREISCSI", B_FALSE, B_TRUE },
- { "iscsioptions", prop_type_string, 0, NULL, prop_inherit,
- ZFS_TYPE_VOLUME, NULL, "ISCSIOPTIONS", B_FALSE, B_FALSE },
- { "xattr", prop_type_boolean, 1, NULL, prop_inherit,
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT,
- "on | off", "XATTR", B_TRUE, B_TRUE },
- { "numclones", prop_type_number, 0, NULL, prop_readonly,
- ZFS_TYPE_SNAPSHOT, NULL, NULL, B_FALSE, B_FALSE },
- { "copies", prop_type_index, 1, "1", prop_inherit,
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
- "1 | 2 | 3", "COPIES", B_TRUE, B_TRUE },
- { "bootfs", prop_type_string, 0, NULL, prop_default,
- ZFS_TYPE_POOL, "<filesystem>", "BOOTFS", B_FALSE, B_TRUE },
-};
-
-#define ZFS_PROP_COUNT ((sizeof (zfs_prop_table))/(sizeof (prop_desc_t)))
-
-/*
- * Returns TRUE if the property applies to the given dataset types.
- */
-int
-zfs_prop_valid_for_type(zfs_prop_t prop, int types)
-{
- return ((zfs_prop_table[prop].pd_types & types) != 0);
-}
-
-/*
- * Determine if the specified property is visible or not.
- */
-boolean_t
-zfs_prop_is_visible(zfs_prop_t prop)
-{
- if (prop < 0)
- return (B_FALSE);
-
- return (zfs_prop_table[prop].pd_visible);
-}
-
-/*
- * Iterate over all properties, calling back into the specified function
- * for each property. We will continue to iterate until we either
- * reach the end or the callback function something other than
- * ZFS_PROP_CONT.
- */
-zfs_prop_t
-zfs_prop_iter_common(zfs_prop_f func, void *cb, zfs_type_t type,
- boolean_t show_all)
-{
- int i;
-
- for (i = 0; i < ZFS_PROP_COUNT; i++) {
- if (zfs_prop_valid_for_type(i, type) &&
- (zfs_prop_is_visible(i) || show_all)) {
- if (func(i, cb) != ZFS_PROP_CONT)
- return (i);
- }
- }
- return (ZFS_PROP_CONT);
-}
+static zprop_desc_t zfs_prop_table[ZFS_NUM_PROPS];
-zfs_prop_t
-zfs_prop_iter(zfs_prop_f func, void *cb, boolean_t show_all)
+zprop_desc_t *
+zfs_prop_get_table(void)
{
- return (zfs_prop_iter_common(func, cb, ZFS_TYPE_ANY, show_all));
+ return (zfs_prop_table);
}
-zpool_prop_t
-zpool_prop_iter(zpool_prop_f func, void *cb, boolean_t show_all)
+void
+zfs_prop_init(void)
{
- return (zfs_prop_iter_common(func, cb, ZFS_TYPE_POOL, show_all));
-}
+ static zprop_index_t checksum_table[] = {
+ { "on", ZIO_CHECKSUM_ON },
+ { "off", ZIO_CHECKSUM_OFF },
+ { "fletcher2", ZIO_CHECKSUM_FLETCHER_2 },
+ { "fletcher4", ZIO_CHECKSUM_FLETCHER_4 },
+ { "sha256", ZIO_CHECKSUM_SHA256 },
+ { NULL }
+ };
+
+ static zprop_index_t compress_table[] = {
+ { "on", ZIO_COMPRESS_ON },
+ { "off", ZIO_COMPRESS_OFF },
+ { "lzjb", ZIO_COMPRESS_LZJB },
+ { "gzip", ZIO_COMPRESS_GZIP_6 }, /* gzip default */
+ { "gzip-1", ZIO_COMPRESS_GZIP_1 },
+ { "gzip-2", ZIO_COMPRESS_GZIP_2 },
+ { "gzip-3", ZIO_COMPRESS_GZIP_3 },
+ { "gzip-4", ZIO_COMPRESS_GZIP_4 },
+ { "gzip-5", ZIO_COMPRESS_GZIP_5 },
+ { "gzip-6", ZIO_COMPRESS_GZIP_6 },
+ { "gzip-7", ZIO_COMPRESS_GZIP_7 },
+ { "gzip-8", ZIO_COMPRESS_GZIP_8 },
+ { "gzip-9", ZIO_COMPRESS_GZIP_9 },
+ { NULL }
+ };
+
+ static zprop_index_t snapdir_table[] = {
+ { "hidden", ZFS_SNAPDIR_HIDDEN },
+ { "visible", ZFS_SNAPDIR_VISIBLE },
+ { NULL }
+ };
+
+ static zprop_index_t acl_mode_table[] = {
+ { "discard", ZFS_ACL_DISCARD },
+ { "groupmask", ZFS_ACL_GROUPMASK },
+ { "passthrough", ZFS_ACL_PASSTHROUGH },
+ { NULL }
+ };
+
+ static zprop_index_t acl_inherit_table[] = {
+ { "discard", ZFS_ACL_DISCARD },
+ { "noallow", ZFS_ACL_NOALLOW },
+ { "restricted", ZFS_ACL_RESTRICTED },
+ { "passthrough", ZFS_ACL_PASSTHROUGH },
+ { "secure", ZFS_ACL_RESTRICTED }, /* bkwrd compatability */
+ { NULL }
+ };
+
+ static zprop_index_t case_table[] = {
+ { "sensitive", ZFS_CASE_SENSITIVE },
+ { "insensitive", ZFS_CASE_INSENSITIVE },
+ { "mixed", ZFS_CASE_MIXED },
+ { NULL }
+ };
+
+ static zprop_index_t copies_table[] = {
+ { "1", 1 },
+ { "2", 2 },
+ { "3", 3 },
+ { NULL }
+ };
-zfs_proptype_t
-zfs_prop_get_type(zfs_prop_t prop)
-{
- return (zfs_prop_table[prop].pd_proptype);
-}
-
-static boolean_t
-propname_match(const char *p, zfs_prop_t prop, size_t len)
-{
- const char *propname = zfs_prop_table[prop].pd_name;
-#ifndef _KERNEL
- const char *colname = zfs_prop_table[prop].pd_colname;
- int c;
-#endif
-
-#ifndef _KERNEL
- if (colname == NULL)
- return (B_FALSE);
-#endif
-
- if (len == strlen(propname) &&
- strncmp(p, propname, len) == 0)
- return (B_TRUE);
-
-#ifndef _KERNEL
- if (len != strlen(colname))
- return (B_FALSE);
-
- for (c = 0; c < len; c++)
- if (p[c] != tolower(colname[c]))
- break;
-
- return (colname[c] == '\0');
-#else
- return (B_FALSE);
-#endif
-}
-
-zfs_prop_t
-zfs_name_to_prop_cb(zfs_prop_t prop, void *cb_data)
-{
- const char *propname = cb_data;
-
- if (propname_match(propname, prop, strlen(propname)))
- return (prop);
-
- return (ZFS_PROP_CONT);
+ /*
+ * Use the unique flags we have to send to u8_strcmp() and/or
+ * u8_textprep() to represent the various normalization property
+ * values.
+ */
+ static zprop_index_t normalize_table[] = {
+ { "none", 0 },
+ { "formD", U8_TEXTPREP_NFD },
+ { "formKC", U8_TEXTPREP_NFKC },
+ { "formC", U8_TEXTPREP_NFC },
+ { "formKD", U8_TEXTPREP_NFKD },
+ { NULL }
+ };
+
+ static zprop_index_t version_table[] = {
+ { "1", 1 },
+ { "2", 2 },
+ { "3", 3 },
+ { "current", ZPL_VERSION },
+ { NULL }
+ };
+
+ static zprop_index_t boolean_table[] = {
+ { "off", 0 },
+ { "on", 1 },
+ { NULL }
+ };
+
+ static zprop_index_t canmount_table[] = {
+ { "off", ZFS_CANMOUNT_OFF },
+ { "on", ZFS_CANMOUNT_ON },
+ { "noauto", ZFS_CANMOUNT_NOAUTO },
+ { NULL }
+ };
+
+ static zprop_index_t cache_table[] = {
+ { "none", ZFS_CACHE_NONE },
+ { "metadata", ZFS_CACHE_METADATA },
+ { "all", ZFS_CACHE_ALL },
+ { NULL }
+ };
+
+ /* inherit index properties */
+ register_index(ZFS_PROP_CHECKSUM, "checksum", ZIO_CHECKSUM_DEFAULT,
+ PROP_INHERIT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
+ "on | off | fletcher2 | fletcher4 | sha256", "CHECKSUM",
+ checksum_table);
+ register_index(ZFS_PROP_COMPRESSION, "compression",
+ ZIO_COMPRESS_DEFAULT, PROP_INHERIT,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
+ "on | off | lzjb | gzip | gzip-[1-9]", "COMPRESS", compress_table);
+ register_index(ZFS_PROP_SNAPDIR, "snapdir", ZFS_SNAPDIR_HIDDEN,
+ PROP_INHERIT, ZFS_TYPE_FILESYSTEM,
+ "hidden | visible", "SNAPDIR", snapdir_table);
+ register_index(ZFS_PROP_ACLMODE, "aclmode", ZFS_ACL_GROUPMASK,
+ PROP_INHERIT, ZFS_TYPE_FILESYSTEM,
+ "discard | groupmask | passthrough", "ACLMODE", acl_mode_table);
+ register_index(ZFS_PROP_ACLINHERIT, "aclinherit", ZFS_ACL_RESTRICTED,
+ PROP_INHERIT, ZFS_TYPE_FILESYSTEM,
+ "discard | noallow | restricted | passthrough",
+ "ACLINHERIT", acl_inherit_table);
+ register_index(ZFS_PROP_COPIES, "copies", 1,
+ PROP_INHERIT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
+ "1 | 2 | 3", "COPIES", copies_table);
+ register_index(ZFS_PROP_PRIMARYCACHE, "primarycache",
+ ZFS_CACHE_ALL, PROP_INHERIT,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT | ZFS_TYPE_VOLUME,
+ "all | none | metadata", "PRIMARYCACHE", cache_table);
+ register_index(ZFS_PROP_SECONDARYCACHE, "secondarycache",
+ ZFS_CACHE_ALL, PROP_INHERIT,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT | ZFS_TYPE_VOLUME,
+ "all | none | metadata", "SECONDARYCACHE", cache_table);
+
+ /* inherit index (boolean) properties */
+ register_index(ZFS_PROP_ATIME, "atime", 1, PROP_INHERIT,
+ ZFS_TYPE_FILESYSTEM, "on | off", "ATIME", boolean_table);
+ register_index(ZFS_PROP_DEVICES, "devices", 1, PROP_INHERIT,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, "on | off", "DEVICES",
+ boolean_table);
+ register_index(ZFS_PROP_EXEC, "exec", 1, PROP_INHERIT,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, "on | off", "EXEC",
+ boolean_table);
+ register_index(ZFS_PROP_SETUID, "setuid", 1, PROP_INHERIT,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, "on | off", "SETUID",
+ boolean_table);
+ register_index(ZFS_PROP_READONLY, "readonly", 0, PROP_INHERIT,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "on | off", "RDONLY",
+ boolean_table);
+ register_index(ZFS_PROP_ZONED, "jailed", 0, PROP_INHERIT,
+ ZFS_TYPE_FILESYSTEM, "on | off", "JAILED", boolean_table);
+ register_index(ZFS_PROP_XATTR, "xattr", 1, PROP_INHERIT,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, "on | off", "XATTR",
+ boolean_table);
+ register_index(ZFS_PROP_VSCAN, "vscan", 0, PROP_INHERIT,
+ ZFS_TYPE_FILESYSTEM, "on | off", "VSCAN",
+ boolean_table);
+ register_index(ZFS_PROP_NBMAND, "nbmand", 0, PROP_INHERIT,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, "on | off", "NBMAND",
+ boolean_table);
+
+ /* default index properties */
+ register_index(ZFS_PROP_VERSION, "version", 0, PROP_DEFAULT,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT,
+ "1 | 2 | 3 | current", "VERSION", version_table);
+ register_index(ZFS_PROP_CANMOUNT, "canmount", ZFS_CANMOUNT_ON,
+ PROP_DEFAULT, ZFS_TYPE_FILESYSTEM, "on | off | noauto",
+ "CANMOUNT", canmount_table);
+
+ /* readonly index (boolean) properties */
+ register_index(ZFS_PROP_MOUNTED, "mounted", 0, PROP_READONLY,
+ ZFS_TYPE_FILESYSTEM, "yes | no", "MOUNTED", boolean_table);
+
+ /* set once index properties */
+ register_index(ZFS_PROP_NORMALIZE, "normalization", 0,
+ PROP_ONETIME, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT,
+ "none | formC | formD | formKC | formKD", "NORMALIZATION",
+ normalize_table);
+ register_index(ZFS_PROP_CASE, "casesensitivity", ZFS_CASE_SENSITIVE,
+ PROP_ONETIME, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT,
+ "sensitive | insensitive | mixed", "CASE", case_table);
+
+ /* set once index (boolean) properties */
+ register_index(ZFS_PROP_UTF8ONLY, "utf8only", 0, PROP_ONETIME,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT,
+ "on | off", "UTF8ONLY", boolean_table);
+
+ /* string properties */
+ register_string(ZFS_PROP_ORIGIN, "origin", NULL, PROP_READONLY,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<snapshot>", "ORIGIN");
+ register_string(ZFS_PROP_MOUNTPOINT, "mountpoint", "/", PROP_INHERIT,
+ ZFS_TYPE_FILESYSTEM, "<path> | legacy | none", "MOUNTPOINT");
+ register_string(ZFS_PROP_SHARENFS, "sharenfs", "off", PROP_INHERIT,
+ ZFS_TYPE_FILESYSTEM, "on | off | share(1M) options", "SHARENFS");
+ register_string(ZFS_PROP_SHAREISCSI, "shareiscsi", "off", PROP_INHERIT,
+ ZFS_TYPE_DATASET, "on | off | type=<type>", "SHAREISCSI");
+ register_string(ZFS_PROP_TYPE, "type", NULL, PROP_READONLY,
+ ZFS_TYPE_DATASET, "filesystem | volume | snapshot", "TYPE");
+ register_string(ZFS_PROP_SHARESMB, "sharesmb", "off", PROP_INHERIT,
+ ZFS_TYPE_FILESYSTEM, "on | off | sharemgr(1M) options", "SHARESMB");
+
+ /* readonly number properties */
+ register_number(ZFS_PROP_USED, "used", 0, PROP_READONLY,
+ ZFS_TYPE_DATASET, "<size>", "USED");
+ register_number(ZFS_PROP_AVAILABLE, "available", 0, PROP_READONLY,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<size>", "AVAIL");
+ register_number(ZFS_PROP_REFERENCED, "referenced", 0, PROP_READONLY,
+ ZFS_TYPE_DATASET, "<size>", "REFER");
+ register_number(ZFS_PROP_COMPRESSRATIO, "compressratio", 0,
+ PROP_READONLY, ZFS_TYPE_DATASET,
+ "<1.00x or higher if compressed>", "RATIO");
+ register_number(ZFS_PROP_VOLBLOCKSIZE, "volblocksize", 8192,
+ PROP_ONETIME,
+ ZFS_TYPE_VOLUME, "512 to 128k, power of 2", "VOLBLOCK");
+ register_number(ZFS_PROP_USEDSNAP, "usedbysnapshots", 0, PROP_READONLY,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<size>", "USEDSNAP");
+ register_number(ZFS_PROP_USEDDS, "usedbydataset", 0, PROP_READONLY,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<size>", "USEDDS");
+ register_number(ZFS_PROP_USEDCHILD, "usedbychildren", 0, PROP_READONLY,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<size>", "USEDCHILD");
+ register_number(ZFS_PROP_USEDREFRESERV, "usedbyrefreservation", 0,
+ PROP_READONLY,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<size>", "USEDREFRESERV");
+
+ /* default number properties */
+ register_number(ZFS_PROP_QUOTA, "quota", 0, PROP_DEFAULT,
+ ZFS_TYPE_FILESYSTEM, "<size> | none", "QUOTA");
+ register_number(ZFS_PROP_RESERVATION, "reservation", 0, PROP_DEFAULT,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<size> | none", "RESERV");
+ register_number(ZFS_PROP_VOLSIZE, "volsize", 0, PROP_DEFAULT,
+ ZFS_TYPE_VOLUME, "<size>", "VOLSIZE");
+ register_number(ZFS_PROP_REFQUOTA, "refquota", 0, PROP_DEFAULT,
+ ZFS_TYPE_FILESYSTEM, "<size> | none", "REFQUOTA");
+ register_number(ZFS_PROP_REFRESERVATION, "refreservation", 0,
+ PROP_DEFAULT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
+ "<size> | none", "REFRESERV");
+
+ /* inherit number properties */
+ register_number(ZFS_PROP_RECORDSIZE, "recordsize", SPA_MAXBLOCKSIZE,
+ PROP_INHERIT,
+ ZFS_TYPE_FILESYSTEM, "512 to 128k, power of 2", "RECSIZE");
+
+ /* hidden properties */
+ register_hidden(ZFS_PROP_CREATETXG, "createtxg", PROP_TYPE_NUMBER,
+ PROP_READONLY, ZFS_TYPE_DATASET, NULL);
+ register_hidden(ZFS_PROP_NUMCLONES, "numclones", PROP_TYPE_NUMBER,
+ PROP_READONLY, ZFS_TYPE_SNAPSHOT, NULL);
+ register_hidden(ZFS_PROP_NAME, "name", PROP_TYPE_STRING,
+ PROP_READONLY, ZFS_TYPE_DATASET, "NAME");
+ register_hidden(ZFS_PROP_ISCSIOPTIONS, "iscsioptions", PROP_TYPE_STRING,
+ PROP_INHERIT, ZFS_TYPE_VOLUME, "ISCSIOPTIONS");
+ register_hidden(ZFS_PROP_GUID, "guid", PROP_TYPE_NUMBER, PROP_READONLY,
+ ZFS_TYPE_DATASET, "GUID");
+
+ /* oddball properties */
+ register_impl(ZFS_PROP_CREATION, "creation", PROP_TYPE_NUMBER, 0, NULL,
+ PROP_READONLY, ZFS_TYPE_DATASET,
+ "<date>", "CREATION", B_FALSE, B_TRUE, NULL);
}
-/*
- * Given a property name and its type, returns the corresponding property ID.
- */
-zfs_prop_t
-zfs_name_to_prop_common(const char *propname, zfs_type_t type)
+boolean_t
+zfs_prop_delegatable(zfs_prop_t prop)
{
- zfs_prop_t prop;
-
- prop = zfs_prop_iter_common(zfs_name_to_prop_cb, (void *)propname,
- type, B_TRUE);
- return (prop == ZFS_PROP_CONT ? ZFS_PROP_INVAL : prop);
+ zprop_desc_t *pd = &zfs_prop_table[prop];
+ return (pd->pd_attr != PROP_READONLY);
}
/*
@@ -308,17 +326,9 @@ zfs_name_to_prop_common(const char *propname, zfs_type_t type)
zfs_prop_t
zfs_name_to_prop(const char *propname)
{
- return (zfs_name_to_prop_common(propname, ZFS_TYPE_ANY));
+ return (zprop_name_to_prop(propname, ZFS_TYPE_DATASET));
}
-/*
- * Given a pool property name, returns the corresponding property ID.
- */
-zpool_prop_t
-zpool_name_to_prop(const char *propname)
-{
- return (zfs_name_to_prop_common(propname, ZFS_TYPE_POOL));
-}
/*
* For user property names, we allow all lowercase alphanumeric characters, plus
@@ -357,179 +367,85 @@ zfs_prop_user(const char *name)
}
/*
- * Return the default value for the given property.
+ * Tables of index types, plus functions to convert between the user view
+ * (strings) and internal representation (uint64_t).
*/
-const char *
-zfs_prop_default_string(zfs_prop_t prop)
+int
+zfs_prop_string_to_index(zfs_prop_t prop, const char *string, uint64_t *index)
{
- return (zfs_prop_table[prop].pd_strdefault);
+ return (zprop_string_to_index(prop, string, index, ZFS_TYPE_DATASET));
}
-uint64_t
-zfs_prop_default_numeric(zfs_prop_t prop)
+int
+zfs_prop_index_to_string(zfs_prop_t prop, uint64_t index, const char **string)
{
- return (zfs_prop_table[prop].pd_numdefault);
+ return (zprop_index_to_string(prop, index, string, ZFS_TYPE_DATASET));
}
/*
- * Returns TRUE if the property is readonly.
+ * Returns TRUE if the property applies to any of the given dataset types.
*/
-int
-zfs_prop_readonly(zfs_prop_t prop)
+boolean_t
+zfs_prop_valid_for_type(int prop, zfs_type_t types)
{
- return (zfs_prop_table[prop].pd_attr == prop_readonly);
+ return (zprop_valid_for_type(prop, types));
}
-/*
- * Given a dataset property ID, returns the corresponding name.
- * Assuming the zfs dataset propety ID is valid.
- */
-const char *
-zfs_prop_to_name(zfs_prop_t prop)
+zprop_type_t
+zfs_prop_get_type(zfs_prop_t prop)
{
- return (zfs_prop_table[prop].pd_name);
+ return (zfs_prop_table[prop].pd_proptype);
}
/*
- * Given a pool property ID, returns the corresponding name.
- * Assuming the pool propety ID is valid.
+ * Returns TRUE if the property is readonly.
*/
-const char *
-zpool_prop_to_name(zpool_prop_t prop)
+boolean_t
+zfs_prop_readonly(zfs_prop_t prop)
{
- return (zfs_prop_table[prop].pd_name);
+ return (zfs_prop_table[prop].pd_attr == PROP_READONLY ||
+ zfs_prop_table[prop].pd_attr == PROP_ONETIME);
}
/*
- * Returns TRUE if the property is inheritable.
+ * Returns TRUE if the property is only allowed to be set once.
*/
-int
-zfs_prop_inheritable(zfs_prop_t prop)
+boolean_t
+zfs_prop_setonce(zfs_prop_t prop)
{
- return (zfs_prop_table[prop].pd_attr == prop_inherit);
+ return (zfs_prop_table[prop].pd_attr == PROP_ONETIME);
}
-typedef struct zfs_index {
- const char *name;
- uint64_t index;
-} zfs_index_t;
-
-static zfs_index_t checksum_table[] = {
- { "on", ZIO_CHECKSUM_ON },
- { "off", ZIO_CHECKSUM_OFF },
- { "fletcher2", ZIO_CHECKSUM_FLETCHER_2 },
- { "fletcher4", ZIO_CHECKSUM_FLETCHER_4 },
- { "sha256", ZIO_CHECKSUM_SHA256 },
- { NULL }
-};
-
-static zfs_index_t compress_table[] = {
- { "on", ZIO_COMPRESS_ON },
- { "off", ZIO_COMPRESS_OFF },
- { "lzjb", ZIO_COMPRESS_LZJB },
- { "gzip", ZIO_COMPRESS_GZIP_6 }, /* the default gzip level */
- { "gzip-1", ZIO_COMPRESS_GZIP_1 },
- { "gzip-2", ZIO_COMPRESS_GZIP_2 },
- { "gzip-3", ZIO_COMPRESS_GZIP_3 },
- { "gzip-4", ZIO_COMPRESS_GZIP_4 },
- { "gzip-5", ZIO_COMPRESS_GZIP_5 },
- { "gzip-6", ZIO_COMPRESS_GZIP_6 },
- { "gzip-7", ZIO_COMPRESS_GZIP_7 },
- { "gzip-8", ZIO_COMPRESS_GZIP_8 },
- { "gzip-9", ZIO_COMPRESS_GZIP_9 },
- { NULL }
-};
-
-static zfs_index_t snapdir_table[] = {
- { "hidden", ZFS_SNAPDIR_HIDDEN },
- { "visible", ZFS_SNAPDIR_VISIBLE },
- { NULL }
-};
-
-static zfs_index_t acl_mode_table[] = {
- { "discard", ZFS_ACL_DISCARD },
- { "groupmask", ZFS_ACL_GROUPMASK },
- { "passthrough", ZFS_ACL_PASSTHROUGH },
- { NULL }
-};
-
-static zfs_index_t acl_inherit_table[] = {
- { "discard", ZFS_ACL_DISCARD },
- { "noallow", ZFS_ACL_NOALLOW },
- { "secure", ZFS_ACL_SECURE },
- { "passthrough", ZFS_ACL_PASSTHROUGH },
- { NULL }
-};
-
-static zfs_index_t copies_table[] = {
- { "1", 1 },
- { "2", 2 },
- { "3", 3 },
- { NULL }
-};
-
-static zfs_index_t *
-zfs_prop_index_table(zfs_prop_t prop)
+const char *
+zfs_prop_default_string(zfs_prop_t prop)
{
- switch (prop) {
- case ZFS_PROP_CHECKSUM:
- return (checksum_table);
- case ZFS_PROP_COMPRESSION:
- return (compress_table);
- case ZFS_PROP_SNAPDIR:
- return (snapdir_table);
- case ZFS_PROP_ACLMODE:
- return (acl_mode_table);
- case ZFS_PROP_ACLINHERIT:
- return (acl_inherit_table);
- case ZFS_PROP_COPIES:
- return (copies_table);
- default:
- return (NULL);
- }
+ return (zfs_prop_table[prop].pd_strdefault);
}
+uint64_t
+zfs_prop_default_numeric(zfs_prop_t prop)
+{
+ return (zfs_prop_table[prop].pd_numdefault);
+}
/*
- * Tables of index types, plus functions to convert between the user view
- * (strings) and internal representation (uint64_t).
+ * Given a dataset property ID, returns the corresponding name.
+ * Assuming the zfs dataset property ID is valid.
*/
-int
-zfs_prop_string_to_index(zfs_prop_t prop, const char *string, uint64_t *index)
+const char *
+zfs_prop_to_name(zfs_prop_t prop)
{
- zfs_index_t *table;
- int i;
-
- if ((table = zfs_prop_index_table(prop)) == NULL)
- return (-1);
-
- for (i = 0; table[i].name != NULL; i++) {
- if (strcmp(string, table[i].name) == 0) {
- *index = table[i].index;
- return (0);
- }
- }
-
- return (-1);
+ return (zfs_prop_table[prop].pd_name);
}
-int
-zfs_prop_index_to_string(zfs_prop_t prop, uint64_t index, const char **string)
+/*
+ * Returns TRUE if the property is inheritable.
+ */
+boolean_t
+zfs_prop_inheritable(zfs_prop_t prop)
{
- zfs_index_t *table;
- int i;
-
- if ((table = zfs_prop_index_table(prop)) == NULL)
- return (-1);
-
- for (i = 0; table[i].name != NULL; i++) {
- if (table[i].index == index) {
- *string = table[i].name;
- return (0);
- }
- }
-
- return (-1);
+ return (zfs_prop_table[prop].pd_attr == PROP_INHERIT ||
+ zfs_prop_table[prop].pd_attr == PROP_ONETIME);
}
#ifndef _KERNEL
@@ -541,22 +457,6 @@ zfs_prop_index_to_string(zfs_prop_t prop, uint64_t index, const char **string)
const char *
zfs_prop_values(zfs_prop_t prop)
{
- if (zfs_prop_table[prop].pd_types == ZFS_TYPE_POOL)
- return (NULL);
-
- return (zfs_prop_table[prop].pd_values);
-}
-
-/*
- * Returns a string describing the set of acceptable values for the given
- * zpool property, or NULL if it cannot be set.
- */
-const char *
-zpool_prop_values(zfs_prop_t prop)
-{
- if (zfs_prop_table[prop].pd_types != ZFS_TYPE_POOL)
- return (NULL);
-
return (zfs_prop_table[prop].pd_values);
}
@@ -568,8 +468,8 @@ zpool_prop_values(zfs_prop_t prop)
int
zfs_prop_is_string(zfs_prop_t prop)
{
- return (zfs_prop_table[prop].pd_proptype == prop_type_string ||
- zfs_prop_table[prop].pd_proptype == prop_type_index);
+ return (zfs_prop_table[prop].pd_proptype == PROP_TYPE_STRING ||
+ zfs_prop_table[prop].pd_proptype == PROP_TYPE_INDEX);
}
/*
@@ -592,66 +492,4 @@ zfs_prop_align_right(zfs_prop_t prop)
return (zfs_prop_table[prop].pd_rightalign);
}
-/*
- * Determines the minimum width for the column, and indicates whether it's fixed
- * or not. Only string columns are non-fixed.
- */
-size_t
-zfs_prop_width(zfs_prop_t prop, boolean_t *fixed)
-{
- prop_desc_t *pd = &zfs_prop_table[prop];
- zfs_index_t *idx;
- size_t ret;
- int i;
-
- *fixed = B_TRUE;
-
- /*
- * Start with the width of the column name.
- */
- ret = strlen(pd->pd_colname);
-
- /*
- * For fixed-width values, make sure the width is large enough to hold
- * any possible value.
- */
- switch (pd->pd_proptype) {
- case prop_type_number:
- /*
- * The maximum length of a human-readable number is 5 characters
- * ("20.4M", for example).
- */
- if (ret < 5)
- ret = 5;
- /*
- * 'creation' is handled specially because it's a number
- * internally, but displayed as a date string.
- */
- if (prop == ZFS_PROP_CREATION)
- *fixed = B_FALSE;
- break;
- case prop_type_boolean:
- /*
- * The maximum length of a boolean value is 3 characters, for
- * "off".
- */
- if (ret < 3)
- ret = 3;
- break;
- case prop_type_index:
- idx = zfs_prop_index_table(prop);
- for (i = 0; idx[i].name != NULL; i++) {
- if (strlen(idx[i].name) > ret)
- ret = strlen(idx[i].name);
- }
- break;
-
- case prop_type_string:
- *fixed = B_FALSE;
- break;
- }
-
- return (ret);
-}
-
#endif
diff --git a/sys/cddl/contrib/opensolaris/common/zfs/zfs_prop.h b/sys/cddl/contrib/opensolaris/common/zfs/zfs_prop.h
index 133e740..da5ae43 100644
--- a/sys/cddl/contrib/opensolaris/common/zfs/zfs_prop.h
+++ b/sys/cddl/contrib/opensolaris/common/zfs/zfs_prop.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -40,14 +40,87 @@ extern "C" {
* in the kernel, but the string value in userland.
*/
typedef enum {
- prop_type_number, /* numeric value */
- prop_type_string, /* string value */
- prop_type_boolean, /* boolean value */
- prop_type_index /* numeric value indexed by string */
-} zfs_proptype_t;
-
-zfs_proptype_t zfs_prop_get_type(zfs_prop_t);
-size_t zfs_prop_width(zfs_prop_t, boolean_t *);
+ PROP_TYPE_NUMBER, /* numeric value */
+ PROP_TYPE_STRING, /* string value */
+ PROP_TYPE_INDEX /* numeric value indexed by string */
+} zprop_type_t;
+
+typedef enum {
+ PROP_DEFAULT,
+ PROP_READONLY,
+ PROP_INHERIT,
+ /*
+ * ONETIME properties are a sort of conglomeration of READONLY
+ * and INHERIT. They can be set only during object creation,
+ * after that they are READONLY. If not explicitly set during
+ * creation, they can be inherited.
+ */
+ PROP_ONETIME
+} zprop_attr_t;
+
+typedef struct zfs_index {
+ const char *pi_name;
+ uint64_t pi_value;
+} zprop_index_t;
+
+typedef struct {
+ const char *pd_name; /* human-readable property name */
+ int pd_propnum; /* property number */
+ zprop_type_t pd_proptype; /* string, boolean, index, number */
+ const char *pd_strdefault; /* default for strings */
+ uint64_t pd_numdefault; /* for boolean / index / number */
+ zprop_attr_t pd_attr; /* default, readonly, inherit */
+ int pd_types; /* bitfield of valid dataset types */
+ /* fs | vol | snap; or pool */
+ const char *pd_values; /* string telling acceptable values */
+ const char *pd_colname; /* column header for "zfs list" */
+ boolean_t pd_rightalign; /* column alignment for "zfs list" */
+ boolean_t pd_visible; /* do we list this property with the */
+ /* "zfs get" help message */
+ const zprop_index_t *pd_table; /* for index properties, a table */
+ /* defining the possible values */
+} zprop_desc_t;
+
+/*
+ * zfs dataset property functions
+ */
+void zfs_prop_init(void);
+zprop_type_t zfs_prop_get_type(zfs_prop_t);
+boolean_t zfs_prop_delegatable(zfs_prop_t prop);
+zprop_desc_t *zfs_prop_get_table(void);
+
+/*
+ * zpool property functions
+ */
+void zpool_prop_init(void);
+zprop_type_t zpool_prop_get_type(zpool_prop_t);
+zprop_desc_t *zpool_prop_get_table(void);
+
+/*
+ * Common routines to initialize property tables
+ */
+void register_impl(int, const char *, zprop_type_t, uint64_t,
+ const char *, zprop_attr_t, int, const char *, const char *,
+ boolean_t, boolean_t, const zprop_index_t *);
+void register_string(int, const char *, const char *, zprop_attr_t attr,
+ int, const char *, const char *);
+void register_number(int, const char *, uint64_t, zprop_attr_t, int,
+ const char *, const char *);
+void register_index(int, const char *, uint64_t, zprop_attr_t, int,
+ const char *, const char *, const zprop_index_t *);
+void register_hidden(int, const char *, zprop_type_t, zprop_attr_t,
+ int, const char *);
+
+/*
+ * Common routines for zfs and zpool property management
+ */
+int zprop_iter_common(zprop_func, void *, boolean_t, boolean_t, zfs_type_t);
+int zprop_name_to_prop(const char *, zfs_type_t);
+int zprop_string_to_index(int, const char *, uint64_t *, zfs_type_t);
+int zprop_index_to_string(int, uint64_t, const char **, zfs_type_t);
+const char *zprop_values(int, zfs_type_t);
+size_t zprop_width(int, boolean_t *, zfs_type_t);
+boolean_t zprop_valid_for_type(int, zfs_type_t);
#ifdef __cplusplus
}
diff --git a/sys/cddl/contrib/opensolaris/common/zfs/zpool_prop.c b/sys/cddl/contrib/opensolaris/common/zfs/zpool_prop.c
new file mode 100644
index 0000000..f5efe18
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/common/zfs/zpool_prop.c
@@ -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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/zio.h>
+#include <sys/spa.h>
+#include <sys/zfs_acl.h>
+#include <sys/zfs_ioctl.h>
+#include <sys/fs/zfs.h>
+
+#include "zfs_prop.h"
+
+#if defined(_KERNEL)
+#include <sys/systm.h>
+#else
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#endif
+
+static zprop_desc_t zpool_prop_table[ZPOOL_NUM_PROPS];
+
+zprop_desc_t *
+zpool_prop_get_table(void)
+{
+ return (zpool_prop_table);
+}
+
+void
+zpool_prop_init(void)
+{
+ static zprop_index_t boolean_table[] = {
+ { "off", 0},
+ { "on", 1},
+ { NULL }
+ };
+
+ static zprop_index_t failuremode_table[] = {
+ { "wait", ZIO_FAILURE_MODE_WAIT },
+ { "continue", ZIO_FAILURE_MODE_CONTINUE },
+ { "panic", ZIO_FAILURE_MODE_PANIC },
+ { NULL }
+ };
+
+ /* string properties */
+ register_string(ZPOOL_PROP_ALTROOT, "altroot", NULL, PROP_DEFAULT,
+ ZFS_TYPE_POOL, "<path>", "ALTROOT");
+ register_string(ZPOOL_PROP_BOOTFS, "bootfs", NULL, PROP_DEFAULT,
+ ZFS_TYPE_POOL, "<filesystem>", "BOOTFS");
+ register_string(ZPOOL_PROP_CACHEFILE, "cachefile", NULL, PROP_DEFAULT,
+ ZFS_TYPE_POOL, "<file> | none", "CACHEFILE");
+
+ /* readonly number properties */
+ register_number(ZPOOL_PROP_SIZE, "size", 0, PROP_READONLY,
+ ZFS_TYPE_POOL, "<size>", "SIZE");
+ register_number(ZPOOL_PROP_USED, "used", 0, PROP_READONLY,
+ ZFS_TYPE_POOL, "<size>", "USED");
+ register_number(ZPOOL_PROP_AVAILABLE, "available", 0, PROP_READONLY,
+ ZFS_TYPE_POOL, "<size>", "AVAIL");
+ register_number(ZPOOL_PROP_CAPACITY, "capacity", 0, PROP_READONLY,
+ ZFS_TYPE_POOL, "<size>", "CAP");
+ register_number(ZPOOL_PROP_GUID, "guid", 0, PROP_READONLY,
+ ZFS_TYPE_POOL, "<guid>", "GUID");
+ register_number(ZPOOL_PROP_HEALTH, "health", 0, PROP_READONLY,
+ ZFS_TYPE_POOL, "<state>", "HEALTH");
+
+ /* default number properties */
+ register_number(ZPOOL_PROP_VERSION, "version", SPA_VERSION,
+ PROP_DEFAULT, ZFS_TYPE_POOL, "<version>", "VERSION");
+
+ /* default index (boolean) properties */
+ register_index(ZPOOL_PROP_DELEGATION, "delegation", 1, PROP_DEFAULT,
+ ZFS_TYPE_POOL, "on | off", "DELEGATION", boolean_table);
+ register_index(ZPOOL_PROP_AUTOREPLACE, "autoreplace", 0, PROP_DEFAULT,
+ ZFS_TYPE_POOL, "on | off", "REPLACE", boolean_table);
+ register_index(ZPOOL_PROP_LISTSNAPS, "listsnapshots", 0, PROP_DEFAULT,
+ ZFS_TYPE_POOL, "on | off", "LISTSNAPS", boolean_table);
+
+ /* default index properties */
+ register_index(ZPOOL_PROP_FAILUREMODE, "failmode",
+ ZIO_FAILURE_MODE_WAIT, PROP_DEFAULT, ZFS_TYPE_POOL,
+ "wait | continue | panic", "FAILMODE", failuremode_table);
+
+ /* hidden properties */
+ register_hidden(ZPOOL_PROP_NAME, "name", PROP_TYPE_STRING,
+ PROP_READONLY, ZFS_TYPE_POOL, "NAME");
+}
+
+/*
+ * Given a property name and its type, returns the corresponding property ID.
+ */
+zpool_prop_t
+zpool_name_to_prop(const char *propname)
+{
+ return (zprop_name_to_prop(propname, ZFS_TYPE_POOL));
+}
+
+/*
+ * Given a pool property ID, returns the corresponding name.
+ * Assuming the pool propety ID is valid.
+ */
+const char *
+zpool_prop_to_name(zpool_prop_t prop)
+{
+ return (zpool_prop_table[prop].pd_name);
+}
+
+zprop_type_t
+zpool_prop_get_type(zpool_prop_t prop)
+{
+ return (zpool_prop_table[prop].pd_proptype);
+}
+
+boolean_t
+zpool_prop_readonly(zpool_prop_t prop)
+{
+ return (zpool_prop_table[prop].pd_attr == PROP_READONLY);
+}
+
+const char *
+zpool_prop_default_string(zpool_prop_t prop)
+{
+ return (zpool_prop_table[prop].pd_strdefault);
+}
+
+uint64_t
+zpool_prop_default_numeric(zpool_prop_t prop)
+{
+ return (zpool_prop_table[prop].pd_numdefault);
+}
+
+int
+zpool_prop_string_to_index(zpool_prop_t prop, const char *string,
+ uint64_t *index)
+{
+ return (zprop_string_to_index(prop, string, index, ZFS_TYPE_POOL));
+}
+
+int
+zpool_prop_index_to_string(zpool_prop_t prop, uint64_t index,
+ const char **string)
+{
+ return (zprop_index_to_string(prop, index, string, ZFS_TYPE_POOL));
+}
+
+#ifndef _KERNEL
+
+const char *
+zpool_prop_values(zpool_prop_t prop)
+{
+ return (zpool_prop_table[prop].pd_values);
+}
+
+const char *
+zpool_prop_column_name(zpool_prop_t prop)
+{
+ return (zpool_prop_table[prop].pd_colname);
+}
+
+boolean_t
+zpool_prop_align_right(zpool_prop_t prop)
+{
+ return (zpool_prop_table[prop].pd_rightalign);
+}
+#endif
diff --git a/sys/cddl/contrib/opensolaris/common/zfs/zprop_common.c b/sys/cddl/contrib/opensolaris/common/zfs/zprop_common.c
new file mode 100644
index 0000000..87619e1
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/common/zfs/zprop_common.c
@@ -0,0 +1,406 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL 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.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Common routines used by zfs and zpool property management.
+ */
+
+#include <sys/zio.h>
+#include <sys/spa.h>
+#include <sys/zfs_acl.h>
+#include <sys/zfs_ioctl.h>
+#include <sys/zfs_znode.h>
+#include <sys/fs/zfs.h>
+
+#include "zfs_prop.h"
+#include "zfs_deleg.h"
+
+#if defined(_KERNEL)
+#include <sys/systm.h>
+#include <sys/libkern.h>
+#else
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#endif
+
+static zprop_desc_t *
+zprop_get_proptable(zfs_type_t type)
+{
+ if (type == ZFS_TYPE_POOL)
+ return (zpool_prop_get_table());
+ else
+ return (zfs_prop_get_table());
+}
+
+static int
+zprop_get_numprops(zfs_type_t type)
+{
+ if (type == ZFS_TYPE_POOL)
+ return (ZPOOL_NUM_PROPS);
+ else
+ return (ZFS_NUM_PROPS);
+}
+
+void
+register_impl(int prop, const char *name, zprop_type_t type,
+ uint64_t numdefault, const char *strdefault, zprop_attr_t attr,
+ int objset_types, const char *values, const char *colname,
+ boolean_t rightalign, boolean_t visible, const zprop_index_t *idx_tbl)
+{
+ zprop_desc_t *prop_tbl = zprop_get_proptable(objset_types);
+ zprop_desc_t *pd;
+
+ pd = &prop_tbl[prop];
+
+ ASSERT(pd->pd_name == NULL || pd->pd_name == name);
+
+ pd->pd_name = name;
+ pd->pd_propnum = prop;
+ pd->pd_proptype = type;
+ pd->pd_numdefault = numdefault;
+ pd->pd_strdefault = strdefault;
+ pd->pd_attr = attr;
+ pd->pd_types = objset_types;
+ pd->pd_values = values;
+ pd->pd_colname = colname;
+ pd->pd_rightalign = rightalign;
+ pd->pd_visible = visible;
+ pd->pd_table = idx_tbl;
+}
+
+void
+register_string(int prop, const char *name, const char *def,
+ zprop_attr_t attr, int objset_types, const char *values,
+ const char *colname)
+{
+ register_impl(prop, name, PROP_TYPE_STRING, 0, def, attr,
+ objset_types, values, colname, B_FALSE, B_TRUE, NULL);
+
+}
+
+void
+register_number(int prop, const char *name, uint64_t def, zprop_attr_t attr,
+ int objset_types, const char *values, const char *colname)
+{
+ register_impl(prop, name, PROP_TYPE_NUMBER, def, NULL, attr,
+ objset_types, values, colname, B_TRUE, B_TRUE, NULL);
+}
+
+void
+register_index(int prop, const char *name, uint64_t def, zprop_attr_t attr,
+ int objset_types, const char *values, const char *colname,
+ const zprop_index_t *idx_tbl)
+{
+ register_impl(prop, name, PROP_TYPE_INDEX, def, NULL, attr,
+ objset_types, values, colname, B_TRUE, B_TRUE, idx_tbl);
+}
+
+void
+register_hidden(int prop, const char *name, zprop_type_t type,
+ zprop_attr_t attr, int objset_types, const char *colname)
+{
+ register_impl(prop, name, type, 0, NULL, attr,
+ objset_types, NULL, colname, B_FALSE, B_FALSE, NULL);
+}
+
+
+/*
+ * A comparison function we can use to order indexes into property tables.
+ */
+static int
+zprop_compare(const void *arg1, const void *arg2)
+{
+ const zprop_desc_t *p1 = *((zprop_desc_t **)arg1);
+ const zprop_desc_t *p2 = *((zprop_desc_t **)arg2);
+ boolean_t p1ro, p2ro;
+
+ p1ro = (p1->pd_attr == PROP_READONLY);
+ p2ro = (p2->pd_attr == PROP_READONLY);
+
+ if (p1ro == p2ro)
+ return (strcmp(p1->pd_name, p2->pd_name));
+
+ return (p1ro ? -1 : 1);
+}
+
+/*
+ * Iterate over all properties in the given property table, calling back
+ * into the specified function for each property. We will continue to
+ * iterate until we either reach the end or the callback function returns
+ * something other than ZPROP_CONT.
+ */
+int
+zprop_iter_common(zprop_func func, void *cb, boolean_t show_all,
+ boolean_t ordered, zfs_type_t type)
+{
+ int i, j, num_props, size, prop;
+ zprop_desc_t *prop_tbl;
+ zprop_desc_t **order;
+
+ prop_tbl = zprop_get_proptable(type);
+ num_props = zprop_get_numprops(type);
+ size = num_props * sizeof (zprop_desc_t *);
+
+#if defined(_KERNEL)
+ order = kmem_alloc(size, KM_SLEEP);
+#else
+ if ((order = malloc(size)) == NULL)
+ return (ZPROP_CONT);
+#endif
+
+ for (j = 0; j < num_props; j++)
+ order[j] = &prop_tbl[j];
+
+ if (ordered) {
+ qsort((void *)order, num_props, sizeof (zprop_desc_t *),
+ zprop_compare);
+ }
+
+ prop = ZPROP_CONT;
+ for (i = 0; i < num_props; i++) {
+ if ((order[i]->pd_visible || show_all) &&
+ (func(order[i]->pd_propnum, cb) != ZPROP_CONT)) {
+ prop = order[i]->pd_propnum;
+ break;
+ }
+ }
+
+#if defined(_KERNEL)
+ kmem_free(order, size);
+#else
+ free(order);
+#endif
+ return (prop);
+}
+
+static boolean_t
+propname_match(const char *p, size_t len, zprop_desc_t *prop_entry)
+{
+ const char *propname = prop_entry->pd_name;
+#ifndef _KERNEL
+ const char *colname = prop_entry->pd_colname;
+ int c;
+
+ if (colname == NULL)
+ return (B_FALSE);
+#endif
+
+ if (len == strlen(propname) &&
+ strncmp(p, propname, len) == 0)
+ return (B_TRUE);
+
+#ifndef _KERNEL
+ if (len != strlen(colname))
+ return (B_FALSE);
+
+ for (c = 0; c < len; c++)
+ if (p[c] != tolower(colname[c]))
+ break;
+
+ return (colname[c] == '\0');
+#else
+ return (B_FALSE);
+#endif
+}
+
+typedef struct name_to_prop_cb {
+ const char *propname;
+ zprop_desc_t *prop_tbl;
+} name_to_prop_cb_t;
+
+static int
+zprop_name_to_prop_cb(int prop, void *cb_data)
+{
+ name_to_prop_cb_t *data = cb_data;
+
+ if (propname_match(data->propname, strlen(data->propname),
+ &data->prop_tbl[prop]))
+ return (prop);
+
+ return (ZPROP_CONT);
+}
+
+int
+zprop_name_to_prop(const char *propname, zfs_type_t type)
+{
+ int prop;
+ name_to_prop_cb_t cb_data;
+
+ cb_data.propname = propname;
+ cb_data.prop_tbl = zprop_get_proptable(type);
+
+ prop = zprop_iter_common(zprop_name_to_prop_cb, &cb_data,
+ B_TRUE, B_FALSE, type);
+
+ return (prop == ZPROP_CONT ? ZPROP_INVAL : prop);
+}
+
+int
+zprop_string_to_index(int prop, const char *string, uint64_t *index,
+ zfs_type_t type)
+{
+ zprop_desc_t *prop_tbl;
+ const zprop_index_t *idx_tbl;
+ int i;
+
+ if (prop == ZPROP_INVAL || prop == ZPROP_CONT)
+ return (-1);
+
+ ASSERT(prop < zprop_get_numprops(type));
+ prop_tbl = zprop_get_proptable(type);
+ if ((idx_tbl = prop_tbl[prop].pd_table) == NULL)
+ return (-1);
+
+ for (i = 0; idx_tbl[i].pi_name != NULL; i++) {
+ if (strcmp(string, idx_tbl[i].pi_name) == 0) {
+ *index = idx_tbl[i].pi_value;
+ return (0);
+ }
+ }
+
+ return (-1);
+}
+
+int
+zprop_index_to_string(int prop, uint64_t index, const char **string,
+ zfs_type_t type)
+{
+ zprop_desc_t *prop_tbl;
+ const zprop_index_t *idx_tbl;
+ int i;
+
+ if (prop == ZPROP_INVAL || prop == ZPROP_CONT)
+ return (-1);
+
+ ASSERT(prop < zprop_get_numprops(type));
+ prop_tbl = zprop_get_proptable(type);
+ if ((idx_tbl = prop_tbl[prop].pd_table) == NULL)
+ return (-1);
+
+ for (i = 0; idx_tbl[i].pi_name != NULL; i++) {
+ if (idx_tbl[i].pi_value == index) {
+ *string = idx_tbl[i].pi_name;
+ return (0);
+ }
+ }
+
+ return (-1);
+}
+
+const char *
+zprop_values(int prop, zfs_type_t type)
+{
+ zprop_desc_t *prop_tbl;
+
+ ASSERT(prop != ZPROP_INVAL && prop != ZPROP_CONT);
+ ASSERT(prop < zprop_get_numprops(type));
+
+ prop_tbl = zprop_get_proptable(type);
+
+ return (prop_tbl[prop].pd_values);
+}
+
+/*
+ * Returns TRUE if the property applies to any of the given dataset types.
+ */
+boolean_t
+zprop_valid_for_type(int prop, zfs_type_t type)
+{
+ zprop_desc_t *prop_tbl;
+
+ if (prop == ZPROP_INVAL || prop == ZPROP_CONT)
+ return (B_FALSE);
+
+ ASSERT(prop < zprop_get_numprops(type));
+ prop_tbl = zprop_get_proptable(type);
+ return ((prop_tbl[prop].pd_types & type) != 0);
+}
+
+#ifndef _KERNEL
+
+/*
+ * Determines the minimum width for the column, and indicates whether it's fixed
+ * or not. Only string columns are non-fixed.
+ */
+size_t
+zprop_width(int prop, boolean_t *fixed, zfs_type_t type)
+{
+ zprop_desc_t *prop_tbl, *pd;
+ const zprop_index_t *idx;
+ size_t ret;
+ int i;
+
+ ASSERT(prop != ZPROP_INVAL && prop != ZPROP_CONT);
+ ASSERT(prop < zprop_get_numprops(type));
+
+ prop_tbl = zprop_get_proptable(type);
+ pd = &prop_tbl[prop];
+
+ *fixed = B_TRUE;
+
+ /*
+ * Start with the width of the column name.
+ */
+ ret = strlen(pd->pd_colname);
+
+ /*
+ * For fixed-width values, make sure the width is large enough to hold
+ * any possible value.
+ */
+ switch (pd->pd_proptype) {
+ case PROP_TYPE_NUMBER:
+ /*
+ * The maximum length of a human-readable number is 5 characters
+ * ("20.4M", for example).
+ */
+ if (ret < 5)
+ ret = 5;
+ /*
+ * 'creation' is handled specially because it's a number
+ * internally, but displayed as a date string.
+ */
+ if (prop == ZFS_PROP_CREATION)
+ *fixed = B_FALSE;
+ break;
+ case PROP_TYPE_INDEX:
+ idx = prop_tbl[prop].pd_table;
+ for (i = 0; idx[i].pi_name != NULL; i++) {
+ if (strlen(idx[i].pi_name) > ret)
+ ret = strlen(idx[i].pi_name);
+ }
+ break;
+
+ case PROP_TYPE_STRING:
+ *fixed = B_FALSE;
+ break;
+ }
+
+ return (ret);
+}
+
+#endif
diff --git a/sys/cddl/contrib/opensolaris/uts/common/Makefile.files b/sys/cddl/contrib/opensolaris/uts/common/Makefile.files
index 1800e79..cf49c78 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/Makefile.files
+++ b/sys/cddl/contrib/opensolaris/uts/common/Makefile.files
@@ -20,11 +20,9 @@
#
#
-# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
-#
# This Makefile defines all file modules for the directory uts/common
# and its children. These are the source files which may be considered
# common to all SunOS systems.
@@ -46,7 +44,9 @@ ZFS_COMMON_OBJS += \
dsl_pool.o \
dsl_synctask.o \
dmu_zfetch.o \
+ dsl_deleg.o \
dsl_prop.o \
+ dsl_scrub.o \
fletcher.o \
gzip.o \
lzjb.o \
@@ -64,6 +64,7 @@ ZFS_COMMON_OBJS += \
unique.o \
vdev.o \
vdev_cache.o \
+ vdev_file.o \
vdev_label.o \
vdev_mirror.o \
vdev_missing.o \
@@ -75,6 +76,7 @@ ZFS_COMMON_OBJS += \
zap_micro.o \
zfs_byteswap.o \
zfs_fm.o \
+ zfs_fuid.o \
zfs_znode.o \
zil.o \
zio.o \
@@ -84,7 +86,11 @@ ZFS_COMMON_OBJS += \
ZFS_SHARED_OBJS += \
zfs_namecheck.o \
- zfs_prop.o
+ zfs_deleg.o \
+ zfs_prop.o \
+ zfs_comutil.o \
+ zpool_prop.o \
+ zprop_common.o
ZFS_OBJS += \
$(ZFS_COMMON_OBJS) \
@@ -96,6 +102,7 @@ ZFS_OBJS += \
zfs_log.o \
zfs_replay.o \
zfs_rlock.o \
+ rrwlock.o \
zfs_vfsops.o \
zfs_vnops.o \
zvol.o
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/gfs.c b/sys/cddl/contrib/opensolaris/uts/common/fs/gfs.c
index dd2aa82..d9eb88a 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/gfs.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/gfs.c
@@ -20,7 +20,7 @@
*/
/* Portions Copyright 2007 Shivakumar GN */
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -35,6 +35,7 @@
#include <sys/mutex.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
+#include <sys/sunddi.h>
#include <sys/uio.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
@@ -60,7 +61,7 @@
*
* These routines are designed to play a support role for existing
* pseudo-filesystems (such as procfs). They simplify common tasks,
- * without enforcing the filesystem to hand over management to GFS. The
+ * without forcing the filesystem to hand over management to GFS. The
* routines covered are:
*
* gfs_readdir_init()
@@ -116,6 +117,42 @@
*/
/*
+ * gfs_get_parent_ino: used to obtain a parent inode number and the
+ * inode number of the given vnode in preparation for calling gfs_readdir_init.
+ */
+int
+gfs_get_parent_ino(vnode_t *dvp, cred_t *cr, caller_context_t *ct,
+ ino64_t *pino, ino64_t *ino)
+{
+ vnode_t *parent;
+ gfs_dir_t *dp = dvp->v_data;
+ int error;
+
+ *ino = dp->gfsd_file.gfs_ino;
+ parent = dp->gfsd_file.gfs_parent;
+
+ if (parent == NULL) {
+ *pino = *ino; /* root of filesystem */
+ } else if (dvp->v_flag & V_XATTRDIR) {
+#ifdef TODO
+ vattr_t va;
+
+ va.va_mask = AT_NODEID;
+ error = VOP_GETATTR(parent, &va, 0, cr, ct);
+ if (error)
+ return (error);
+ *pino = va.va_nodeid;
+#else
+ panic("%s:%u: not implemented", __func__, __LINE__);
+#endif
+ } else {
+ *pino = ((gfs_file_t *)(parent->v_data))->gfs_ino;
+ }
+
+ return (0);
+}
+
+/*
* gfs_readdir_init: initiate a generic readdir
* st - a pointer to an uninitialized gfs_readdir_state_t structure
* name_max - the directory's maximum file name length
@@ -123,6 +160,7 @@
* uiop - the uiop passed to readdir
* parent - the parent directory's inode
* self - this directory's inode
+ * flags - flags from VOP_READDIR
*
* Returns 0 or a non-zero errno.
*
@@ -153,8 +191,10 @@
*/
int
gfs_readdir_init(gfs_readdir_state_t *st, int name_max, int ureclen,
- uio_t *uiop, ino64_t parent, ino64_t self)
+ uio_t *uiop, ino64_t parent, ino64_t self, int flags)
{
+ size_t dirent_size;
+
if (uiop->uio_loffset < 0 || uiop->uio_resid <= 0 ||
(uiop->uio_loffset % ureclen) != 0)
return (EINVAL);
@@ -162,9 +202,14 @@ gfs_readdir_init(gfs_readdir_state_t *st, int name_max, int ureclen,
st->grd_ureclen = ureclen;
st->grd_oresid = uiop->uio_resid;
st->grd_namlen = name_max;
- st->grd_dirent = kmem_zalloc(DIRENT64_RECLEN(st->grd_namlen), KM_SLEEP);
+ if (flags & V_RDDIR_ENTFLAGS)
+ dirent_size = EDIRENT_RECLEN(st->grd_namlen);
+ else
+ dirent_size = DIRENT64_RECLEN(st->grd_namlen);
+ st->grd_dirent = kmem_zalloc(dirent_size, KM_SLEEP);
st->grd_parent = parent;
st->grd_self = self;
+ st->grd_flags = flags;
return (0);
}
@@ -172,8 +217,8 @@ gfs_readdir_init(gfs_readdir_state_t *st, int name_max, int ureclen,
/*
* gfs_readdir_emit_int: internal routine to emit directory entry
*
- * st - the current readdir state, which must have d_ino and d_name
- * set
+ * st - the current readdir state, which must have d_ino/ed_ino
+ * and d_name/ed_name set
* uiop - caller-supplied uio pointer
* next - the offset of the next entry
*/
@@ -182,9 +227,18 @@ gfs_readdir_emit_int(gfs_readdir_state_t *st, uio_t *uiop, offset_t next,
int *ncookies, u_long **cookies)
{
int reclen, namlen;
+ dirent64_t *dp;
+ edirent_t *edp;
- namlen = strlen(st->grd_dirent->d_name);
- reclen = DIRENT64_RECLEN(namlen);
+ if (st->grd_flags & V_RDDIR_ENTFLAGS) {
+ edp = st->grd_dirent;
+ namlen = strlen(edp->ed_name);
+ reclen = EDIRENT_RECLEN(namlen);
+ } else {
+ dp = st->grd_dirent;
+ namlen = strlen(dp->d_name);
+ reclen = DIRENT64_RECLEN(namlen);
+ }
if (reclen > uiop->uio_resid) {
/*
@@ -195,10 +249,15 @@ gfs_readdir_emit_int(gfs_readdir_state_t *st, uio_t *uiop, offset_t next,
return (-1);
}
- /* XXX: This can change in the future. */
- st->grd_dirent->d_type = DT_DIR;
- st->grd_dirent->d_reclen = (ushort_t)reclen;
- st->grd_dirent->d_namlen = namlen;
+ if (st->grd_flags & V_RDDIR_ENTFLAGS) {
+ edp->ed_off = next;
+ edp->ed_reclen = (ushort_t)reclen;
+ } else {
+ /* XXX: This can change in the future. */
+ dp->d_reclen = (ushort_t)reclen;
+ dp->d_type = DT_DIR;
+ dp->d_namlen = namlen;
+ }
if (uiomove((caddr_t)st->grd_dirent, reclen, UIO_READ, uiop))
return (EFAULT);
@@ -219,6 +278,7 @@ gfs_readdir_emit_int(gfs_readdir_state_t *st, uio_t *uiop, offset_t next,
* voff - the virtual offset (obtained from gfs_readdir_pred)
* ino - the entry's inode
* name - the entry's name
+ * eflags - value for ed_eflags (if processing edirent_t)
*
* Returns a 0 on success, a non-zero errno on failure, or -1 if the
* readdir loop should terminate. A non-zero result (either errno or
@@ -227,12 +287,22 @@ gfs_readdir_emit_int(gfs_readdir_state_t *st, uio_t *uiop, offset_t next,
*/
int
gfs_readdir_emit(gfs_readdir_state_t *st, uio_t *uiop, offset_t voff,
- ino64_t ino, const char *name, int *ncookies, u_long **cookies)
+ ino64_t ino, const char *name, int eflags, int *ncookies, u_long **cookies)
{
offset_t off = (voff + 2) * st->grd_ureclen;
- st->grd_dirent->d_ino = ino;
- (void) strncpy(st->grd_dirent->d_name, name, st->grd_namlen);
+ if (st->grd_flags & V_RDDIR_ENTFLAGS) {
+ edirent_t *edp = st->grd_dirent;
+
+ edp->ed_ino = ino;
+ (void) strncpy(edp->ed_name, name, st->grd_namlen);
+ edp->ed_eflags = eflags;
+ } else {
+ dirent64_t *dp = st->grd_dirent;
+
+ dp->d_ino = ino;
+ (void) strncpy(dp->d_name, name, st->grd_namlen);
+ }
/*
* Inter-entry offsets are invalid, so we assume a record size of
@@ -266,11 +336,11 @@ top:
voff = off - 2;
if (off == 0) {
if ((error = gfs_readdir_emit(st, uiop, voff, st->grd_self,
- ".", ncookies, cookies)) == 0)
+ ".", 0, ncookies, cookies)) == 0)
goto top;
} else if (off == 1) {
if ((error = gfs_readdir_emit(st, uiop, voff, st->grd_parent,
- "..", ncookies, cookies)) == 0)
+ "..", 0, ncookies, cookies)) == 0)
goto top;
} else {
*voffp = voff;
@@ -292,7 +362,13 @@ top:
int
gfs_readdir_fini(gfs_readdir_state_t *st, int error, int *eofp, int eof)
{
- kmem_free(st->grd_dirent, DIRENT64_RECLEN(st->grd_namlen));
+ size_t dirent_size;
+
+ if (st->grd_flags & V_RDDIR_ENTFLAGS)
+ dirent_size = EDIRENT_RECLEN(st->grd_namlen);
+ else
+ dirent_size = DIRENT64_RECLEN(st->grd_namlen);
+ kmem_free(st->grd_dirent, dirent_size);
if (error > 0)
return (error);
if (eofp)
@@ -485,7 +561,7 @@ gfs_file_inactive(vnode_t *vp)
gfs_dir_t *dp = NULL;
void *data;
- if (fp->gfs_parent == NULL)
+ if (fp->gfs_parent == NULL || (vp->v_flag & V_XATTRDIR))
goto found;
dp = fp->gfs_parent->v_data;
@@ -511,6 +587,8 @@ gfs_file_inactive(vnode_t *vp)
ge = NULL;
found:
+ if (vp->v_flag & V_XATTRDIR)
+ VI_LOCK(fp->gfs_parent);
VI_LOCK(vp);
ASSERT(vp->v_count < 2);
/*
@@ -535,7 +613,8 @@ found:
* Free vnode and release parent
*/
if (fp->gfs_parent) {
- gfs_dir_unlock(dp);
+ if (dp)
+ gfs_dir_unlock(dp);
VI_LOCK(fp->gfs_parent);
fp->gfs_parent->v_usecount--;
VI_UNLOCK(fp->gfs_parent);
@@ -543,6 +622,8 @@ found:
ASSERT(vp->v_vfsp != NULL);
VFS_RELE(vp->v_vfsp);
}
+ if (vp->v_flag & V_XATTRDIR)
+ VI_UNLOCK(fp->gfs_parent);
return (data);
}
@@ -570,55 +651,119 @@ gfs_dir_inactive(vnode_t *vp)
}
/*
- * gfs_dir_lookup()
+ * gfs_dir_lookup_dynamic()
*
- * Looks up the given name in the directory and returns the corresponding vnode,
- * if found.
+ * This routine looks up the provided name amongst the dynamic entries
+ * in the gfs directory and returns the corresponding vnode, if found.
*
- * First, we search statically defined entries, if any. If a match is found,
- * and GFS_CACHE_VNODE is set and the vnode exists, we simply return the
- * existing vnode. Otherwise, we call the static entry's callback routine,
- * caching the result if necessary.
+ * The gfs directory is expected to be locked by the caller prior to
+ * calling this function. The directory will be unlocked during the
+ * execution of this function, but will be locked upon return from the
+ * function. This function returns 0 on success, non-zero on error.
*
- * If no static entry is found, we invoke the lookup callback, if any. The
- * arguments to this callback are:
+ * The dynamic lookups are performed by invoking the lookup
+ * callback, which is passed to this function as the first argument.
+ * The arguments to the callback are:
*
- * int gfs_lookup_cb(vnode_t *pvp, const char *nm, vnode_t **vpp);
+ * int gfs_lookup_cb(vnode_t *pvp, const char *nm, vnode_t **vpp, cred_t *cr,
+ * int flags, int *deflgs, pathname_t *rpnp);
*
* pvp - parent vnode
* nm - name of entry
* vpp - pointer to resulting vnode
+ * cr - pointer to cred
+ * flags - flags value from lookup request
+ * ignored here; currently only used to request
+ * insensitive lookups
+ * direntflgs - output parameter, directory entry flags
+ * ignored here; currently only used to indicate a lookup
+ * has more than one possible match when case is not considered
+ * realpnp - output parameter, real pathname
+ * ignored here; when lookup was performed case-insensitively,
+ * this field contains the "real" name of the file.
*
* Returns 0 on success, non-zero on error.
*/
-int
-gfs_dir_lookup(vnode_t *dvp, const char *nm, vnode_t **vpp)
+static int
+gfs_dir_lookup_dynamic(gfs_lookup_cb callback, gfs_dir_t *dp,
+ const char *nm, vnode_t *dvp, vnode_t **vpp, cred_t *cr, int flags,
+ int *direntflags, pathname_t *realpnp)
{
- int i;
- gfs_dirent_t *ge;
- vnode_t *vp;
- gfs_dir_t *dp = dvp->v_data;
- int ret = 0;
-
- ASSERT(dvp->v_type == VDIR);
+ gfs_file_t *fp;
+ ino64_t ino;
+ int ret;
- if (gfs_lookup_dot(vpp, dvp, dp->gfsd_file.gfs_parent, nm) == 0)
- return (0);
+ ASSERT(GFS_DIR_LOCKED(dp));
+ /*
+ * Drop the directory lock, as the lookup routine
+ * will need to allocate memory, or otherwise deadlock on this
+ * directory.
+ */
+ gfs_dir_unlock(dp);
+ ret = callback(dvp, nm, vpp, &ino, cr, flags, direntflags, realpnp);
gfs_dir_lock(dp);
/*
+ * The callback for extended attributes returns a vnode
+ * with v_data from an underlying fs.
+ */
+ if (ret == 0 && !IS_XATTRDIR(dvp)) {
+ fp = (gfs_file_t *)((*vpp)->v_data);
+ fp->gfs_index = -1;
+ fp->gfs_ino = ino;
+ }
+
+ return (ret);
+}
+
+/*
+ * gfs_dir_lookup_static()
+ *
+ * This routine looks up the provided name amongst the static entries
+ * in the gfs directory and returns the corresponding vnode, if found.
+ * The first argument to the function is a pointer to the comparison
+ * function this function should use to decide if names are a match.
+ *
+ * If a match is found, and GFS_CACHE_VNODE is set and the vnode
+ * exists, we simply return the existing vnode. Otherwise, we call
+ * the static entry's callback routine, caching the result if
+ * necessary. If the idx pointer argument is non-NULL, we use it to
+ * return the index of the matching static entry.
+ *
+ * The gfs directory is expected to be locked by the caller prior to calling
+ * this function. The directory may be unlocked during the execution of
+ * this function, but will be locked upon return from the function.
+ *
+ * This function returns 0 if a match is found, ENOENT if not.
+ */
+static int
+gfs_dir_lookup_static(int (*compare)(const char *, const char *),
+ gfs_dir_t *dp, const char *nm, vnode_t *dvp, int *idx,
+ vnode_t **vpp, pathname_t *rpnp)
+{
+ gfs_dirent_t *ge;
+ vnode_t *vp = NULL;
+ int i;
+
+ ASSERT(GFS_DIR_LOCKED(dp));
+
+ /*
* Search static entries.
*/
for (i = 0; i < dp->gfsd_nstatic; i++) {
ge = &dp->gfsd_static[i];
- if (strcmp(ge->gfse_name, nm) == 0) {
+ if (compare(ge->gfse_name, nm) == 0) {
+ if (rpnp)
+ (void) strlcpy(rpnp->pn_buf, ge->gfse_name,
+ rpnp->pn_bufsize);
+
if (ge->gfse_vnode) {
ASSERT(ge->gfse_flags & GFS_CACHE_VNODE);
vp = ge->gfse_vnode;
VN_HOLD(vp);
- goto out;
+ break;
}
/*
@@ -626,8 +771,8 @@ gfs_dir_lookup(vnode_t *dvp, const char *nm, vnode_t **vpp)
* need to do KM_SLEEP allocations. If we return from
* the constructor only to find that a parallel
* operation has completed, and GFS_CACHE_VNODE is set
- * for this entry, we discard the result in favor of the
- * cached vnode.
+ * for this entry, we discard the result in favor of
+ * the cached vnode.
*/
gfs_dir_unlock(dp);
vp = ge->gfse_ctor(dvp);
@@ -660,49 +805,94 @@ gfs_dir_lookup(vnode_t *dvp, const char *nm, vnode_t **vpp)
gfs_dir_lock(dp);
}
}
-
- goto out;
+ break;
}
}
- /*
- * See if there is a dynamic constructor.
- */
- if (dp->gfsd_lookup) {
- ino64_t ino;
- gfs_file_t *fp;
+ if (vp == NULL)
+ return (ENOENT);
+ else if (idx)
+ *idx = i;
+ *vpp = vp;
+ return (0);
+}
- /*
- * Once again, drop the directory lock, as the lookup routine
- * will need to allocate memory, or otherwise deadlock on this
- * directory.
- */
- gfs_dir_unlock(dp);
- ret = dp->gfsd_lookup(dvp, nm, &vp, &ino);
- gfs_dir_lock(dp);
- if (ret != 0)
- goto out;
+/*
+ * gfs_dir_lookup()
+ *
+ * Looks up the given name in the directory and returns the corresponding
+ * vnode, if found.
+ *
+ * First, we search statically defined entries, if any, with a call to
+ * gfs_dir_lookup_static(). If no static entry is found, and we have
+ * a callback function we try a dynamic lookup via gfs_dir_lookup_dynamic().
+ *
+ * This function returns 0 on success, non-zero on error.
+ */
+int
+gfs_dir_lookup(vnode_t *dvp, const char *nm, vnode_t **vpp, cred_t *cr,
+ int flags, int *direntflags, pathname_t *realpnp)
+{
+ gfs_dir_t *dp = dvp->v_data;
+ boolean_t casecheck;
+ vnode_t *dynvp = NULL;
+ vnode_t *vp = NULL;
+ int (*compare)(const char *, const char *);
+ int error, idx;
- fp = (gfs_file_t *)vp->v_data;
- fp->gfs_index = -1;
- fp->gfs_ino = ino;
- } else {
- /*
- * No static entry found, and there is no lookup callback, so
- * return ENOENT.
- */
- ret = ENOENT;
+ ASSERT(dvp->v_type == VDIR);
+
+ if (gfs_lookup_dot(vpp, dvp, dp->gfsd_file.gfs_parent, nm) == 0)
+ return (0);
+
+ casecheck = (flags & FIGNORECASE) != 0 && direntflags != NULL;
+ if (vfs_has_feature(dvp->v_vfsp, VFSFT_NOCASESENSITIVE) ||
+ (flags & FIGNORECASE))
+ compare = strcasecmp;
+ else
+ compare = strcmp;
+
+ gfs_dir_lock(dp);
+
+ error = gfs_dir_lookup_static(compare, dp, nm, dvp, &idx, &vp, realpnp);
+
+ if (vp && casecheck) {
+ gfs_dirent_t *ge;
+ int i;
+
+ for (i = idx + 1; i < dp->gfsd_nstatic; i++) {
+ ge = &dp->gfsd_static[i];
+
+ if (strcasecmp(ge->gfse_name, nm) == 0) {
+ *direntflags |= ED_CASE_CONFLICT;
+ goto out;
+ }
+ }
+ }
+
+ if ((error || casecheck) && dp->gfsd_lookup)
+ error = gfs_dir_lookup_dynamic(dp->gfsd_lookup, dp, nm, dvp,
+ &dynvp, cr, flags, direntflags, vp ? NULL : realpnp);
+
+ if (vp && dynvp) {
+ /* static and dynamic entries are case-insensitive conflict */
+ ASSERT(casecheck);
+ *direntflags |= ED_CASE_CONFLICT;
+ VN_RELE(dynvp);
+ } else if (vp == NULL) {
+ vp = dynvp;
+ } else if (error == ENOENT) {
+ error = 0;
+ } else if (error) {
+ VN_RELE(vp);
+ vp = NULL;
}
out:
gfs_dir_unlock(dp);
- if (ret == 0)
- *vpp = vp;
- else
- *vpp = NULL;
-
- return (ret);
+ *vpp = vp;
+ return (error);
}
/*
@@ -731,13 +921,15 @@ out:
* This is significantly more complex, thanks to the particulars of
* VOP_READDIR().
*
- * int gfs_readdir_cb(vnode_t *vp, struct dirent64 *dp, int *eofp,
- * offset_t *off, offset_t *nextoff, void *data)
+ * int gfs_readdir_cb(vnode_t *vp, void *dp, int *eofp,
+ * offset_t *off, offset_t *nextoff, void *data, int flags)
*
* vp - directory vnode
* dp - directory entry, sized according to maxlen given to
* gfs_dir_create(). callback must fill in d_name and
- * d_ino.
+ * d_ino (if a dirent64_t), or ed_name, ed_ino, and ed_eflags
+ * (if an edirent_t). edirent_t is used if V_RDDIR_ENTFLAGS
+ * is set in 'flags'.
* eofp - callback must set to 1 when EOF has been reached
* off - on entry, the last offset read from the directory. Callback
* must set to the offset of the current entry, typically left
@@ -745,12 +937,13 @@ out:
* nextoff - callback must set to offset of next entry. Typically
* (off + 1)
* data - caller-supplied data
+ * flags - VOP_READDIR flags
*
* Return 0 on success, or error on failure.
*/
int
gfs_dir_readdir(vnode_t *dvp, uio_t *uiop, int *eofp, int *ncookies,
- u_long **cookies, void *data)
+ u_long **cookies, void *data, cred_t *cr, int flags)
{
gfs_readdir_state_t gstate;
int error, eof = 0;
@@ -758,16 +951,12 @@ gfs_dir_readdir(vnode_t *dvp, uio_t *uiop, int *eofp, int *ncookies,
offset_t off, next;
gfs_dir_t *dp = dvp->v_data;
- ino = dp->gfsd_file.gfs_ino;
-
- if (dp->gfsd_file.gfs_parent == NULL)
- pino = ino; /* root of filesystem */
- else
- pino = ((gfs_file_t *)
- (dp->gfsd_file.gfs_parent->v_data))->gfs_ino;
+ error = gfs_get_parent_ino(dvp, cr, NULL, &pino, &ino);
+ if (error)
+ return (error);
if ((error = gfs_readdir_init(&gstate, dp->gfsd_maxlen, 1, uiop,
- pino, ino)) != 0)
+ pino, ino, flags)) != 0)
return (error);
while ((error = gfs_readdir_pred(&gstate, uiop, &off, ncookies,
@@ -777,8 +966,8 @@ gfs_dir_readdir(vnode_t *dvp, uio_t *uiop, int *eofp, int *ncookies,
ino = dp->gfsd_inode(dvp, off);
if ((error = gfs_readdir_emit(&gstate, uiop,
- off, ino, dp->gfsd_static[off].gfse_name, ncookies,
- cookies)) != 0)
+ off, ino, dp->gfsd_static[off].gfse_name, 0,
+ ncookies, cookies)) != 0)
break;
} else if (dp->gfsd_readdir) {
@@ -786,7 +975,7 @@ gfs_dir_readdir(vnode_t *dvp, uio_t *uiop, int *eofp, int *ncookies,
if ((error = dp->gfsd_readdir(dvp,
gstate.grd_dirent, &eof, &off, &next,
- data)) != 0 || eof)
+ data, flags)) != 0 || eof)
break;
off += dp->gfsd_nstatic + 2;
@@ -808,6 +997,21 @@ gfs_dir_readdir(vnode_t *dvp, uio_t *uiop, int *eofp, int *ncookies,
}
/*
+ * gfs_vop_lookup: VOP_LOOKUP() entry point
+ *
+ * For use directly in vnode ops table. Given a GFS directory, calls
+ * gfs_dir_lookup() as necessary.
+ */
+/* ARGSUSED */
+int
+gfs_vop_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp,
+ int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
+ int *direntflags, pathname_t *realpnp)
+{
+ return (gfs_dir_lookup(dvp, nm, vpp, cr, flags, direntflags, realpnp));
+}
+
+/*
* gfs_vop_readdir: VOP_READDIR() entry point
*
* For use directly in vnode ops table. Given a GFS directory, calls
@@ -827,6 +1031,7 @@ gfs_vop_readdir(ap)
{
vnode_t *vp = ap->a_vp;
uio_t *uiop = ap->a_uio;
+ cred_t *cr = ap->a_cred;
int *eofp = ap->a_eofflag;
int ncookies = 0;
u_long *cookies = NULL;
@@ -842,7 +1047,8 @@ gfs_vop_readdir(ap)
*ap->a_ncookies = ncookies;
}
- error = gfs_dir_readdir(vp, uiop, eofp, &ncookies, &cookies, NULL);
+ error = gfs_dir_readdir(vp, uiop, eofp, &ncookies, &cookies, NULL,
+ cr, 0);
if (error == 0) {
/* Subtract unused cookies */
@@ -882,6 +1088,9 @@ gfs_vop_inactive(ap)
if (data != NULL)
kmem_free(data, fp->gfs_size);
+
+ VI_LOCK(vp);
vp->v_data = NULL;
+ VI_UNLOCK(vp);
return (0);
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/vnode.c b/sys/cddl/contrib/opensolaris/uts/common/fs/vnode.c
new file mode 100644
index 0000000..00a10aa
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/vnode.c
@@ -0,0 +1,74 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL 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 (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/vnode.h>
+
+/* Extensible attribute (xva) routines. */
+
+/*
+ * Zero out the structure, set the size of the requested/returned bitmaps,
+ * set AT_XVATTR in the embedded vattr_t's va_mask, and set up the pointer
+ * to the returned attributes array.
+ */
+void
+xva_init(xvattr_t *xvap)
+{
+ bzero(xvap, sizeof (xvattr_t));
+ xvap->xva_mapsize = XVA_MAPSIZE;
+ xvap->xva_magic = XVA_MAGIC;
+ xvap->xva_vattr.va_mask = AT_XVATTR;
+ xvap->xva_rtnattrmapp = &(xvap->xva_rtnattrmap)[0];
+}
+
+/*
+ * If AT_XVATTR is set, returns a pointer to the embedded xoptattr_t
+ * structure. Otherwise, returns NULL.
+ */
+xoptattr_t *
+xva_getxoptattr(xvattr_t *xvap)
+{
+ xoptattr_t *xoap = NULL;
+ if (xvap->xva_vattr.va_mask & AT_XVATTR)
+ xoap = &xvap->xva_xoptattrs;
+ return (xoap);
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/arc.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/arc.c
index 420f802..7ca5280 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/arc.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/arc.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* DVA-based Adjustable Replacement Cache
*
@@ -47,13 +45,13 @@
* There are times when it is not possible to evict the requested
* space. In these circumstances we are unable to adjust the cache
* size. To prevent the cache growing unbounded at these times we
- * implement a "cache throttle" that slowes the flow of new data
- * into the cache until we can make space avaiable.
+ * implement a "cache throttle" that slows the flow of new data
+ * into the cache until we can make space available.
*
* 2. The Megiddo and Modha model assumes a fixed cache size.
* Pages are evicted when the cache is full and there is a cache
* miss. Our model has a variable sized cache. It grows with
- * high use, but also tries to react to memory preasure from the
+ * high use, but also tries to react to memory pressure from the
* operating system: decreasing its size when system memory is
* tight.
*
@@ -75,7 +73,7 @@
*
* A new reference to a cache buffer can be obtained in two
* ways: 1) via a hash table lookup using the DVA as a key,
- * or 2) via one of the ARC lists. The arc_read() inerface
+ * or 2) via one of the ARC lists. The arc_read() interface
* uses method 1, while the internal arc algorithms for
* adjusting the cache use method 2. We therefor provide two
* types of locks: 1) the hash table lock array, and 2) the
@@ -109,6 +107,14 @@
*
* Note that the majority of the performance stats are manipulated
* with atomic operations.
+ *
+ * The L2ARC uses the l2arc_buflist_mtx global mutex for the following:
+ *
+ * - L2ARC buflist creation
+ * - L2ARC buflist eviction
+ * - L2ARC write completion, which walks L2ARC buflists
+ * - ARC header destruction, as it removes from L2ARC buflists
+ * - ARC header release, as it removes from L2ARC buflists
*/
#include <sys/spa.h>
@@ -117,6 +123,7 @@
#include <sys/zfs_context.h>
#include <sys/arc.h>
#include <sys/refcount.h>
+#include <sys/vdev.h>
#ifdef _KERNEL
#include <sys/dnlc.h>
#endif
@@ -128,6 +135,10 @@ static kmutex_t arc_reclaim_thr_lock;
static kcondvar_t arc_reclaim_thr_cv; /* used to signal reclaim thr */
static uint8_t arc_thread_exit;
+extern int zfs_write_limit_shift;
+extern uint64_t zfs_write_limit_max;
+extern kmutex_t zfs_write_limit_lock;
+
#define ARC_REDUCE_DNLC_PERCENT 3
uint_t arc_reduce_dnlc_percent = ARC_REDUCE_DNLC_PERCENT;
@@ -148,28 +159,45 @@ static int arc_min_prefetch_lifespan;
static int arc_dead;
/*
+ * The arc has filled available memory and has now warmed up.
+ */
+static boolean_t arc_warm;
+
+/*
* These tunables are for performance analysis.
*/
-u_long zfs_arc_max;
-u_long zfs_arc_min;
-TUNABLE_ULONG("vfs.zfs.arc_max", &zfs_arc_max);
-TUNABLE_ULONG("vfs.zfs.arc_min", &zfs_arc_min);
+uint64_t zfs_arc_max;
+uint64_t zfs_arc_min;
+uint64_t zfs_arc_meta_limit = 0;
+int zfs_mdcomp_disable = 0;
+
+TUNABLE_QUAD("vfs.zfs.arc_max", &zfs_arc_max);
+TUNABLE_QUAD("vfs.zfs.arc_min", &zfs_arc_min);
+TUNABLE_QUAD("vfs.zfs.arc_meta_limit", &zfs_arc_meta_limit);
+TUNABLE_INT("vfs.zfs.mdcomp_disable", &zfs_mdcomp_disable);
SYSCTL_DECL(_vfs_zfs);
-SYSCTL_ULONG(_vfs_zfs, OID_AUTO, arc_max, CTLFLAG_RDTUN, &zfs_arc_max, 0,
+SYSCTL_QUAD(_vfs_zfs, OID_AUTO, arc_max, CTLFLAG_RDTUN, &zfs_arc_max, 0,
"Maximum ARC size");
-SYSCTL_ULONG(_vfs_zfs, OID_AUTO, arc_min, CTLFLAG_RDTUN, &zfs_arc_min, 0,
+SYSCTL_QUAD(_vfs_zfs, OID_AUTO, arc_min, CTLFLAG_RDTUN, &zfs_arc_min, 0,
"Minimum ARC size");
+SYSCTL_INT(_vfs_zfs, OID_AUTO, mdcomp_disable, CTLFLAG_RDTUN,
+ &zfs_mdcomp_disable, 0, "Disable metadata compression");
/*
- * Note that buffers can be on one of 5 states:
+ * Note that buffers can be in one of 6 states:
* ARC_anon - anonymous (discussed below)
* ARC_mru - recently used, currently cached
* ARC_mru_ghost - recentely used, no longer in cache
* ARC_mfu - frequently used, currently cached
* ARC_mfu_ghost - frequently used, no longer in cache
- * When there are no active references to the buffer, they
- * are linked onto one of the lists in arc. These are the
- * only buffers that can be evicted or deleted.
+ * ARC_l2c_only - exists in L2ARC but not other states
+ * When there are no active references to the buffer, they are
+ * are linked onto a list in one of these arc states. These are
+ * the only buffers that can be evicted or deleted. Within each
+ * state there are multiple lists, one for meta-data and one for
+ * non-meta-data. Meta-data (indirect blocks, blocks of dnodes,
+ * etc.) is tracked separately so that it can be managed more
+ * explicitly: favored over data, limited explicitly.
*
* Anonymous buffers are buffers that are not associated with
* a DVA. These are buffers that hold dirty block copies
@@ -177,21 +205,30 @@ SYSCTL_ULONG(_vfs_zfs, OID_AUTO, arc_min, CTLFLAG_RDTUN, &zfs_arc_min, 0,
* they are "ref'd" and are considered part of arc_mru
* that cannot be freed. Generally, they will aquire a DVA
* as they are written and migrate onto the arc_mru list.
+ *
+ * The ARC_l2c_only state is for buffers that are in the second
+ * level ARC but no longer in any of the ARC_m* lists. The second
+ * level ARC itself may also contain buffers that are in any of
+ * the ARC_m* states - meaning that a buffer can exist in two
+ * places. The reason for the ARC_l2c_only state is to keep the
+ * buffer header in the hash table, so that reads that hit the
+ * second level ARC benefit from these fast lookups.
*/
typedef struct arc_state {
- list_t arcs_list; /* linked list of evictable buffer in state */
- uint64_t arcs_lsize; /* total size of buffers in the linked list */
- uint64_t arcs_size; /* total size of all buffers in this state */
+ list_t arcs_list[ARC_BUFC_NUMTYPES]; /* list of evictable buffers */
+ uint64_t arcs_lsize[ARC_BUFC_NUMTYPES]; /* amount of evictable data */
+ uint64_t arcs_size; /* total amount of data in this state */
kmutex_t arcs_mtx;
} arc_state_t;
-/* The 5 states: */
+/* The 6 states: */
static arc_state_t ARC_anon;
static arc_state_t ARC_mru;
static arc_state_t ARC_mru_ghost;
static arc_state_t ARC_mfu;
static arc_state_t ARC_mfu_ghost;
+static arc_state_t ARC_l2c_only;
typedef struct arc_stats {
kstat_named_t arcstat_hits;
@@ -222,6 +259,24 @@ typedef struct arc_stats {
kstat_named_t arcstat_c_min;
kstat_named_t arcstat_c_max;
kstat_named_t arcstat_size;
+ kstat_named_t arcstat_hdr_size;
+ kstat_named_t arcstat_l2_hits;
+ kstat_named_t arcstat_l2_misses;
+ kstat_named_t arcstat_l2_feeds;
+ kstat_named_t arcstat_l2_rw_clash;
+ kstat_named_t arcstat_l2_writes_sent;
+ kstat_named_t arcstat_l2_writes_done;
+ kstat_named_t arcstat_l2_writes_error;
+ kstat_named_t arcstat_l2_writes_hdr_miss;
+ kstat_named_t arcstat_l2_evict_lock_retry;
+ kstat_named_t arcstat_l2_evict_reading;
+ kstat_named_t arcstat_l2_free_on_write;
+ kstat_named_t arcstat_l2_abort_lowmem;
+ kstat_named_t arcstat_l2_cksum_bad;
+ kstat_named_t arcstat_l2_io_error;
+ kstat_named_t arcstat_l2_size;
+ kstat_named_t arcstat_l2_hdr_size;
+ kstat_named_t arcstat_memory_throttle_count;
} arc_stats_t;
static arc_stats_t arc_stats = {
@@ -252,7 +307,25 @@ static arc_stats_t arc_stats = {
{ "c", KSTAT_DATA_UINT64 },
{ "c_min", KSTAT_DATA_UINT64 },
{ "c_max", KSTAT_DATA_UINT64 },
- { "size", KSTAT_DATA_UINT64 }
+ { "size", KSTAT_DATA_UINT64 },
+ { "hdr_size", KSTAT_DATA_UINT64 },
+ { "l2_hits", KSTAT_DATA_UINT64 },
+ { "l2_misses", KSTAT_DATA_UINT64 },
+ { "l2_feeds", KSTAT_DATA_UINT64 },
+ { "l2_rw_clash", KSTAT_DATA_UINT64 },
+ { "l2_writes_sent", KSTAT_DATA_UINT64 },
+ { "l2_writes_done", KSTAT_DATA_UINT64 },
+ { "l2_writes_error", KSTAT_DATA_UINT64 },
+ { "l2_writes_hdr_miss", KSTAT_DATA_UINT64 },
+ { "l2_evict_lock_retry", KSTAT_DATA_UINT64 },
+ { "l2_evict_reading", KSTAT_DATA_UINT64 },
+ { "l2_free_on_write", KSTAT_DATA_UINT64 },
+ { "l2_abort_lowmem", KSTAT_DATA_UINT64 },
+ { "l2_cksum_bad", KSTAT_DATA_UINT64 },
+ { "l2_io_error", KSTAT_DATA_UINT64 },
+ { "l2_size", KSTAT_DATA_UINT64 },
+ { "l2_hdr_size", KSTAT_DATA_UINT64 },
+ { "memory_throttle_count", KSTAT_DATA_UINT64 }
};
#define ARCSTAT(stat) (arc_stats.stat.value.ui64)
@@ -299,6 +372,7 @@ static arc_state_t *arc_mru;
static arc_state_t *arc_mru_ghost;
static arc_state_t *arc_mfu;
static arc_state_t *arc_mfu_ghost;
+static arc_state_t *arc_l2c_only;
/*
* There are several ARC variables that are critical to export as kstats --
@@ -316,13 +390,21 @@ static arc_state_t *arc_mfu_ghost;
static int arc_no_grow; /* Don't try to grow cache size */
static uint64_t arc_tempreserve;
+static uint64_t arc_meta_used;
+static uint64_t arc_meta_limit;
+static uint64_t arc_meta_max = 0;
+SYSCTL_QUAD(_vfs_zfs, OID_AUTO, arc_meta_used, CTLFLAG_RDTUN,
+ &arc_meta_used, 0, "ARC metadata used");
+SYSCTL_QUAD(_vfs_zfs, OID_AUTO, arc_meta_limit, CTLFLAG_RDTUN,
+ &arc_meta_limit, 0, "ARC metadata limit");
+
+typedef struct l2arc_buf_hdr l2arc_buf_hdr_t;
typedef struct arc_callback arc_callback_t;
struct arc_callback {
void *acb_private;
arc_done_func_t *acb_done;
- arc_byteswap_func_t *acb_byteswap;
arc_buf_t *acb_buf;
zio_t *acb_zio_dummy;
arc_callback_t *acb_next;
@@ -368,6 +450,9 @@ struct arc_buf_hdr {
/* self protecting */
refcount_t b_refcnt;
+
+ l2arc_buf_hdr_t *b_l2hdr;
+ list_node_t b_l2node;
};
static arc_buf_t *arc_eviction_list;
@@ -375,9 +460,12 @@ static kmutex_t arc_eviction_mtx;
static arc_buf_hdr_t arc_eviction_hdr;
static void arc_get_data_buf(arc_buf_t *buf);
static void arc_access(arc_buf_hdr_t *buf, kmutex_t *hash_lock);
+static int arc_evict_needed(arc_buf_contents_t type);
+static void arc_evict_ghost(arc_state_t *state, spa_t *spa, int64_t bytes);
#define GHOST_STATE(state) \
- ((state) == arc_mru_ghost || (state) == arc_mfu_ghost)
+ ((state) == arc_mru_ghost || (state) == arc_mfu_ghost || \
+ (state) == arc_l2c_only)
/*
* Private ARC flags. These flags are private ARC only flags that will show up
@@ -393,12 +481,31 @@ static void arc_access(arc_buf_hdr_t *buf, kmutex_t *hash_lock);
#define ARC_FREED_IN_READ (1 << 12) /* buf freed while in read */
#define ARC_BUF_AVAILABLE (1 << 13) /* block not in active use */
#define ARC_INDIRECT (1 << 14) /* this is an indirect block */
+#define ARC_FREE_IN_PROGRESS (1 << 15) /* hdr about to be freed */
+#define ARC_L2_WRITING (1 << 16) /* L2ARC write in progress */
+#define ARC_L2_EVICTED (1 << 17) /* evicted during I/O */
+#define ARC_L2_WRITE_HEAD (1 << 18) /* head of write list */
+#define ARC_STORED (1 << 19) /* has been store()d to */
#define HDR_IN_HASH_TABLE(hdr) ((hdr)->b_flags & ARC_IN_HASH_TABLE)
#define HDR_IO_IN_PROGRESS(hdr) ((hdr)->b_flags & ARC_IO_IN_PROGRESS)
#define HDR_IO_ERROR(hdr) ((hdr)->b_flags & ARC_IO_ERROR)
#define HDR_FREED_IN_READ(hdr) ((hdr)->b_flags & ARC_FREED_IN_READ)
#define HDR_BUF_AVAILABLE(hdr) ((hdr)->b_flags & ARC_BUF_AVAILABLE)
+#define HDR_FREE_IN_PROGRESS(hdr) ((hdr)->b_flags & ARC_FREE_IN_PROGRESS)
+#define HDR_L2CACHE(hdr) ((hdr)->b_flags & ARC_L2CACHE)
+#define HDR_L2_READING(hdr) ((hdr)->b_flags & ARC_IO_IN_PROGRESS && \
+ (hdr)->b_l2hdr != NULL)
+#define HDR_L2_WRITING(hdr) ((hdr)->b_flags & ARC_L2_WRITING)
+#define HDR_L2_EVICTED(hdr) ((hdr)->b_flags & ARC_L2_EVICTED)
+#define HDR_L2_WRITE_HEAD(hdr) ((hdr)->b_flags & ARC_L2_WRITE_HEAD)
+
+/*
+ * Other sizes
+ */
+
+#define HDR_SIZE ((int64_t)sizeof (arc_buf_hdr_t))
+#define L2HDR_SIZE ((int64_t)sizeof (l2arc_buf_hdr_t))
/*
* Hash table routines
@@ -431,8 +538,90 @@ static buf_hash_table_t buf_hash_table;
uint64_t zfs_crc64_table[256];
+/*
+ * Level 2 ARC
+ */
+
+#define L2ARC_WRITE_SIZE (8 * 1024 * 1024) /* initial write max */
+#define L2ARC_HEADROOM 4 /* num of writes */
+#define L2ARC_FEED_SECS 1 /* caching interval */
+
+#define l2arc_writes_sent ARCSTAT(arcstat_l2_writes_sent)
+#define l2arc_writes_done ARCSTAT(arcstat_l2_writes_done)
+
+/*
+ * L2ARC Performance Tunables
+ */
+uint64_t l2arc_write_max = L2ARC_WRITE_SIZE; /* default max write size */
+uint64_t l2arc_write_boost = L2ARC_WRITE_SIZE; /* extra write during warmup */
+uint64_t l2arc_headroom = L2ARC_HEADROOM; /* number of dev writes */
+uint64_t l2arc_feed_secs = L2ARC_FEED_SECS; /* interval seconds */
+boolean_t l2arc_noprefetch = B_TRUE; /* don't cache prefetch bufs */
+
+/*
+ * L2ARC Internals
+ */
+typedef struct l2arc_dev {
+ vdev_t *l2ad_vdev; /* vdev */
+ spa_t *l2ad_spa; /* spa */
+ uint64_t l2ad_hand; /* next write location */
+ uint64_t l2ad_write; /* desired write size, bytes */
+ uint64_t l2ad_boost; /* warmup write boost, bytes */
+ uint64_t l2ad_start; /* first addr on device */
+ uint64_t l2ad_end; /* last addr on device */
+ uint64_t l2ad_evict; /* last addr eviction reached */
+ boolean_t l2ad_first; /* first sweep through */
+ list_t *l2ad_buflist; /* buffer list */
+ list_node_t l2ad_node; /* device list node */
+} l2arc_dev_t;
+
+static list_t L2ARC_dev_list; /* device list */
+static list_t *l2arc_dev_list; /* device list pointer */
+static kmutex_t l2arc_dev_mtx; /* device list mutex */
+static l2arc_dev_t *l2arc_dev_last; /* last device used */
+static kmutex_t l2arc_buflist_mtx; /* mutex for all buflists */
+static list_t L2ARC_free_on_write; /* free after write buf list */
+static list_t *l2arc_free_on_write; /* free after write list ptr */
+static kmutex_t l2arc_free_on_write_mtx; /* mutex for list */
+static uint64_t l2arc_ndev; /* number of devices */
+
+typedef struct l2arc_read_callback {
+ arc_buf_t *l2rcb_buf; /* read buffer */
+ spa_t *l2rcb_spa; /* spa */
+ blkptr_t l2rcb_bp; /* original blkptr */
+ zbookmark_t l2rcb_zb; /* original bookmark */
+ int l2rcb_flags; /* original flags */
+} l2arc_read_callback_t;
+
+typedef struct l2arc_write_callback {
+ l2arc_dev_t *l2wcb_dev; /* device info */
+ arc_buf_hdr_t *l2wcb_head; /* head of write buflist */
+} l2arc_write_callback_t;
+
+struct l2arc_buf_hdr {
+ /* protected by arc_buf_hdr mutex */
+ l2arc_dev_t *b_dev; /* L2ARC device */
+ daddr_t b_daddr; /* disk address, offset byte */
+};
+
+typedef struct l2arc_data_free {
+ /* protected by l2arc_free_on_write_mtx */
+ void *l2df_data;
+ size_t l2df_size;
+ void (*l2df_func)(void *, size_t);
+ list_node_t l2df_list_node;
+} l2arc_data_free_t;
+
+static kmutex_t l2arc_feed_thr_lock;
+static kcondvar_t l2arc_feed_thr_cv;
+static uint8_t l2arc_thread_exit;
+
+static void l2arc_read_done(zio_t *zio);
+static void l2arc_hdr_stat_add(void);
+static void l2arc_hdr_stat_remove(void);
+
static uint64_t
-buf_hash(spa_t *spa, dva_t *dva, uint64_t birth)
+buf_hash(spa_t *spa, const dva_t *dva, uint64_t birth)
{
uintptr_t spav = (uintptr_t)spa;
uint8_t *vdva = (uint8_t *)dva;
@@ -460,7 +649,7 @@ buf_hash(spa_t *spa, dva_t *dva, uint64_t birth)
((buf)->b_birth == birth) && ((buf)->b_spa == spa)
static arc_buf_hdr_t *
-buf_hash_find(spa_t *spa, dva_t *dva, uint64_t birth, kmutex_t **lockp)
+buf_hash_find(spa_t *spa, const dva_t *dva, uint64_t birth, kmutex_t **lockp)
{
uint64_t idx = BUF_HASH_INDEX(spa, dva, birth);
kmutex_t *hash_lock = BUF_HASH_LOCK(idx);
@@ -579,6 +768,20 @@ hdr_cons(void *vbuf, void *unused, int kmflag)
bzero(buf, sizeof (arc_buf_hdr_t));
refcount_create(&buf->b_refcnt);
cv_init(&buf->b_cv, NULL, CV_DEFAULT, NULL);
+ mutex_init(&buf->b_freeze_lock, NULL, MUTEX_DEFAULT, NULL);
+
+ ARCSTAT_INCR(arcstat_hdr_size, HDR_SIZE);
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+buf_cons(void *vbuf, void *unused, int kmflag)
+{
+ arc_buf_t *buf = vbuf;
+
+ bzero(buf, sizeof (arc_buf_t));
+ rw_init(&buf->b_lock, NULL, RW_DEFAULT, NULL);
return (0);
}
@@ -594,6 +797,18 @@ hdr_dest(void *vbuf, void *unused)
refcount_destroy(&buf->b_refcnt);
cv_destroy(&buf->b_cv);
+ mutex_destroy(&buf->b_freeze_lock);
+
+ ARCSTAT_INCR(arcstat_hdr_size, -HDR_SIZE);
+}
+
+/* ARGSUSED */
+static void
+buf_dest(void *vbuf, void *unused)
+{
+ arc_buf_t *buf = vbuf;
+
+ rw_destroy(&buf->b_lock);
}
/*
@@ -639,7 +854,7 @@ retry:
hdr_cache = kmem_cache_create("arc_buf_hdr_t", sizeof (arc_buf_hdr_t),
0, hdr_cons, hdr_dest, hdr_recl, NULL, NULL, 0);
buf_cache = kmem_cache_create("arc_buf_t", sizeof (arc_buf_t),
- 0, NULL, NULL, NULL, NULL, NULL, 0);
+ 0, buf_cons, buf_dest, NULL, NULL, NULL, 0);
for (i = 0; i < 256; i++)
for (ct = zfs_crc64_table + i, *ct = i, j = 8; j > 0; j--)
@@ -673,10 +888,24 @@ arc_cksum_verify(arc_buf_t *buf)
mutex_exit(&buf->b_hdr->b_freeze_lock);
}
+static int
+arc_cksum_equal(arc_buf_t *buf)
+{
+ zio_cksum_t zc;
+ int equal;
+
+ mutex_enter(&buf->b_hdr->b_freeze_lock);
+ fletcher_2_native(buf->b_data, buf->b_hdr->b_size, &zc);
+ equal = ZIO_CHECKSUM_EQUAL(*buf->b_hdr->b_freeze_cksum, zc);
+ mutex_exit(&buf->b_hdr->b_freeze_lock);
+
+ return (equal);
+}
+
static void
-arc_cksum_compute(arc_buf_t *buf)
+arc_cksum_compute(arc_buf_t *buf, boolean_t force)
{
- if (!(zfs_flags & ZFS_DEBUG_MODIFY))
+ if (!force && !(zfs_flags & ZFS_DEBUG_MODIFY))
return;
mutex_enter(&buf->b_hdr->b_freeze_lock);
@@ -693,14 +922,14 @@ arc_cksum_compute(arc_buf_t *buf)
void
arc_buf_thaw(arc_buf_t *buf)
{
- if (!(zfs_flags & ZFS_DEBUG_MODIFY))
- return;
+ if (zfs_flags & ZFS_DEBUG_MODIFY) {
+ if (buf->b_hdr->b_state != arc_anon)
+ panic("modifying non-anon buffer!");
+ if (buf->b_hdr->b_flags & ARC_IO_IN_PROGRESS)
+ panic("modifying buffer while i/o in progress!");
+ arc_cksum_verify(buf);
+ }
- if (buf->b_hdr->b_state != arc_anon)
- panic("modifying non-anon buffer!");
- if (buf->b_hdr->b_flags & ARC_IO_IN_PROGRESS)
- panic("modifying buffer while i/o in progress!");
- arc_cksum_verify(buf);
mutex_enter(&buf->b_hdr->b_freeze_lock);
if (buf->b_hdr->b_freeze_cksum != NULL) {
kmem_free(buf->b_hdr->b_freeze_cksum, sizeof (zio_cksum_t));
@@ -717,7 +946,7 @@ arc_buf_freeze(arc_buf_t *buf)
ASSERT(buf->b_hdr->b_freeze_cksum != NULL ||
buf->b_hdr->b_state == arc_anon);
- arc_cksum_compute(buf);
+ arc_cksum_compute(buf, B_FALSE);
}
static void
@@ -728,21 +957,23 @@ add_reference(arc_buf_hdr_t *ab, kmutex_t *hash_lock, void *tag)
if ((refcount_add(&ab->b_refcnt, tag) == 1) &&
(ab->b_state != arc_anon)) {
uint64_t delta = ab->b_size * ab->b_datacnt;
+ list_t *list = &ab->b_state->arcs_list[ab->b_type];
+ uint64_t *size = &ab->b_state->arcs_lsize[ab->b_type];
ASSERT(!MUTEX_HELD(&ab->b_state->arcs_mtx));
mutex_enter(&ab->b_state->arcs_mtx);
ASSERT(list_link_active(&ab->b_arc_node));
- list_remove(&ab->b_state->arcs_list, ab);
+ list_remove(list, ab);
if (GHOST_STATE(ab->b_state)) {
ASSERT3U(ab->b_datacnt, ==, 0);
ASSERT3P(ab->b_buf, ==, NULL);
delta = ab->b_size;
}
ASSERT(delta > 0);
- ASSERT3U(ab->b_state->arcs_lsize, >=, delta);
- atomic_add_64(&ab->b_state->arcs_lsize, -delta);
+ ASSERT3U(*size, >=, delta);
+ atomic_add_64(size, -delta);
mutex_exit(&ab->b_state->arcs_mtx);
- /* remove the prefetch flag is we get a reference */
+ /* remove the prefetch flag if we get a reference */
if (ab->b_flags & ARC_PREFETCH)
ab->b_flags &= ~ARC_PREFETCH;
}
@@ -759,13 +990,14 @@ remove_reference(arc_buf_hdr_t *ab, kmutex_t *hash_lock, void *tag)
if (((cnt = refcount_remove(&ab->b_refcnt, tag)) == 0) &&
(state != arc_anon)) {
+ uint64_t *size = &state->arcs_lsize[ab->b_type];
+
ASSERT(!MUTEX_HELD(&state->arcs_mtx));
mutex_enter(&state->arcs_mtx);
ASSERT(!list_link_active(&ab->b_arc_node));
- list_insert_head(&state->arcs_list, ab);
+ list_insert_head(&state->arcs_list[ab->b_type], ab);
ASSERT(ab->b_datacnt > 0);
- atomic_add_64(&state->arcs_lsize, ab->b_size * ab->b_datacnt);
- ASSERT3U(state->arcs_size, >=, state->arcs_lsize);
+ atomic_add_64(size, ab->b_size * ab->b_datacnt);
mutex_exit(&state->arcs_mtx);
}
return (cnt);
@@ -796,12 +1028,13 @@ arc_change_state(arc_state_t *new_state, arc_buf_hdr_t *ab, kmutex_t *hash_lock)
if (refcnt == 0) {
if (old_state != arc_anon) {
int use_mutex = !MUTEX_HELD(&old_state->arcs_mtx);
+ uint64_t *size = &old_state->arcs_lsize[ab->b_type];
if (use_mutex)
mutex_enter(&old_state->arcs_mtx);
ASSERT(list_link_active(&ab->b_arc_node));
- list_remove(&old_state->arcs_list, ab);
+ list_remove(&old_state->arcs_list[ab->b_type], ab);
/*
* If prefetching out of the ghost cache,
@@ -812,19 +1045,20 @@ arc_change_state(arc_state_t *new_state, arc_buf_hdr_t *ab, kmutex_t *hash_lock)
ASSERT(ab->b_buf == NULL);
from_delta = ab->b_size;
}
- ASSERT3U(old_state->arcs_lsize, >=, from_delta);
- atomic_add_64(&old_state->arcs_lsize, -from_delta);
+ ASSERT3U(*size, >=, from_delta);
+ atomic_add_64(size, -from_delta);
if (use_mutex)
mutex_exit(&old_state->arcs_mtx);
}
if (new_state != arc_anon) {
int use_mutex = !MUTEX_HELD(&new_state->arcs_mtx);
+ uint64_t *size = &new_state->arcs_lsize[ab->b_type];
if (use_mutex)
mutex_enter(&new_state->arcs_mtx);
- list_insert_head(&new_state->arcs_list, ab);
+ list_insert_head(&new_state->arcs_list[ab->b_type], ab);
/* ghost elements have a ghost size */
if (GHOST_STATE(new_state)) {
@@ -832,9 +1066,7 @@ arc_change_state(arc_state_t *new_state, arc_buf_hdr_t *ab, kmutex_t *hash_lock)
ASSERT(ab->b_buf == NULL);
to_delta = ab->b_size;
}
- atomic_add_64(&new_state->arcs_lsize, to_delta);
- ASSERT3U(new_state->arcs_size + to_delta, >=,
- new_state->arcs_lsize);
+ atomic_add_64(size, to_delta);
if (use_mutex)
mutex_exit(&new_state->arcs_mtx);
@@ -842,7 +1074,7 @@ arc_change_state(arc_state_t *new_state, arc_buf_hdr_t *ab, kmutex_t *hash_lock)
}
ASSERT(!BUF_EMPTY(ab));
- if (new_state == arc_anon && old_state != arc_anon) {
+ if (new_state == arc_anon) {
buf_hash_remove(ab);
}
@@ -854,6 +1086,47 @@ arc_change_state(arc_state_t *new_state, arc_buf_hdr_t *ab, kmutex_t *hash_lock)
atomic_add_64(&old_state->arcs_size, -from_delta);
}
ab->b_state = new_state;
+
+ /* adjust l2arc hdr stats */
+ if (new_state == arc_l2c_only)
+ l2arc_hdr_stat_add();
+ else if (old_state == arc_l2c_only)
+ l2arc_hdr_stat_remove();
+}
+
+void
+arc_space_consume(uint64_t space)
+{
+ atomic_add_64(&arc_meta_used, space);
+ atomic_add_64(&arc_size, space);
+}
+
+void
+arc_space_return(uint64_t space)
+{
+ ASSERT(arc_meta_used >= space);
+ if (arc_meta_max < arc_meta_used)
+ arc_meta_max = arc_meta_used;
+ atomic_add_64(&arc_meta_used, -space);
+ ASSERT(arc_size >= space);
+ atomic_add_64(&arc_size, -space);
+}
+
+void *
+arc_data_buf_alloc(uint64_t size)
+{
+ if (arc_evict_needed(ARC_BUFC_DATA))
+ cv_signal(&arc_reclaim_thr_cv);
+ atomic_add_64(&arc_size, size);
+ return (zio_data_buf_alloc(size));
+}
+
+void
+arc_data_buf_free(void *buf, uint64_t size)
+{
+ zio_data_buf_free(buf, size);
+ ASSERT(arc_size >= size);
+ atomic_add_64(&arc_size, -size);
}
arc_buf_t *
@@ -863,15 +1136,14 @@ arc_buf_alloc(spa_t *spa, int size, void *tag, arc_buf_contents_t type)
arc_buf_t *buf;
ASSERT3U(size, >, 0);
- hdr = kmem_cache_alloc(hdr_cache, KM_SLEEP);
+ hdr = kmem_cache_alloc(hdr_cache, KM_PUSHPAGE);
ASSERT(BUF_EMPTY(hdr));
hdr->b_size = size;
hdr->b_type = type;
hdr->b_spa = spa;
hdr->b_state = arc_anon;
hdr->b_arc_access = 0;
- mutex_init(&hdr->b_freeze_lock, NULL, MUTEX_DEFAULT, NULL);
- buf = kmem_cache_alloc(buf_cache, KM_SLEEP);
+ buf = kmem_cache_alloc(buf_cache, KM_PUSHPAGE);
buf->b_hdr = hdr;
buf->b_data = NULL;
buf->b_efunc = NULL;
@@ -894,7 +1166,7 @@ arc_buf_clone(arc_buf_t *from)
arc_buf_hdr_t *hdr = from->b_hdr;
uint64_t size = hdr->b_size;
- buf = kmem_cache_alloc(buf_cache, KM_SLEEP);
+ buf = kmem_cache_alloc(buf_cache, KM_PUSHPAGE);
buf->b_hdr = hdr;
buf->b_data = NULL;
buf->b_efunc = NULL;
@@ -914,28 +1186,21 @@ arc_buf_add_ref(arc_buf_t *buf, void* tag)
kmutex_t *hash_lock;
/*
- * Check to see if this buffer is currently being evicted via
- * arc_do_user_evicts().
+ * Check to see if this buffer is evicted. Callers
+ * must verify b_data != NULL to know if the add_ref
+ * was successful.
*/
- mutex_enter(&arc_eviction_mtx);
- hdr = buf->b_hdr;
- if (hdr == NULL) {
- mutex_exit(&arc_eviction_mtx);
+ rw_enter(&buf->b_lock, RW_READER);
+ if (buf->b_data == NULL) {
+ rw_exit(&buf->b_lock);
return;
}
+ hdr = buf->b_hdr;
+ ASSERT(hdr != NULL);
hash_lock = HDR_LOCK(hdr);
- mutex_exit(&arc_eviction_mtx);
-
mutex_enter(hash_lock);
- if (buf->b_data == NULL) {
- /*
- * This buffer is evicted.
- */
- mutex_exit(hash_lock);
- return;
- }
+ rw_exit(&buf->b_lock);
- ASSERT(buf->b_hdr == hdr);
ASSERT(hdr->b_state == arc_mru || hdr->b_state == arc_mfu);
add_reference(hdr, hash_lock, tag);
arc_access(hdr, hash_lock);
@@ -946,6 +1211,29 @@ arc_buf_add_ref(arc_buf_t *buf, void* tag)
data, metadata, hits);
}
+/*
+ * Free the arc data buffer. If it is an l2arc write in progress,
+ * the buffer is placed on l2arc_free_on_write to be freed later.
+ */
+static void
+arc_buf_data_free(arc_buf_hdr_t *hdr, void (*free_func)(void *, size_t),
+ void *data, size_t size)
+{
+ if (HDR_L2_WRITING(hdr)) {
+ l2arc_data_free_t *df;
+ df = kmem_alloc(sizeof (l2arc_data_free_t), KM_SLEEP);
+ df->l2df_data = data;
+ df->l2df_size = size;
+ df->l2df_func = free_func;
+ mutex_enter(&l2arc_free_on_write_mtx);
+ list_insert_head(l2arc_free_on_write, df);
+ mutex_exit(&l2arc_free_on_write_mtx);
+ ARCSTAT_BUMP(arcstat_l2_free_on_write);
+ } else {
+ free_func(data, size);
+ }
+}
+
static void
arc_buf_destroy(arc_buf_t *buf, boolean_t recycle, boolean_t all)
{
@@ -960,18 +1248,24 @@ arc_buf_destroy(arc_buf_t *buf, boolean_t recycle, boolean_t all)
arc_cksum_verify(buf);
if (!recycle) {
if (type == ARC_BUFC_METADATA) {
- zio_buf_free(buf->b_data, size);
+ arc_buf_data_free(buf->b_hdr, zio_buf_free,
+ buf->b_data, size);
+ arc_space_return(size);
} else {
ASSERT(type == ARC_BUFC_DATA);
- zio_data_buf_free(buf->b_data, size);
+ arc_buf_data_free(buf->b_hdr,
+ zio_data_buf_free, buf->b_data, size);
+ atomic_add_64(&arc_size, -size);
}
- atomic_add_64(&arc_size, -size);
}
if (list_link_active(&buf->b_hdr->b_arc_node)) {
+ uint64_t *cnt = &state->arcs_lsize[type];
+
ASSERT(refcount_is_zero(&buf->b_hdr->b_refcnt));
ASSERT(state != arc_anon);
- ASSERT3U(state->arcs_lsize, >=, size);
- atomic_add_64(&state->arcs_lsize, -size);
+
+ ASSERT3U(*cnt, >=, size);
+ atomic_add_64(cnt, -size);
}
ASSERT3U(state->arcs_size, >=, size);
atomic_add_64(&state->arcs_size, -size);
@@ -1002,6 +1296,35 @@ arc_hdr_destroy(arc_buf_hdr_t *hdr)
ASSERT(refcount_is_zero(&hdr->b_refcnt));
ASSERT3P(hdr->b_state, ==, arc_anon);
ASSERT(!HDR_IO_IN_PROGRESS(hdr));
+ ASSERT(!(hdr->b_flags & ARC_STORED));
+
+ if (hdr->b_l2hdr != NULL) {
+ if (!MUTEX_HELD(&l2arc_buflist_mtx)) {
+ /*
+ * To prevent arc_free() and l2arc_evict() from
+ * attempting to free the same buffer at the same time,
+ * a FREE_IN_PROGRESS flag is given to arc_free() to
+ * give it priority. l2arc_evict() can't destroy this
+ * header while we are waiting on l2arc_buflist_mtx.
+ *
+ * The hdr may be removed from l2ad_buflist before we
+ * grab l2arc_buflist_mtx, so b_l2hdr is rechecked.
+ */
+ mutex_enter(&l2arc_buflist_mtx);
+ if (hdr->b_l2hdr != NULL) {
+ list_remove(hdr->b_l2hdr->b_dev->l2ad_buflist,
+ hdr);
+ }
+ mutex_exit(&l2arc_buflist_mtx);
+ } else {
+ list_remove(hdr->b_l2hdr->b_dev->l2ad_buflist, hdr);
+ }
+ ARCSTAT_INCR(arcstat_l2_size, -hdr->b_size);
+ kmem_free(hdr->b_l2hdr, sizeof (l2arc_buf_hdr_t));
+ if (hdr->b_state == arc_l2c_only)
+ l2arc_hdr_stat_remove();
+ hdr->b_l2hdr = NULL;
+ }
if (!BUF_EMPTY(hdr)) {
ASSERT(!HDR_IN_HASH_TABLE(hdr));
@@ -1014,12 +1337,14 @@ arc_hdr_destroy(arc_buf_hdr_t *hdr)
if (buf->b_efunc) {
mutex_enter(&arc_eviction_mtx);
+ rw_enter(&buf->b_lock, RW_WRITER);
ASSERT(buf->b_hdr != NULL);
arc_buf_destroy(hdr->b_buf, FALSE, FALSE);
hdr->b_buf = buf->b_next;
buf->b_hdr = &arc_eviction_hdr;
buf->b_next = arc_eviction_list;
arc_eviction_list = buf;
+ rw_exit(&buf->b_lock);
mutex_exit(&arc_eviction_mtx);
} else {
arc_buf_destroy(hdr->b_buf, FALSE, TRUE);
@@ -1029,7 +1354,6 @@ arc_hdr_destroy(arc_buf_hdr_t *hdr)
kmem_free(hdr->b_freeze_cksum, sizeof (zio_cksum_t));
hdr->b_freeze_cksum = NULL;
}
- mutex_destroy(&hdr->b_freeze_lock);
ASSERT(!list_link_active(&hdr->b_arc_node));
ASSERT3P(hdr->b_hash_next, ==, NULL);
@@ -1124,14 +1448,19 @@ arc_buf_size(arc_buf_t *buf)
* - return the data block from this buffer rather than freeing it.
* This flag is used by callers that are trying to make space for a
* new buffer in a full arc cache.
+ *
+ * This function makes a "best effort". It skips over any buffers
+ * it can't get a hash_lock on, and so may not catch all candidates.
+ * It may also return without evicting as much space as requested.
*/
static void *
-arc_evict(arc_state_t *state, int64_t bytes, boolean_t recycle,
+arc_evict(arc_state_t *state, spa_t *spa, int64_t bytes, boolean_t recycle,
arc_buf_contents_t type)
{
arc_state_t *evicted_state;
uint64_t bytes_evicted = 0, skipped = 0, missed = 0;
arc_buf_hdr_t *ab, *ab_prev = NULL;
+ list_t *list = &state->arcs_list[type];
kmutex_t *hash_lock;
boolean_t have_lock;
void *stolen = NULL;
@@ -1143,10 +1472,11 @@ arc_evict(arc_state_t *state, int64_t bytes, boolean_t recycle,
mutex_enter(&state->arcs_mtx);
mutex_enter(&evicted_state->arcs_mtx);
- for (ab = list_tail(&state->arcs_list); ab; ab = ab_prev) {
- ab_prev = list_prev(&state->arcs_list, ab);
+ for (ab = list_tail(list); ab; ab = ab_prev) {
+ ab_prev = list_prev(list, ab);
/* prefetch buffers have a minimum lifespan */
if (HDR_IO_IN_PROGRESS(ab) ||
+ (spa && ab->b_spa != spa) ||
(ab->b_flags & (ARC_PREFETCH|ARC_INDIRECT) &&
LBOLT - ab->b_arc_access < arc_min_prefetch_lifespan)) {
skipped++;
@@ -1163,10 +1493,15 @@ arc_evict(arc_state_t *state, int64_t bytes, boolean_t recycle,
ASSERT(ab->b_datacnt > 0);
while (ab->b_buf) {
arc_buf_t *buf = ab->b_buf;
+ if (!rw_tryenter(&buf->b_lock, RW_WRITER)) {
+ missed += 1;
+ break;
+ }
if (buf->b_data) {
bytes_evicted += ab->b_size;
if (recycle && ab->b_type == type &&
- ab->b_size == bytes) {
+ ab->b_size == bytes &&
+ !HDR_L2_WRITING(ab)) {
stolen = buf->b_data;
recycle = FALSE;
}
@@ -1180,16 +1515,20 @@ arc_evict(arc_state_t *state, int64_t bytes, boolean_t recycle,
buf->b_next = arc_eviction_list;
arc_eviction_list = buf;
mutex_exit(&arc_eviction_mtx);
+ rw_exit(&buf->b_lock);
} else {
+ rw_exit(&buf->b_lock);
arc_buf_destroy(buf,
buf->b_data == stolen, TRUE);
}
}
- ASSERT(ab->b_datacnt == 0);
- arc_change_state(evicted_state, ab, hash_lock);
- ASSERT(HDR_IN_HASH_TABLE(ab));
- ab->b_flags = ARC_IN_HASH_TABLE;
- DTRACE_PROBE1(arc__evict, arc_buf_hdr_t *, ab);
+ if (ab->b_datacnt == 0) {
+ arc_change_state(evicted_state, ab, hash_lock);
+ ASSERT(HDR_IN_HASH_TABLE(ab));
+ ab->b_flags |= ARC_IN_HASH_TABLE;
+ ab->b_flags &= ~ARC_BUF_AVAILABLE;
+ DTRACE_PROBE1(arc__evict, arc_buf_hdr_t *, ab);
+ }
if (!have_lock)
mutex_exit(hash_lock);
if (bytes >= 0 && bytes_evicted >= bytes)
@@ -1212,6 +1551,27 @@ arc_evict(arc_state_t *state, int64_t bytes, boolean_t recycle,
if (missed)
ARCSTAT_INCR(arcstat_mutex_miss, missed);
+ /*
+ * We have just evicted some date into the ghost state, make
+ * sure we also adjust the ghost state size if necessary.
+ */
+ if (arc_no_grow &&
+ arc_mru_ghost->arcs_size + arc_mfu_ghost->arcs_size > arc_c) {
+ int64_t mru_over = arc_anon->arcs_size + arc_mru->arcs_size +
+ arc_mru_ghost->arcs_size - arc_c;
+
+ if (mru_over > 0 && arc_mru_ghost->arcs_lsize[type] > 0) {
+ int64_t todelete =
+ MIN(arc_mru_ghost->arcs_lsize[type], mru_over);
+ arc_evict_ghost(arc_mru_ghost, NULL, todelete);
+ } else if (arc_mfu_ghost->arcs_lsize[type] > 0) {
+ int64_t todelete = MIN(arc_mfu_ghost->arcs_lsize[type],
+ arc_mru_ghost->arcs_size +
+ arc_mfu_ghost->arcs_size - arc_c);
+ arc_evict_ghost(arc_mfu_ghost, NULL, todelete);
+ }
+ }
+
return (stolen);
}
@@ -1220,9 +1580,10 @@ arc_evict(arc_state_t *state, int64_t bytes, boolean_t recycle,
* bytes. Destroy the buffers that are removed.
*/
static void
-arc_evict_ghost(arc_state_t *state, int64_t bytes)
+arc_evict_ghost(arc_state_t *state, spa_t *spa, int64_t bytes)
{
arc_buf_hdr_t *ab, *ab_prev;
+ list_t *list = &state->arcs_list[ARC_BUFC_DATA];
kmutex_t *hash_lock;
uint64_t bytes_deleted = 0;
uint64_t bufs_skipped = 0;
@@ -1230,17 +1591,30 @@ arc_evict_ghost(arc_state_t *state, int64_t bytes)
ASSERT(GHOST_STATE(state));
top:
mutex_enter(&state->arcs_mtx);
- for (ab = list_tail(&state->arcs_list); ab; ab = ab_prev) {
- ab_prev = list_prev(&state->arcs_list, ab);
+ for (ab = list_tail(list); ab; ab = ab_prev) {
+ ab_prev = list_prev(list, ab);
+ if (spa && ab->b_spa != spa)
+ continue;
hash_lock = HDR_LOCK(ab);
if (mutex_tryenter(hash_lock)) {
ASSERT(!HDR_IO_IN_PROGRESS(ab));
ASSERT(ab->b_buf == NULL);
- arc_change_state(arc_anon, ab, hash_lock);
- mutex_exit(hash_lock);
ARCSTAT_BUMP(arcstat_deleted);
bytes_deleted += ab->b_size;
- arc_hdr_destroy(ab);
+
+ if (ab->b_l2hdr != NULL) {
+ /*
+ * This buffer is cached on the 2nd Level ARC;
+ * don't destroy the header.
+ */
+ arc_change_state(arc_l2c_only, ab, hash_lock);
+ mutex_exit(hash_lock);
+ } else {
+ arc_change_state(arc_anon, ab, hash_lock);
+ mutex_exit(hash_lock);
+ arc_hdr_destroy(ab);
+ }
+
DTRACE_PROBE1(arc__delete, arc_buf_hdr_t *, ab);
if (bytes >= 0 && bytes_deleted >= bytes)
break;
@@ -1256,6 +1630,12 @@ top:
}
mutex_exit(&state->arcs_mtx);
+ if (list == &state->arcs_list[ARC_BUFC_DATA] &&
+ (bytes < 0 || bytes_deleted < bytes)) {
+ list = &state->arcs_list[ARC_BUFC_METADATA];
+ goto top;
+ }
+
if (bufs_skipped) {
ARCSTAT_INCR(arcstat_mutex_miss, bufs_skipped);
ASSERT(bytes >= 0);
@@ -1271,38 +1651,58 @@ arc_adjust(void)
{
int64_t top_sz, mru_over, arc_over, todelete;
- top_sz = arc_anon->arcs_size + arc_mru->arcs_size;
+ top_sz = arc_anon->arcs_size + arc_mru->arcs_size + arc_meta_used;
+
+ if (top_sz > arc_p && arc_mru->arcs_lsize[ARC_BUFC_DATA] > 0) {
+ int64_t toevict =
+ MIN(arc_mru->arcs_lsize[ARC_BUFC_DATA], top_sz - arc_p);
+ (void) arc_evict(arc_mru, NULL, toevict, FALSE, ARC_BUFC_DATA);
+ top_sz = arc_anon->arcs_size + arc_mru->arcs_size;
+ }
- if (top_sz > arc_p && arc_mru->arcs_lsize > 0) {
- int64_t toevict = MIN(arc_mru->arcs_lsize, top_sz - arc_p);
- (void) arc_evict(arc_mru, toevict, FALSE, ARC_BUFC_UNDEF);
+ if (top_sz > arc_p && arc_mru->arcs_lsize[ARC_BUFC_METADATA] > 0) {
+ int64_t toevict =
+ MIN(arc_mru->arcs_lsize[ARC_BUFC_METADATA], top_sz - arc_p);
+ (void) arc_evict(arc_mru, NULL, toevict, FALSE,
+ ARC_BUFC_METADATA);
top_sz = arc_anon->arcs_size + arc_mru->arcs_size;
}
mru_over = top_sz + arc_mru_ghost->arcs_size - arc_c;
if (mru_over > 0) {
- if (arc_mru_ghost->arcs_lsize > 0) {
- todelete = MIN(arc_mru_ghost->arcs_lsize, mru_over);
- arc_evict_ghost(arc_mru_ghost, todelete);
+ if (arc_mru_ghost->arcs_size > 0) {
+ todelete = MIN(arc_mru_ghost->arcs_size, mru_over);
+ arc_evict_ghost(arc_mru_ghost, NULL, todelete);
}
}
if ((arc_over = arc_size - arc_c) > 0) {
int64_t tbl_over;
- if (arc_mfu->arcs_lsize > 0) {
- int64_t toevict = MIN(arc_mfu->arcs_lsize, arc_over);
- (void) arc_evict(arc_mfu, toevict, FALSE,
- ARC_BUFC_UNDEF);
+ if (arc_mfu->arcs_lsize[ARC_BUFC_DATA] > 0) {
+ int64_t toevict =
+ MIN(arc_mfu->arcs_lsize[ARC_BUFC_DATA], arc_over);
+ (void) arc_evict(arc_mfu, NULL, toevict, FALSE,
+ ARC_BUFC_DATA);
+ arc_over = arc_size - arc_c;
+ }
+
+ if (arc_over > 0 &&
+ arc_mfu->arcs_lsize[ARC_BUFC_METADATA] > 0) {
+ int64_t toevict =
+ MIN(arc_mfu->arcs_lsize[ARC_BUFC_METADATA],
+ arc_over);
+ (void) arc_evict(arc_mfu, NULL, toevict, FALSE,
+ ARC_BUFC_METADATA);
}
- tbl_over = arc_size + arc_mru_ghost->arcs_lsize +
- arc_mfu_ghost->arcs_lsize - arc_c*2;
+ tbl_over = arc_size + arc_mru_ghost->arcs_size +
+ arc_mfu_ghost->arcs_size - arc_c * 2;
- if (tbl_over > 0 && arc_mfu_ghost->arcs_lsize > 0) {
- todelete = MIN(arc_mfu_ghost->arcs_lsize, tbl_over);
- arc_evict_ghost(arc_mfu_ghost, todelete);
+ if (tbl_over > 0 && arc_mfu_ghost->arcs_size > 0) {
+ todelete = MIN(arc_mfu_ghost->arcs_size, tbl_over);
+ arc_evict_ghost(arc_mfu_ghost, NULL, todelete);
}
}
}
@@ -1314,7 +1714,9 @@ arc_do_user_evicts(void)
while (arc_eviction_list != NULL) {
arc_buf_t *buf = arc_eviction_list;
arc_eviction_list = buf->b_next;
+ rw_enter(&buf->b_lock, RW_WRITER);
buf->b_hdr = NULL;
+ rw_exit(&buf->b_lock);
mutex_exit(&arc_eviction_mtx);
if (buf->b_efunc != NULL)
@@ -1329,24 +1731,40 @@ arc_do_user_evicts(void)
}
/*
- * Flush all *evictable* data from the cache.
+ * Flush all *evictable* data from the cache for the given spa.
* NOTE: this will not touch "active" (i.e. referenced) data.
*/
void
-arc_flush(void)
+arc_flush(spa_t *spa)
{
- while (list_head(&arc_mru->arcs_list))
- (void) arc_evict(arc_mru, -1, FALSE, ARC_BUFC_UNDEF);
- while (list_head(&arc_mfu->arcs_list))
- (void) arc_evict(arc_mfu, -1, FALSE, ARC_BUFC_UNDEF);
+ while (list_head(&arc_mru->arcs_list[ARC_BUFC_DATA])) {
+ (void) arc_evict(arc_mru, spa, -1, FALSE, ARC_BUFC_DATA);
+ if (spa)
+ break;
+ }
+ while (list_head(&arc_mru->arcs_list[ARC_BUFC_METADATA])) {
+ (void) arc_evict(arc_mru, spa, -1, FALSE, ARC_BUFC_METADATA);
+ if (spa)
+ break;
+ }
+ while (list_head(&arc_mfu->arcs_list[ARC_BUFC_DATA])) {
+ (void) arc_evict(arc_mfu, spa, -1, FALSE, ARC_BUFC_DATA);
+ if (spa)
+ break;
+ }
+ while (list_head(&arc_mfu->arcs_list[ARC_BUFC_METADATA])) {
+ (void) arc_evict(arc_mfu, spa, -1, FALSE, ARC_BUFC_METADATA);
+ if (spa)
+ break;
+ }
- arc_evict_ghost(arc_mru_ghost, -1);
- arc_evict_ghost(arc_mfu_ghost, -1);
+ arc_evict_ghost(arc_mru_ghost, spa, -1);
+ arc_evict_ghost(arc_mfu_ghost, spa, -1);
mutex_enter(&arc_reclaim_thr_lock);
arc_do_user_evicts();
mutex_exit(&arc_reclaim_thr_lock);
- ASSERT(arc_eviction_list == NULL);
+ ASSERT(spa || arc_eviction_list == NULL);
}
int arc_shrink_shift = 5; /* log2(fraction of arc to reclaim) */
@@ -1380,7 +1798,7 @@ arc_shrink(void)
arc_adjust();
}
-static int zfs_needfree = 0;
+static int needfree = 0;
static int
arc_reclaim_needed(void)
@@ -1391,13 +1809,28 @@ arc_reclaim_needed(void)
#ifdef _KERNEL
- if (zfs_needfree)
+ if (needfree)
return (1);
#if 0
/*
+ * take 'desfree' extra pages, so we reclaim sooner, rather than later
+ */
+ extra = desfree;
+
+ /*
+ * check that we're out of range of the pageout scanner. It starts to
+ * schedule paging if freemem is less than lotsfree and needfree.
+ * lotsfree is the high-water mark for pageout, and needfree is the
+ * number of needed free pages. We add extra pages here to make sure
+ * the scanner doesn't start up while we're freeing memory.
+ */
+ if (freemem < lotsfree + needfree + extra)
+ return (1);
+
+ /*
* check to make sure that swapfs has enough space so that anon
- * reservations can still succeeed. anon_resvmem() checks that the
+ * reservations can still succeed. anon_resvmem() checks that the
* availrmem is greater than swapfs_minfree, and the number of reserved
* swap pages. We also add a bit of extra here just to prevent
* circumstances from getting really dire.
@@ -1405,23 +1838,6 @@ arc_reclaim_needed(void)
if (availrmem < swapfs_minfree + swapfs_reserve + extra)
return (1);
- /*
- * If zio data pages are being allocated out of a separate heap segment,
- * then check that the size of available vmem for this area remains
- * above 1/4th free. This needs to be done when the size of the
- * non-default segment is smaller than physical memory, so we could
- * conceivably run out of VA in that segment before running out of
- * physical memory.
- */
- if (zio_arena != NULL) {
- size_t arc_ziosize =
- btop(vmem_size(zio_arena, VMEM_FREE | VMEM_ALLOC));
-
- if ((physmem > arc_ziosize) &&
- (btop(vmem_size(zio_arena, VMEM_FREE)) < arc_ziosize >> 2))
- return (1);
- }
-
#if defined(__i386)
/*
* If we're on an i386 platform, it's possible that we'll exhaust the
@@ -1431,7 +1847,7 @@ arc_reclaim_needed(void)
* can have in the system. However, this is generally fixed at 25 pages
* which is so low that it's useless. In this comparison, we seek to
* calculate the total heap-size, and reclaim if more than 3/4ths of the
- * heap is allocated. (Or, in the caclulation, if less than 1/4th is
+ * heap is allocated. (Or, in the calculation, if less than 1/4th is
* free)
*/
if (btop(vmem_size(heap_arena, VMEM_FREE)) <
@@ -1462,12 +1878,13 @@ arc_kmem_reap_now(arc_reclaim_strategy_t strat)
#endif
#ifdef _KERNEL
- /*
- * First purge some DNLC entries, in case the DNLC is using
- * up too much memory.
- */
- dnlc_reduce_cache((void *)(uintptr_t)arc_reduce_dnlc_percent);
-
+ if (arc_meta_used >= arc_meta_limit) {
+ /*
+ * We are exceeding our meta-data cache limit.
+ * Purge some DNLC entries to release holds on meta-data.
+ */
+ dnlc_reduce_cache((void *)(uintptr_t)arc_reduce_dnlc_percent);
+ }
#if defined(__i386)
/*
* Reclaim unused memory from all kmem caches.
@@ -1477,7 +1894,7 @@ arc_kmem_reap_now(arc_reclaim_strategy_t strat)
#endif
/*
- * An agressive reclamation will shrink the cache size as well as
+ * An aggressive reclamation will shrink the cache size as well as
* reap free buffers from the arc kmem caches.
*/
if (strat == ARC_RECLAIM_AGGR)
@@ -1526,11 +1943,10 @@ arc_reclaim_thread(void *dummy __unused)
/* reset the growth delay for every reclaim */
growtime = LBOLT + (arc_grow_retry * hz);
- ASSERT(growtime > 0);
- if (zfs_needfree && last_reclaim == ARC_RECLAIM_CONS) {
+ if (needfree && last_reclaim == ARC_RECLAIM_CONS) {
/*
- * If zfs_needfree is TRUE our vm_lowmem hook
+ * If needfree is TRUE our vm_lowmem hook
* was called and in that case we must free some
* memory, so switch to aggressive mode.
*/
@@ -1538,11 +1954,13 @@ arc_reclaim_thread(void *dummy __unused)
last_reclaim = ARC_RECLAIM_AGGR;
}
arc_kmem_reap_now(last_reclaim);
- } else if ((growtime > 0) && ((growtime - LBOLT) <= 0)) {
+ arc_warm = B_TRUE;
+
+ } else if (arc_no_grow && LBOLT >= growtime) {
arc_no_grow = FALSE;
}
- if (zfs_needfree ||
+ if (needfree ||
(2 * arc_c < arc_size +
arc_mru_ghost->arcs_size + arc_mfu_ghost->arcs_size))
arc_adjust();
@@ -1551,9 +1969,9 @@ arc_reclaim_thread(void *dummy __unused)
arc_do_user_evicts();
if (arc_reclaim_needed()) {
- zfs_needfree = 0;
+ needfree = 0;
#ifdef _KERNEL
- wakeup(&zfs_needfree);
+ wakeup(&needfree);
#endif
}
@@ -1580,6 +1998,9 @@ arc_adapt(int bytes, arc_state_t *state)
{
int mult;
+ if (state == arc_l2c_only)
+ return;
+
ASSERT(bytes > 0);
/*
* Adapt the target size of the MRU list:
@@ -1634,8 +2055,25 @@ arc_adapt(int bytes, arc_state_t *state)
* prior to insert.
*/
static int
-arc_evict_needed()
+arc_evict_needed(arc_buf_contents_t type)
{
+ if (type == ARC_BUFC_METADATA && arc_meta_used >= arc_meta_limit)
+ return (1);
+
+#if 0
+#ifdef _KERNEL
+ /*
+ * If zio data pages are being allocated out of a separate heap segment,
+ * then enforce that the size of available vmem for this area remains
+ * above about 1/32nd free.
+ */
+ if (type == ARC_BUFC_DATA && zio_arena != NULL &&
+ vmem_size(zio_arena, VMEM_FREE) <
+ (vmem_size(zio_arena, VMEM_ALLOC) >> 5))
+ return (1);
+#endif
+#endif
+
if (arc_reclaim_needed())
return (1);
@@ -1678,14 +2116,15 @@ arc_get_data_buf(arc_buf_t *buf)
* We have not yet reached cache maximum size,
* just allocate a new buffer.
*/
- if (!arc_evict_needed()) {
+ if (!arc_evict_needed(type)) {
if (type == ARC_BUFC_METADATA) {
buf->b_data = zio_buf_alloc(size);
+ arc_space_consume(size);
} else {
ASSERT(type == ARC_BUFC_DATA);
buf->b_data = zio_data_buf_alloc(size);
+ atomic_add_64(&arc_size, size);
}
- atomic_add_64(&arc_size, size);
goto out;
}
@@ -1700,20 +2139,23 @@ arc_get_data_buf(arc_buf_t *buf)
if (state == arc_mru || state == arc_anon) {
uint64_t mru_used = arc_anon->arcs_size + arc_mru->arcs_size;
- state = (arc_p > mru_used) ? arc_mfu : arc_mru;
+ state = (arc_mfu->arcs_lsize[type] > 0 &&
+ arc_p > mru_used) ? arc_mfu : arc_mru;
} else {
/* MFU cases */
uint64_t mfu_space = arc_c - arc_p;
- state = (mfu_space > arc_mfu->arcs_size) ? arc_mru : arc_mfu;
+ state = (arc_mru->arcs_lsize[type] > 0 &&
+ mfu_space > arc_mfu->arcs_size) ? arc_mru : arc_mfu;
}
- if ((buf->b_data = arc_evict(state, size, TRUE, type)) == NULL) {
+ if ((buf->b_data = arc_evict(state, NULL, size, TRUE, type)) == NULL) {
if (type == ARC_BUFC_METADATA) {
buf->b_data = zio_buf_alloc(size);
+ arc_space_consume(size);
} else {
ASSERT(type == ARC_BUFC_DATA);
buf->b_data = zio_data_buf_alloc(size);
+ atomic_add_64(&arc_size, size);
}
- atomic_add_64(&arc_size, size);
ARCSTAT_BUMP(arcstat_recycle_miss);
}
ASSERT(buf->b_data != NULL);
@@ -1728,7 +2170,7 @@ out:
atomic_add_64(&hdr->b_state->arcs_size, size);
if (list_link_active(&hdr->b_arc_node)) {
ASSERT(refcount_is_zero(&hdr->b_refcnt));
- atomic_add_64(&hdr->b_state->arcs_lsize, size);
+ atomic_add_64(&hdr->b_state->arcs_lsize[type], size);
}
/*
* If we are growing the cache, and we are adding anonymous
@@ -1773,10 +2215,6 @@ arc_access(arc_buf_hdr_t *buf, kmutex_t *hash_lock)
if ((buf->b_flags & ARC_PREFETCH) != 0) {
if (refcount_count(&buf->b_refcnt) == 0) {
ASSERT(list_link_active(&buf->b_arc_node));
- mutex_enter(&arc_mru->arcs_mtx);
- list_remove(&arc_mru->arcs_list, buf);
- list_insert_head(&arc_mru->arcs_list, buf);
- mutex_exit(&arc_mru->arcs_mtx);
} else {
buf->b_flags &= ~ARC_PREFETCH;
ARCSTAT_BUMP(arcstat_mru_hits);
@@ -1836,10 +2274,6 @@ arc_access(arc_buf_hdr_t *buf, kmutex_t *hash_lock)
if ((buf->b_flags & ARC_PREFETCH) != 0) {
ASSERT(refcount_count(&buf->b_refcnt) == 0);
ASSERT(list_link_active(&buf->b_arc_node));
- mutex_enter(&arc_mfu->arcs_mtx);
- list_remove(&arc_mfu->arcs_list, buf);
- list_insert_head(&arc_mfu->arcs_list, buf);
- mutex_exit(&arc_mfu->arcs_mtx);
}
ARCSTAT_BUMP(arcstat_mfu_hits);
buf->b_arc_access = LBOLT;
@@ -1865,6 +2299,14 @@ arc_access(arc_buf_hdr_t *buf, kmutex_t *hash_lock)
arc_change_state(new_state, buf, hash_lock);
ARCSTAT_BUMP(arcstat_mfu_ghost_hits);
+ } else if (buf->b_state == arc_l2c_only) {
+ /*
+ * This buffer is on the 2nd Level ARC.
+ */
+
+ buf->b_arc_access = LBOLT;
+ DTRACE_PROBE1(new_state__mfu, arc_buf_hdr_t *, buf);
+ arc_change_state(arc_mfu, buf, hash_lock);
} else {
ASSERT(!"invalid arc state");
}
@@ -1879,7 +2321,7 @@ arc_bcopy_func(zio_t *zio, arc_buf_t *buf, void *arg)
VERIFY(arc_buf_remove_ref(buf, arg) == 1);
}
-/* a generic arc_done_func_t which you can use */
+/* a generic arc_done_func_t */
void
arc_getbuf_func(zio_t *zio, arc_buf_t *buf, void *arg)
{
@@ -1917,15 +2359,24 @@ arc_read_done(zio_t *zio)
&hash_lock);
ASSERT((found == NULL && HDR_FREED_IN_READ(hdr) && hash_lock == NULL) ||
- (found == hdr && DVA_EQUAL(&hdr->b_dva, BP_IDENTITY(zio->io_bp))));
+ (found == hdr && DVA_EQUAL(&hdr->b_dva, BP_IDENTITY(zio->io_bp))) ||
+ (found == hdr && HDR_L2_READING(hdr)));
+
+ hdr->b_flags &= ~ARC_L2_EVICTED;
+ if (l2arc_noprefetch && (hdr->b_flags & ARC_PREFETCH))
+ hdr->b_flags &= ~ARC_L2CACHE;
/* byteswap if necessary */
callback_list = hdr->b_acb;
ASSERT(callback_list != NULL);
- if (BP_SHOULD_BYTESWAP(zio->io_bp) && callback_list->acb_byteswap)
- callback_list->acb_byteswap(buf->b_data, hdr->b_size);
+ if (BP_SHOULD_BYTESWAP(zio->io_bp)) {
+ arc_byteswap_func_t *func = BP_GET_LEVEL(zio->io_bp) > 0 ?
+ byteswap_uint64_array :
+ dmu_ot[BP_GET_TYPE(zio->io_bp)].ot_byteswap;
+ func(buf->b_data, hdr->b_size);
+ }
- arc_cksum_compute(buf);
+ arc_cksum_compute(buf, B_FALSE);
/* create copies of the data buffer for the callers */
abuf = buf;
@@ -1952,9 +2403,6 @@ arc_read_done(zio_t *zio)
if (HDR_IN_HASH_TABLE(hdr))
buf_hash_remove(hdr);
freeable = refcount_is_zero(&hdr->b_refcnt);
- /* convert checksum errors into IO errors */
- if (zio->io_error == ECKSUM)
- zio->io_error = EIO;
}
/*
@@ -2020,16 +2468,40 @@ arc_read_done(zio_t *zio)
*
* arc_read_done() will invoke all the requested "done" functions
* for readers of this block.
+ *
+ * Normal callers should use arc_read and pass the arc buffer and offset
+ * for the bp. But if you know you don't need locking, you can use
+ * arc_read_bp.
*/
int
-arc_read(zio_t *pio, spa_t *spa, blkptr_t *bp, arc_byteswap_func_t *swap,
- arc_done_func_t *done, void *private, int priority, int flags,
- uint32_t *arc_flags, zbookmark_t *zb)
+arc_read(zio_t *pio, spa_t *spa, blkptr_t *bp, arc_buf_t *pbuf,
+ arc_done_func_t *done, void *private, int priority, int zio_flags,
+ uint32_t *arc_flags, const zbookmark_t *zb)
+{
+ int err;
+ arc_buf_hdr_t *hdr = pbuf->b_hdr;
+
+ ASSERT(!refcount_is_zero(&pbuf->b_hdr->b_refcnt));
+ ASSERT3U((char *)bp - (char *)pbuf->b_data, <, pbuf->b_hdr->b_size);
+ rw_enter(&pbuf->b_lock, RW_READER);
+
+ err = arc_read_nolock(pio, spa, bp, done, private, priority,
+ zio_flags, arc_flags, zb);
+
+ ASSERT3P(hdr, ==, pbuf->b_hdr);
+ rw_exit(&pbuf->b_lock);
+ return (err);
+}
+
+int
+arc_read_nolock(zio_t *pio, spa_t *spa, blkptr_t *bp,
+ arc_done_func_t *done, void *private, int priority, int zio_flags,
+ uint32_t *arc_flags, const zbookmark_t *zb)
{
arc_buf_hdr_t *hdr;
arc_buf_t *buf;
kmutex_t *hash_lock;
- zio_t *rzio;
+ zio_t *rzio;
top:
hdr = buf_hash_find(spa, BP_IDENTITY(bp), bp->blk_birth, &hash_lock);
@@ -2053,10 +2525,9 @@ top:
KM_SLEEP);
acb->acb_done = done;
acb->acb_private = private;
- acb->acb_byteswap = swap;
if (pio != NULL)
acb->acb_zio_dummy = zio_null(pio,
- spa, NULL, NULL, flags);
+ spa, NULL, NULL, zio_flags);
ASSERT(acb->acb_done != NULL);
acb->acb_next = hdr->b_acb;
@@ -2093,6 +2564,8 @@ top:
}
DTRACE_PROBE1(arc__hit, arc_buf_hdr_t *, hdr);
arc_access(hdr, hash_lock);
+ if (*arc_flags & ARC_L2CACHE)
+ hdr->b_flags |= ARC_L2CACHE;
mutex_exit(hash_lock);
ARCSTAT_BUMP(arcstat_hits);
ARCSTAT_CONDSTAT(!(hdr->b_flags & ARC_PREFETCH),
@@ -2104,6 +2577,8 @@ top:
} else {
uint64_t size = BP_GET_LSIZE(bp);
arc_callback_t *acb;
+ vdev_t *vd = NULL;
+ daddr_t addr;
if (hdr == NULL) {
/* this block is not in the cache */
@@ -2130,6 +2605,8 @@ top:
private);
hdr->b_flags |= ARC_PREFETCH;
}
+ if (*arc_flags & ARC_L2CACHE)
+ hdr->b_flags |= ARC_L2CACHE;
if (BP_GET_LEVEL(bp) > 0)
hdr->b_flags |= ARC_INDIRECT;
} else {
@@ -2144,7 +2621,9 @@ top:
hdr->b_flags |= ARC_PREFETCH;
else
add_reference(hdr, hash_lock, private);
- buf = kmem_cache_alloc(buf_cache, KM_SLEEP);
+ if (*arc_flags & ARC_L2CACHE)
+ hdr->b_flags |= ARC_L2CACHE;
+ buf = kmem_cache_alloc(buf_cache, KM_PUSHPAGE);
buf->b_hdr = hdr;
buf->b_data = NULL;
buf->b_efunc = NULL;
@@ -2160,7 +2639,6 @@ top:
acb = kmem_zalloc(sizeof (arc_callback_t), KM_SLEEP);
acb->acb_done = done;
acb->acb_private = private;
- acb->acb_byteswap = swap;
ASSERT(hdr->b_acb == NULL);
hdr->b_acb = acb;
@@ -2176,6 +2654,18 @@ top:
if (GHOST_STATE(hdr->b_state))
arc_access(hdr, hash_lock);
+
+ if (HDR_L2CACHE(hdr) && hdr->b_l2hdr != NULL &&
+ (vd = hdr->b_l2hdr->b_dev->l2ad_vdev) != NULL) {
+ addr = hdr->b_l2hdr->b_daddr;
+ /*
+ * Lock out device removal.
+ */
+ if (vdev_is_dead(vd) ||
+ !spa_config_tryenter(spa, SCL_L2ARC, vd, RW_READER))
+ vd = NULL;
+ }
+
mutex_exit(hash_lock);
ASSERT3U(hdr->b_size, ==, size);
@@ -2186,8 +2676,65 @@ top:
demand, prefetch, hdr->b_type != ARC_BUFC_METADATA,
data, metadata, misses);
+ if (vd != NULL) {
+ /*
+ * Read from the L2ARC if the following are true:
+ * 1. The L2ARC vdev was previously cached.
+ * 2. This buffer still has L2ARC metadata.
+ * 3. This buffer isn't currently writing to the L2ARC.
+ * 4. The L2ARC entry wasn't evicted, which may
+ * also have invalidated the vdev.
+ */
+ if (hdr->b_l2hdr != NULL &&
+ !HDR_L2_WRITING(hdr) && !HDR_L2_EVICTED(hdr)) {
+ l2arc_read_callback_t *cb;
+
+ DTRACE_PROBE1(l2arc__hit, arc_buf_hdr_t *, hdr);
+ ARCSTAT_BUMP(arcstat_l2_hits);
+
+ cb = kmem_zalloc(sizeof (l2arc_read_callback_t),
+ KM_SLEEP);
+ cb->l2rcb_buf = buf;
+ cb->l2rcb_spa = spa;
+ cb->l2rcb_bp = *bp;
+ cb->l2rcb_zb = *zb;
+ cb->l2rcb_flags = zio_flags;
+
+ /*
+ * l2arc read. The SCL_L2ARC lock will be
+ * released by l2arc_read_done().
+ */
+ rzio = zio_read_phys(pio, vd, addr, size,
+ buf->b_data, ZIO_CHECKSUM_OFF,
+ l2arc_read_done, cb, priority, zio_flags |
+ ZIO_FLAG_DONT_CACHE | ZIO_FLAG_CANFAIL |
+ ZIO_FLAG_DONT_PROPAGATE |
+ ZIO_FLAG_DONT_RETRY, B_FALSE);
+ DTRACE_PROBE2(l2arc__read, vdev_t *, vd,
+ zio_t *, rzio);
+
+ if (*arc_flags & ARC_NOWAIT) {
+ zio_nowait(rzio);
+ return (0);
+ }
+
+ ASSERT(*arc_flags & ARC_WAIT);
+ if (zio_wait(rzio) == 0)
+ return (0);
+
+ /* l2arc read error; goto zio_read() */
+ } else {
+ DTRACE_PROBE1(l2arc__miss,
+ arc_buf_hdr_t *, hdr);
+ ARCSTAT_BUMP(arcstat_l2_misses);
+ if (HDR_L2_WRITING(hdr))
+ ARCSTAT_BUMP(arcstat_l2_rw_clash);
+ spa_config_exit(spa, SCL_L2ARC, vd);
+ }
+ }
+
rzio = zio_read(pio, spa, bp, buf->b_data, size,
- arc_read_done, buf, priority, flags, zb);
+ arc_read_done, buf, priority, zio_flags, zb);
if (*arc_flags & ARC_WAIT)
return (zio_wait(rzio));
@@ -2254,45 +2801,28 @@ arc_buf_evict(arc_buf_t *buf)
kmutex_t *hash_lock;
arc_buf_t **bufp;
- mutex_enter(&arc_eviction_mtx);
+ rw_enter(&buf->b_lock, RW_WRITER);
hdr = buf->b_hdr;
if (hdr == NULL) {
/*
* We are in arc_do_user_evicts().
*/
ASSERT(buf->b_data == NULL);
- mutex_exit(&arc_eviction_mtx);
+ rw_exit(&buf->b_lock);
return (0);
- }
- hash_lock = HDR_LOCK(hdr);
- mutex_exit(&arc_eviction_mtx);
-
- mutex_enter(hash_lock);
-
- if (buf->b_data == NULL) {
+ } else if (buf->b_data == NULL) {
+ arc_buf_t copy = *buf; /* structure assignment */
/*
- * We are on the eviction list.
+ * We are on the eviction list; process this buffer now
+ * but let arc_do_user_evicts() do the reaping.
*/
- mutex_exit(hash_lock);
- mutex_enter(&arc_eviction_mtx);
- if (buf->b_hdr == NULL) {
- /*
- * We are already in arc_do_user_evicts().
- */
- mutex_exit(&arc_eviction_mtx);
- return (0);
- } else {
- arc_buf_t copy = *buf; /* structure assignment */
- /*
- * Process this buffer now
- * but let arc_do_user_evicts() do the reaping.
- */
- buf->b_efunc = NULL;
- mutex_exit(&arc_eviction_mtx);
- VERIFY(copy.b_efunc(&copy) == 0);
- return (1);
- }
+ buf->b_efunc = NULL;
+ rw_exit(&buf->b_lock);
+ VERIFY(copy.b_efunc(&copy) == 0);
+ return (1);
}
+ hash_lock = HDR_LOCK(hdr);
+ mutex_enter(hash_lock);
ASSERT(buf->b_hdr == hdr);
ASSERT3U(refcount_count(&hdr->b_refcnt), <, hdr->b_datacnt);
@@ -2323,12 +2853,14 @@ arc_buf_evict(arc_buf_t *buf)
arc_change_state(evicted_state, hdr, hash_lock);
ASSERT(HDR_IN_HASH_TABLE(hdr));
- hdr->b_flags = ARC_IN_HASH_TABLE;
+ hdr->b_flags |= ARC_IN_HASH_TABLE;
+ hdr->b_flags &= ~ARC_BUF_AVAILABLE;
mutex_exit(&evicted_state->arcs_mtx);
mutex_exit(&old_state->arcs_mtx);
}
mutex_exit(hash_lock);
+ rw_exit(&buf->b_lock);
VERIFY(buf->b_efunc(buf) == 0);
buf->b_efunc = NULL;
@@ -2342,16 +2874,22 @@ arc_buf_evict(arc_buf_t *buf)
* Release this buffer from the cache. This must be done
* after a read and prior to modifying the buffer contents.
* If the buffer has more than one reference, we must make
- * make a new hdr for the buffer.
+ * a new hdr for the buffer.
*/
void
arc_release(arc_buf_t *buf, void *tag)
{
- arc_buf_hdr_t *hdr = buf->b_hdr;
- kmutex_t *hash_lock = HDR_LOCK(hdr);
+ arc_buf_hdr_t *hdr;
+ kmutex_t *hash_lock;
+ l2arc_buf_hdr_t *l2hdr;
+ uint64_t buf_size;
+
+ rw_enter(&buf->b_lock, RW_WRITER);
+ hdr = buf->b_hdr;
/* this buffer is not on any list */
ASSERT(refcount_count(&hdr->b_refcnt) > 0);
+ ASSERT(!(hdr->b_flags & ARC_STORED));
if (hdr->b_state == arc_anon) {
/* this buffer is already released */
@@ -2359,22 +2897,32 @@ arc_release(arc_buf_t *buf, void *tag)
ASSERT(BUF_EMPTY(hdr));
ASSERT(buf->b_efunc == NULL);
arc_buf_thaw(buf);
+ rw_exit(&buf->b_lock);
return;
}
+ hash_lock = HDR_LOCK(hdr);
mutex_enter(hash_lock);
+ l2hdr = hdr->b_l2hdr;
+ if (l2hdr) {
+ mutex_enter(&l2arc_buflist_mtx);
+ hdr->b_l2hdr = NULL;
+ buf_size = hdr->b_size;
+ }
+
/*
* Do we have more than one buf?
*/
- if (hdr->b_buf != buf || buf->b_next != NULL) {
+ if (hdr->b_datacnt > 1) {
arc_buf_hdr_t *nhdr;
arc_buf_t **bufp;
uint64_t blksz = hdr->b_size;
spa_t *spa = hdr->b_spa;
arc_buf_contents_t type = hdr->b_type;
+ uint32_t flags = hdr->b_flags;
- ASSERT(hdr->b_datacnt > 1);
+ ASSERT(hdr->b_buf != buf || buf->b_next != NULL);
/*
* Pull the data off of this buf and attach it to
* a new anonymous buf.
@@ -2389,37 +2937,39 @@ arc_release(arc_buf_t *buf, void *tag)
ASSERT3U(hdr->b_state->arcs_size, >=, hdr->b_size);
atomic_add_64(&hdr->b_state->arcs_size, -hdr->b_size);
if (refcount_is_zero(&hdr->b_refcnt)) {
- ASSERT3U(hdr->b_state->arcs_lsize, >=, hdr->b_size);
- atomic_add_64(&hdr->b_state->arcs_lsize, -hdr->b_size);
+ uint64_t *size = &hdr->b_state->arcs_lsize[hdr->b_type];
+ ASSERT3U(*size, >=, hdr->b_size);
+ atomic_add_64(size, -hdr->b_size);
}
hdr->b_datacnt -= 1;
arc_cksum_verify(buf);
mutex_exit(hash_lock);
- nhdr = kmem_cache_alloc(hdr_cache, KM_SLEEP);
+ nhdr = kmem_cache_alloc(hdr_cache, KM_PUSHPAGE);
nhdr->b_size = blksz;
nhdr->b_spa = spa;
nhdr->b_type = type;
nhdr->b_buf = buf;
nhdr->b_state = arc_anon;
nhdr->b_arc_access = 0;
- nhdr->b_flags = 0;
+ nhdr->b_flags = flags & ARC_L2_WRITING;
+ nhdr->b_l2hdr = NULL;
nhdr->b_datacnt = 1;
nhdr->b_freeze_cksum = NULL;
- mutex_init(&nhdr->b_freeze_lock, NULL, MUTEX_DEFAULT, NULL);
(void) refcount_add(&nhdr->b_refcnt, tag);
buf->b_hdr = nhdr;
+ rw_exit(&buf->b_lock);
atomic_add_64(&arc_anon->arcs_size, blksz);
-
- hdr = nhdr;
} else {
+ rw_exit(&buf->b_lock);
ASSERT(refcount_count(&hdr->b_refcnt) == 1);
ASSERT(!list_link_active(&hdr->b_arc_node));
ASSERT(!HDR_IO_IN_PROGRESS(hdr));
arc_change_state(arc_anon, hdr, hash_lock);
hdr->b_arc_access = 0;
mutex_exit(hash_lock);
+
bzero(&hdr->b_dva, sizeof (dva_t));
hdr->b_birth = 0;
hdr->b_cksum0 = 0;
@@ -2427,25 +2977,47 @@ arc_release(arc_buf_t *buf, void *tag)
}
buf->b_efunc = NULL;
buf->b_private = NULL;
+
+ if (l2hdr) {
+ list_remove(l2hdr->b_dev->l2ad_buflist, hdr);
+ kmem_free(l2hdr, sizeof (l2arc_buf_hdr_t));
+ ARCSTAT_INCR(arcstat_l2_size, -buf_size);
+ mutex_exit(&l2arc_buflist_mtx);
+ }
}
int
arc_released(arc_buf_t *buf)
{
- return (buf->b_data != NULL && buf->b_hdr->b_state == arc_anon);
+ int released;
+
+ rw_enter(&buf->b_lock, RW_READER);
+ released = (buf->b_data != NULL && buf->b_hdr->b_state == arc_anon);
+ rw_exit(&buf->b_lock);
+ return (released);
}
int
arc_has_callback(arc_buf_t *buf)
{
- return (buf->b_efunc != NULL);
+ int callback;
+
+ rw_enter(&buf->b_lock, RW_READER);
+ callback = (buf->b_efunc != NULL);
+ rw_exit(&buf->b_lock);
+ return (callback);
}
#ifdef ZFS_DEBUG
int
arc_referenced(arc_buf_t *buf)
{
- return (refcount_count(&buf->b_hdr->b_refcnt));
+ int referenced;
+
+ rw_enter(&buf->b_lock, RW_READER);
+ referenced = (refcount_count(&buf->b_hdr->b_refcnt));
+ rw_exit(&buf->b_lock);
+ return (referenced);
}
#endif
@@ -2454,12 +3026,27 @@ arc_write_ready(zio_t *zio)
{
arc_write_callback_t *callback = zio->io_private;
arc_buf_t *buf = callback->awcb_buf;
+ arc_buf_hdr_t *hdr = buf->b_hdr;
- if (callback->awcb_ready) {
- ASSERT(!refcount_is_zero(&buf->b_hdr->b_refcnt));
- callback->awcb_ready(zio, buf, callback->awcb_private);
+ ASSERT(!refcount_is_zero(&buf->b_hdr->b_refcnt));
+ callback->awcb_ready(zio, buf, callback->awcb_private);
+
+ /*
+ * If the IO is already in progress, then this is a re-write
+ * attempt, so we need to thaw and re-compute the cksum.
+ * It is the responsibility of the callback to handle the
+ * accounting for any re-write attempt.
+ */
+ if (HDR_IO_IN_PROGRESS(hdr)) {
+ mutex_enter(&hdr->b_freeze_lock);
+ if (hdr->b_freeze_cksum != NULL) {
+ kmem_free(hdr->b_freeze_cksum, sizeof (zio_cksum_t));
+ hdr->b_freeze_cksum = NULL;
+ }
+ mutex_exit(&hdr->b_freeze_lock);
}
- arc_cksum_compute(buf);
+ arc_cksum_compute(buf, B_FALSE);
+ hdr->b_flags |= ARC_IO_IN_PROGRESS;
}
static void
@@ -2471,9 +3058,6 @@ arc_write_done(zio_t *zio)
hdr->b_acb = NULL;
- /* this buffer is on no lists and is not in the hash table */
- ASSERT3P(hdr->b_state, ==, arc_anon);
-
hdr->b_dva = *BP_IDENTITY(zio->io_bp);
hdr->b_birth = zio->io_bp->blk_birth;
hdr->b_cksum0 = zio->io_bp->blk_cksum.zc_word[0];
@@ -2496,6 +3080,7 @@ arc_write_done(zio_t *zio)
* sync-to-convergence, because we remove
* buffers from the hash table when we arc_free().
*/
+ ASSERT(zio->io_flags & ZIO_FLAG_IO_REWRITE);
ASSERT(DVA_EQUAL(BP_IDENTITY(&zio->io_bp_orig),
BP_IDENTITY(zio->io_bp)));
ASSERT3U(zio->io_bp_orig.blk_birth, ==,
@@ -2509,7 +3094,9 @@ arc_write_done(zio_t *zio)
ASSERT3P(exists, ==, NULL);
}
hdr->b_flags &= ~ARC_IO_IN_PROGRESS;
- arc_access(hdr, hash_lock);
+ /* if it's not anon, we are doing a scrub */
+ if (hdr->b_state == arc_anon)
+ arc_access(hdr, hash_lock);
mutex_exit(hash_lock);
} else if (callback->awcb_done == NULL) {
int destroy_hdr;
@@ -2526,6 +3113,7 @@ arc_write_done(zio_t *zio)
} else {
hdr->b_flags &= ~ARC_IO_IN_PROGRESS;
}
+ hdr->b_flags &= ~ARC_STORED;
if (callback->awcb_done) {
ASSERT(!refcount_is_zero(&hdr->b_refcnt));
@@ -2535,31 +3123,74 @@ arc_write_done(zio_t *zio)
kmem_free(callback, sizeof (arc_write_callback_t));
}
+static void
+write_policy(spa_t *spa, const writeprops_t *wp, zio_prop_t *zp)
+{
+ boolean_t ismd = (wp->wp_level > 0 || dmu_ot[wp->wp_type].ot_metadata);
+
+ /* Determine checksum setting */
+ if (ismd) {
+ /*
+ * Metadata always gets checksummed. If the data
+ * checksum is multi-bit correctable, and it's not a
+ * ZBT-style checksum, then it's suitable for metadata
+ * as well. Otherwise, the metadata checksum defaults
+ * to fletcher4.
+ */
+ if (zio_checksum_table[wp->wp_oschecksum].ci_correctable &&
+ !zio_checksum_table[wp->wp_oschecksum].ci_zbt)
+ zp->zp_checksum = wp->wp_oschecksum;
+ else
+ zp->zp_checksum = ZIO_CHECKSUM_FLETCHER_4;
+ } else {
+ zp->zp_checksum = zio_checksum_select(wp->wp_dnchecksum,
+ wp->wp_oschecksum);
+ }
+
+ /* Determine compression setting */
+ if (ismd) {
+ /*
+ * XXX -- we should design a compression algorithm
+ * that specializes in arrays of bps.
+ */
+ zp->zp_compress = zfs_mdcomp_disable ? ZIO_COMPRESS_EMPTY :
+ ZIO_COMPRESS_LZJB;
+ } else {
+ zp->zp_compress = zio_compress_select(wp->wp_dncompress,
+ wp->wp_oscompress);
+ }
+
+ zp->zp_type = wp->wp_type;
+ zp->zp_level = wp->wp_level;
+ zp->zp_ndvas = MIN(wp->wp_copies + ismd, spa_max_replication(spa));
+}
+
zio_t *
-arc_write(zio_t *pio, spa_t *spa, int checksum, int compress, int ncopies,
- uint64_t txg, blkptr_t *bp, arc_buf_t *buf,
+arc_write(zio_t *pio, spa_t *spa, const writeprops_t *wp,
+ boolean_t l2arc, uint64_t txg, blkptr_t *bp, arc_buf_t *buf,
arc_done_func_t *ready, arc_done_func_t *done, void *private, int priority,
- int flags, zbookmark_t *zb)
+ int zio_flags, const zbookmark_t *zb)
{
arc_buf_hdr_t *hdr = buf->b_hdr;
arc_write_callback_t *callback;
- zio_t *zio;
+ zio_t *zio;
+ zio_prop_t zp;
- /* this is a private buffer - no locking required */
- ASSERT3P(hdr->b_state, ==, arc_anon);
- ASSERT(BUF_EMPTY(hdr));
+ ASSERT(ready != NULL);
ASSERT(!HDR_IO_ERROR(hdr));
ASSERT((hdr->b_flags & ARC_IO_IN_PROGRESS) == 0);
ASSERT(hdr->b_acb == 0);
+ if (l2arc)
+ hdr->b_flags |= ARC_L2CACHE;
callback = kmem_zalloc(sizeof (arc_write_callback_t), KM_SLEEP);
callback->awcb_ready = ready;
callback->awcb_done = done;
callback->awcb_private = private;
callback->awcb_buf = buf;
- hdr->b_flags |= ARC_IO_IN_PROGRESS;
- zio = zio_write(pio, spa, checksum, compress, ncopies, txg, bp,
- buf->b_data, hdr->b_size, arc_write_ready, arc_write_done, callback,
- priority, flags, zb);
+
+ write_policy(spa, wp, &zp);
+ zio = zio_write(pio, spa, txg, bp, buf->b_data, hdr->b_size, &zp,
+ arc_write_ready, arc_write_done, callback, priority, zio_flags, zb);
return (zio);
}
@@ -2584,7 +3215,9 @@ arc_free(zio_t *pio, spa_t *spa, uint64_t txg, blkptr_t *bp,
* nonzero, it should match what we have in the cache.
*/
ASSERT(bp->blk_cksum.zc_word[0] == 0 ||
- ab->b_cksum0 == bp->blk_cksum.zc_word[0]);
+ bp->blk_cksum.zc_word[0] == ab->b_cksum0 ||
+ bp->blk_fill == BLK_FILL_ALREADY_FREED);
+
if (ab->b_state != arc_anon)
arc_change_state(arc_anon, ab, hash_lock);
if (HDR_IO_IN_PROGRESS(ab)) {
@@ -2604,6 +3237,7 @@ arc_free(zio_t *pio, spa_t *spa, uint64_t txg, blkptr_t *bp,
ab->b_buf->b_private = NULL;
mutex_exit(hash_lock);
} else if (refcount_is_zero(&ab->b_refcnt)) {
+ ab->b_flags |= ARC_FREE_IN_PROGRESS;
mutex_exit(hash_lock);
arc_hdr_destroy(ab);
ARCSTAT_BUMP(arcstat_deleted);
@@ -2624,7 +3258,7 @@ arc_free(zio_t *pio, spa_t *spa, uint64_t txg, blkptr_t *bp,
}
}
- zio = zio_free(pio, spa, txg, bp, done, private);
+ zio = zio_free(pio, spa, txg, bp, done, private, ZIO_FLAG_MUSTSUCCEED);
if (arc_flags & ARC_WAIT)
return (zio_wait(zio));
@@ -2635,16 +3269,75 @@ arc_free(zio_t *pio, spa_t *spa, uint64_t txg, blkptr_t *bp,
return (0);
}
+static int
+arc_memory_throttle(uint64_t reserve, uint64_t txg)
+{
+#ifdef _KERNEL
+ uint64_t inflight_data = arc_anon->arcs_size;
+ uint64_t available_memory = ptoa((uintmax_t)cnt.v_free_count);
+ static uint64_t page_load = 0;
+ static uint64_t last_txg = 0;
+
+#if 0
+#if defined(__i386)
+ available_memory =
+ MIN(available_memory, vmem_size(heap_arena, VMEM_FREE));
+#endif
+#endif
+ if (available_memory >= zfs_write_limit_max)
+ return (0);
+
+ if (txg > last_txg) {
+ last_txg = txg;
+ page_load = 0;
+ }
+ /*
+ * If we are in pageout, we know that memory is already tight,
+ * the arc is already going to be evicting, so we just want to
+ * continue to let page writes occur as quickly as possible.
+ */
+ if (curproc == pageproc) {
+ if (page_load > available_memory / 4)
+ return (ERESTART);
+ /* Note: reserve is inflated, so we deflate */
+ page_load += reserve / 8;
+ return (0);
+ } else if (page_load > 0 && arc_reclaim_needed()) {
+ /* memory is low, delay before restarting */
+ ARCSTAT_INCR(arcstat_memory_throttle_count, 1);
+ return (EAGAIN);
+ }
+ page_load = 0;
+
+ if (arc_size > arc_c_min) {
+ uint64_t evictable_memory =
+ arc_mru->arcs_lsize[ARC_BUFC_DATA] +
+ arc_mru->arcs_lsize[ARC_BUFC_METADATA] +
+ arc_mfu->arcs_lsize[ARC_BUFC_DATA] +
+ arc_mfu->arcs_lsize[ARC_BUFC_METADATA];
+ available_memory += MIN(evictable_memory, arc_size - arc_c_min);
+ }
+
+ if (inflight_data > available_memory / 4) {
+ ARCSTAT_INCR(arcstat_memory_throttle_count, 1);
+ return (ERESTART);
+ }
+#endif
+ return (0);
+}
+
void
-arc_tempreserve_clear(uint64_t tempreserve)
+arc_tempreserve_clear(uint64_t reserve)
{
- atomic_add_64(&arc_tempreserve, -tempreserve);
+ atomic_add_64(&arc_tempreserve, -reserve);
ASSERT((int64_t)arc_tempreserve >= 0);
}
int
-arc_tempreserve_space(uint64_t tempreserve)
+arc_tempreserve_space(uint64_t reserve, uint64_t txg)
{
+ int error;
+
#ifdef ZFS_DEBUG
/*
* Once in a while, fail for no reason. Everything should cope.
@@ -2654,31 +3347,37 @@ arc_tempreserve_space(uint64_t tempreserve)
return (ERESTART);
}
#endif
- if (tempreserve > arc_c/4 && !arc_no_grow)
- arc_c = MIN(arc_c_max, tempreserve * 4);
- if (tempreserve > arc_c)
+ if (reserve > arc_c/4 && !arc_no_grow)
+ arc_c = MIN(arc_c_max, reserve * 4);
+ if (reserve > arc_c)
return (ENOMEM);
/*
+ * Writes will, almost always, require additional memory allocations
+ * in order to compress/encrypt/etc the data. We therefor need to
+ * make sure that there is sufficient available memory for this.
+ */
+ if (error = arc_memory_throttle(reserve, txg))
+ return (error);
+
+ /*
* Throttle writes when the amount of dirty data in the cache
* gets too large. We try to keep the cache less than half full
* of dirty blocks so that our sync times don't grow too large.
* Note: if two requests come in concurrently, we might let them
* both succeed, when one of them should fail. Not a huge deal.
- *
- * XXX The limit should be adjusted dynamically to keep the time
- * to sync a dataset fixed (around 1-5 seconds?).
*/
-
- if (tempreserve + arc_tempreserve + arc_anon->arcs_size > arc_c / 2 &&
- arc_tempreserve + arc_anon->arcs_size > arc_c / 4) {
- dprintf("failing, arc_tempreserve=%lluK anon=%lluK "
- "tempreserve=%lluK arc_c=%lluK\n",
- arc_tempreserve>>10, arc_anon->arcs_lsize>>10,
- tempreserve>>10, arc_c>>10);
+ if (reserve + arc_tempreserve + arc_anon->arcs_size > arc_c / 2 &&
+ arc_anon->arcs_size > arc_c / 4) {
+ dprintf("failing, arc_tempreserve=%lluK anon_meta=%lluK "
+ "anon_data=%lluK tempreserve=%lluK arc_c=%lluK\n",
+ arc_tempreserve>>10,
+ arc_anon->arcs_lsize[ARC_BUFC_METADATA]>>10,
+ arc_anon->arcs_lsize[ARC_BUFC_DATA]>>10,
+ reserve>>10, arc_c>>10);
return (ERESTART);
}
- atomic_add_64(&arc_tempreserve, tempreserve);
+ atomic_add_64(&arc_tempreserve, reserve);
return (0);
}
@@ -2692,10 +3391,10 @@ arc_lowmem(void *arg __unused, int howto __unused)
/* Serialize access via arc_lowmem_lock. */
mutex_enter(&arc_lowmem_lock);
- zfs_needfree = 1;
+ needfree = 1;
cv_signal(&arc_reclaim_thr_cv);
- while (zfs_needfree)
- tsleep(&zfs_needfree, 0, "zfs:lowmem", hz / 5);
+ while (needfree)
+ tsleep(&needfree, 0, "zfs:lowmem", hz / 5);
mutex_exit(&arc_lowmem_lock);
}
#endif
@@ -2743,6 +3442,16 @@ arc_init(void)
arc_c = arc_c_max;
arc_p = (arc_c >> 1);
+ /* limit meta-data to 1/4 of the arc capacity */
+ arc_meta_limit = arc_c_max / 4;
+
+ /* Allow the tunable to override if it is reasonable */
+ if (zfs_arc_meta_limit > 0 && zfs_arc_meta_limit <= arc_c_max)
+ arc_meta_limit = zfs_arc_meta_limit;
+
+ if (arc_c_min < arc_meta_limit / 2 && zfs_arc_min == 0)
+ arc_c_min = arc_meta_limit / 2;
+
/* if kmem_flags are set, lets try to use less memory */
if (kmem_debugging())
arc_c = arc_c / 2;
@@ -2757,6 +3466,7 @@ arc_init(void)
arc_mru_ghost = &ARC_mru_ghost;
arc_mfu = &ARC_mfu;
arc_mfu_ghost = &ARC_mfu_ghost;
+ arc_l2c_only = &ARC_l2c_only;
arc_size = 0;
mutex_init(&arc_anon->arcs_mtx, NULL, MUTEX_DEFAULT, NULL);
@@ -2764,15 +3474,28 @@ arc_init(void)
mutex_init(&arc_mru_ghost->arcs_mtx, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&arc_mfu->arcs_mtx, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&arc_mfu_ghost->arcs_mtx, NULL, MUTEX_DEFAULT, NULL);
-
- list_create(&arc_mru->arcs_list, sizeof (arc_buf_hdr_t),
- offsetof(arc_buf_hdr_t, b_arc_node));
- list_create(&arc_mru_ghost->arcs_list, sizeof (arc_buf_hdr_t),
- offsetof(arc_buf_hdr_t, b_arc_node));
- list_create(&arc_mfu->arcs_list, sizeof (arc_buf_hdr_t),
- offsetof(arc_buf_hdr_t, b_arc_node));
- list_create(&arc_mfu_ghost->arcs_list, sizeof (arc_buf_hdr_t),
- offsetof(arc_buf_hdr_t, b_arc_node));
+ mutex_init(&arc_l2c_only->arcs_mtx, NULL, MUTEX_DEFAULT, NULL);
+
+ list_create(&arc_mru->arcs_list[ARC_BUFC_METADATA],
+ sizeof (arc_buf_hdr_t), offsetof(arc_buf_hdr_t, b_arc_node));
+ list_create(&arc_mru->arcs_list[ARC_BUFC_DATA],
+ sizeof (arc_buf_hdr_t), offsetof(arc_buf_hdr_t, b_arc_node));
+ list_create(&arc_mru_ghost->arcs_list[ARC_BUFC_METADATA],
+ sizeof (arc_buf_hdr_t), offsetof(arc_buf_hdr_t, b_arc_node));
+ list_create(&arc_mru_ghost->arcs_list[ARC_BUFC_DATA],
+ sizeof (arc_buf_hdr_t), offsetof(arc_buf_hdr_t, b_arc_node));
+ list_create(&arc_mfu->arcs_list[ARC_BUFC_METADATA],
+ sizeof (arc_buf_hdr_t), offsetof(arc_buf_hdr_t, b_arc_node));
+ list_create(&arc_mfu->arcs_list[ARC_BUFC_DATA],
+ sizeof (arc_buf_hdr_t), offsetof(arc_buf_hdr_t, b_arc_node));
+ list_create(&arc_mfu_ghost->arcs_list[ARC_BUFC_METADATA],
+ sizeof (arc_buf_hdr_t), offsetof(arc_buf_hdr_t, b_arc_node));
+ list_create(&arc_mfu_ghost->arcs_list[ARC_BUFC_DATA],
+ sizeof (arc_buf_hdr_t), offsetof(arc_buf_hdr_t, b_arc_node));
+ list_create(&arc_l2c_only->arcs_list[ARC_BUFC_METADATA],
+ sizeof (arc_buf_hdr_t), offsetof(arc_buf_hdr_t, b_arc_node));
+ list_create(&arc_l2c_only->arcs_list[ARC_BUFC_DATA],
+ sizeof (arc_buf_hdr_t), offsetof(arc_buf_hdr_t, b_arc_node));
buf_init();
@@ -2798,6 +3521,13 @@ arc_init(void)
#endif
arc_dead = FALSE;
+ arc_warm = B_FALSE;
+
+ if (zfs_write_limit_max == 0)
+ zfs_write_limit_max = ptob(physmem) >> zfs_write_limit_shift;
+ else
+ zfs_write_limit_shift = 0;
+ mutex_init(&zfs_write_limit_lock, NULL, MUTEX_DEFAULT, NULL);
#ifdef _KERNEL
/* Warn about ZFS memory and address space requirements. */
@@ -2808,9 +3538,9 @@ arc_init(void)
if (kmem_size() < 512 * (1 << 20)) {
printf("ZFS WARNING: Recommended minimum kmem_size is 512MB; "
"expect unstable behavior.\n");
- printf(" Consider tuning vm.kmem_size and "
+ printf(" Consider tuning vm.kmem_size and "
"vm.kmem_size_max\n");
- printf(" in /boot/loader.conf.\n");
+ printf(" in /boot/loader.conf.\n");
}
#endif
}
@@ -2818,6 +3548,7 @@ arc_init(void)
void
arc_fini(void)
{
+
mutex_enter(&arc_reclaim_thr_lock);
arc_thread_exit = 1;
cv_signal(&arc_reclaim_thr_cv);
@@ -2825,7 +3556,7 @@ arc_fini(void)
cv_wait(&arc_reclaim_thr_cv, &arc_reclaim_thr_lock);
mutex_exit(&arc_reclaim_thr_lock);
- arc_flush();
+ arc_flush(NULL);
arc_dead = TRUE;
@@ -2838,10 +3569,14 @@ arc_fini(void)
mutex_destroy(&arc_reclaim_thr_lock);
cv_destroy(&arc_reclaim_thr_cv);
- list_destroy(&arc_mru->arcs_list);
- list_destroy(&arc_mru_ghost->arcs_list);
- list_destroy(&arc_mfu->arcs_list);
- list_destroy(&arc_mfu_ghost->arcs_list);
+ list_destroy(&arc_mru->arcs_list[ARC_BUFC_METADATA]);
+ list_destroy(&arc_mru_ghost->arcs_list[ARC_BUFC_METADATA]);
+ list_destroy(&arc_mfu->arcs_list[ARC_BUFC_METADATA]);
+ list_destroy(&arc_mfu_ghost->arcs_list[ARC_BUFC_METADATA]);
+ list_destroy(&arc_mru->arcs_list[ARC_BUFC_DATA]);
+ list_destroy(&arc_mru_ghost->arcs_list[ARC_BUFC_DATA]);
+ list_destroy(&arc_mfu->arcs_list[ARC_BUFC_DATA]);
+ list_destroy(&arc_mfu_ghost->arcs_list[ARC_BUFC_DATA]);
mutex_destroy(&arc_anon->arcs_mtx);
mutex_destroy(&arc_mru->arcs_mtx);
@@ -2849,6 +3584,8 @@ arc_fini(void)
mutex_destroy(&arc_mfu->arcs_mtx);
mutex_destroy(&arc_mfu_ghost->arcs_mtx);
+ mutex_destroy(&zfs_write_limit_lock);
+
buf_fini();
mutex_destroy(&arc_lowmem_lock);
@@ -2857,3 +3594,985 @@ arc_fini(void)
EVENTHANDLER_DEREGISTER(vm_lowmem, arc_event_lowmem);
#endif
}
+
+/*
+ * Level 2 ARC
+ *
+ * The level 2 ARC (L2ARC) is a cache layer in-between main memory and disk.
+ * It uses dedicated storage devices to hold cached data, which are populated
+ * using large infrequent writes. The main role of this cache is to boost
+ * the performance of random read workloads. The intended L2ARC devices
+ * include short-stroked disks, solid state disks, and other media with
+ * substantially faster read latency than disk.
+ *
+ * +-----------------------+
+ * | ARC |
+ * +-----------------------+
+ * | ^ ^
+ * | | |
+ * l2arc_feed_thread() arc_read()
+ * | | |
+ * | l2arc read |
+ * V | |
+ * +---------------+ |
+ * | L2ARC | |
+ * +---------------+ |
+ * | ^ |
+ * l2arc_write() | |
+ * | | |
+ * V | |
+ * +-------+ +-------+
+ * | vdev | | vdev |
+ * | cache | | cache |
+ * +-------+ +-------+
+ * +=========+ .-----.
+ * : L2ARC : |-_____-|
+ * : devices : | Disks |
+ * +=========+ `-_____-'
+ *
+ * Read requests are satisfied from the following sources, in order:
+ *
+ * 1) ARC
+ * 2) vdev cache of L2ARC devices
+ * 3) L2ARC devices
+ * 4) vdev cache of disks
+ * 5) disks
+ *
+ * Some L2ARC device types exhibit extremely slow write performance.
+ * To accommodate for this there are some significant differences between
+ * the L2ARC and traditional cache design:
+ *
+ * 1. There is no eviction path from the ARC to the L2ARC. Evictions from
+ * the ARC behave as usual, freeing buffers and placing headers on ghost
+ * lists. The ARC does not send buffers to the L2ARC during eviction as
+ * this would add inflated write latencies for all ARC memory pressure.
+ *
+ * 2. The L2ARC attempts to cache data from the ARC before it is evicted.
+ * It does this by periodically scanning buffers from the eviction-end of
+ * the MFU and MRU ARC lists, copying them to the L2ARC devices if they are
+ * not already there. It scans until a headroom of buffers is satisfied,
+ * which itself is a buffer for ARC eviction. The thread that does this is
+ * l2arc_feed_thread(), illustrated below; example sizes are included to
+ * provide a better sense of ratio than this diagram:
+ *
+ * head --> tail
+ * +---------------------+----------+
+ * ARC_mfu |:::::#:::::::::::::::|o#o###o###|-->. # already on L2ARC
+ * +---------------------+----------+ | o L2ARC eligible
+ * ARC_mru |:#:::::::::::::::::::|#o#ooo####|-->| : ARC buffer
+ * +---------------------+----------+ |
+ * 15.9 Gbytes ^ 32 Mbytes |
+ * headroom |
+ * l2arc_feed_thread()
+ * |
+ * l2arc write hand <--[oooo]--'
+ * | 8 Mbyte
+ * | write max
+ * V
+ * +==============================+
+ * L2ARC dev |####|#|###|###| |####| ... |
+ * +==============================+
+ * 32 Gbytes
+ *
+ * 3. If an ARC buffer is copied to the L2ARC but then hit instead of
+ * evicted, then the L2ARC has cached a buffer much sooner than it probably
+ * needed to, potentially wasting L2ARC device bandwidth and storage. It is
+ * safe to say that this is an uncommon case, since buffers at the end of
+ * the ARC lists have moved there due to inactivity.
+ *
+ * 4. If the ARC evicts faster than the L2ARC can maintain a headroom,
+ * then the L2ARC simply misses copying some buffers. This serves as a
+ * pressure valve to prevent heavy read workloads from both stalling the ARC
+ * with waits and clogging the L2ARC with writes. This also helps prevent
+ * the potential for the L2ARC to churn if it attempts to cache content too
+ * quickly, such as during backups of the entire pool.
+ *
+ * 5. After system boot and before the ARC has filled main memory, there are
+ * no evictions from the ARC and so the tails of the ARC_mfu and ARC_mru
+ * lists can remain mostly static. Instead of searching from tail of these
+ * lists as pictured, the l2arc_feed_thread() will search from the list heads
+ * for eligible buffers, greatly increasing its chance of finding them.
+ *
+ * The L2ARC device write speed is also boosted during this time so that
+ * the L2ARC warms up faster. Since there have been no ARC evictions yet,
+ * there are no L2ARC reads, and no fear of degrading read performance
+ * through increased writes.
+ *
+ * 6. Writes to the L2ARC devices are grouped and sent in-sequence, so that
+ * the vdev queue can aggregate them into larger and fewer writes. Each
+ * device is written to in a rotor fashion, sweeping writes through
+ * available space then repeating.
+ *
+ * 7. The L2ARC does not store dirty content. It never needs to flush
+ * write buffers back to disk based storage.
+ *
+ * 8. If an ARC buffer is written (and dirtied) which also exists in the
+ * L2ARC, the now stale L2ARC buffer is immediately dropped.
+ *
+ * The performance of the L2ARC can be tweaked by a number of tunables, which
+ * may be necessary for different workloads:
+ *
+ * l2arc_write_max max write bytes per interval
+ * l2arc_write_boost extra write bytes during device warmup
+ * l2arc_noprefetch skip caching prefetched buffers
+ * l2arc_headroom number of max device writes to precache
+ * l2arc_feed_secs seconds between L2ARC writing
+ *
+ * Tunables may be removed or added as future performance improvements are
+ * integrated, and also may become zpool properties.
+ */
+
+static void
+l2arc_hdr_stat_add(void)
+{
+ ARCSTAT_INCR(arcstat_l2_hdr_size, HDR_SIZE + L2HDR_SIZE);
+ ARCSTAT_INCR(arcstat_hdr_size, -HDR_SIZE);
+}
+
+static void
+l2arc_hdr_stat_remove(void)
+{
+ ARCSTAT_INCR(arcstat_l2_hdr_size, -(HDR_SIZE + L2HDR_SIZE));
+ ARCSTAT_INCR(arcstat_hdr_size, HDR_SIZE);
+}
+
+/*
+ * Cycle through L2ARC devices. This is how L2ARC load balances.
+ * If a device is returned, this also returns holding the spa config lock.
+ */
+static l2arc_dev_t *
+l2arc_dev_get_next(void)
+{
+ l2arc_dev_t *first, *next = NULL;
+
+ /*
+ * Lock out the removal of spas (spa_namespace_lock), then removal
+ * of cache devices (l2arc_dev_mtx). Once a device has been selected,
+ * both locks will be dropped and a spa config lock held instead.
+ */
+ mutex_enter(&spa_namespace_lock);
+ mutex_enter(&l2arc_dev_mtx);
+
+ /* if there are no vdevs, there is nothing to do */
+ if (l2arc_ndev == 0)
+ goto out;
+
+ first = NULL;
+ next = l2arc_dev_last;
+ do {
+ /* loop around the list looking for a non-faulted vdev */
+ if (next == NULL) {
+ next = list_head(l2arc_dev_list);
+ } else {
+ next = list_next(l2arc_dev_list, next);
+ if (next == NULL)
+ next = list_head(l2arc_dev_list);
+ }
+
+ /* if we have come back to the start, bail out */
+ if (first == NULL)
+ first = next;
+ else if (next == first)
+ break;
+
+ } while (vdev_is_dead(next->l2ad_vdev));
+
+ /* if we were unable to find any usable vdevs, return NULL */
+ if (vdev_is_dead(next->l2ad_vdev))
+ next = NULL;
+
+ l2arc_dev_last = next;
+
+out:
+ mutex_exit(&l2arc_dev_mtx);
+
+ /*
+ * Grab the config lock to prevent the 'next' device from being
+ * removed while we are writing to it.
+ */
+ if (next != NULL)
+ spa_config_enter(next->l2ad_spa, SCL_L2ARC, next, RW_READER);
+ mutex_exit(&spa_namespace_lock);
+
+ return (next);
+}
+
+/*
+ * Free buffers that were tagged for destruction.
+ */
+static void
+l2arc_do_free_on_write()
+{
+ list_t *buflist;
+ l2arc_data_free_t *df, *df_prev;
+
+ mutex_enter(&l2arc_free_on_write_mtx);
+ buflist = l2arc_free_on_write;
+
+ for (df = list_tail(buflist); df; df = df_prev) {
+ df_prev = list_prev(buflist, df);
+ ASSERT(df->l2df_data != NULL);
+ ASSERT(df->l2df_func != NULL);
+ df->l2df_func(df->l2df_data, df->l2df_size);
+ list_remove(buflist, df);
+ kmem_free(df, sizeof (l2arc_data_free_t));
+ }
+
+ mutex_exit(&l2arc_free_on_write_mtx);
+}
+
+/*
+ * A write to a cache device has completed. Update all headers to allow
+ * reads from these buffers to begin.
+ */
+static void
+l2arc_write_done(zio_t *zio)
+{
+ l2arc_write_callback_t *cb;
+ l2arc_dev_t *dev;
+ list_t *buflist;
+ arc_buf_hdr_t *head, *ab, *ab_prev;
+ l2arc_buf_hdr_t *abl2;
+ kmutex_t *hash_lock;
+
+ cb = zio->io_private;
+ ASSERT(cb != NULL);
+ dev = cb->l2wcb_dev;
+ ASSERT(dev != NULL);
+ head = cb->l2wcb_head;
+ ASSERT(head != NULL);
+ buflist = dev->l2ad_buflist;
+ ASSERT(buflist != NULL);
+ DTRACE_PROBE2(l2arc__iodone, zio_t *, zio,
+ l2arc_write_callback_t *, cb);
+
+ if (zio->io_error != 0)
+ ARCSTAT_BUMP(arcstat_l2_writes_error);
+
+ mutex_enter(&l2arc_buflist_mtx);
+
+ /*
+ * All writes completed, or an error was hit.
+ */
+ for (ab = list_prev(buflist, head); ab; ab = ab_prev) {
+ ab_prev = list_prev(buflist, ab);
+
+ hash_lock = HDR_LOCK(ab);
+ if (!mutex_tryenter(hash_lock)) {
+ /*
+ * This buffer misses out. It may be in a stage
+ * of eviction. Its ARC_L2_WRITING flag will be
+ * left set, denying reads to this buffer.
+ */
+ ARCSTAT_BUMP(arcstat_l2_writes_hdr_miss);
+ continue;
+ }
+
+ if (zio->io_error != 0) {
+ /*
+ * Error - drop L2ARC entry.
+ */
+ list_remove(buflist, ab);
+ abl2 = ab->b_l2hdr;
+ ab->b_l2hdr = NULL;
+ kmem_free(abl2, sizeof (l2arc_buf_hdr_t));
+ ARCSTAT_INCR(arcstat_l2_size, -ab->b_size);
+ }
+
+ /*
+ * Allow ARC to begin reads to this L2ARC entry.
+ */
+ ab->b_flags &= ~ARC_L2_WRITING;
+
+ mutex_exit(hash_lock);
+ }
+
+ atomic_inc_64(&l2arc_writes_done);
+ list_remove(buflist, head);
+ kmem_cache_free(hdr_cache, head);
+ mutex_exit(&l2arc_buflist_mtx);
+
+ l2arc_do_free_on_write();
+
+ kmem_free(cb, sizeof (l2arc_write_callback_t));
+}
+
+/*
+ * A read to a cache device completed. Validate buffer contents before
+ * handing over to the regular ARC routines.
+ */
+static void
+l2arc_read_done(zio_t *zio)
+{
+ l2arc_read_callback_t *cb;
+ arc_buf_hdr_t *hdr;
+ arc_buf_t *buf;
+ kmutex_t *hash_lock;
+ int equal;
+
+ ASSERT(zio->io_vd != NULL);
+ ASSERT(zio->io_flags & ZIO_FLAG_DONT_PROPAGATE);
+
+ spa_config_exit(zio->io_spa, SCL_L2ARC, zio->io_vd);
+
+ cb = zio->io_private;
+ ASSERT(cb != NULL);
+ buf = cb->l2rcb_buf;
+ ASSERT(buf != NULL);
+ hdr = buf->b_hdr;
+ ASSERT(hdr != NULL);
+
+ hash_lock = HDR_LOCK(hdr);
+ mutex_enter(hash_lock);
+
+ /*
+ * Check this survived the L2ARC journey.
+ */
+ equal = arc_cksum_equal(buf);
+ if (equal && zio->io_error == 0 && !HDR_L2_EVICTED(hdr)) {
+ mutex_exit(hash_lock);
+ zio->io_private = buf;
+ zio->io_bp_copy = cb->l2rcb_bp; /* XXX fix in L2ARC 2.0 */
+ zio->io_bp = &zio->io_bp_copy; /* XXX fix in L2ARC 2.0 */
+ arc_read_done(zio);
+ } else {
+ mutex_exit(hash_lock);
+ /*
+ * Buffer didn't survive caching. Increment stats and
+ * reissue to the original storage device.
+ */
+ if (zio->io_error != 0) {
+ ARCSTAT_BUMP(arcstat_l2_io_error);
+ } else {
+ zio->io_error = EIO;
+ }
+ if (!equal)
+ ARCSTAT_BUMP(arcstat_l2_cksum_bad);
+
+ /*
+ * If there's no waiter, issue an async i/o to the primary
+ * storage now. If there *is* a waiter, the caller must
+ * issue the i/o in a context where it's OK to block.
+ */
+ if (zio->io_waiter == NULL)
+ zio_nowait(zio_read(zio->io_parent,
+ cb->l2rcb_spa, &cb->l2rcb_bp,
+ buf->b_data, zio->io_size, arc_read_done, buf,
+ zio->io_priority, cb->l2rcb_flags, &cb->l2rcb_zb));
+ }
+
+ kmem_free(cb, sizeof (l2arc_read_callback_t));
+}
+
+/*
+ * This is the list priority from which the L2ARC will search for pages to
+ * cache. This is used within loops (0..3) to cycle through lists in the
+ * desired order. This order can have a significant effect on cache
+ * performance.
+ *
+ * Currently the metadata lists are hit first, MFU then MRU, followed by
+ * the data lists. This function returns a locked list, and also returns
+ * the lock pointer.
+ */
+static list_t *
+l2arc_list_locked(int list_num, kmutex_t **lock)
+{
+ list_t *list;
+
+ ASSERT(list_num >= 0 && list_num <= 3);
+
+ switch (list_num) {
+ case 0:
+ list = &arc_mfu->arcs_list[ARC_BUFC_METADATA];
+ *lock = &arc_mfu->arcs_mtx;
+ break;
+ case 1:
+ list = &arc_mru->arcs_list[ARC_BUFC_METADATA];
+ *lock = &arc_mru->arcs_mtx;
+ break;
+ case 2:
+ list = &arc_mfu->arcs_list[ARC_BUFC_DATA];
+ *lock = &arc_mfu->arcs_mtx;
+ break;
+ case 3:
+ list = &arc_mru->arcs_list[ARC_BUFC_DATA];
+ *lock = &arc_mru->arcs_mtx;
+ break;
+ }
+
+ ASSERT(!(MUTEX_HELD(*lock)));
+ mutex_enter(*lock);
+ return (list);
+}
+
+/*
+ * Evict buffers from the device write hand to the distance specified in
+ * bytes. This distance may span populated buffers, it may span nothing.
+ * This is clearing a region on the L2ARC device ready for writing.
+ * If the 'all' boolean is set, every buffer is evicted.
+ */
+static void
+l2arc_evict(l2arc_dev_t *dev, uint64_t distance, boolean_t all)
+{
+ list_t *buflist;
+ l2arc_buf_hdr_t *abl2;
+ arc_buf_hdr_t *ab, *ab_prev;
+ kmutex_t *hash_lock;
+ uint64_t taddr;
+
+ buflist = dev->l2ad_buflist;
+
+ if (buflist == NULL)
+ return;
+
+ if (!all && dev->l2ad_first) {
+ /*
+ * This is the first sweep through the device. There is
+ * nothing to evict.
+ */
+ return;
+ }
+
+ if (dev->l2ad_hand >= (dev->l2ad_end - (2 * distance))) {
+ /*
+ * When nearing the end of the device, evict to the end
+ * before the device write hand jumps to the start.
+ */
+ taddr = dev->l2ad_end;
+ } else {
+ taddr = dev->l2ad_hand + distance;
+ }
+ DTRACE_PROBE4(l2arc__evict, l2arc_dev_t *, dev, list_t *, buflist,
+ uint64_t, taddr, boolean_t, all);
+
+top:
+ mutex_enter(&l2arc_buflist_mtx);
+ for (ab = list_tail(buflist); ab; ab = ab_prev) {
+ ab_prev = list_prev(buflist, ab);
+
+ hash_lock = HDR_LOCK(ab);
+ if (!mutex_tryenter(hash_lock)) {
+ /*
+ * Missed the hash lock. Retry.
+ */
+ ARCSTAT_BUMP(arcstat_l2_evict_lock_retry);
+ mutex_exit(&l2arc_buflist_mtx);
+ mutex_enter(hash_lock);
+ mutex_exit(hash_lock);
+ goto top;
+ }
+
+ if (HDR_L2_WRITE_HEAD(ab)) {
+ /*
+ * We hit a write head node. Leave it for
+ * l2arc_write_done().
+ */
+ list_remove(buflist, ab);
+ mutex_exit(hash_lock);
+ continue;
+ }
+
+ if (!all && ab->b_l2hdr != NULL &&
+ (ab->b_l2hdr->b_daddr > taddr ||
+ ab->b_l2hdr->b_daddr < dev->l2ad_hand)) {
+ /*
+ * We've evicted to the target address,
+ * or the end of the device.
+ */
+ mutex_exit(hash_lock);
+ break;
+ }
+
+ if (HDR_FREE_IN_PROGRESS(ab)) {
+ /*
+ * Already on the path to destruction.
+ */
+ mutex_exit(hash_lock);
+ continue;
+ }
+
+ if (ab->b_state == arc_l2c_only) {
+ ASSERT(!HDR_L2_READING(ab));
+ /*
+ * This doesn't exist in the ARC. Destroy.
+ * arc_hdr_destroy() will call list_remove()
+ * and decrement arcstat_l2_size.
+ */
+ arc_change_state(arc_anon, ab, hash_lock);
+ arc_hdr_destroy(ab);
+ } else {
+ /*
+ * Invalidate issued or about to be issued
+ * reads, since we may be about to write
+ * over this location.
+ */
+ if (HDR_L2_READING(ab)) {
+ ARCSTAT_BUMP(arcstat_l2_evict_reading);
+ ab->b_flags |= ARC_L2_EVICTED;
+ }
+
+ /*
+ * Tell ARC this no longer exists in L2ARC.
+ */
+ if (ab->b_l2hdr != NULL) {
+ abl2 = ab->b_l2hdr;
+ ab->b_l2hdr = NULL;
+ kmem_free(abl2, sizeof (l2arc_buf_hdr_t));
+ ARCSTAT_INCR(arcstat_l2_size, -ab->b_size);
+ }
+ list_remove(buflist, ab);
+
+ /*
+ * This may have been leftover after a
+ * failed write.
+ */
+ ab->b_flags &= ~ARC_L2_WRITING;
+ }
+ mutex_exit(hash_lock);
+ }
+ mutex_exit(&l2arc_buflist_mtx);
+
+ spa_l2cache_space_update(dev->l2ad_vdev, 0, -(taddr - dev->l2ad_evict));
+ dev->l2ad_evict = taddr;
+}
+
+/*
+ * Find and write ARC buffers to the L2ARC device.
+ *
+ * An ARC_L2_WRITING flag is set so that the L2ARC buffers are not valid
+ * for reading until they have completed writing.
+ */
+static void
+l2arc_write_buffers(spa_t *spa, l2arc_dev_t *dev, uint64_t target_sz)
+{
+ arc_buf_hdr_t *ab, *ab_prev, *head;
+ l2arc_buf_hdr_t *hdrl2;
+ list_t *list;
+ uint64_t passed_sz, write_sz, buf_sz, headroom;
+ void *buf_data;
+ kmutex_t *hash_lock, *list_lock;
+ boolean_t have_lock, full;
+ l2arc_write_callback_t *cb;
+ zio_t *pio, *wzio;
+ int try;
+
+ ASSERT(dev->l2ad_vdev != NULL);
+
+ pio = NULL;
+ write_sz = 0;
+ full = B_FALSE;
+ head = kmem_cache_alloc(hdr_cache, KM_PUSHPAGE);
+ head->b_flags |= ARC_L2_WRITE_HEAD;
+
+ /*
+ * Copy buffers for L2ARC writing.
+ */
+ mutex_enter(&l2arc_buflist_mtx);
+ for (try = 0; try <= 3; try++) {
+ list = l2arc_list_locked(try, &list_lock);
+ passed_sz = 0;
+
+ /*
+ * L2ARC fast warmup.
+ *
+ * Until the ARC is warm and starts to evict, read from the
+ * head of the ARC lists rather than the tail.
+ */
+ headroom = target_sz * l2arc_headroom;
+ if (arc_warm == B_FALSE)
+ ab = list_head(list);
+ else
+ ab = list_tail(list);
+
+ for (; ab; ab = ab_prev) {
+ if (arc_warm == B_FALSE)
+ ab_prev = list_next(list, ab);
+ else
+ ab_prev = list_prev(list, ab);
+
+ hash_lock = HDR_LOCK(ab);
+ have_lock = MUTEX_HELD(hash_lock);
+ if (!have_lock && !mutex_tryenter(hash_lock)) {
+ /*
+ * Skip this buffer rather than waiting.
+ */
+ continue;
+ }
+
+ passed_sz += ab->b_size;
+ if (passed_sz > headroom) {
+ /*
+ * Searched too far.
+ */
+ mutex_exit(hash_lock);
+ break;
+ }
+
+ if (ab->b_spa != spa) {
+ mutex_exit(hash_lock);
+ continue;
+ }
+
+ if (ab->b_l2hdr != NULL) {
+ /*
+ * Already in L2ARC.
+ */
+ mutex_exit(hash_lock);
+ continue;
+ }
+
+ if (HDR_IO_IN_PROGRESS(ab) || !HDR_L2CACHE(ab)) {
+ mutex_exit(hash_lock);
+ continue;
+ }
+
+ if ((write_sz + ab->b_size) > target_sz) {
+ full = B_TRUE;
+ mutex_exit(hash_lock);
+ break;
+ }
+
+ if (ab->b_buf == NULL) {
+ DTRACE_PROBE1(l2arc__buf__null, void *, ab);
+ mutex_exit(hash_lock);
+ continue;
+ }
+
+ if (pio == NULL) {
+ /*
+ * Insert a dummy header on the buflist so
+ * l2arc_write_done() can find where the
+ * write buffers begin without searching.
+ */
+ list_insert_head(dev->l2ad_buflist, head);
+
+ cb = kmem_alloc(
+ sizeof (l2arc_write_callback_t), KM_SLEEP);
+ cb->l2wcb_dev = dev;
+ cb->l2wcb_head = head;
+ pio = zio_root(spa, l2arc_write_done, cb,
+ ZIO_FLAG_CANFAIL);
+ }
+
+ /*
+ * Create and add a new L2ARC header.
+ */
+ hdrl2 = kmem_zalloc(sizeof (l2arc_buf_hdr_t), KM_SLEEP);
+ hdrl2->b_dev = dev;
+ hdrl2->b_daddr = dev->l2ad_hand;
+
+ ab->b_flags |= ARC_L2_WRITING;
+ ab->b_l2hdr = hdrl2;
+ list_insert_head(dev->l2ad_buflist, ab);
+ buf_data = ab->b_buf->b_data;
+ buf_sz = ab->b_size;
+
+ /*
+ * Compute and store the buffer cksum before
+ * writing. On debug the cksum is verified first.
+ */
+ arc_cksum_verify(ab->b_buf);
+ arc_cksum_compute(ab->b_buf, B_TRUE);
+
+ mutex_exit(hash_lock);
+
+ wzio = zio_write_phys(pio, dev->l2ad_vdev,
+ dev->l2ad_hand, buf_sz, buf_data, ZIO_CHECKSUM_OFF,
+ NULL, NULL, ZIO_PRIORITY_ASYNC_WRITE,
+ ZIO_FLAG_CANFAIL, B_FALSE);
+
+ DTRACE_PROBE2(l2arc__write, vdev_t *, dev->l2ad_vdev,
+ zio_t *, wzio);
+ (void) zio_nowait(wzio);
+
+ /*
+ * Keep the clock hand suitably device-aligned.
+ */
+ buf_sz = vdev_psize_to_asize(dev->l2ad_vdev, buf_sz);
+
+ write_sz += buf_sz;
+ dev->l2ad_hand += buf_sz;
+ }
+
+ mutex_exit(list_lock);
+
+ if (full == B_TRUE)
+ break;
+ }
+ mutex_exit(&l2arc_buflist_mtx);
+
+ if (pio == NULL) {
+ ASSERT3U(write_sz, ==, 0);
+ kmem_cache_free(hdr_cache, head);
+ return;
+ }
+
+ ASSERT3U(write_sz, <=, target_sz);
+ ARCSTAT_BUMP(arcstat_l2_writes_sent);
+ ARCSTAT_INCR(arcstat_l2_size, write_sz);
+ spa_l2cache_space_update(dev->l2ad_vdev, 0, write_sz);
+
+ /*
+ * Bump device hand to the device start if it is approaching the end.
+ * l2arc_evict() will already have evicted ahead for this case.
+ */
+ if (dev->l2ad_hand >= (dev->l2ad_end - target_sz)) {
+ spa_l2cache_space_update(dev->l2ad_vdev, 0,
+ dev->l2ad_end - dev->l2ad_hand);
+ dev->l2ad_hand = dev->l2ad_start;
+ dev->l2ad_evict = dev->l2ad_start;
+ dev->l2ad_first = B_FALSE;
+ }
+
+ (void) zio_wait(pio);
+}
+
+/*
+ * This thread feeds the L2ARC at regular intervals. This is the beating
+ * heart of the L2ARC.
+ */
+static void
+l2arc_feed_thread(void *dummy __unused)
+{
+ callb_cpr_t cpr;
+ l2arc_dev_t *dev;
+ spa_t *spa;
+ uint64_t size;
+
+ CALLB_CPR_INIT(&cpr, &l2arc_feed_thr_lock, callb_generic_cpr, FTAG);
+
+ mutex_enter(&l2arc_feed_thr_lock);
+
+ while (l2arc_thread_exit == 0) {
+ /*
+ * Pause for l2arc_feed_secs seconds between writes.
+ */
+ CALLB_CPR_SAFE_BEGIN(&cpr);
+ (void) cv_timedwait(&l2arc_feed_thr_cv, &l2arc_feed_thr_lock,
+ hz * l2arc_feed_secs);
+ CALLB_CPR_SAFE_END(&cpr, &l2arc_feed_thr_lock);
+
+ /*
+ * Quick check for L2ARC devices.
+ */
+ mutex_enter(&l2arc_dev_mtx);
+ if (l2arc_ndev == 0) {
+ mutex_exit(&l2arc_dev_mtx);
+ continue;
+ }
+ mutex_exit(&l2arc_dev_mtx);
+
+ /*
+ * This selects the next l2arc device to write to, and in
+ * doing so the next spa to feed from: dev->l2ad_spa. This
+ * will return NULL if there are now no l2arc devices or if
+ * they are all faulted.
+ *
+ * If a device is returned, its spa's config lock is also
+ * held to prevent device removal. l2arc_dev_get_next()
+ * will grab and release l2arc_dev_mtx.
+ */
+ if ((dev = l2arc_dev_get_next()) == NULL)
+ continue;
+
+ spa = dev->l2ad_spa;
+ ASSERT(spa != NULL);
+
+ /*
+ * Avoid contributing to memory pressure.
+ */
+ if (arc_reclaim_needed()) {
+ ARCSTAT_BUMP(arcstat_l2_abort_lowmem);
+ spa_config_exit(spa, SCL_L2ARC, dev);
+ continue;
+ }
+
+ ARCSTAT_BUMP(arcstat_l2_feeds);
+
+ size = dev->l2ad_write;
+ if (arc_warm == B_FALSE)
+ size += dev->l2ad_boost;
+
+ /*
+ * Evict L2ARC buffers that will be overwritten.
+ */
+ l2arc_evict(dev, size, B_FALSE);
+
+ /*
+ * Write ARC buffers.
+ */
+ l2arc_write_buffers(spa, dev, size);
+ spa_config_exit(spa, SCL_L2ARC, dev);
+ }
+
+ l2arc_thread_exit = 0;
+ cv_broadcast(&l2arc_feed_thr_cv);
+ CALLB_CPR_EXIT(&cpr); /* drops l2arc_feed_thr_lock */
+ thread_exit();
+}
+
+boolean_t
+l2arc_vdev_present(vdev_t *vd)
+{
+ l2arc_dev_t *dev;
+
+ mutex_enter(&l2arc_dev_mtx);
+ for (dev = list_head(l2arc_dev_list); dev != NULL;
+ dev = list_next(l2arc_dev_list, dev)) {
+ if (dev->l2ad_vdev == vd)
+ break;
+ }
+ mutex_exit(&l2arc_dev_mtx);
+
+ return (dev != NULL);
+}
+
+/*
+ * Add a vdev for use by the L2ARC. By this point the spa has already
+ * validated the vdev and opened it.
+ */
+void
+l2arc_add_vdev(spa_t *spa, vdev_t *vd, uint64_t start, uint64_t end)
+{
+ l2arc_dev_t *adddev;
+
+ ASSERT(!l2arc_vdev_present(vd));
+
+ /*
+ * Create a new l2arc device entry.
+ */
+ adddev = kmem_zalloc(sizeof (l2arc_dev_t), KM_SLEEP);
+ adddev->l2ad_spa = spa;
+ adddev->l2ad_vdev = vd;
+ adddev->l2ad_write = l2arc_write_max;
+ adddev->l2ad_boost = l2arc_write_boost;
+ adddev->l2ad_start = start;
+ adddev->l2ad_end = end;
+ adddev->l2ad_hand = adddev->l2ad_start;
+ adddev->l2ad_evict = adddev->l2ad_start;
+ adddev->l2ad_first = B_TRUE;
+ ASSERT3U(adddev->l2ad_write, >, 0);
+
+ /*
+ * This is a list of all ARC buffers that are still valid on the
+ * device.
+ */
+ adddev->l2ad_buflist = kmem_zalloc(sizeof (list_t), KM_SLEEP);
+ list_create(adddev->l2ad_buflist, sizeof (arc_buf_hdr_t),
+ offsetof(arc_buf_hdr_t, b_l2node));
+
+ spa_l2cache_space_update(vd, adddev->l2ad_end - adddev->l2ad_hand, 0);
+
+ /*
+ * Add device to global list
+ */
+ mutex_enter(&l2arc_dev_mtx);
+ list_insert_head(l2arc_dev_list, adddev);
+ atomic_inc_64(&l2arc_ndev);
+ mutex_exit(&l2arc_dev_mtx);
+}
+
+/*
+ * Remove a vdev from the L2ARC.
+ */
+void
+l2arc_remove_vdev(vdev_t *vd)
+{
+ l2arc_dev_t *dev, *nextdev, *remdev = NULL;
+
+ /*
+ * Find the device by vdev
+ */
+ mutex_enter(&l2arc_dev_mtx);
+ for (dev = list_head(l2arc_dev_list); dev; dev = nextdev) {
+ nextdev = list_next(l2arc_dev_list, dev);
+ if (vd == dev->l2ad_vdev) {
+ remdev = dev;
+ break;
+ }
+ }
+ ASSERT(remdev != NULL);
+
+ /*
+ * Remove device from global list
+ */
+ list_remove(l2arc_dev_list, remdev);
+ l2arc_dev_last = NULL; /* may have been invalidated */
+ atomic_dec_64(&l2arc_ndev);
+ mutex_exit(&l2arc_dev_mtx);
+
+ /*
+ * Clear all buflists and ARC references. L2ARC device flush.
+ */
+ l2arc_evict(remdev, 0, B_TRUE);
+ list_destroy(remdev->l2ad_buflist);
+ kmem_free(remdev->l2ad_buflist, sizeof (list_t));
+ kmem_free(remdev, sizeof (l2arc_dev_t));
+}
+
+void
+l2arc_init(void)
+{
+ l2arc_thread_exit = 0;
+ l2arc_ndev = 0;
+ l2arc_writes_sent = 0;
+ l2arc_writes_done = 0;
+
+ mutex_init(&l2arc_feed_thr_lock, NULL, MUTEX_DEFAULT, NULL);
+ cv_init(&l2arc_feed_thr_cv, NULL, CV_DEFAULT, NULL);
+ mutex_init(&l2arc_dev_mtx, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&l2arc_buflist_mtx, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&l2arc_free_on_write_mtx, NULL, MUTEX_DEFAULT, NULL);
+
+ l2arc_dev_list = &L2ARC_dev_list;
+ l2arc_free_on_write = &L2ARC_free_on_write;
+ list_create(l2arc_dev_list, sizeof (l2arc_dev_t),
+ offsetof(l2arc_dev_t, l2ad_node));
+ list_create(l2arc_free_on_write, sizeof (l2arc_data_free_t),
+ offsetof(l2arc_data_free_t, l2df_list_node));
+}
+
+void
+l2arc_fini(void)
+{
+ /*
+ * This is called from dmu_fini(), which is called from spa_fini();
+ * Because of this, we can assume that all l2arc devices have
+ * already been removed when the pools themselves were removed.
+ */
+
+ l2arc_do_free_on_write();
+
+ mutex_destroy(&l2arc_feed_thr_lock);
+ cv_destroy(&l2arc_feed_thr_cv);
+ mutex_destroy(&l2arc_dev_mtx);
+ mutex_destroy(&l2arc_buflist_mtx);
+ mutex_destroy(&l2arc_free_on_write_mtx);
+
+ list_destroy(l2arc_dev_list);
+ list_destroy(l2arc_free_on_write);
+}
+
+void
+l2arc_start(void)
+{
+ if (!(spa_mode & FWRITE))
+ return;
+
+ (void) thread_create(NULL, 0, l2arc_feed_thread, NULL, 0, &p0,
+ TS_RUN, minclsyspri);
+}
+
+void
+l2arc_stop(void)
+{
+ if (!(spa_mode & FWRITE))
+ return;
+
+ mutex_enter(&l2arc_feed_thr_lock);
+ cv_signal(&l2arc_feed_thr_cv); /* kick thread out of startup */
+ l2arc_thread_exit = 1;
+ while (l2arc_thread_exit != 0)
+ cv_wait(&l2arc_feed_thr_cv, &l2arc_feed_thr_lock);
+ mutex_exit(&l2arc_feed_thr_lock);
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/bplist.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/bplist.c
index 4442b1f..93b7741 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/bplist.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/bplist.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/bplist.h>
#include <sys/zfs_context.h>
@@ -47,7 +45,7 @@ bplist_create(objset_t *mos, int blocksize, dmu_tx_t *tx)
{
int size;
- size = spa_version(dmu_objset_spa(mos)) < ZFS_VERSION_BPLIST_ACCOUNT ?
+ size = spa_version(dmu_objset_spa(mos)) < SPA_VERSION_BPLIST_ACCOUNT ?
BPLIST_SIZE_V0 : sizeof (bplist_phys_t);
return (dmu_object_alloc(mos, DMU_OT_BPLIST, blocksize,
@@ -181,7 +179,7 @@ bplist_iterate(bplist_t *bpl, uint64_t *itorp, blkptr_t *bp)
}
int
-bplist_enqueue(bplist_t *bpl, blkptr_t *bp, dmu_tx_t *tx)
+bplist_enqueue(bplist_t *bpl, const blkptr_t *bp, dmu_tx_t *tx)
{
uint64_t blk, off;
blkptr_t *bparray;
@@ -229,7 +227,7 @@ bplist_enqueue(bplist_t *bpl, blkptr_t *bp, dmu_tx_t *tx)
* Deferred entry; will be written later by bplist_sync().
*/
void
-bplist_enqueue_deferred(bplist_t *bpl, blkptr_t *bp)
+bplist_enqueue_deferred(bplist_t *bpl, const blkptr_t *bp)
{
bplist_q_t *bpq = kmem_alloc(sizeof (*bpq), KM_SLEEP);
@@ -278,9 +276,7 @@ bplist_vacate(bplist_t *bpl, dmu_tx_t *tx)
int
bplist_space(bplist_t *bpl, uint64_t *usedp, uint64_t *compp, uint64_t *uncompp)
{
- uint64_t itor = 0, comp = 0, uncomp = 0;
int err;
- blkptr_t bp;
mutex_enter(&bpl->bpl_lock);
@@ -298,6 +294,9 @@ bplist_space(bplist_t *bpl, uint64_t *usedp, uint64_t *compp, uint64_t *uncompp)
mutex_exit(&bpl->bpl_lock);
if (!bpl->bpl_havecomp) {
+ uint64_t itor = 0, comp = 0, uncomp = 0;
+ blkptr_t bp;
+
while ((err = bplist_iterate(bpl, &itor, &bp)) == 0) {
comp += BP_GET_PSIZE(&bp);
uncomp += BP_GET_UCSIZE(&bp);
@@ -310,3 +309,41 @@ bplist_space(bplist_t *bpl, uint64_t *usedp, uint64_t *compp, uint64_t *uncompp)
return (err);
}
+
+/*
+ * Return (in *dasizep) the amount of space on the deadlist which is:
+ * mintxg < blk_birth <= maxtxg
+ */
+int
+bplist_space_birthrange(bplist_t *bpl, uint64_t mintxg, uint64_t maxtxg,
+ uint64_t *dasizep)
+{
+ uint64_t size = 0;
+ uint64_t itor = 0;
+ blkptr_t bp;
+ int err;
+
+ /*
+ * As an optimization, if they want the whole txg range, just
+ * get bpl_bytes rather than iterating over the bps.
+ */
+ if (mintxg < TXG_INITIAL && maxtxg == UINT64_MAX) {
+ mutex_enter(&bpl->bpl_lock);
+ err = bplist_hold(bpl);
+ if (err == 0)
+ *dasizep = bpl->bpl_phys->bpl_bytes;
+ mutex_exit(&bpl->bpl_lock);
+ return (err);
+ }
+
+ while ((err = bplist_iterate(bpl, &itor, &bp)) == 0) {
+ if (bp.blk_birth > mintxg && bp.blk_birth <= maxtxg) {
+ size +=
+ bp_get_dasize(dmu_objset_spa(bpl->bpl_mos), &bp);
+ }
+ }
+ if (err == ENOENT)
+ err = 0;
+ *dasizep = size;
+ return (err);
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dbuf.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dbuf.c
index 94c6308..2494c1e 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dbuf.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dbuf.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/zfs_context.h>
#include <sys/dmu.h>
#include <sys/dmu_impl.h>
@@ -39,17 +37,10 @@
static void dbuf_destroy(dmu_buf_impl_t *db);
static int dbuf_undirty(dmu_buf_impl_t *db, dmu_tx_t *tx);
-static void dbuf_write(dbuf_dirty_record_t *dr, arc_buf_t *data, int checksum,
- int compress, dmu_tx_t *tx);
+static void dbuf_write(dbuf_dirty_record_t *dr, arc_buf_t *data, dmu_tx_t *tx);
static arc_done_func_t dbuf_write_ready;
static arc_done_func_t dbuf_write_done;
-int zfs_mdcomp_disable = 0;
-SYSCTL_DECL(_vfs_zfs);
-TUNABLE_INT("vfs.zfs.mdcomp_disable", &zfs_mdcomp_disable);
-SYSCTL_INT(_vfs_zfs, OID_AUTO, mdcomp_disable, CTLFLAG_RDTUN,
- &zfs_mdcomp_disable, 0, "Disable metadata compression");
-
/*
* Global data structures and functions for the dbuf cache.
*/
@@ -311,7 +302,7 @@ dbuf_verify(dmu_buf_impl_t *db)
}
if (db->db_blkid == DB_BONUS_BLKID) {
ASSERT(dn != NULL);
- ASSERT3U(db->db.db_size, ==, dn->dn_bonuslen);
+ ASSERT3U(db->db.db_size, >=, dn->dn_bonuslen);
ASSERT3U(db->db.db_offset, ==, DB_BONUS_BLKID);
} else {
ASSERT3U(db->db.db_offset, ==, db->db_blkid * db->db.db_size);
@@ -460,45 +451,45 @@ dbuf_read_done(zio_t *zio, arc_buf_t *buf, void *vdb)
static void
dbuf_read_impl(dmu_buf_impl_t *db, zio_t *zio, uint32_t *flags)
{
- blkptr_t *bp;
+ dnode_t *dn = db->db_dnode;
zbookmark_t zb;
uint32_t aflags = ARC_NOWAIT;
+ arc_buf_t *pbuf;
ASSERT(!refcount_is_zero(&db->db_holds));
/* We need the struct_rwlock to prevent db_blkptr from changing. */
- ASSERT(RW_LOCK_HELD(&db->db_dnode->dn_struct_rwlock));
+ ASSERT(RW_LOCK_HELD(&dn->dn_struct_rwlock));
ASSERT(MUTEX_HELD(&db->db_mtx));
ASSERT(db->db_state == DB_UNCACHED);
ASSERT(db->db_buf == NULL);
if (db->db_blkid == DB_BONUS_BLKID) {
- ASSERT3U(db->db_dnode->dn_bonuslen, ==, db->db.db_size);
+ int bonuslen = dn->dn_bonuslen;
+
+ ASSERT3U(bonuslen, <=, db->db.db_size);
db->db.db_data = zio_buf_alloc(DN_MAX_BONUSLEN);
- if (db->db.db_size < DN_MAX_BONUSLEN)
+ arc_space_consume(DN_MAX_BONUSLEN);
+ if (bonuslen < DN_MAX_BONUSLEN)
bzero(db->db.db_data, DN_MAX_BONUSLEN);
- bcopy(DN_BONUS(db->db_dnode->dn_phys), db->db.db_data,
- db->db.db_size);
+ bcopy(DN_BONUS(dn->dn_phys), db->db.db_data,
+ bonuslen);
dbuf_update_data(db);
db->db_state = DB_CACHED;
mutex_exit(&db->db_mtx);
return;
}
- if (db->db_level == 0 && dnode_block_freed(db->db_dnode, db->db_blkid))
- bp = NULL;
- else
- bp = db->db_blkptr;
-
- if (bp == NULL)
- dprintf_dbuf(db, "blkptr: %s\n", "NULL");
- else
- dprintf_dbuf_bp(db, bp, "%s", "blkptr:");
-
- if (bp == NULL || BP_IS_HOLE(bp)) {
+ /*
+ * Recheck BP_IS_HOLE() after dnode_block_freed() in case dnode_sync()
+ * processes the delete record and clears the bp while we are waiting
+ * for the dn_mtx (resulting in a "no" from block_freed).
+ */
+ if (db->db_blkptr == NULL || BP_IS_HOLE(db->db_blkptr) ||
+ (db->db_level == 0 && (dnode_block_freed(dn, db->db_blkid) ||
+ BP_IS_HOLE(db->db_blkptr)))) {
arc_buf_contents_t type = DBUF_GET_BUFC_TYPE(db);
- ASSERT(bp == NULL || BP_IS_HOLE(bp));
- dbuf_set_data(db, arc_buf_alloc(db->db_dnode->dn_objset->os_spa,
+ dbuf_set_data(db, arc_buf_alloc(dn->dn_objset->os_spa,
db->db.db_size, db, type));
bzero(db->db.db_data, db->db.db_size);
db->db_state = DB_CACHED;
@@ -510,6 +501,9 @@ dbuf_read_impl(dmu_buf_impl_t *db, zio_t *zio, uint32_t *flags)
db->db_state = DB_READ;
mutex_exit(&db->db_mtx);
+ if (DBUF_IS_L2CACHEABLE(db))
+ aflags |= ARC_L2CACHE;
+
zb.zb_objset = db->db_objset->os_dsl_dataset ?
db->db_objset->os_dsl_dataset->ds_object : 0;
zb.zb_object = db->db.db_object;
@@ -518,10 +512,13 @@ dbuf_read_impl(dmu_buf_impl_t *db, zio_t *zio, uint32_t *flags)
dbuf_add_ref(db, NULL);
/* ZIO_FLAG_CANFAIL callers have to check the parent zio's error */
- ASSERT3U(db->db_dnode->dn_type, <, DMU_OT_NUMTYPES);
- (void) arc_read(zio, db->db_dnode->dn_objset->os_spa, bp,
- db->db_level > 0 ? byteswap_uint64_array :
- dmu_ot[db->db_dnode->dn_type].ot_byteswap,
+
+ if (db->db_parent)
+ pbuf = db->db_parent->db_buf;
+ else
+ pbuf = db->db_objset->os_phys_buf;
+
+ (void) arc_read(zio, dn->dn_objset->os_spa, db->db_blkptr, pbuf,
dbuf_read_done, db, ZIO_PRIORITY_SYNC_READ,
(*flags & DB_RF_CANFAIL) ? ZIO_FLAG_CANFAIL : ZIO_FLAG_MUSTSUCCEED,
&aflags, &zb);
@@ -546,7 +543,8 @@ dbuf_read(dmu_buf_impl_t *db, zio_t *zio, uint32_t flags)
rw_enter(&db->db_dnode->dn_struct_rwlock, RW_READER);
prefetch = db->db_level == 0 && db->db_blkid != DB_BONUS_BLKID &&
- (flags & DB_RF_NOPREFETCH) == 0 && db->db_dnode != NULL;
+ (flags & DB_RF_NOPREFETCH) == 0 && db->db_dnode != NULL &&
+ DBUF_IS_CACHEABLE(db);
mutex_enter(&db->db_mtx);
if (db->db_state == DB_CACHED) {
@@ -661,6 +659,7 @@ dbuf_fix_old_data(dmu_buf_impl_t *db, uint64_t txg)
if (db->db_blkid == DB_BONUS_BLKID) {
/* Note that the data bufs here are zio_bufs */
dr->dt.dl.dr_data = zio_buf_alloc(DN_MAX_BONUSLEN);
+ arc_space_consume(DN_MAX_BONUSLEN);
bcopy(db->db.db_data, dr->dt.dl.dr_data, DN_MAX_BONUSLEN);
} else if (refcount_count(&db->db_holds) > db->db_dirtycnt) {
int size = db->db.db_size;
@@ -690,7 +689,8 @@ dbuf_unoverride(dbuf_dirty_record_t *dr)
/* free this block */
if (!BP_IS_HOLE(&dr->dt.dl.dr_overridden_by)) {
/* XXX can get silent EIO here */
- (void) arc_free(NULL, db->db_dnode->dn_objset->os_spa,
+ (void) dsl_free(NULL,
+ spa_get_dsl(db->db_dnode->dn_objset->os_spa),
txg, &dr->dt.dl.dr_overridden_by, NULL, NULL, ARC_WAIT);
}
dr->dt.dl.dr_override_state = DR_NOT_OVERRIDDEN;
@@ -705,22 +705,50 @@ dbuf_unoverride(dbuf_dirty_record_t *dr)
arc_release(dr->dt.dl.dr_data, db);
}
+/*
+ * Evict (if its unreferenced) or clear (if its referenced) any level-0
+ * data blocks in the free range, so that any future readers will find
+ * empty blocks. Also, if we happen accross any level-1 dbufs in the
+ * range that have not already been marked dirty, mark them dirty so
+ * they stay in memory.
+ */
void
-dbuf_free_range(dnode_t *dn, uint64_t blkid, uint64_t nblks, dmu_tx_t *tx)
+dbuf_free_range(dnode_t *dn, uint64_t start, uint64_t end, dmu_tx_t *tx)
{
dmu_buf_impl_t *db, *db_next;
uint64_t txg = tx->tx_txg;
+ int epbs = dn->dn_indblkshift - SPA_BLKPTRSHIFT;
+ uint64_t first_l1 = start >> epbs;
+ uint64_t last_l1 = end >> epbs;
- dprintf_dnode(dn, "blkid=%llu nblks=%llu\n", blkid, nblks);
+ if (end > dn->dn_maxblkid) {
+ end = dn->dn_maxblkid;
+ last_l1 = end >> epbs;
+ }
+ dprintf_dnode(dn, "start=%llu end=%llu\n", start, end);
mutex_enter(&dn->dn_dbufs_mtx);
for (db = list_head(&dn->dn_dbufs); db; db = db_next) {
db_next = list_next(&dn->dn_dbufs, db);
ASSERT(db->db_blkid != DB_BONUS_BLKID);
+
+ if (db->db_level == 1 &&
+ db->db_blkid >= first_l1 && db->db_blkid <= last_l1) {
+ mutex_enter(&db->db_mtx);
+ if (db->db_last_dirty &&
+ db->db_last_dirty->dr_txg < txg) {
+ dbuf_add_ref(db, FTAG);
+ mutex_exit(&db->db_mtx);
+ dbuf_will_dirty(db, tx);
+ dbuf_rele(db, FTAG);
+ } else {
+ mutex_exit(&db->db_mtx);
+ }
+ }
+
if (db->db_level != 0)
continue;
dprintf_dbuf(db, "found buf %s\n", "");
- if (db->db_blkid < blkid ||
- db->db_blkid >= blkid+nblks)
+ if (db->db_blkid < start || db->db_blkid > end)
continue;
/* found a level 0 buffer in the range */
@@ -783,31 +811,28 @@ dbuf_free_range(dnode_t *dn, uint64_t blkid, uint64_t nblks, dmu_tx_t *tx)
}
static int
-dbuf_new_block(dmu_buf_impl_t *db)
+dbuf_block_freeable(dmu_buf_impl_t *db)
{
dsl_dataset_t *ds = db->db_objset->os_dsl_dataset;
uint64_t birth_txg = 0;
- /* Don't count meta-objects */
- if (ds == NULL)
- return (FALSE);
-
/*
* We don't need any locking to protect db_blkptr:
* If it's syncing, then db_last_dirty will be set
* so we'll ignore db_blkptr.
*/
ASSERT(MUTEX_HELD(&db->db_mtx));
- /* If we have been dirtied since the last snapshot, its not new */
if (db->db_last_dirty)
birth_txg = db->db_last_dirty->dr_txg;
else if (db->db_blkptr)
birth_txg = db->db_blkptr->blk_birth;
+ /* If we don't exist or are in a snapshot, we can't be freed */
if (birth_txg)
- return (!dsl_dataset_block_freeable(ds, birth_txg));
+ return (ds == NULL ||
+ dsl_dataset_block_freeable(ds, birth_txg));
else
- return (TRUE);
+ return (FALSE);
}
void
@@ -865,6 +890,7 @@ dbuf_dirty(dmu_buf_impl_t *db, dmu_tx_t *tx)
objset_impl_t *os = dn->dn_objset;
dbuf_dirty_record_t **drp, *dr;
int drop_struct_lock = FALSE;
+ boolean_t do_free_accounting = B_FALSE;
int txgoff = tx->tx_txg & TXG_MASK;
ASSERT(tx->tx_txg != 0);
@@ -922,20 +948,20 @@ dbuf_dirty(dmu_buf_impl_t *db, dmu_tx_t *tx)
drp = &db->db_last_dirty;
ASSERT(*drp == NULL || (*drp)->dr_txg <= tx->tx_txg ||
db->db.db_object == DMU_META_DNODE_OBJECT);
- while (*drp && (*drp)->dr_txg > tx->tx_txg)
- drp = &(*drp)->dr_next;
- if (*drp && (*drp)->dr_txg == tx->tx_txg) {
+ while ((dr = *drp) != NULL && dr->dr_txg > tx->tx_txg)
+ drp = &dr->dr_next;
+ if (dr && dr->dr_txg == tx->tx_txg) {
if (db->db_level == 0 && db->db_blkid != DB_BONUS_BLKID) {
/*
* If this buffer has already been written out,
* we now need to reset its state.
*/
- dbuf_unoverride(*drp);
+ dbuf_unoverride(dr);
if (db->db.db_object != DMU_META_DNODE_OBJECT)
arc_buf_thaw(db->db_buf);
}
mutex_exit(&db->db_mtx);
- return (*drp);
+ return (dr);
}
/*
@@ -966,6 +992,18 @@ dbuf_dirty(dmu_buf_impl_t *db, dmu_tx_t *tx)
dprintf_dbuf(db, "size=%llx\n", (u_longlong_t)db->db.db_size);
+ if (db->db_blkid != DB_BONUS_BLKID) {
+ /*
+ * Update the accounting.
+ * Note: we delay "free accounting" until after we drop
+ * the db_mtx. This keeps us from grabbing other locks
+ * (and possibly deadlocking) in bp_get_dasize() while
+ * also holding the db_mtx.
+ */
+ dnode_willuse_space(dn, db->db.db_size, tx);
+ do_free_accounting = dbuf_block_freeable(db);
+ }
+
/*
* If this buffer is dirty in an old transaction group we need
* to make a copy of it so that the changes we make in this
@@ -1015,25 +1053,6 @@ dbuf_dirty(dmu_buf_impl_t *db, dmu_tx_t *tx)
db->db_freed_in_flight = FALSE;
}
- if (db->db_blkid != DB_BONUS_BLKID) {
- /*
- * Update the accounting.
- */
- if (!dbuf_new_block(db) && db->db_blkptr) {
- /*
- * This is only a guess -- if the dbuf is dirty
- * in a previous txg, we don't know how much
- * space it will use on disk yet. We should
- * really have the struct_rwlock to access
- * db_blkptr, but since this is just a guess,
- * it's OK if we get an odd answer.
- */
- dnode_willuse_space(dn,
- -bp_get_dasize(os->os_spa, db->db_blkptr), tx);
- }
- dnode_willuse_space(dn, db->db.db_size, tx);
- }
-
/*
* This buffer is now part of this txg
*/
@@ -1050,11 +1069,19 @@ dbuf_dirty(dmu_buf_impl_t *db, dmu_tx_t *tx)
mutex_exit(&dn->dn_mtx);
dnode_setdirty(dn, tx);
return (dr);
- }
-
- if (db->db_level == 0) {
- dnode_new_blkid(dn, db->db_blkid, tx);
- ASSERT(dn->dn_maxblkid >= db->db_blkid);
+ } else if (do_free_accounting) {
+ blkptr_t *bp = db->db_blkptr;
+ int64_t willfree = (bp && !BP_IS_HOLE(bp)) ?
+ bp_get_dasize(os->os_spa, bp) : db->db.db_size;
+ /*
+ * This is only a guess -- if the dbuf is dirty
+ * in a previous txg, we don't know how much
+ * space it will use on disk yet. We should
+ * really have the struct_rwlock to access
+ * db_blkptr, but since this is just a guess,
+ * it's OK if we get an odd answer.
+ */
+ dnode_willuse_space(dn, -willfree, tx);
}
if (!RW_WRITE_HELD(&dn->dn_struct_rwlock)) {
@@ -1062,6 +1089,11 @@ dbuf_dirty(dmu_buf_impl_t *db, dmu_tx_t *tx)
drop_struct_lock = TRUE;
}
+ if (db->db_level == 0) {
+ dnode_new_blkid(dn, db->db_blkid, tx, drop_struct_lock);
+ ASSERT(dn->dn_maxblkid >= db->db_blkid);
+ }
+
if (db->db_level+1 < dn->dn_nlevels) {
dmu_buf_impl_t *parent = db->db_parent;
dbuf_dirty_record_t *di;
@@ -1115,7 +1147,7 @@ dbuf_undirty(dmu_buf_impl_t *db, dmu_tx_t *tx)
{
dnode_t *dn = db->db_dnode;
uint64_t txg = tx->tx_txg;
- dbuf_dirty_record_t *dr;
+ dbuf_dirty_record_t *dr, **drp;
ASSERT(txg != 0);
ASSERT(db->db_blkid != DB_BONUS_BLKID);
@@ -1125,7 +1157,7 @@ dbuf_undirty(dmu_buf_impl_t *db, dmu_tx_t *tx)
/*
* If this buffer is not dirty, we're done.
*/
- for (dr = db->db_last_dirty; dr; dr = dr->dr_next)
+ for (drp = &db->db_last_dirty; (dr = *drp) != NULL; drp = &dr->dr_next)
if (dr->dr_txg <= txg)
break;
if (dr == NULL || dr->dr_txg < txg) {
@@ -1155,14 +1187,14 @@ dbuf_undirty(dmu_buf_impl_t *db, dmu_tx_t *tx)
/* XXX would be nice to fix up dn_towrite_space[] */
- db->db_last_dirty = dr->dr_next;
+ *drp = dr->dr_next;
if (dr->dr_parent) {
mutex_enter(&dr->dr_parent->dt.di.dr_mtx);
list_remove(&dr->dr_parent->dt.di.dr_children, dr);
mutex_exit(&dr->dr_parent->dt.di.dr_mtx);
} else if (db->db_level+1 == dn->dn_nlevels) {
- ASSERT3P(db->db_parent, ==, dn->dn_dbuf);
+ ASSERT(db->db_blkptr == NULL || db->db_parent == dn->dn_dbuf);
mutex_enter(&dn->dn_mtx);
list_remove(&dn->dn_dirty_records[txg & TXG_MASK], dr);
mutex_exit(&dn->dn_mtx);
@@ -1178,8 +1210,8 @@ dbuf_undirty(dmu_buf_impl_t *db, dmu_tx_t *tx)
} else {
ASSERT(db->db_buf != NULL);
ASSERT(list_head(&dr->dt.di.dr_children) == NULL);
- list_destroy(&dr->dt.di.dr_children);
mutex_destroy(&dr->dt.di.dr_mtx);
+ list_destroy(&dr->dt.di.dr_children);
}
kmem_free(dr, sizeof (dbuf_dirty_record_t));
@@ -1204,7 +1236,7 @@ dbuf_undirty(dmu_buf_impl_t *db, dmu_tx_t *tx)
void
dbuf_will_dirty(dmu_buf_impl_t *db, dmu_tx_t *tx)
{
- int rf = DB_RF_MUST_SUCCEED;
+ int rf = DB_RF_MUST_SUCCEED | DB_RF_NOPREFETCH;
ASSERT(tx->tx_txg != 0);
ASSERT(!refcount_is_zero(&db->db_holds));
@@ -1282,8 +1314,10 @@ dbuf_clear(dmu_buf_impl_t *db)
if (db->db_state == DB_CACHED) {
ASSERT(db->db.db_data != NULL);
- if (db->db_blkid == DB_BONUS_BLKID)
+ if (db->db_blkid == DB_BONUS_BLKID) {
zio_buf_free(db->db.db_data, DN_MAX_BONUSLEN);
+ arc_space_return(DN_MAX_BONUSLEN);
+ }
db->db.db_data = NULL;
db->db_state = DB_UNCACHED;
}
@@ -1297,6 +1331,7 @@ dbuf_clear(dmu_buf_impl_t *db)
if (db->db_blkid != DB_BONUS_BLKID && MUTEX_HELD(&dn->dn_dbufs_mtx)) {
list_remove(&dn->dn_dbufs, db);
dnode_rele(dn, db);
+ db->db_dnode = NULL;
}
if (db->db_buf)
@@ -1397,10 +1432,13 @@ dbuf_create(dnode_t *dn, uint8_t level, uint64_t blkid,
if (blkid == DB_BONUS_BLKID) {
ASSERT3P(parent, ==, dn->dn_dbuf);
- db->db.db_size = dn->dn_bonuslen;
+ db->db.db_size = DN_MAX_BONUSLEN -
+ (dn->dn_nblkptr-1) * sizeof (blkptr_t);
+ ASSERT3U(db->db.db_size, >=, dn->dn_bonuslen);
db->db.db_offset = DB_BONUS_BLKID;
db->db_state = DB_UNCACHED;
/* the bonus dbuf is not placed in the hash table */
+ arc_space_consume(sizeof (dmu_buf_impl_t));
return (db);
} else {
int blocksize =
@@ -1427,6 +1465,7 @@ dbuf_create(dnode_t *dn, uint8_t level, uint64_t blkid,
list_insert_head(&dn->dn_dbufs, db);
db->db_state = DB_UNCACHED;
mutex_exit(&dn->dn_dbufs_mtx);
+ arc_space_consume(sizeof (dmu_buf_impl_t));
if (parent && parent != dn->dn_dbuf)
dbuf_add_ref(parent, db);
@@ -1469,31 +1508,33 @@ dbuf_destroy(dmu_buf_impl_t *db)
ASSERT(refcount_is_zero(&db->db_holds));
if (db->db_blkid != DB_BONUS_BLKID) {
- dnode_t *dn = db->db_dnode;
-
/*
* If this dbuf is still on the dn_dbufs list,
* remove it from that list.
*/
- if (list_link_active(&db->db_link)) {
+ if (db->db_dnode) {
+ dnode_t *dn = db->db_dnode;
+
mutex_enter(&dn->dn_dbufs_mtx);
list_remove(&dn->dn_dbufs, db);
mutex_exit(&dn->dn_dbufs_mtx);
dnode_rele(dn, db);
+ db->db_dnode = NULL;
}
dbuf_hash_remove(db);
}
db->db_parent = NULL;
- db->db_dnode = NULL;
db->db_buf = NULL;
+ ASSERT(!list_link_active(&db->db_link));
ASSERT(db->db.db_data == NULL);
ASSERT(db->db_hash_next == NULL);
ASSERT(db->db_blkptr == NULL);
ASSERT(db->db_data_pending == NULL);
kmem_cache_free(dbuf_cache, db);
+ arc_space_return(sizeof (dmu_buf_impl_t));
}
void
@@ -1525,6 +1566,7 @@ dbuf_prefetch(dnode_t *dn, uint64_t blkid)
if (dbuf_findbp(dn, 0, blkid, TRUE, &db, &bp) == 0) {
if (bp && !BP_IS_HOLE(bp)) {
+ arc_buf_t *pbuf;
uint32_t aflags = ARC_NOWAIT | ARC_PREFETCH;
zbookmark_t zb;
zb.zb_objset = dn->dn_objset->os_dsl_dataset ?
@@ -1533,9 +1575,13 @@ dbuf_prefetch(dnode_t *dn, uint64_t blkid)
zb.zb_level = 0;
zb.zb_blkid = blkid;
- (void) arc_read(NULL, dn->dn_objset->os_spa, bp,
- dmu_ot[dn->dn_type].ot_byteswap,
- NULL, NULL, ZIO_PRIORITY_ASYNC_READ,
+ if (db)
+ pbuf = db->db_buf;
+ else
+ pbuf = dn->dn_objset->os_phys_buf;
+
+ (void) arc_read(NULL, dn->dn_objset->os_spa,
+ bp, pbuf, NULL, NULL, ZIO_PRIORITY_ASYNC_READ,
ZIO_FLAG_CANFAIL | ZIO_FLAG_SPECULATIVE,
&aflags, &zb);
}
@@ -1652,16 +1698,13 @@ dbuf_hold_level(dnode_t *dn, int level, uint64_t blkid, void *tag)
return (err ? NULL : db);
}
-dmu_buf_impl_t *
+void
dbuf_create_bonus(dnode_t *dn)
{
- dmu_buf_impl_t *db = dn->dn_bonus;
-
ASSERT(RW_WRITE_HELD(&dn->dn_struct_rwlock));
ASSERT(dn->dn_bonus == NULL);
- db = dbuf_create(dn, 0, DB_BONUS_BLKID, dn->dn_dbuf, NULL);
- return (db);
+ dn->dn_bonus = dbuf_create(dn, 0, DB_BONUS_BLKID, dn->dn_dbuf, NULL);
}
#pragma weak dmu_buf_add_ref = dbuf_add_ref
@@ -1716,7 +1759,10 @@ dbuf_rele(dmu_buf_impl_t *db, void *tag)
dbuf_evict(db);
} else {
VERIFY(arc_buf_remove_ref(db->db_buf, db) == 0);
- mutex_exit(&db->db_mtx);
+ if (!DBUF_IS_CACHEABLE(db))
+ dbuf_clear(db);
+ else
+ mutex_exit(&db->db_mtx);
}
} else {
mutex_exit(&db->db_mtx);
@@ -1852,15 +1898,8 @@ dbuf_sync_indirect(dbuf_dirty_record_t *dr, dmu_tx_t *tx)
db->db_data_pending = dr;
- arc_release(db->db_buf, db);
mutex_exit(&db->db_mtx);
-
- /*
- * XXX -- we should design a compression algorithm
- * that specializes in arrays of bps.
- */
- dbuf_write(dr, db->db_buf, ZIO_CHECKSUM_FLETCHER_4,
- zfs_mdcomp_disable ? ZIO_COMPRESS_EMPTY : ZIO_COMPRESS_LZJB, tx);
+ dbuf_write(dr, db->db_buf, tx);
zio = dr->dr_zio;
mutex_enter(&dr->dt.di.dr_mtx);
@@ -1878,7 +1917,6 @@ dbuf_sync_leaf(dbuf_dirty_record_t *dr, dmu_tx_t *tx)
dnode_t *dn = db->db_dnode;
objset_impl_t *os = dn->dn_objset;
uint64_t txg = tx->tx_txg;
- int checksum, compress;
int blksz;
ASSERT(dmu_tx_is_syncing(tx));
@@ -1909,23 +1947,21 @@ dbuf_sync_leaf(dbuf_dirty_record_t *dr, dmu_tx_t *tx)
*/
if (db->db_blkid == DB_BONUS_BLKID) {
dbuf_dirty_record_t **drp;
- /*
- * Use dn_phys->dn_bonuslen since db.db_size is the length
- * of the bonus buffer in the open transaction rather than
- * the syncing transaction.
- */
+
ASSERT(*datap != NULL);
ASSERT3U(db->db_level, ==, 0);
ASSERT3U(dn->dn_phys->dn_bonuslen, <=, DN_MAX_BONUSLEN);
bcopy(*datap, DN_BONUS(dn->dn_phys), dn->dn_phys->dn_bonuslen);
- if (*datap != db->db.db_data)
+ if (*datap != db->db.db_data) {
zio_buf_free(*datap, DN_MAX_BONUSLEN);
+ arc_space_return(DN_MAX_BONUSLEN);
+ }
db->db_data_pending = NULL;
drp = &db->db_last_dirty;
while (*drp != dr)
drp = &(*drp)->dr_next;
- ASSERT((*drp)->dr_next == NULL);
- *drp = NULL;
+ ASSERT(dr->dr_next == NULL);
+ *drp = dr->dr_next;
if (dr->dr_dbuf->db_level != 0) {
list_destroy(&dr->dt.di.dr_children);
mutex_destroy(&dr->dt.di.dr_mtx);
@@ -1939,6 +1975,14 @@ dbuf_sync_leaf(dbuf_dirty_record_t *dr, dmu_tx_t *tx)
}
/*
+ * This function may have dropped the db_mtx lock allowing a dmu_sync
+ * operation to sneak in. As a result, we need to ensure that we
+ * don't check the dr_override_state until we have returned from
+ * dbuf_check_blkptr.
+ */
+ dbuf_check_blkptr(dn, db);
+
+ /*
* If this buffer is in the middle of an immdiate write,
* wait for the synchronous IO to complete.
*/
@@ -1948,8 +1992,6 @@ dbuf_sync_leaf(dbuf_dirty_record_t *dr, dmu_tx_t *tx)
ASSERT(dr->dt.dl.dr_override_state != DR_NOT_OVERRIDDEN);
}
- dbuf_check_blkptr(dn, db);
-
/*
* If this dbuf has already been written out via an immediate write,
* just complete the write by copying over the new block pointer and
@@ -1963,6 +2005,7 @@ dbuf_sync_leaf(dbuf_dirty_record_t *dr, dmu_tx_t *tx)
zio_fake.io_bp = db->db_blkptr;
zio_fake.io_bp_orig = *db->db_blkptr;
zio_fake.io_txg = txg;
+ zio_fake.io_flags = 0;
*db->db_blkptr = dr->dt.dl.dr_overridden_by;
dr->dt.dl.dr_override_state = DR_NOT_OVERRIDDEN;
@@ -1970,8 +2013,12 @@ dbuf_sync_leaf(dbuf_dirty_record_t *dr, dmu_tx_t *tx)
dr->dr_zio = &zio_fake;
mutex_exit(&db->db_mtx);
+ ASSERT(!DVA_EQUAL(BP_IDENTITY(zio_fake.io_bp),
+ BP_IDENTITY(&zio_fake.io_bp_orig)) ||
+ BP_IS_HOLE(zio_fake.io_bp));
+
if (BP_IS_OLDER(&zio_fake.io_bp_orig, txg))
- dsl_dataset_block_kill(os->os_dsl_dataset,
+ (void) dsl_dataset_block_kill(os->os_dsl_dataset,
&zio_fake.io_bp_orig, dn->dn_zio, tx);
dbuf_write_ready(&zio_fake, db->db_buf, db);
@@ -1997,14 +2044,6 @@ dbuf_sync_leaf(dbuf_dirty_record_t *dr, dmu_tx_t *tx)
*datap = arc_buf_alloc(os->os_spa, blksz, db, type);
bcopy(db->db.db_data, (*datap)->b_data, blksz);
}
- } else {
- /*
- * Private object buffers are released here rather
- * than in dbuf_dirty() since they are only modified
- * in the syncing context and we don't want the
- * overhead of making multiple copies of the data.
- */
- arc_release(db->db_buf, db);
}
ASSERT(*datap != NULL);
@@ -2012,22 +2051,7 @@ dbuf_sync_leaf(dbuf_dirty_record_t *dr, dmu_tx_t *tx)
mutex_exit(&db->db_mtx);
- /*
- * Allow dnode settings to override objset settings,
- * except for metadata checksums.
- */
- if (dmu_ot[dn->dn_type].ot_metadata) {
- checksum = os->os_md_checksum;
- compress = zio_compress_select(dn->dn_compress,
- os->os_md_compress);
- } else {
- checksum = zio_checksum_select(dn->dn_checksum,
- os->os_checksum);
- compress = zio_compress_select(dn->dn_compress,
- os->os_compress);
- }
-
- dbuf_write(dr, *datap, checksum, compress, tx);
+ dbuf_write(dr, *datap, tx);
ASSERT(!list_link_active(&dr->dr_dirty_node));
if (dn->dn_object == DMU_META_DNODE_OBJECT)
@@ -2063,8 +2087,7 @@ dbuf_sync_list(list_t *list, dmu_tx_t *tx)
}
static void
-dbuf_write(dbuf_dirty_record_t *dr, arc_buf_t *data, int checksum,
- int compress, dmu_tx_t *tx)
+dbuf_write(dbuf_dirty_record_t *dr, arc_buf_t *data, dmu_tx_t *tx)
{
dmu_buf_impl_t *db = dr->dr_dbuf;
dnode_t *dn = db->db_dnode;
@@ -2072,8 +2095,23 @@ dbuf_write(dbuf_dirty_record_t *dr, arc_buf_t *data, int checksum,
dmu_buf_impl_t *parent = db->db_parent;
uint64_t txg = tx->tx_txg;
zbookmark_t zb;
+ writeprops_t wp = { 0 };
zio_t *zio;
- int zio_flags;
+
+ if (!BP_IS_HOLE(db->db_blkptr) &&
+ (db->db_level > 0 || dn->dn_type == DMU_OT_DNODE)) {
+ /*
+ * Private object buffers are released here rather
+ * than in dbuf_dirty() since they are only modified
+ * in the syncing context and we don't want the
+ * overhead of making multiple copies of the data.
+ */
+ arc_release(data, db);
+ } else {
+ ASSERT(arc_released(data));
+ /* XXX why do we need to thaw here? */
+ arc_buf_thaw(data);
+ }
if (parent != dn->dn_dbuf) {
ASSERT(parent && parent->db_data_pending);
@@ -2096,17 +2134,22 @@ dbuf_write(dbuf_dirty_record_t *dr, arc_buf_t *data, int checksum,
zb.zb_level = db->db_level;
zb.zb_blkid = db->db_blkid;
- zio_flags = ZIO_FLAG_MUSTSUCCEED;
- if (dmu_ot[dn->dn_type].ot_metadata || zb.zb_level != 0)
- zio_flags |= ZIO_FLAG_METADATA;
+ wp.wp_type = dn->dn_type;
+ wp.wp_level = db->db_level;
+ wp.wp_copies = os->os_copies;
+ wp.wp_dncompress = dn->dn_compress;
+ wp.wp_oscompress = os->os_compress;
+ wp.wp_dnchecksum = dn->dn_checksum;
+ wp.wp_oschecksum = os->os_checksum;
+
if (BP_IS_OLDER(db->db_blkptr, txg))
- dsl_dataset_block_kill(
+ (void) dsl_dataset_block_kill(
os->os_dsl_dataset, db->db_blkptr, zio, tx);
- dr->dr_zio = arc_write(zio, os->os_spa, checksum, compress,
- dmu_get_replication_level(os, &zb, dn->dn_type), txg,
- db->db_blkptr, data, dbuf_write_ready, dbuf_write_done, db,
- ZIO_PRIORITY_ASYNC_WRITE, zio_flags, &zb);
+ dr->dr_zio = arc_write(zio, os->os_spa, &wp,
+ DBUF_IS_L2CACHEABLE(db), txg, db->db_blkptr,
+ data, dbuf_write_ready, dbuf_write_done, db,
+ ZIO_PRIORITY_ASYNC_WRITE, ZIO_FLAG_MUSTSUCCEED, &zb);
}
/* ARGSUSED */
@@ -2116,27 +2159,33 @@ dbuf_write_ready(zio_t *zio, arc_buf_t *buf, void *vdb)
dmu_buf_impl_t *db = vdb;
dnode_t *dn = db->db_dnode;
objset_impl_t *os = dn->dn_objset;
+ blkptr_t *bp = zio->io_bp;
blkptr_t *bp_orig = &zio->io_bp_orig;
uint64_t fill = 0;
int old_size, new_size, i;
+ ASSERT(db->db_blkptr == bp);
+
dprintf_dbuf_bp(db, bp_orig, "bp_orig: %s", "");
old_size = bp_get_dasize(os->os_spa, bp_orig);
- new_size = bp_get_dasize(os->os_spa, zio->io_bp);
+ new_size = bp_get_dasize(os->os_spa, bp);
- dnode_diduse_space(dn, new_size-old_size);
+ dnode_diduse_space(dn, new_size - old_size);
- if (BP_IS_HOLE(zio->io_bp)) {
+ if (BP_IS_HOLE(bp)) {
dsl_dataset_t *ds = os->os_dsl_dataset;
dmu_tx_t *tx = os->os_synctx;
if (bp_orig->blk_birth == tx->tx_txg)
- dsl_dataset_block_kill(ds, bp_orig, NULL, tx);
- ASSERT3U(db->db_blkptr->blk_fill, ==, 0);
+ (void) dsl_dataset_block_kill(ds, bp_orig, zio, tx);
+ ASSERT3U(bp->blk_fill, ==, 0);
return;
}
+ ASSERT(BP_GET_TYPE(bp) == dn->dn_type);
+ ASSERT(BP_GET_LEVEL(bp) == db->db_level);
+
mutex_enter(&db->db_mtx);
if (db->db_level == 0) {
@@ -2156,32 +2205,31 @@ dbuf_write_ready(zio_t *zio, arc_buf_t *buf, void *vdb)
fill = 1;
}
} else {
- blkptr_t *bp = db->db.db_data;
+ blkptr_t *ibp = db->db.db_data;
ASSERT3U(db->db.db_size, ==, 1<<dn->dn_phys->dn_indblkshift);
- for (i = db->db.db_size >> SPA_BLKPTRSHIFT; i > 0; i--, bp++) {
- if (BP_IS_HOLE(bp))
+ for (i = db->db.db_size >> SPA_BLKPTRSHIFT; i > 0; i--, ibp++) {
+ if (BP_IS_HOLE(ibp))
continue;
- ASSERT3U(BP_GET_LSIZE(bp), ==,
+ ASSERT3U(BP_GET_LSIZE(ibp), ==,
db->db_level == 1 ? dn->dn_datablksz :
(1<<dn->dn_phys->dn_indblkshift));
- fill += bp->blk_fill;
+ fill += ibp->blk_fill;
}
}
- db->db_blkptr->blk_fill = fill;
- BP_SET_TYPE(db->db_blkptr, dn->dn_type);
- BP_SET_LEVEL(db->db_blkptr, db->db_level);
+ bp->blk_fill = fill;
mutex_exit(&db->db_mtx);
- /* We must do this after we've set the bp's type and level */
- if (!DVA_EQUAL(BP_IDENTITY(zio->io_bp), BP_IDENTITY(bp_orig))) {
+ if (zio->io_flags & ZIO_FLAG_IO_REWRITE) {
+ ASSERT(DVA_EQUAL(BP_IDENTITY(bp), BP_IDENTITY(bp_orig)));
+ } else {
dsl_dataset_t *ds = os->os_dsl_dataset;
dmu_tx_t *tx = os->os_synctx;
if (bp_orig->blk_birth == tx->tx_txg)
- dsl_dataset_block_kill(ds, bp_orig, NULL, tx);
- dsl_dataset_block_born(ds, zio->io_bp, tx);
+ (void) dsl_dataset_block_kill(ds, bp_orig, zio, tx);
+ dsl_dataset_block_born(ds, bp, tx);
}
}
@@ -2198,13 +2246,12 @@ dbuf_write_done(zio_t *zio, arc_buf_t *buf, void *vdb)
mutex_enter(&db->db_mtx);
drp = &db->db_last_dirty;
- while (*drp != db->db_data_pending)
- drp = &(*drp)->dr_next;
- ASSERT(!list_link_active(&(*drp)->dr_dirty_node));
- ASSERT((*drp)->dr_txg == txg);
- ASSERT((*drp)->dr_next == NULL);
- dr = *drp;
- *drp = NULL;
+ while ((dr = *drp) != db->db_data_pending)
+ drp = &dr->dr_next;
+ ASSERT(!list_link_active(&dr->dr_dirty_node));
+ ASSERT(dr->dr_txg == txg);
+ ASSERT(dr->dr_next == NULL);
+ *drp = dr->dr_next;
if (db->db_level == 0) {
ASSERT(db->db_blkid != DB_BONUS_BLKID);
@@ -2230,8 +2277,8 @@ dbuf_write_done(zio_t *zio, arc_buf_t *buf, void *vdb)
>> (db->db_level * epbs), >=, db->db_blkid);
arc_set_callback(db->db_buf, dbuf_do_evict, db);
}
- list_destroy(&dr->dt.di.dr_children);
mutex_destroy(&dr->dt.di.dr_mtx);
+ list_destroy(&dr->dt.di.dr_children);
}
kmem_free(dr, sizeof (dbuf_dirty_record_t));
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu.c
index d3be6b4..377efb9 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/dmu.h>
#include <sys/dmu_impl.h>
#include <sys/dmu_tx.h>
@@ -42,6 +40,7 @@
#include <sys/zfs_ioctl.h>
#include <sys/zap.h>
#include <sys/zio_checksum.h>
+#include <sys/zfs_znode.h>
const dmu_object_type_info_t dmu_ot[DMU_OT_NUMTYPES] = {
{ byteswap_uint8_array, TRUE, "unallocated" },
@@ -62,7 +61,7 @@ const dmu_object_type_info_t dmu_ot[DMU_OT_NUMTYPES] = {
{ zap_byteswap, TRUE, "DSL props" },
{ byteswap_uint64_array, TRUE, "DSL dataset" },
{ zfs_znode_byteswap, TRUE, "ZFS znode" },
- { zfs_acl_byteswap, TRUE, "ZFS ACL" },
+ { zfs_oldacl_byteswap, TRUE, "ZFS V0 ACL" },
{ byteswap_uint8_array, FALSE, "ZFS plain file" },
{ zap_byteswap, TRUE, "ZFS directory" },
{ zap_byteswap, TRUE, "ZFS master node" },
@@ -75,7 +74,14 @@ const dmu_object_type_info_t dmu_ot[DMU_OT_NUMTYPES] = {
{ zap_byteswap, TRUE, "persistent error log" },
{ byteswap_uint8_array, TRUE, "SPA history" },
{ byteswap_uint64_array, TRUE, "SPA history offsets" },
- { zap_byteswap, TRUE, "Pool properties" },
+ { zap_byteswap, TRUE, "Pool properties" },
+ { zap_byteswap, TRUE, "DSL permissions" },
+ { zfs_acl_byteswap, TRUE, "ZFS ACL" },
+ { byteswap_uint8_array, TRUE, "ZFS SYSACL" },
+ { byteswap_uint8_array, TRUE, "FUID table" },
+ { byteswap_uint64_array, TRUE, "FUID table size" },
+ { zap_byteswap, TRUE, "DSL dataset next clones"},
+ { zap_byteswap, TRUE, "scrub work queue" },
};
int
@@ -115,6 +121,19 @@ dmu_bonus_max(void)
return (DN_MAX_BONUSLEN);
}
+int
+dmu_set_bonus(dmu_buf_t *db, int newsize, dmu_tx_t *tx)
+{
+ dnode_t *dn = ((dmu_buf_impl_t *)db)->db_dnode;
+
+ if (dn->dn_bonus != (dmu_buf_impl_t *)db)
+ return (EINVAL);
+ if (newsize < 0 || newsize > db->db_size)
+ return (EINVAL);
+ dnode_setbonuslen(dn, newsize, tx);
+ return (0);
+}
+
/*
* returns ENOENT, EIO, or 0.
*/
@@ -122,27 +141,27 @@ int
dmu_bonus_hold(objset_t *os, uint64_t object, void *tag, dmu_buf_t **dbp)
{
dnode_t *dn;
- int err, count;
dmu_buf_impl_t *db;
+ int error;
- err = dnode_hold(os->os, object, FTAG, &dn);
- if (err)
- return (err);
+ error = dnode_hold(os->os, object, FTAG, &dn);
+ if (error)
+ return (error);
rw_enter(&dn->dn_struct_rwlock, RW_READER);
if (dn->dn_bonus == NULL) {
rw_exit(&dn->dn_struct_rwlock);
rw_enter(&dn->dn_struct_rwlock, RW_WRITER);
if (dn->dn_bonus == NULL)
- dn->dn_bonus = dbuf_create_bonus(dn);
+ dbuf_create_bonus(dn);
}
db = dn->dn_bonus;
rw_exit(&dn->dn_struct_rwlock);
- mutex_enter(&db->db_mtx);
- count = refcount_add(&db->db_holds, tag);
- mutex_exit(&db->db_mtx);
- if (count == 1)
- dnode_add_ref(dn, db);
+
+ /* as long as the bonus buf is held, the dnode will be held */
+ if (refcount_add(&db->db_holds, tag) == 1)
+ VERIFY(dnode_add_ref(dn, db));
+
dnode_rele(dn, FTAG);
VERIFY(0 == dbuf_read(db, NULL, DB_RF_MUST_SUCCEED));
@@ -161,11 +180,13 @@ static int
dmu_buf_hold_array_by_dnode(dnode_t *dn, uint64_t offset,
uint64_t length, int read, void *tag, int *numbufsp, dmu_buf_t ***dbpp)
{
+ dsl_pool_t *dp = NULL;
dmu_buf_t **dbp;
uint64_t blkid, nblks, i;
uint32_t flags;
int err;
zio_t *zio;
+ hrtime_t start;
ASSERT(length <= DMU_MAX_ACCESS);
@@ -192,7 +213,11 @@ dmu_buf_hold_array_by_dnode(dnode_t *dn, uint64_t offset,
}
dbp = kmem_zalloc(sizeof (dmu_buf_t *) * nblks, KM_SLEEP);
- zio = zio_root(dn->dn_objset->os_spa, NULL, NULL, TRUE);
+ if (dn->dn_objset->os_dsl_dataset)
+ dp = dn->dn_objset->os_dsl_dataset->ds_dir->dd_pool;
+ if (dp && dsl_pool_sync_context(dp))
+ start = gethrtime();
+ zio = zio_root(dn->dn_objset->os_spa, NULL, NULL, ZIO_FLAG_CANFAIL);
blkid = dbuf_whichblock(dn, offset);
for (i = 0; i < nblks; i++) {
dmu_buf_impl_t *db = dbuf_hold(dn, blkid+i, tag);
@@ -214,6 +239,9 @@ dmu_buf_hold_array_by_dnode(dnode_t *dn, uint64_t offset,
/* wait for async i/o */
err = zio_wait(zio);
+ /* track read overhead when we are in sync context */
+ if (dp && dsl_pool_sync_context(dp))
+ dp->dp_read_overhead += gethrtime() - start;
if (err) {
dmu_buf_rele_array(dbp, nblks, tag);
return (err);
@@ -343,6 +371,155 @@ dmu_prefetch(objset_t *os, uint64_t object, uint64_t offset, uint64_t len)
dnode_rele(dn, FTAG);
}
+static int
+get_next_chunk(dnode_t *dn, uint64_t *offset, uint64_t limit)
+{
+ uint64_t len = *offset - limit;
+ uint64_t chunk_len = dn->dn_datablksz * DMU_MAX_DELETEBLKCNT;
+ uint64_t subchunk =
+ dn->dn_datablksz * EPB(dn->dn_indblkshift, SPA_BLKPTRSHIFT);
+
+ ASSERT(limit <= *offset);
+
+ if (len <= chunk_len) {
+ *offset = limit;
+ return (0);
+ }
+
+ ASSERT(ISP2(subchunk));
+
+ while (*offset > limit) {
+ uint64_t initial_offset = P2ROUNDUP(*offset, subchunk);
+ uint64_t delta;
+ int err;
+
+ /* skip over allocated data */
+ err = dnode_next_offset(dn,
+ DNODE_FIND_HOLE|DNODE_FIND_BACKWARDS, offset, 1, 1, 0);
+ if (err == ESRCH)
+ *offset = limit;
+ else if (err)
+ return (err);
+
+ ASSERT3U(*offset, <=, initial_offset);
+ *offset = P2ALIGN(*offset, subchunk);
+ delta = initial_offset - *offset;
+ if (delta >= chunk_len) {
+ *offset += delta - chunk_len;
+ return (0);
+ }
+ chunk_len -= delta;
+
+ /* skip over unallocated data */
+ err = dnode_next_offset(dn,
+ DNODE_FIND_BACKWARDS, offset, 1, 1, 0);
+ if (err == ESRCH)
+ *offset = limit;
+ else if (err)
+ return (err);
+
+ if (*offset < limit)
+ *offset = limit;
+ ASSERT3U(*offset, <, initial_offset);
+ }
+ return (0);
+}
+
+static int
+dmu_free_long_range_impl(objset_t *os, dnode_t *dn, uint64_t offset,
+ uint64_t length, boolean_t free_dnode)
+{
+ dmu_tx_t *tx;
+ uint64_t object_size, start, end, len;
+ boolean_t trunc = (length == DMU_OBJECT_END);
+ int align, err;
+
+ align = 1 << dn->dn_datablkshift;
+ ASSERT(align > 0);
+ object_size = align == 1 ? dn->dn_datablksz :
+ (dn->dn_maxblkid + 1) << dn->dn_datablkshift;
+
+ if (trunc || (end = offset + length) > object_size)
+ end = object_size;
+ if (end <= offset)
+ return (0);
+ length = end - offset;
+
+ while (length) {
+ start = end;
+ err = get_next_chunk(dn, &start, offset);
+ if (err)
+ return (err);
+ len = trunc ? DMU_OBJECT_END : end - start;
+
+ tx = dmu_tx_create(os);
+ dmu_tx_hold_free(tx, dn->dn_object, start, len);
+ err = dmu_tx_assign(tx, TXG_WAIT);
+ if (err) {
+ dmu_tx_abort(tx);
+ return (err);
+ }
+
+ dnode_free_range(dn, start, trunc ? -1 : len, tx);
+
+ if (start == 0 && free_dnode) {
+ ASSERT(trunc);
+ dnode_free(dn, tx);
+ }
+
+ length -= end - start;
+
+ dmu_tx_commit(tx);
+ end = start;
+ }
+ return (0);
+}
+
+int
+dmu_free_long_range(objset_t *os, uint64_t object,
+ uint64_t offset, uint64_t length)
+{
+ dnode_t *dn;
+ int err;
+
+ err = dnode_hold(os->os, object, FTAG, &dn);
+ if (err != 0)
+ return (err);
+ err = dmu_free_long_range_impl(os, dn, offset, length, FALSE);
+ dnode_rele(dn, FTAG);
+ return (err);
+}
+
+int
+dmu_free_object(objset_t *os, uint64_t object)
+{
+ dnode_t *dn;
+ dmu_tx_t *tx;
+ int err;
+
+ err = dnode_hold_impl(os->os, object, DNODE_MUST_BE_ALLOCATED,
+ FTAG, &dn);
+ if (err != 0)
+ return (err);
+ if (dn->dn_nlevels == 1) {
+ tx = dmu_tx_create(os);
+ dmu_tx_hold_bonus(tx, object);
+ dmu_tx_hold_free(tx, dn->dn_object, 0, DMU_OBJECT_END);
+ err = dmu_tx_assign(tx, TXG_WAIT);
+ if (err == 0) {
+ dnode_free_range(dn, 0, DMU_OBJECT_END, tx);
+ dnode_free(dn, tx);
+ dmu_tx_commit(tx);
+ } else {
+ dmu_tx_abort(tx);
+ }
+ } else {
+ err = dmu_free_long_range_impl(os, dn, 0, DMU_OBJECT_END, TRUE);
+ }
+ dnode_rele(dn, FTAG);
+ return (err);
+}
+
int
dmu_free_range(objset_t *os, uint64_t object, uint64_t offset,
uint64_t size, dmu_tx_t *tx)
@@ -384,7 +561,6 @@ dmu_read(objset_t *os, uint64_t object, uint64_t offset, uint64_t size,
while (size > 0) {
uint64_t mylen = MIN(size, DMU_MAX_ACCESS / 2);
- int err;
/*
* NB: we could do this block-at-a-time, but it's nice
@@ -393,7 +569,7 @@ dmu_read(objset_t *os, uint64_t object, uint64_t offset, uint64_t size,
err = dmu_buf_hold_array_by_dnode(dn, offset, mylen,
TRUE, FTAG, &numbufs, &dbp);
if (err)
- return (err);
+ break;
for (i = 0; i < numbufs; i++) {
int tocpy;
@@ -414,7 +590,7 @@ dmu_read(objset_t *os, uint64_t object, uint64_t offset, uint64_t size,
dmu_buf_rele_array(dbp, numbufs, FTAG);
}
dnode_rele(dn, FTAG);
- return (0);
+ return (err);
}
void
@@ -590,9 +766,9 @@ dmu_write_pages(objset_t *os, uint64_t object, uint64_t offset, uint64_t size,
for (copied = 0; copied < tocpy; copied += PAGESIZE) {
ASSERT3U(pp->p_offset, ==, db->db_offset + bufoff);
thiscpy = MIN(PAGESIZE, tocpy - copied);
- va = ppmapin(pp, PROT_READ, (caddr_t)-1);
+ va = zfs_map_page(pp, S_READ);
bcopy(va, (char *)db->db_data + bufoff, thiscpy);
- ppmapout(va);
+ zfs_unmap_page(pp, va);
pp = pp->p_next;
bufoff += PAGESIZE;
}
@@ -620,6 +796,22 @@ typedef struct {
/* ARGSUSED */
static void
+dmu_sync_ready(zio_t *zio, arc_buf_t *buf, void *varg)
+{
+ blkptr_t *bp = zio->io_bp;
+
+ if (!BP_IS_HOLE(bp)) {
+ dmu_sync_arg_t *in = varg;
+ dbuf_dirty_record_t *dr = in->dr;
+ dmu_buf_impl_t *db = dr->dr_dbuf;
+ ASSERT(BP_GET_TYPE(bp) == db->db_dnode->dn_type);
+ ASSERT(BP_GET_LEVEL(bp) == 0);
+ bp->blk_fill = 1;
+ }
+}
+
+/* ARGSUSED */
+static void
dmu_sync_done(zio_t *zio, arc_buf_t *buf, void *varg)
{
dmu_sync_arg_t *in = varg;
@@ -627,12 +819,6 @@ dmu_sync_done(zio_t *zio, arc_buf_t *buf, void *varg)
dmu_buf_impl_t *db = dr->dr_dbuf;
dmu_sync_cb_t *done = in->done;
- if (!BP_IS_HOLE(zio->io_bp)) {
- zio->io_bp->blk_fill = 1;
- BP_SET_TYPE(zio->io_bp, db->db_dnode->dn_type);
- BP_SET_LEVEL(zio->io_bp, 0);
- }
-
mutex_enter(&db->db_mtx);
ASSERT(dr->dt.dl.dr_override_state == DR_IN_DMU_SYNC);
dr->dt.dl.dr_overridden_by = *zio->io_bp; /* structure assignment */
@@ -679,14 +865,13 @@ dmu_sync(zio_t *pio, dmu_buf_t *db_fake,
dbuf_dirty_record_t *dr;
dmu_sync_arg_t *in;
zbookmark_t zb;
+ writeprops_t wp = { 0 };
zio_t *zio;
- int zio_flags;
int err;
ASSERT(BP_IS_HOLE(bp));
ASSERT(txg != 0);
-
dprintf("dmu_sync txg=%llu, s,o,q %llu %llu %llu\n",
txg, tx->tx_synced_txg, tx->tx_open_txg, tx->tx_quiesced_txg);
@@ -791,15 +976,20 @@ dmu_sync(zio_t *pio, dmu_buf_t *db_fake,
zb.zb_object = db->db.db_object;
zb.zb_level = db->db_level;
zb.zb_blkid = db->db_blkid;
- zio_flags = ZIO_FLAG_MUSTSUCCEED;
- if (dmu_ot[db->db_dnode->dn_type].ot_metadata || zb.zb_level != 0)
- zio_flags |= ZIO_FLAG_METADATA;
- zio = arc_write(pio, os->os_spa,
- zio_checksum_select(db->db_dnode->dn_checksum, os->os_checksum),
- zio_compress_select(db->db_dnode->dn_compress, os->os_compress),
- dmu_get_replication_level(os, &zb, db->db_dnode->dn_type),
- txg, bp, dr->dt.dl.dr_data, NULL, dmu_sync_done, in,
- ZIO_PRIORITY_SYNC_WRITE, zio_flags, &zb);
+
+ wp.wp_type = db->db_dnode->dn_type;
+ wp.wp_level = db->db_level;
+ wp.wp_copies = os->os_copies;
+ wp.wp_dnchecksum = db->db_dnode->dn_checksum;
+ wp.wp_oschecksum = os->os_checksum;
+ wp.wp_dncompress = db->db_dnode->dn_compress;
+ wp.wp_oscompress = os->os_compress;
+
+ ASSERT(BP_IS_HOLE(bp));
+
+ zio = arc_write(pio, os->os_spa, &wp, DBUF_IS_L2CACHEABLE(db),
+ txg, bp, dr->dt.dl.dr_data, dmu_sync_ready, dmu_sync_done, in,
+ ZIO_PRIORITY_SYNC_WRITE, ZIO_FLAG_MUSTSUCCEED, &zb);
if (pio) {
zio_nowait(zio);
@@ -855,21 +1045,6 @@ dmu_object_set_compress(objset_t *os, uint64_t object, uint8_t compress,
}
int
-dmu_get_replication_level(objset_impl_t *os,
- zbookmark_t *zb, dmu_object_type_t ot)
-{
- int ncopies = os->os_copies;
-
- /* If it's the mos, it should have max copies set. */
- ASSERT(zb->zb_objset != 0 ||
- ncopies == spa_max_replication(os->os_spa));
-
- if (dmu_ot[ot].ot_metadata || zb->zb_level != 0)
- ncopies++;
- return (MIN(ncopies, spa_max_replication(os->os_spa)));
-}
-
-int
dmu_offset_next(objset_t *os, uint64_t object, boolean_t hole, uint64_t *off)
{
dnode_t *dn;
@@ -894,7 +1069,7 @@ dmu_offset_next(objset_t *os, uint64_t object, boolean_t hole, uint64_t *off)
return (err);
}
- err = dnode_next_offset(dn, hole, off, 1, 1, 0);
+ err = dnode_next_offset(dn, (hole ? DNODE_FIND_HOLE : 0), off, 1, 1, 0);
dnode_rele(dn, FTAG);
return (err);
@@ -1018,6 +1193,7 @@ dmu_init(void)
dbuf_init();
dnode_init();
arc_init();
+ l2arc_init();
}
void
@@ -1026,4 +1202,5 @@ dmu_fini(void)
arc_fini();
dnode_fini();
dbuf_fini();
+ l2arc_fini();
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_object.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_object.c
index 93168cc..1b9247d 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_object.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_object.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -54,7 +54,8 @@ dmu_object_alloc(objset_t *os, dmu_object_type_t ot, int blocksize,
if (P2PHASE(object, L2_dnode_count) == 0) {
uint64_t offset = restarted ? object << DNODE_SHIFT : 0;
int error = dnode_next_offset(osi->os_meta_dnode,
- B_TRUE, &offset, 2, DNODES_PER_BLOCK >> 2, 0);
+ DNODE_FIND_HOLE,
+ &offset, 2, DNODES_PER_BLOCK >> 2, 0);
restarted = B_TRUE;
if (error == 0)
object = offset >> DNODE_SHIFT;
@@ -139,6 +140,7 @@ dmu_object_free(objset_t *os, uint64_t object, dmu_tx_t *tx)
return (err);
ASSERT(dn->dn_type != DMU_OT_NONE);
+ dnode_free_range(dn, 0, DMU_OBJECT_END, tx);
dnode_free(dn, tx);
dnode_rele(dn, FTAG);
@@ -152,7 +154,7 @@ dmu_object_next(objset_t *os, uint64_t *objectp, boolean_t hole, uint64_t txg)
int error;
error = dnode_next_offset(os->os->os_meta_dnode,
- hole, &offset, 0, DNODES_PER_BLOCK, txg);
+ (hole ? DNODE_FIND_HOLE : 0), &offset, 0, DNODES_PER_BLOCK, txg);
*objectp = offset >> DNODE_SHIFT;
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_objset.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_objset.c
index 378fe8c..7981e06 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_objset.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_objset.c
@@ -19,12 +19,11 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
+#include <sys/cred.h>
#include <sys/zfs_context.h>
#include <sys/dmu_objset.h>
#include <sys/dsl_dir.h>
@@ -32,6 +31,7 @@
#include <sys/dsl_prop.h>
#include <sys/dsl_pool.h>
#include <sys/dsl_synctask.h>
+#include <sys/dsl_deleg.h>
#include <sys/dnode.h>
#include <sys/dbuf.h>
#include <sys/zvol.h>
@@ -40,7 +40,7 @@
#include <sys/zap.h>
#include <sys/zil.h>
#include <sys/dmu_impl.h>
-
+#include <sys/zfs_ioctl.h>
spa_t *
dmu_objset_spa(objset_t *os)
@@ -131,6 +131,34 @@ copies_changed_cb(void *arg, uint64_t newval)
osi->os_copies = newval;
}
+static void
+primary_cache_changed_cb(void *arg, uint64_t newval)
+{
+ objset_impl_t *osi = arg;
+
+ /*
+ * Inheritance and range checking should have been done by now.
+ */
+ ASSERT(newval == ZFS_CACHE_ALL || newval == ZFS_CACHE_NONE ||
+ newval == ZFS_CACHE_METADATA);
+
+ osi->os_primary_cache = newval;
+}
+
+static void
+secondary_cache_changed_cb(void *arg, uint64_t newval)
+{
+ objset_impl_t *osi = arg;
+
+ /*
+ * Inheritance and range checking should have been done by now.
+ */
+ ASSERT(newval == ZFS_CACHE_ALL || newval == ZFS_CACHE_NONE ||
+ newval == ZFS_CACHE_METADATA);
+
+ osi->os_secondary_cache = newval;
+}
+
void
dmu_objset_byteswap(void *buf, size_t size)
{
@@ -146,8 +174,10 @@ int
dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp,
objset_impl_t **osip)
{
- objset_impl_t *winner, *osi;
- int i, err, checksum;
+ objset_impl_t *osi;
+ int i, err;
+
+ ASSERT(ds == NULL || MUTEX_HELD(&ds->ds_opening_lock));
osi = kmem_zalloc(sizeof (objset_impl_t), KM_SLEEP);
osi->os.os = osi;
@@ -161,18 +191,26 @@ dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp,
zb.zb_object = 0;
zb.zb_level = -1;
zb.zb_blkid = 0;
+ if (DMU_OS_IS_L2CACHEABLE(osi))
+ aflags |= ARC_L2CACHE;
dprintf_bp(osi->os_rootbp, "reading %s", "");
- err = arc_read(NULL, spa, osi->os_rootbp,
- dmu_ot[DMU_OT_OBJSET].ot_byteswap,
+ /*
+ * NB: when bprewrite scrub can change the bp,
+ * and this is called from dmu_objset_open_ds_os, the bp
+ * could change, and we'll need a lock.
+ */
+ err = arc_read_nolock(NULL, spa, osi->os_rootbp,
arc_getbuf_func, &osi->os_phys_buf,
ZIO_PRIORITY_SYNC_READ, ZIO_FLAG_CANFAIL, &aflags, &zb);
if (err) {
kmem_free(osi, sizeof (objset_impl_t));
+ /* convert checksum errors into IO errors */
+ if (err == ECKSUM)
+ err = EIO;
return (err);
}
osi->os_phys = osi->os_phys_buf->b_data;
- arc_release(osi->os_phys_buf, &osi->os_phys_buf);
} else {
osi->os_phys_buf = arc_buf_alloc(spa, sizeof (objset_phys_t),
&osi->os_phys_buf, ARC_BUFC_METADATA);
@@ -183,18 +221,26 @@ dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp,
/*
* Note: the changed_cb will be called once before the register
* func returns, thus changing the checksum/compression from the
- * default (fletcher2/off). Snapshots don't need to know, and
- * registering would complicate clone promotion.
+ * default (fletcher2/off). Snapshots don't need to know about
+ * checksum/compression/copies.
*/
- if (ds && ds->ds_phys->ds_num_children == 0) {
- err = dsl_prop_register(ds, "checksum",
- checksum_changed_cb, osi);
- if (err == 0)
- err = dsl_prop_register(ds, "compression",
- compression_changed_cb, osi);
+ if (ds) {
+ err = dsl_prop_register(ds, "primarycache",
+ primary_cache_changed_cb, osi);
if (err == 0)
- err = dsl_prop_register(ds, "copies",
- copies_changed_cb, osi);
+ err = dsl_prop_register(ds, "secondarycache",
+ secondary_cache_changed_cb, osi);
+ if (!dsl_dataset_is_snapshot(ds)) {
+ if (err == 0)
+ err = dsl_prop_register(ds, "checksum",
+ checksum_changed_cb, osi);
+ if (err == 0)
+ err = dsl_prop_register(ds, "compression",
+ compression_changed_cb, osi);
+ if (err == 0)
+ err = dsl_prop_register(ds, "copies",
+ copies_changed_cb, osi);
+ }
if (err) {
VERIFY(arc_buf_remove_ref(osi->os_phys_buf,
&osi->os_phys_buf) == 1);
@@ -206,24 +252,12 @@ dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp,
osi->os_checksum = ZIO_CHECKSUM_FLETCHER_4;
osi->os_compress = ZIO_COMPRESS_LZJB;
osi->os_copies = spa_max_replication(spa);
+ osi->os_primary_cache = ZFS_CACHE_ALL;
+ osi->os_secondary_cache = ZFS_CACHE_ALL;
}
- osi->os_zil = zil_alloc(&osi->os, &osi->os_phys->os_zil_header);
-
- /*
- * Metadata always gets compressed and checksummed.
- * If the data checksum is multi-bit correctable, and it's not
- * a ZBT-style checksum, then it's suitable for metadata as well.
- * Otherwise, the metadata checksum defaults to fletcher4.
- */
- checksum = osi->os_checksum;
-
- if (zio_checksum_table[checksum].ci_correctable &&
- !zio_checksum_table[checksum].ci_zbt)
- osi->os_md_checksum = checksum;
- else
- osi->os_md_checksum = ZIO_CHECKSUM_FLETCHER_4;
- osi->os_md_compress = ZIO_COMPRESS_LZJB;
+ osi->os_zil_header = osi->os_phys->os_zil_header;
+ osi->os_zil = zil_alloc(&osi->os, &osi->os_zil_header);
for (i = 0; i < TXG_SIZE; i++) {
list_create(&osi->os_dirty_dnodes[i], sizeof (dnode_t),
@@ -238,70 +272,118 @@ dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp,
mutex_init(&osi->os_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&osi->os_obj_lock, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&osi->os_user_ptr_lock, NULL, MUTEX_DEFAULT, NULL);
osi->os_meta_dnode = dnode_special_open(osi,
&osi->os_phys->os_meta_dnode, DMU_META_DNODE_OBJECT);
- if (ds != NULL) {
- winner = dsl_dataset_set_user_ptr(ds, osi, dmu_objset_evict);
- if (winner) {
- dmu_objset_evict(ds, osi);
- osi = winner;
- }
+ /*
+ * We should be the only thread trying to do this because we
+ * have ds_opening_lock
+ */
+ if (ds) {
+ VERIFY(NULL == dsl_dataset_set_user_ptr(ds, osi,
+ dmu_objset_evict));
}
*osip = osi;
return (0);
}
-/* called from zpl */
-int
-dmu_objset_open(const char *name, dmu_objset_type_t type, int mode,
- objset_t **osp)
+static int
+dmu_objset_open_ds_os(dsl_dataset_t *ds, objset_t *os, dmu_objset_type_t type)
{
- dsl_dataset_t *ds;
- int err;
- objset_t *os;
objset_impl_t *osi;
- os = kmem_alloc(sizeof (objset_t), KM_SLEEP);
- err = dsl_dataset_open(name, mode, os, &ds);
- if (err) {
- kmem_free(os, sizeof (objset_t));
- return (err);
- }
-
+ mutex_enter(&ds->ds_opening_lock);
osi = dsl_dataset_get_user_ptr(ds);
if (osi == NULL) {
+ int err;
+
err = dmu_objset_open_impl(dsl_dataset_get_spa(ds),
ds, &ds->ds_phys->ds_bp, &osi);
if (err) {
- dsl_dataset_close(ds, mode, os);
- kmem_free(os, sizeof (objset_t));
+ mutex_exit(&ds->ds_opening_lock);
return (err);
}
}
+ mutex_exit(&ds->ds_opening_lock);
os->os = osi;
- os->os_mode = mode;
+ os->os_mode = DS_MODE_NOHOLD;
- if (type != DMU_OST_ANY && type != os->os->os_phys->os_type) {
- dmu_objset_close(os);
+ if (type != DMU_OST_ANY && type != os->os->os_phys->os_type)
return (EINVAL);
- }
- *osp = os;
return (0);
}
+int
+dmu_objset_open_ds(dsl_dataset_t *ds, dmu_objset_type_t type, objset_t **osp)
+{
+ objset_t *os;
+ int err;
+
+ os = kmem_alloc(sizeof (objset_t), KM_SLEEP);
+ err = dmu_objset_open_ds_os(ds, os, type);
+ if (err)
+ kmem_free(os, sizeof (objset_t));
+ else
+ *osp = os;
+ return (err);
+}
+
+/* called from zpl */
+int
+dmu_objset_open(const char *name, dmu_objset_type_t type, int mode,
+ objset_t **osp)
+{
+ objset_t *os;
+ dsl_dataset_t *ds;
+ int err;
+
+ ASSERT(DS_MODE_TYPE(mode) == DS_MODE_USER ||
+ DS_MODE_TYPE(mode) == DS_MODE_OWNER);
+
+ os = kmem_alloc(sizeof (objset_t), KM_SLEEP);
+ if (DS_MODE_TYPE(mode) == DS_MODE_USER)
+ err = dsl_dataset_hold(name, os, &ds);
+ else
+ err = dsl_dataset_own(name, mode, os, &ds);
+ if (err) {
+ kmem_free(os, sizeof (objset_t));
+ return (err);
+ }
+
+ err = dmu_objset_open_ds_os(ds, os, type);
+ if (err) {
+ if (DS_MODE_TYPE(mode) == DS_MODE_USER)
+ dsl_dataset_rele(ds, os);
+ else
+ dsl_dataset_disown(ds, os);
+ kmem_free(os, sizeof (objset_t));
+ } else {
+ os->os_mode = mode;
+ *osp = os;
+ }
+ return (err);
+}
+
void
dmu_objset_close(objset_t *os)
{
- dsl_dataset_close(os->os->os_dsl_dataset, os->os_mode, os);
+ ASSERT(DS_MODE_TYPE(os->os_mode) == DS_MODE_USER ||
+ DS_MODE_TYPE(os->os_mode) == DS_MODE_OWNER ||
+ DS_MODE_TYPE(os->os_mode) == DS_MODE_NOHOLD);
+
+ if (DS_MODE_TYPE(os->os_mode) == DS_MODE_USER)
+ dsl_dataset_rele(os->os->os_dsl_dataset, os);
+ else if (DS_MODE_TYPE(os->os_mode) == DS_MODE_OWNER)
+ dsl_dataset_disown(os->os->os_dsl_dataset, os);
kmem_free(os, sizeof (objset_t));
}
int
-dmu_objset_evict_dbufs(objset_t *os, int try)
+dmu_objset_evict_dbufs(objset_t *os)
{
objset_impl_t *osi = os->os;
dnode_t *dn;
@@ -319,34 +401,25 @@ dmu_objset_evict_dbufs(objset_t *os, int try)
* skip.
*/
for (dn = list_head(&osi->os_dnodes);
- dn && refcount_is_zero(&dn->dn_holds);
+ dn && !dnode_add_ref(dn, FTAG);
dn = list_next(&osi->os_dnodes, dn))
continue;
- if (dn)
- dnode_add_ref(dn, FTAG);
while (dn) {
dnode_t *next_dn = dn;
do {
next_dn = list_next(&osi->os_dnodes, next_dn);
- } while (next_dn && refcount_is_zero(&next_dn->dn_holds));
- if (next_dn)
- dnode_add_ref(next_dn, FTAG);
+ } while (next_dn && !dnode_add_ref(next_dn, FTAG));
mutex_exit(&osi->os_lock);
- if (dnode_evict_dbufs(dn, try)) {
- dnode_rele(dn, FTAG);
- if (next_dn)
- dnode_rele(next_dn, FTAG);
- return (1);
- }
+ dnode_evict_dbufs(dn);
dnode_rele(dn, FTAG);
mutex_enter(&osi->os_lock);
dn = next_dn;
}
mutex_exit(&osi->os_lock);
- return (0);
+ return (list_head(&osi->os_dnodes) != osi->os_meta_dnode);
}
void
@@ -361,13 +434,19 @@ dmu_objset_evict(dsl_dataset_t *ds, void *arg)
ASSERT(list_head(&osi->os_free_dnodes[i]) == NULL);
}
- if (ds && ds->ds_phys->ds_num_children == 0) {
- VERIFY(0 == dsl_prop_unregister(ds, "checksum",
- checksum_changed_cb, osi));
- VERIFY(0 == dsl_prop_unregister(ds, "compression",
- compression_changed_cb, osi));
- VERIFY(0 == dsl_prop_unregister(ds, "copies",
- copies_changed_cb, osi));
+ if (ds) {
+ if (!dsl_dataset_is_snapshot(ds)) {
+ VERIFY(0 == dsl_prop_unregister(ds, "checksum",
+ checksum_changed_cb, osi));
+ VERIFY(0 == dsl_prop_unregister(ds, "compression",
+ compression_changed_cb, osi));
+ VERIFY(0 == dsl_prop_unregister(ds, "copies",
+ copies_changed_cb, osi));
+ }
+ VERIFY(0 == dsl_prop_unregister(ds, "primarycache",
+ primary_cache_changed_cb, osi));
+ VERIFY(0 == dsl_prop_unregister(ds, "secondarycache",
+ secondary_cache_changed_cb, osi));
}
/*
@@ -375,7 +454,7 @@ dmu_objset_evict(dsl_dataset_t *ds, void *arg)
* nothing can be added to the list at this point.
*/
os.os = osi;
- (void) dmu_objset_evict_dbufs(&os, 0);
+ (void) dmu_objset_evict_dbufs(&os);
ASSERT3P(list_head(&osi->os_dnodes), ==, osi->os_meta_dnode);
ASSERT3P(list_tail(&osi->os_dnodes), ==, osi->os_meta_dnode);
@@ -387,6 +466,7 @@ dmu_objset_evict(dsl_dataset_t *ds, void *arg)
VERIFY(arc_buf_remove_ref(osi->os_phys_buf, &osi->os_phys_buf) == 1);
mutex_destroy(&osi->os_lock);
mutex_destroy(&osi->os_obj_lock);
+ mutex_destroy(&osi->os_user_ptr_lock);
kmem_free(osi, sizeof (objset_impl_t));
}
@@ -399,7 +479,11 @@ dmu_objset_create_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp,
dnode_t *mdn;
ASSERT(dmu_tx_is_syncing(tx));
+ if (ds)
+ mutex_enter(&ds->ds_opening_lock);
VERIFY(0 == dmu_objset_open_impl(spa, ds, bp, &osi));
+ if (ds)
+ mutex_exit(&ds->ds_opening_lock);
mdn = osi->os_meta_dnode;
dnode_allocate(mdn, DMU_OT_DNODE, 1 << DNODE_BLOCK_SHIFT,
@@ -443,14 +527,15 @@ dmu_objset_create_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp,
}
struct oscarg {
- void (*userfunc)(objset_t *os, void *arg, dmu_tx_t *tx);
+ void (*userfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx);
void *userarg;
dsl_dataset_t *clone_parent;
const char *lastname;
dmu_objset_type_t type;
+ uint64_t flags;
};
-/* ARGSUSED */
+/*ARGSUSED*/
static int
dmu_objset_create_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
@@ -478,11 +563,12 @@ dmu_objset_create_check(void *arg1, void *arg2, dmu_tx_t *tx)
if (oa->clone_parent->ds_phys->ds_num_children == 0)
return (EINVAL);
}
+
return (0);
}
static void
-dmu_objset_create_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+dmu_objset_create_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
dsl_dir_t *dd = arg1;
struct oscarg *oa = arg2;
@@ -493,10 +579,9 @@ dmu_objset_create_sync(void *arg1, void *arg2, dmu_tx_t *tx)
ASSERT(dmu_tx_is_syncing(tx));
dsobj = dsl_dataset_create_sync(dd, oa->lastname,
- oa->clone_parent, tx);
+ oa->clone_parent, oa->flags, cr, tx);
- VERIFY(0 == dsl_dataset_open_obj(dd->dd_pool, dsobj, NULL,
- DS_MODE_STANDARD | DS_MODE_READONLY, FTAG, &ds));
+ VERIFY(0 == dsl_dataset_hold_obj(dd->dd_pool, dsobj, FTAG, &ds));
bp = dsl_dataset_get_blkptr(ds);
if (BP_IS_HOLE(bp)) {
objset_impl_t *osi;
@@ -506,15 +591,19 @@ dmu_objset_create_sync(void *arg1, void *arg2, dmu_tx_t *tx)
ds, bp, oa->type, tx);
if (oa->userfunc)
- oa->userfunc(&osi->os, oa->userarg, tx);
+ oa->userfunc(&osi->os, oa->userarg, cr, tx);
}
- dsl_dataset_close(ds, DS_MODE_STANDARD | DS_MODE_READONLY, FTAG);
+
+ spa_history_internal_log(LOG_DS_CREATE, dd->dd_pool->dp_spa,
+ tx, cr, "dataset = %llu", dsobj);
+
+ dsl_dataset_rele(ds, FTAG);
}
int
dmu_objset_create(const char *name, dmu_objset_type_t type,
- objset_t *clone_parent,
- void (*func)(objset_t *os, void *arg, dmu_tx_t *tx), void *arg)
+ objset_t *clone_parent, uint64_t flags,
+ void (*func)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx), void *arg)
{
dsl_dir_t *pdd;
const char *tail;
@@ -536,6 +625,8 @@ dmu_objset_create(const char *name, dmu_objset_type_t type,
oa.userarg = arg;
oa.lastname = tail;
oa.type = type;
+ oa.flags = flags;
+
if (clone_parent != NULL) {
/*
* You can't clone to a different type.
@@ -564,33 +655,47 @@ dmu_objset_destroy(const char *name)
* It would be nicer to do this in dsl_dataset_destroy_sync(),
* but the replay log objset is modified in open context.
*/
- error = dmu_objset_open(name, DMU_OST_ANY, DS_MODE_EXCLUSIVE, &os);
+ error = dmu_objset_open(name, DMU_OST_ANY,
+ DS_MODE_OWNER|DS_MODE_READONLY|DS_MODE_INCONSISTENT, &os);
if (error == 0) {
+ dsl_dataset_t *ds = os->os->os_dsl_dataset;
zil_destroy(dmu_objset_zil(os), B_FALSE);
- dmu_objset_close(os);
+
+ error = dsl_dataset_destroy(ds, os);
+ /*
+ * dsl_dataset_destroy() closes the ds.
+ */
+ kmem_free(os, sizeof (objset_t));
}
- return (dsl_dataset_destroy(name));
+ return (error);
}
+/*
+ * This will close the objset.
+ */
int
-dmu_objset_rollback(const char *name)
+dmu_objset_rollback(objset_t *os)
{
int err;
- objset_t *os;
+ dsl_dataset_t *ds;
- err = dmu_objset_open(name, DMU_OST_ANY,
- DS_MODE_EXCLUSIVE | DS_MODE_INCONSISTENT, &os);
- if (err == 0) {
- err = zil_suspend(dmu_objset_zil(os));
- if (err == 0)
- zil_resume(dmu_objset_zil(os));
- if (err == 0) {
- /* XXX uncache everything? */
- err = dsl_dataset_rollback(os->os->os_dsl_dataset);
- }
+ ds = os->os->os_dsl_dataset;
+
+ if (!dsl_dataset_tryown(ds, TRUE, os)) {
dmu_objset_close(os);
+ return (EBUSY);
}
+
+ err = dsl_dataset_rollback(ds, os->os->os_phys->os_type);
+
+ /*
+ * NB: we close the objset manually because the rollback
+ * actually implicitly called dmu_objset_evict(), thus freeing
+ * the objset_impl_t.
+ */
+ dsl_dataset_disown(ds, os);
+ kmem_free(os, sizeof (objset_t));
return (err);
}
@@ -598,6 +703,13 @@ struct snaparg {
dsl_sync_task_group_t *dstg;
char *snapname;
char failed[MAXPATHLEN];
+ boolean_t checkperms;
+ list_t objsets;
+};
+
+struct osnode {
+ list_node_t node;
+ objset_t *os;
};
static int
@@ -605,20 +717,25 @@ dmu_objset_snapshot_one(char *name, void *arg)
{
struct snaparg *sn = arg;
objset_t *os;
- dmu_objset_stats_t stat;
int err;
(void) strcpy(sn->failed, name);
- err = dmu_objset_open(name, DMU_OST_ANY, DS_MODE_STANDARD, &os);
+ /*
+ * Check permissions only when requested. This only applies when
+ * doing a recursive snapshot. The permission checks for the starting
+ * dataset have already been performed in zfs_secpolicy_snapshot()
+ */
+ if (sn->checkperms == B_TRUE &&
+ (err = zfs_secpolicy_snapshot_perms(name, CRED())))
+ return (err);
+
+ err = dmu_objset_open(name, DMU_OST_ANY, DS_MODE_USER, &os);
if (err != 0)
return (err);
- /*
- * If the objset is in an inconsistent state, return busy.
- */
- dmu_objset_fast_stat(os, &stat);
- if (stat.dds_inconsistent) {
+ /* If the objset is in an inconsistent state, return busy */
+ if (os->os->os_dsl_dataset->ds_phys->ds_flags & DS_FLAG_INCONSISTENT) {
dmu_objset_close(os);
return (EBUSY);
}
@@ -630,8 +747,13 @@ dmu_objset_snapshot_one(char *name, void *arg)
*/
err = zil_suspend(dmu_objset_zil(os));
if (err == 0) {
+ struct osnode *osn;
dsl_sync_task_create(sn->dstg, dsl_dataset_snapshot_check,
- dsl_dataset_snapshot_sync, os, sn->snapname, 3);
+ dsl_dataset_snapshot_sync, os->os->os_dsl_dataset,
+ sn->snapname, 3);
+ osn = kmem_alloc(sizeof (struct osnode), KM_SLEEP);
+ osn->os = os;
+ list_insert_tail(&sn->objsets, osn);
} else {
dmu_objset_close(os);
}
@@ -643,31 +765,28 @@ int
dmu_objset_snapshot(char *fsname, char *snapname, boolean_t recursive)
{
dsl_sync_task_t *dst;
+ struct osnode *osn;
struct snaparg sn = { 0 };
- char *cp;
spa_t *spa;
int err;
(void) strcpy(sn.failed, fsname);
- cp = strchr(fsname, '/');
- if (cp) {
- *cp = '\0';
- err = spa_open(fsname, &spa, FTAG);
- *cp = '/';
- } else {
- err = spa_open(fsname, &spa, FTAG);
- }
+ err = spa_open(fsname, &spa, FTAG);
if (err)
return (err);
sn.dstg = dsl_sync_task_group_create(spa_get_dsl(spa));
sn.snapname = snapname;
+ list_create(&sn.objsets, sizeof (struct osnode),
+ offsetof(struct osnode, node));
if (recursive) {
+ sn.checkperms = B_TRUE;
err = dmu_objset_find(fsname,
dmu_objset_snapshot_one, &sn, DS_FIND_CHILDREN);
} else {
+ sn.checkperms = B_FALSE;
err = dmu_objset_snapshot_one(fsname, &sn);
}
@@ -678,13 +797,20 @@ dmu_objset_snapshot(char *fsname, char *snapname, boolean_t recursive)
for (dst = list_head(&sn.dstg->dstg_tasks); dst;
dst = list_next(&sn.dstg->dstg_tasks, dst)) {
- objset_t *os = dst->dst_arg1;
+ dsl_dataset_t *ds = dst->dst_arg1;
if (dst->dst_err)
- dmu_objset_name(os, sn.failed);
- zil_resume(dmu_objset_zil(os));
- dmu_objset_close(os);
+ dsl_dataset_name(ds, sn.failed);
}
+
out:
+ while (osn = list_head(&sn.objsets)) {
+ list_remove(&sn.objsets, osn);
+ zil_resume(dmu_objset_zil(osn->os));
+ dmu_objset_close(osn->os);
+ kmem_free(osn, sizeof (struct osnode));
+ }
+ list_destroy(&sn.objsets);
+
if (err)
(void) strcpy(fsname, sn.failed);
dsl_sync_task_group_destroy(sn.dstg);
@@ -717,39 +843,30 @@ dmu_objset_sync_dnodes(list_t *list, dmu_tx_t *tx)
static void
ready(zio_t *zio, arc_buf_t *abuf, void *arg)
{
+ blkptr_t *bp = zio->io_bp;
+ blkptr_t *bp_orig = &zio->io_bp_orig;
objset_impl_t *os = arg;
- blkptr_t *bp = os->os_rootbp;
dnode_phys_t *dnp = &os->os_phys->os_meta_dnode;
- int i;
+
+ ASSERT(bp == os->os_rootbp);
+ ASSERT(BP_GET_TYPE(bp) == DMU_OT_OBJSET);
+ ASSERT(BP_GET_LEVEL(bp) == 0);
/*
* Update rootbp fill count.
*/
bp->blk_fill = 1; /* count the meta-dnode */
- for (i = 0; i < dnp->dn_nblkptr; i++)
+ for (int i = 0; i < dnp->dn_nblkptr; i++)
bp->blk_fill += dnp->dn_blkptr[i].blk_fill;
-}
-/* ARGSUSED */
-static void
-killer(zio_t *zio, arc_buf_t *abuf, void *arg)
-{
- objset_impl_t *os = arg;
-
- ASSERT3U(zio->io_error, ==, 0);
-
- BP_SET_TYPE(zio->io_bp, DMU_OT_OBJSET);
- BP_SET_LEVEL(zio->io_bp, 0);
-
- if (!DVA_EQUAL(BP_IDENTITY(zio->io_bp),
- BP_IDENTITY(&zio->io_bp_orig))) {
+ if (zio->io_flags & ZIO_FLAG_IO_REWRITE) {
+ ASSERT(DVA_EQUAL(BP_IDENTITY(bp), BP_IDENTITY(bp_orig)));
+ } else {
if (zio->io_bp_orig.blk_birth == os->os_synctx->tx_txg)
- dsl_dataset_block_kill(os->os_dsl_dataset,
- &zio->io_bp_orig, NULL, os->os_synctx);
- dsl_dataset_block_born(os->os_dsl_dataset, zio->io_bp,
- os->os_synctx);
+ (void) dsl_dataset_block_kill(os->os_dsl_dataset,
+ &zio->io_bp_orig, zio, os->os_synctx);
+ dsl_dataset_block_born(os->os_dsl_dataset, bp, os->os_synctx);
}
- arc_release(os->os_phys_buf, &os->os_phys_buf);
}
/* called from dsl */
@@ -758,10 +875,10 @@ dmu_objset_sync(objset_impl_t *os, zio_t *pio, dmu_tx_t *tx)
{
int txgoff;
zbookmark_t zb;
+ writeprops_t wp = { 0 };
zio_t *zio;
list_t *list;
dbuf_dirty_record_t *dr;
- int zio_flags;
dprintf_ds(os->os_dsl_dataset, "txg=%llu\n", tx->tx_txg);
@@ -783,19 +900,24 @@ dmu_objset_sync(objset_impl_t *os, zio_t *pio, dmu_tx_t *tx)
*/
zb.zb_objset = os->os_dsl_dataset ? os->os_dsl_dataset->ds_object : 0;
zb.zb_object = 0;
- zb.zb_level = -1;
+ zb.zb_level = -1; /* for block ordering; it's level 0 on disk */
zb.zb_blkid = 0;
- zio_flags = ZIO_FLAG_MUSTSUCCEED;
- if (dmu_ot[DMU_OT_OBJSET].ot_metadata || zb.zb_level != 0)
- zio_flags |= ZIO_FLAG_METADATA;
- if (BP_IS_OLDER(os->os_rootbp, tx->tx_txg))
- dsl_dataset_block_kill(os->os_dsl_dataset,
+
+ wp.wp_type = DMU_OT_OBJSET;
+ wp.wp_level = 0; /* on-disk BP level; see above */
+ wp.wp_copies = os->os_copies;
+ wp.wp_oschecksum = os->os_checksum;
+ wp.wp_oscompress = os->os_compress;
+
+ if (BP_IS_OLDER(os->os_rootbp, tx->tx_txg)) {
+ (void) dsl_dataset_block_kill(os->os_dsl_dataset,
os->os_rootbp, pio, tx);
- zio = arc_write(pio, os->os_spa, os->os_md_checksum,
- os->os_md_compress,
- dmu_get_replication_level(os, &zb, DMU_OT_OBJSET),
- tx->tx_txg, os->os_rootbp, os->os_phys_buf, ready, killer, os,
- ZIO_PRIORITY_ASYNC_WRITE, zio_flags, &zb);
+ }
+
+ arc_release(os->os_phys_buf, &os->os_phys_buf);
+ zio = arc_write(pio, os->os_spa, &wp, DMU_OS_IS_L2CACHEABLE(os),
+ tx->tx_txg, os->os_rootbp, os->os_phys_buf, ready, NULL, os,
+ ZIO_PRIORITY_ASYNC_WRITE, ZIO_FLAG_MUSTSUCCEED, &zb);
/*
* Sync meta-dnode - the parent IO for the sync is the root block
@@ -819,6 +941,7 @@ dmu_objset_sync(objset_impl_t *os, zio_t *pio, dmu_tx_t *tx)
* Free intent log blocks up to this tx.
*/
zil_sync(os->os_zil, tx);
+ os->os_phys->os_zil_header = os->os_zil_header;
zio_nowait(zio);
}
@@ -867,8 +990,23 @@ dmu_objset_is_snapshot(objset_t *os)
}
int
+dmu_snapshot_realname(objset_t *os, char *name, char *real, int maxlen,
+ boolean_t *conflict)
+{
+ dsl_dataset_t *ds = os->os->os_dsl_dataset;
+ uint64_t ignored;
+
+ if (ds->ds_phys->ds_snapnames_zapobj == 0)
+ return (ENOENT);
+
+ return (zap_lookup_norm(ds->ds_dir->dd_pool->dp_meta_objset,
+ ds->ds_phys->ds_snapnames_zapobj, name, 8, 1, &ignored, MT_FIRST,
+ real, maxlen, conflict));
+}
+
+int
dmu_snapshot_list_next(objset_t *os, int namelen, char *name,
- uint64_t *idp, uint64_t *offp)
+ uint64_t *idp, uint64_t *offp, boolean_t *case_conflict)
{
dsl_dataset_t *ds = os->os->os_dsl_dataset;
zap_cursor_t cursor;
@@ -894,6 +1032,8 @@ dmu_snapshot_list_next(objset_t *os, int namelen, char *name,
(void) strcpy(name, attr.za_name);
if (idp)
*idp = attr.za_first_integer;
+ if (case_conflict)
+ *case_conflict = attr.za_normalization_conflict;
zap_cursor_advance(&cursor);
*offp = zap_cursor_serialize(&cursor);
zap_cursor_fini(&cursor);
@@ -938,48 +1078,80 @@ dmu_dir_list_next(objset_t *os, int namelen, char *name,
return (0);
}
+struct findarg {
+ int (*func)(char *, void *);
+ void *arg;
+};
+
+/* ARGSUSED */
+static int
+findfunc(spa_t *spa, uint64_t dsobj, const char *dsname, void *arg)
+{
+ struct findarg *fa = arg;
+ return (fa->func((char *)dsname, fa->arg));
+}
+
/*
* Find all objsets under name, and for each, call 'func(child_name, arg)'.
+ * Perhaps change all callers to use dmu_objset_find_spa()?
*/
int
dmu_objset_find(char *name, int func(char *, void *), void *arg, int flags)
{
+ struct findarg fa;
+ fa.func = func;
+ fa.arg = arg;
+ return (dmu_objset_find_spa(NULL, name, findfunc, &fa, flags));
+}
+
+/*
+ * Find all objsets under name, call func on each
+ */
+int
+dmu_objset_find_spa(spa_t *spa, const char *name,
+ int func(spa_t *, uint64_t, const char *, void *), void *arg, int flags)
+{
dsl_dir_t *dd;
- objset_t *os;
- uint64_t snapobj;
+ dsl_pool_t *dp;
+ dsl_dataset_t *ds;
zap_cursor_t zc;
zap_attribute_t *attr;
char *child;
- int do_self, err;
+ uint64_t thisobj;
+ int err;
- err = dsl_dir_open(name, FTAG, &dd, NULL);
+ if (name == NULL)
+ name = spa_name(spa);
+ err = dsl_dir_open_spa(spa, name, FTAG, &dd, NULL);
if (err)
return (err);
- /* NB: the $MOS dir doesn't have a head dataset */
- do_self = (dd->dd_phys->dd_head_dataset_obj != 0);
+ /* Don't visit hidden ($MOS & $ORIGIN) objsets. */
+ if (dd->dd_myname[0] == '$') {
+ dsl_dir_close(dd, FTAG);
+ return (0);
+ }
+
+ thisobj = dd->dd_phys->dd_head_dataset_obj;
attr = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP);
+ dp = dd->dd_pool;
/*
* Iterate over all children.
*/
if (flags & DS_FIND_CHILDREN) {
- for (zap_cursor_init(&zc, dd->dd_pool->dp_meta_objset,
+ for (zap_cursor_init(&zc, dp->dp_meta_objset,
dd->dd_phys->dd_child_dir_zapobj);
zap_cursor_retrieve(&zc, attr) == 0;
(void) zap_cursor_advance(&zc)) {
ASSERT(attr->za_integer_length == sizeof (uint64_t));
ASSERT(attr->za_num_integers == 1);
- /*
- * No separating '/' because parent's name ends in /.
- */
child = kmem_alloc(MAXPATHLEN, KM_SLEEP);
- /* XXX could probably just use name here */
- dsl_dir_name(dd, child);
+ (void) strcpy(child, name);
(void) strcat(child, "/");
(void) strcat(child, attr->za_name);
- err = dmu_objset_find(child, func, arg, flags);
+ err = dmu_objset_find_spa(spa, child, func, arg, flags);
kmem_free(child, MAXPATHLEN);
if (err)
break;
@@ -996,30 +1168,36 @@ dmu_objset_find(char *name, int func(char *, void *), void *arg, int flags)
/*
* Iterate over all snapshots.
*/
- if ((flags & DS_FIND_SNAPSHOTS) &&
- dmu_objset_open(name, DMU_OST_ANY,
- DS_MODE_STANDARD | DS_MODE_READONLY, &os) == 0) {
-
- snapobj = os->os->os_dsl_dataset->ds_phys->ds_snapnames_zapobj;
- dmu_objset_close(os);
-
- for (zap_cursor_init(&zc, dd->dd_pool->dp_meta_objset, snapobj);
- zap_cursor_retrieve(&zc, attr) == 0;
- (void) zap_cursor_advance(&zc)) {
- ASSERT(attr->za_integer_length == sizeof (uint64_t));
- ASSERT(attr->za_num_integers == 1);
+ if (flags & DS_FIND_SNAPSHOTS) {
+ if (!dsl_pool_sync_context(dp))
+ rw_enter(&dp->dp_config_rwlock, RW_READER);
+ err = dsl_dataset_hold_obj(dp, thisobj, FTAG, &ds);
+ if (!dsl_pool_sync_context(dp))
+ rw_exit(&dp->dp_config_rwlock);
- child = kmem_alloc(MAXPATHLEN, KM_SLEEP);
- /* XXX could probably just use name here */
- dsl_dir_name(dd, child);
- (void) strcat(child, "@");
- (void) strcat(child, attr->za_name);
- err = func(child, arg);
- kmem_free(child, MAXPATHLEN);
- if (err)
- break;
+ if (err == 0) {
+ uint64_t snapobj = ds->ds_phys->ds_snapnames_zapobj;
+ dsl_dataset_rele(ds, FTAG);
+
+ for (zap_cursor_init(&zc, dp->dp_meta_objset, snapobj);
+ zap_cursor_retrieve(&zc, attr) == 0;
+ (void) zap_cursor_advance(&zc)) {
+ ASSERT(attr->za_integer_length ==
+ sizeof (uint64_t));
+ ASSERT(attr->za_num_integers == 1);
+
+ child = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+ (void) strcpy(child, name);
+ (void) strcat(child, "@");
+ (void) strcat(child, attr->za_name);
+ err = func(spa, attr->za_first_integer,
+ child, arg);
+ kmem_free(child, MAXPATHLEN);
+ if (err)
+ break;
+ }
+ zap_cursor_fini(&zc);
}
- zap_cursor_fini(&zc);
}
dsl_dir_close(dd, FTAG);
@@ -1031,7 +1209,20 @@ dmu_objset_find(char *name, int func(char *, void *), void *arg, int flags)
/*
* Apply to self if appropriate.
*/
- if (do_self)
- err = func(name, arg);
+ err = func(spa, thisobj, name, arg);
return (err);
}
+
+void
+dmu_objset_set_user(objset_t *os, void *user_ptr)
+{
+ ASSERT(MUTEX_HELD(&os->os->os_user_ptr_lock));
+ os->os->os_user_ptr = user_ptr;
+}
+
+void *
+dmu_objset_get_user(objset_t *os)
+{
+ ASSERT(MUTEX_HELD(&os->os->os_user_ptr_lock));
+ return (os->os->os_user_ptr);
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c
index 3e55dc3..1294581 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -41,10 +41,13 @@
#include <sys/zap.h>
#include <sys/zio_checksum.h>
+static char *dmu_recv_tag = "dmu_recv_tag";
+
struct backuparg {
dmu_replay_record_t *drr;
kthread_t *td;
struct file *fp;
+ offset_t *off;
objset_t *os;
zio_cksum_t zc;
int err;
@@ -77,6 +80,7 @@ dump_bytes(struct backuparg *ba, void *buf, int len)
fprintf(stderr, "%s: returning EOPNOTSUPP\n", __func__);
ba->err = EOPNOTSUPP;
#endif
+ *ba->off += len;
return (ba->err);
}
@@ -179,7 +183,7 @@ backup_cb(traverse_blk_cache_t *bc, spa_t *spa, void *arg)
void *data = bc->bc_data;
int err = 0;
- if (SIGPENDING(curthread))
+ if (issig(JUSTLOOKING) && issig(FORREAL))
return (EINTR);
ASSERT(data || bp == NULL);
@@ -215,10 +219,9 @@ backup_cb(traverse_blk_cache_t *bc, spa_t *spa, void *arg)
zb.zb_object = object;
zb.zb_level = level;
zb.zb_blkid = blkid;
- (void) arc_read(NULL, spa, bp,
- dmu_ot[type].ot_byteswap, arc_getbuf_func, &abuf,
- ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_MUSTSUCCEED,
- &aflags, &zb);
+ (void) arc_read_nolock(NULL, spa, bp,
+ arc_getbuf_func, &abuf, ZIO_PRIORITY_ASYNC_READ,
+ ZIO_FLAG_MUSTSUCCEED, &aflags, &zb);
if (abuf) {
err = dump_data(ba, type, object, blkid * blksz,
@@ -236,13 +239,15 @@ backup_cb(traverse_blk_cache_t *bc, spa_t *spa, void *arg)
}
int
-dmu_sendbackup(objset_t *tosnap, objset_t *fromsnap, struct file *fp)
+dmu_sendbackup(objset_t *tosnap, objset_t *fromsnap, boolean_t fromorigin,
+ struct file *fp, offset_t *off)
{
dsl_dataset_t *ds = tosnap->os->os_dsl_dataset;
dsl_dataset_t *fromds = fromsnap ? fromsnap->os->os_dsl_dataset : NULL;
dmu_replay_record_t *drr;
struct backuparg ba;
int err;
+ uint64_t fromtxg = 0;
/* tosnap must be a snapshot */
if (ds->ds_phys->ds_next_snap_obj == 0)
@@ -250,26 +255,55 @@ dmu_sendbackup(objset_t *tosnap, objset_t *fromsnap, struct file *fp)
/* fromsnap must be an earlier snapshot from the same fs as tosnap */
if (fromds && (ds->ds_dir != fromds->ds_dir ||
- fromds->ds_phys->ds_creation_txg >=
- ds->ds_phys->ds_creation_txg))
+ fromds->ds_phys->ds_creation_txg >= ds->ds_phys->ds_creation_txg))
return (EXDEV);
+ if (fromorigin) {
+ dsl_pool_t *dp = ds->ds_dir->dd_pool;
+
+ if (fromsnap)
+ return (EINVAL);
+
+ if (dsl_dir_is_clone(ds->ds_dir)) {
+ rw_enter(&dp->dp_config_rwlock, RW_READER);
+ err = dsl_dataset_hold_obj(dp,
+ ds->ds_dir->dd_phys->dd_origin_obj, FTAG, &fromds);
+ rw_exit(&dp->dp_config_rwlock);
+ if (err)
+ return (err);
+ } else {
+ fromorigin = B_FALSE;
+ }
+ }
+
+
drr = kmem_zalloc(sizeof (dmu_replay_record_t), KM_SLEEP);
drr->drr_type = DRR_BEGIN;
drr->drr_u.drr_begin.drr_magic = DMU_BACKUP_MAGIC;
- drr->drr_u.drr_begin.drr_version = DMU_BACKUP_VERSION;
+ drr->drr_u.drr_begin.drr_version = DMU_BACKUP_STREAM_VERSION;
drr->drr_u.drr_begin.drr_creation_time =
ds->ds_phys->ds_creation_time;
drr->drr_u.drr_begin.drr_type = tosnap->os->os_phys->os_type;
+ if (fromorigin)
+ drr->drr_u.drr_begin.drr_flags |= DRR_FLAG_CLONE;
drr->drr_u.drr_begin.drr_toguid = ds->ds_phys->ds_guid;
+ if (ds->ds_phys->ds_flags & DS_FLAG_CI_DATASET)
+ drr->drr_u.drr_begin.drr_flags |= DRR_FLAG_CI_DATA;
+
if (fromds)
drr->drr_u.drr_begin.drr_fromguid = fromds->ds_phys->ds_guid;
dsl_dataset_name(ds, drr->drr_u.drr_begin.drr_toname);
+ if (fromds)
+ fromtxg = fromds->ds_phys->ds_creation_txg;
+ if (fromorigin)
+ dsl_dataset_rele(fromds, FTAG);
+
ba.drr = drr;
ba.td = curthread;
ba.fp = fp;
ba.os = tosnap;
+ ba.off = off;
ZIO_SET_CHECKSUM(&ba.zc, 0, 0, 0, 0);
if (dump_bytes(&ba, drr, sizeof (dmu_replay_record_t))) {
@@ -277,8 +311,7 @@ dmu_sendbackup(objset_t *tosnap, objset_t *fromsnap, struct file *fp)
return (ba.err);
}
- err = traverse_dsl_dataset(ds,
- fromds ? fromds->ds_phys->ds_creation_txg : 0,
+ err = traverse_dsl_dataset(ds, fromtxg,
ADVANCE_PRE | ADVANCE_HOLES | ADVANCE_DATA | ADVANCE_NOLOCK,
backup_cb, &ba);
@@ -303,164 +336,384 @@ dmu_sendbackup(objset_t *tosnap, objset_t *fromsnap, struct file *fp)
return (0);
}
-struct restorearg {
- int err;
- int byteswap;
- kthread_t *td;
- struct file *fp;
- char *buf;
- uint64_t voff;
- int buflen; /* number of valid bytes in buf */
- int bufoff; /* next offset to read */
- int bufsize; /* amount of memory allocated for buf */
- zio_cksum_t zc;
+struct recvbeginsyncarg {
+ const char *tofs;
+ const char *tosnap;
+ dsl_dataset_t *origin;
+ uint64_t fromguid;
+ dmu_objset_type_t type;
+ void *tag;
+ boolean_t force;
+ uint64_t dsflags;
+ char clonelastname[MAXNAMELEN];
+ dsl_dataset_t *ds; /* the ds to recv into; returned from the syncfunc */
};
+static dsl_dataset_t *
+recv_full_sync_impl(dsl_pool_t *dp, uint64_t dsobj, dmu_objset_type_t type,
+ cred_t *cr, dmu_tx_t *tx)
+{
+ dsl_dataset_t *ds;
+
+ /* This should always work, since we just created it */
+ /* XXX - create should return an owned ds */
+ VERIFY(0 == dsl_dataset_own_obj(dp, dsobj,
+ DS_MODE_INCONSISTENT, dmu_recv_tag, &ds));
+
+ if (type != DMU_OST_NONE) {
+ (void) dmu_objset_create_impl(dp->dp_spa,
+ ds, &ds->ds_phys->ds_bp, type, tx);
+ }
+
+ spa_history_internal_log(LOG_DS_REPLAY_FULL_SYNC,
+ dp->dp_spa, tx, cr, "dataset = %lld", dsobj);
+
+ return (ds);
+}
+
/* ARGSUSED */
static int
-replay_incremental_check(void *arg1, void *arg2, dmu_tx_t *tx)
+recv_full_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
- dsl_dataset_t *ds = arg1;
- struct drr_begin *drrb = arg2;
- const char *snapname;
- int err;
+ dsl_dir_t *dd = arg1;
+ struct recvbeginsyncarg *rbsa = arg2;
+ objset_t *mos = dd->dd_pool->dp_meta_objset;
uint64_t val;
+ int err;
- /* must already be a snapshot of this fs */
- if (ds->ds_phys->ds_prev_snap_obj == 0)
- return (ENODEV);
+ err = zap_lookup(mos, dd->dd_phys->dd_child_dir_zapobj,
+ strrchr(rbsa->tofs, '/') + 1, sizeof (uint64_t), 1, &val);
- /* most recent snapshot must match fromguid */
- if (ds->ds_prev->ds_phys->ds_guid != drrb->drr_fromguid)
- return (ENODEV);
- /* must not have any changes since most recent snapshot */
- if (ds->ds_phys->ds_bp.blk_birth >
- ds->ds_prev->ds_phys->ds_creation_txg)
- return (ETXTBSY);
+ if (err != ENOENT)
+ return (err ? err : EEXIST);
- /* new snapshot name must not exist */
- snapname = strrchr(drrb->drr_toname, '@');
- if (snapname == NULL)
- return (EEXIST);
+ if (rbsa->origin) {
+ /* make sure it's a snap in the same pool */
+ if (rbsa->origin->ds_dir->dd_pool != dd->dd_pool)
+ return (EXDEV);
+ if (rbsa->origin->ds_phys->ds_num_children == 0)
+ return (EINVAL);
+ if (rbsa->origin->ds_phys->ds_guid != rbsa->fromguid)
+ return (ENODEV);
+ }
- snapname++;
- err = zap_lookup(ds->ds_dir->dd_pool->dp_meta_objset,
- ds->ds_phys->ds_snapnames_zapobj, snapname, 8, 1, &val);
- if (err == 0)
- return (EEXIST);
- if (err != ENOENT)
+ return (0);
+}
+
+static void
+recv_full_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
+{
+ dsl_dir_t *dd = arg1;
+ struct recvbeginsyncarg *rbsa = arg2;
+ uint64_t flags = DS_FLAG_INCONSISTENT | rbsa->dsflags;
+ uint64_t dsobj;
+
+ dsobj = dsl_dataset_create_sync(dd, strrchr(rbsa->tofs, '/') + 1,
+ rbsa->origin, flags, cr, tx);
+
+ rbsa->ds = recv_full_sync_impl(dd->dd_pool, dsobj,
+ rbsa->origin ? DMU_OST_NONE : rbsa->type, cr, tx);
+}
+
+static int
+recv_full_existing_check(void *arg1, void *arg2, dmu_tx_t *tx)
+{
+ dsl_dataset_t *ds = arg1;
+ struct recvbeginsyncarg *rbsa = arg2;
+ int err;
+
+ /* must be a head ds */
+ if (ds->ds_phys->ds_next_snap_obj != 0)
+ return (EINVAL);
+
+ /* must not be a clone ds */
+ if (dsl_dir_is_clone(ds->ds_dir))
+ return (EINVAL);
+
+ err = dsl_dataset_destroy_check(ds, rbsa->tag, tx);
+ if (err)
return (err);
+ if (rbsa->origin) {
+ /* make sure it's a snap in the same pool */
+ if (rbsa->origin->ds_dir->dd_pool != ds->ds_dir->dd_pool)
+ return (EXDEV);
+ if (rbsa->origin->ds_phys->ds_num_children == 0)
+ return (EINVAL);
+ if (rbsa->origin->ds_phys->ds_guid != rbsa->fromguid)
+ return (ENODEV);
+ }
+
return (0);
}
-/* ARGSUSED */
static void
-replay_incremental_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+recv_full_existing_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
dsl_dataset_t *ds = arg1;
- dmu_buf_will_dirty(ds->ds_dbuf, tx);
- ds->ds_phys->ds_flags |= DS_FLAG_INCONSISTENT;
+ struct recvbeginsyncarg *rbsa = arg2;
+ dsl_dir_t *dd = ds->ds_dir;
+ uint64_t flags = DS_FLAG_INCONSISTENT | rbsa->dsflags;
+ uint64_t dsobj;
+
+ /*
+ * NB: caller must provide an extra hold on the dsl_dir_t, so it
+ * won't go away when dsl_dataset_destroy_sync() closes the
+ * dataset.
+ */
+ dsl_dataset_destroy_sync(ds, rbsa->tag, cr, tx);
+
+ dsobj = dsl_dataset_create_sync_dd(dd, rbsa->origin, flags, tx);
+
+ rbsa->ds = recv_full_sync_impl(dd->dd_pool, dsobj,
+ rbsa->origin ? DMU_OST_NONE : rbsa->type, cr, tx);
}
/* ARGSUSED */
static int
-replay_full_check(void *arg1, void *arg2, dmu_tx_t *tx)
+recv_incremental_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
- dsl_dir_t *dd = arg1;
- struct drr_begin *drrb = arg2;
- objset_t *mos = dd->dd_pool->dp_meta_objset;
- char *cp;
- uint64_t val;
+ dsl_dataset_t *ds = arg1;
+ struct recvbeginsyncarg *rbsa = arg2;
int err;
+ uint64_t val;
- cp = strchr(drrb->drr_toname, '@');
- *cp = '\0';
- err = zap_lookup(mos, dd->dd_phys->dd_child_dir_zapobj,
- strrchr(drrb->drr_toname, '/') + 1,
- sizeof (uint64_t), 1, &val);
- *cp = '@';
+ /* must not have any changes since most recent snapshot */
+ if (!rbsa->force && dsl_dataset_modified_since_lastsnap(ds))
+ return (ETXTBSY);
+
+ /* must already be a snapshot of this fs */
+ if (ds->ds_phys->ds_prev_snap_obj == 0)
+ return (ENODEV);
+
+ /* most recent snapshot must match fromguid */
+ if (ds->ds_prev->ds_phys->ds_guid != rbsa->fromguid)
+ return (ENODEV);
+ /* temporary clone name must not exist */
+ err = zap_lookup(ds->ds_dir->dd_pool->dp_meta_objset,
+ ds->ds_dir->dd_phys->dd_child_dir_zapobj,
+ rbsa->clonelastname, 8, 1, &val);
+ if (err == 0)
+ return (EEXIST);
if (err != ENOENT)
- return (err ? err : EEXIST);
+ return (err);
+ /* new snapshot name must not exist */
+ err = zap_lookup(ds->ds_dir->dd_pool->dp_meta_objset,
+ ds->ds_phys->ds_snapnames_zapobj, rbsa->tosnap, 8, 1, &val);
+ if (err == 0)
+ return (EEXIST);
+ if (err != ENOENT)
+ return (err);
return (0);
}
+/* ARGSUSED */
static void
-replay_full_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+recv_online_incremental_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
- dsl_dir_t *dd = arg1;
- struct drr_begin *drrb = arg2;
- char *cp;
- dsl_dataset_t *ds;
+ dsl_dataset_t *ohds = arg1;
+ struct recvbeginsyncarg *rbsa = arg2;
+ dsl_pool_t *dp = ohds->ds_dir->dd_pool;
+ dsl_dataset_t *ods, *cds;
+ uint64_t flags = DS_FLAG_INCONSISTENT | rbsa->dsflags;
uint64_t dsobj;
- cp = strchr(drrb->drr_toname, '@');
- *cp = '\0';
- dsobj = dsl_dataset_create_sync(dd, strrchr(drrb->drr_toname, '/') + 1,
- NULL, tx);
- *cp = '@';
+ /* create the temporary clone */
+ VERIFY(0 == dsl_dataset_hold_obj(dp, ohds->ds_phys->ds_prev_snap_obj,
+ FTAG, &ods));
+ dsobj = dsl_dataset_create_sync(ohds->ds_dir,
+ rbsa->clonelastname, ods, flags, cr, tx);
+ dsl_dataset_rele(ods, FTAG);
+
+ /* open the temporary clone */
+ VERIFY(0 == dsl_dataset_own_obj(dp, dsobj,
+ DS_MODE_INCONSISTENT, dmu_recv_tag, &cds));
- VERIFY(0 == dsl_dataset_open_obj(dd->dd_pool, dsobj, NULL,
- DS_MODE_EXCLUSIVE, FTAG, &ds));
+ /* copy the refquota from the target fs to the clone */
+ if (ohds->ds_quota > 0)
+ dsl_dataset_set_quota_sync(cds, &ohds->ds_quota, cr, tx);
- (void) dmu_objset_create_impl(dsl_dataset_get_spa(ds),
- ds, &ds->ds_phys->ds_bp, drrb->drr_type, tx);
+ rbsa->ds = cds;
+
+ spa_history_internal_log(LOG_DS_REPLAY_INC_SYNC,
+ dp->dp_spa, tx, cr, "dataset = %lld", dsobj);
+}
+
+/* ARGSUSED */
+static void
+recv_offline_incremental_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
+{
+ dsl_dataset_t *ds = arg1;
dmu_buf_will_dirty(ds->ds_dbuf, tx);
ds->ds_phys->ds_flags |= DS_FLAG_INCONSISTENT;
- dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG);
+ spa_history_internal_log(LOG_DS_REPLAY_INC_SYNC,
+ ds->ds_dir->dd_pool->dp_spa, tx, cr, "dataset = %lld",
+ ds->ds_object);
}
-static int
-replay_end_check(void *arg1, void *arg2, dmu_tx_t *tx)
+/*
+ * NB: callers *MUST* call dmu_recv_stream() if dmu_recv_begin()
+ * succeeds; otherwise we will leak the holds on the datasets.
+ */
+int
+dmu_recv_begin(char *tofs, char *tosnap, struct drr_begin *drrb,
+ boolean_t force, objset_t *origin, boolean_t online, dmu_recv_cookie_t *drc)
{
- objset_t *os = arg1;
- struct drr_begin *drrb = arg2;
- char *snapname;
+ int err = 0;
+ boolean_t byteswap;
+ struct recvbeginsyncarg rbsa;
+ uint64_t version;
+ int flags;
+ dsl_dataset_t *ds;
- /* XXX verify that drr_toname is in dd */
+ if (drrb->drr_magic == DMU_BACKUP_MAGIC)
+ byteswap = FALSE;
+ else if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC))
+ byteswap = TRUE;
+ else
+ return (EINVAL);
- snapname = strchr(drrb->drr_toname, '@');
- if (snapname == NULL)
+ rbsa.tofs = tofs;
+ rbsa.tosnap = tosnap;
+ rbsa.origin = origin ? origin->os->os_dsl_dataset : NULL;
+ rbsa.fromguid = drrb->drr_fromguid;
+ rbsa.type = drrb->drr_type;
+ rbsa.tag = FTAG;
+ rbsa.dsflags = 0;
+ version = drrb->drr_version;
+ flags = drrb->drr_flags;
+
+ if (byteswap) {
+ rbsa.type = BSWAP_32(rbsa.type);
+ rbsa.fromguid = BSWAP_64(rbsa.fromguid);
+ version = BSWAP_64(version);
+ flags = BSWAP_32(flags);
+ }
+
+ if (version != DMU_BACKUP_STREAM_VERSION ||
+ rbsa.type >= DMU_OST_NUMTYPES ||
+ ((flags & DRR_FLAG_CLONE) && origin == NULL))
return (EINVAL);
- snapname++;
- return (dsl_dataset_snapshot_check(os, snapname, tx));
-}
+ if (flags & DRR_FLAG_CI_DATA)
+ rbsa.dsflags = DS_FLAG_CI_DATASET;
-static void
-replay_end_sync(void *arg1, void *arg2, dmu_tx_t *tx)
-{
- objset_t *os = arg1;
- struct drr_begin *drrb = arg2;
- char *snapname;
- dsl_dataset_t *ds, *hds;
+ bzero(drc, sizeof (dmu_recv_cookie_t));
+ drc->drc_drrb = drrb;
+ drc->drc_tosnap = tosnap;
+ drc->drc_force = force;
- snapname = strchr(drrb->drr_toname, '@') + 1;
+ /*
+ * Process the begin in syncing context.
+ */
+ if (rbsa.fromguid && !(flags & DRR_FLAG_CLONE) && !online) {
+ /* offline incremental receive */
+ err = dsl_dataset_own(tofs, 0, dmu_recv_tag, &ds);
+ if (err)
+ return (err);
- dsl_dataset_snapshot_sync(os, snapname, tx);
+ /*
+ * Only do the rollback if the most recent snapshot
+ * matches the incremental source
+ */
+ if (force) {
+ if (ds->ds_prev == NULL ||
+ ds->ds_prev->ds_phys->ds_guid !=
+ rbsa.fromguid) {
+ dsl_dataset_disown(ds, dmu_recv_tag);
+ return (ENODEV);
+ }
+ (void) dsl_dataset_rollback(ds, DMU_OST_NONE);
+ }
+ rbsa.force = B_FALSE;
+ err = dsl_sync_task_do(ds->ds_dir->dd_pool,
+ recv_incremental_check,
+ recv_offline_incremental_sync, ds, &rbsa, 1);
+ if (err) {
+ dsl_dataset_disown(ds, dmu_recv_tag);
+ return (err);
+ }
+ drc->drc_logical_ds = drc->drc_real_ds = ds;
+ } else if (rbsa.fromguid && !(flags & DRR_FLAG_CLONE)) {
+ /* online incremental receive */
- /* set snapshot's creation time and guid */
- hds = os->os->os_dsl_dataset;
- VERIFY(0 == dsl_dataset_open_obj(hds->ds_dir->dd_pool,
- hds->ds_phys->ds_prev_snap_obj, NULL,
- DS_MODE_PRIMARY | DS_MODE_READONLY | DS_MODE_INCONSISTENT,
- FTAG, &ds));
+ /* tmp clone name is: tofs/%tosnap" */
+ (void) snprintf(rbsa.clonelastname, sizeof (rbsa.clonelastname),
+ "%%%s", tosnap);
- dmu_buf_will_dirty(ds->ds_dbuf, tx);
- ds->ds_phys->ds_creation_time = drrb->drr_creation_time;
- ds->ds_phys->ds_guid = drrb->drr_toguid;
- ds->ds_phys->ds_flags &= ~DS_FLAG_INCONSISTENT;
+ /* open the dataset we are logically receiving into */
+ err = dsl_dataset_hold(tofs, dmu_recv_tag, &ds);
+ if (err)
+ return (err);
- dsl_dataset_close(ds, DS_MODE_PRIMARY, FTAG);
+ rbsa.force = force;
+ err = dsl_sync_task_do(ds->ds_dir->dd_pool,
+ recv_incremental_check,
+ recv_online_incremental_sync, ds, &rbsa, 5);
+ if (err) {
+ dsl_dataset_rele(ds, dmu_recv_tag);
+ return (err);
+ }
+ drc->drc_logical_ds = ds;
+ drc->drc_real_ds = rbsa.ds;
+ } else {
+ /* create new fs -- full backup or clone */
+ dsl_dir_t *dd = NULL;
+ const char *tail;
+
+ err = dsl_dir_open(tofs, FTAG, &dd, &tail);
+ if (err)
+ return (err);
+ if (tail == NULL) {
+ if (!force) {
+ dsl_dir_close(dd, FTAG);
+ return (EEXIST);
+ }
+
+ rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
+ err = dsl_dataset_own_obj(dd->dd_pool,
+ dd->dd_phys->dd_head_dataset_obj,
+ DS_MODE_INCONSISTENT, FTAG, &ds);
+ rw_exit(&dd->dd_pool->dp_config_rwlock);
+ if (err) {
+ dsl_dir_close(dd, FTAG);
+ return (err);
+ }
+
+ dsl_dataset_make_exclusive(ds, FTAG);
+ err = dsl_sync_task_do(dd->dd_pool,
+ recv_full_existing_check,
+ recv_full_existing_sync, ds, &rbsa, 5);
+ dsl_dataset_disown(ds, FTAG);
+ } else {
+ err = dsl_sync_task_do(dd->dd_pool, recv_full_check,
+ recv_full_sync, dd, &rbsa, 5);
+ }
+ dsl_dir_close(dd, FTAG);
+ if (err)
+ return (err);
+ drc->drc_logical_ds = drc->drc_real_ds = rbsa.ds;
+ drc->drc_newfs = B_TRUE;
+ }
- dmu_buf_will_dirty(hds->ds_dbuf, tx);
- hds->ds_phys->ds_flags &= ~DS_FLAG_INCONSISTENT;
+ return (0);
}
+struct restorearg {
+ int err;
+ int byteswap;
+ kthread_t *td;
+ struct file *fp;
+ char *buf;
+ uint64_t voff;
+ int bufsize; /* amount of memory allocated for buf */
+ zio_cksum_t cksum;
+};
+
static int
restore_bytes(struct restorearg *ra, void *buf, int len, off_t off, int *resid)
{
@@ -491,37 +744,31 @@ static void *
restore_read(struct restorearg *ra, int len)
{
void *rv;
+ int done = 0;
/* some things will require 8-byte alignment, so everything must */
ASSERT3U(len % 8, ==, 0);
- while (ra->buflen - ra->bufoff < len) {
+ while (done < len) {
int resid;
- int leftover = ra->buflen - ra->bufoff;
- (void) memmove(ra->buf, ra->buf + ra->bufoff, leftover);
+ ra->err = restore_bytes(ra, (caddr_t)ra->buf + done,
+ len - done, ra->voff, &resid);
- ra->err = restore_bytes(ra, (caddr_t)ra->buf + leftover,
- ra->bufsize - leftover, ra->voff, &resid);
-
- ra->voff += ra->bufsize - leftover - resid;
- ra->buflen = ra->bufsize - resid;
- ra->bufoff = 0;
- if (resid == ra->bufsize - leftover)
+ if (resid == len - done)
ra->err = EINVAL;
+ ra->voff += len - done - resid;
+ done = len - resid;
if (ra->err)
return (NULL);
- /* Could compute checksum here? */
}
- ASSERT3U(ra->bufoff % 8, ==, 0);
- ASSERT3U(ra->buflen - ra->bufoff, >=, len);
- rv = ra->buf + ra->bufoff;
- ra->bufoff += len;
+ ASSERT3U(done, ==, len);
+ rv = ra->buf;
if (ra->byteswap)
- fletcher_4_incremental_byteswap(rv, len, &ra->zc);
+ fletcher_4_incremental_byteswap(rv, len, &ra->cksum);
else
- fletcher_4_incremental_native(rv, len, &ra->zc);
+ fletcher_4_incremental_native(rv, len, &ra->cksum);
return (rv);
}
@@ -531,12 +778,14 @@ backup_byteswap(dmu_replay_record_t *drr)
#define DO64(X) (drr->drr_u.X = BSWAP_64(drr->drr_u.X))
#define DO32(X) (drr->drr_u.X = BSWAP_32(drr->drr_u.X))
drr->drr_type = BSWAP_32(drr->drr_type);
+ drr->drr_payloadlen = BSWAP_32(drr->drr_payloadlen);
switch (drr->drr_type) {
case DRR_BEGIN:
DO64(drr_begin.drr_magic);
DO64(drr_begin.drr_version);
DO64(drr_begin.drr_creation_time);
DO32(drr_begin.drr_type);
+ DO32(drr_begin.drr_flags);
DO64(drr_begin.drr_toguid);
DO64(drr_begin.drr_fromguid);
break;
@@ -643,13 +892,13 @@ restore_object(struct restorearg *ra, objset_t *os, struct drr_object *drro)
VERIFY(0 == dmu_bonus_hold(os, drro->drr_object, FTAG, &db));
dmu_buf_will_dirty(db, tx);
- ASSERT3U(db->db_size, ==, drro->drr_bonuslen);
- data = restore_read(ra, P2ROUNDUP(db->db_size, 8));
+ ASSERT3U(db->db_size, >=, drro->drr_bonuslen);
+ data = restore_read(ra, P2ROUNDUP(drro->drr_bonuslen, 8));
if (data == NULL) {
dmu_tx_commit(tx);
return (ra->err);
}
- bcopy(data, db->db_data, db->db_size);
+ bcopy(data, db->db_data, drro->drr_bonuslen);
if (ra->byteswap) {
dmu_ot[drro->drr_bonustype].ot_byteswap(db->db_data,
drro->drr_bonuslen);
@@ -673,23 +922,14 @@ restore_freeobjects(struct restorearg *ra, objset_t *os,
for (obj = drrfo->drr_firstobj;
obj < drrfo->drr_firstobj + drrfo->drr_numobjs;
(void) dmu_object_next(os, &obj, FALSE, 0)) {
- dmu_tx_t *tx;
int err;
if (dmu_object_info(os, obj, NULL) != 0)
continue;
- tx = dmu_tx_create(os);
- dmu_tx_hold_bonus(tx, obj);
- err = dmu_tx_assign(tx, TXG_WAIT);
- if (err) {
- dmu_tx_abort(tx);
+ err = dmu_free_object(os, obj);
+ if (err)
return (err);
- }
- err = dmu_object_free(os, obj, tx);
- dmu_tx_commit(tx);
- if (err && err != ENOENT)
- return (EINVAL);
}
return (0);
}
@@ -735,7 +975,6 @@ static int
restore_free(struct restorearg *ra, objset_t *os,
struct drr_free *drrf)
{
- dmu_tx_t *tx;
int err;
if (drrf->drr_length != -1ULL &&
@@ -745,66 +984,65 @@ restore_free(struct restorearg *ra, objset_t *os,
if (dmu_object_info(os, drrf->drr_object, NULL) != 0)
return (EINVAL);
- tx = dmu_tx_create(os);
-
- dmu_tx_hold_free(tx, drrf->drr_object,
+ err = dmu_free_long_range(os, drrf->drr_object,
drrf->drr_offset, drrf->drr_length);
- err = dmu_tx_assign(tx, TXG_WAIT);
- if (err) {
- dmu_tx_abort(tx);
- return (err);
- }
- err = dmu_free_range(os, drrf->drr_object,
- drrf->drr_offset, drrf->drr_length, tx);
- dmu_tx_commit(tx);
return (err);
}
+void
+dmu_recv_abort_cleanup(dmu_recv_cookie_t *drc)
+{
+ if (drc->drc_newfs || drc->drc_real_ds != drc->drc_logical_ds) {
+ /*
+ * online incremental or new fs: destroy the fs (which
+ * may be a clone) that we created
+ */
+ (void) dsl_dataset_destroy(drc->drc_real_ds, dmu_recv_tag);
+ if (drc->drc_real_ds != drc->drc_logical_ds)
+ dsl_dataset_rele(drc->drc_logical_ds, dmu_recv_tag);
+ } else {
+ /*
+ * offline incremental: rollback to most recent snapshot.
+ */
+ (void) dsl_dataset_rollback(drc->drc_real_ds, DMU_OST_NONE);
+ dsl_dataset_disown(drc->drc_real_ds, dmu_recv_tag);
+ }
+}
+
+/*
+ * NB: callers *must* call dmu_recv_end() if this succeeds.
+ */
int
-dmu_recvbackup(char *tosnap, struct drr_begin *drrb, uint64_t *sizep,
- boolean_t force, struct file *fp, uint64_t voffset)
+dmu_recv_stream(dmu_recv_cookie_t *drc, struct file *fp, offset_t *voffp)
{
kthread_t *td = curthread;
- struct restorearg ra;
+ struct restorearg ra = { 0 };
dmu_replay_record_t *drr;
- char *cp;
- objset_t *os = NULL;
- zio_cksum_t pzc;
-
- bzero(&ra, sizeof (ra));
- ra.td = td;
- ra.fp = fp;
- ra.voff = voffset;
- ra.bufsize = 1<<20;
- ra.buf = kmem_alloc(ra.bufsize, KM_SLEEP);
+ objset_t *os;
+ zio_cksum_t pcksum;
- if (drrb->drr_magic == DMU_BACKUP_MAGIC) {
- ra.byteswap = FALSE;
- } else if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) {
+ if (drc->drc_drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC))
ra.byteswap = TRUE;
- } else {
- ra.err = EINVAL;
- goto out;
- }
- /*
- * NB: this assumes that struct drr_begin will be the largest in
- * dmu_replay_record_t's drr_u, and thus we don't need to pad it
- * with zeros to make it the same length as we wrote out.
- */
- ((dmu_replay_record_t *)ra.buf)->drr_type = DRR_BEGIN;
- ((dmu_replay_record_t *)ra.buf)->drr_pad = 0;
- ((dmu_replay_record_t *)ra.buf)->drr_u.drr_begin = *drrb;
- if (ra.byteswap) {
- fletcher_4_incremental_byteswap(ra.buf,
- sizeof (dmu_replay_record_t), &ra.zc);
- } else {
- fletcher_4_incremental_native(ra.buf,
- sizeof (dmu_replay_record_t), &ra.zc);
+ {
+ /* compute checksum of drr_begin record */
+ dmu_replay_record_t *drr;
+ drr = kmem_zalloc(sizeof (dmu_replay_record_t), KM_SLEEP);
+
+ drr->drr_type = DRR_BEGIN;
+ drr->drr_u.drr_begin = *drc->drc_drrb;
+ if (ra.byteswap) {
+ fletcher_4_incremental_byteswap(drr,
+ sizeof (dmu_replay_record_t), &ra.cksum);
+ } else {
+ fletcher_4_incremental_native(drr,
+ sizeof (dmu_replay_record_t), &ra.cksum);
+ }
+ kmem_free(drr, sizeof (dmu_replay_record_t));
}
- (void) strcpy(drrb->drr_toname, tosnap); /* for the sync funcs */
if (ra.byteswap) {
+ struct drr_begin *drrb = drc->drc_drrb;
drrb->drr_magic = BSWAP_64(drrb->drr_magic);
drrb->drr_version = BSWAP_64(drrb->drr_version);
drrb->drr_creation_time = BSWAP_64(drrb->drr_creation_time);
@@ -813,94 +1051,30 @@ dmu_recvbackup(char *tosnap, struct drr_begin *drrb, uint64_t *sizep,
drrb->drr_fromguid = BSWAP_64(drrb->drr_fromguid);
}
- ASSERT3U(drrb->drr_magic, ==, DMU_BACKUP_MAGIC);
-
- if (drrb->drr_version != DMU_BACKUP_VERSION ||
- drrb->drr_type >= DMU_OST_NUMTYPES ||
- strchr(drrb->drr_toname, '@') == NULL) {
- ra.err = EINVAL;
- goto out;
- }
-
- /*
- * Process the begin in syncing context.
- */
- if (drrb->drr_fromguid) {
- /* incremental backup */
- dsl_dataset_t *ds = NULL;
-
- cp = strchr(tosnap, '@');
- *cp = '\0';
- ra.err = dsl_dataset_open(tosnap, DS_MODE_EXCLUSIVE, FTAG, &ds);
- *cp = '@';
- if (ra.err)
- goto out;
-
- /*
- * Only do the rollback if the most recent snapshot
- * matches the incremental source
- */
- if (force) {
- if (ds->ds_prev == NULL ||
- ds->ds_prev->ds_phys->ds_guid !=
- drrb->drr_fromguid) {
- dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG);
- kmem_free(ra.buf, ra.bufsize);
- return (ENODEV);
- }
- (void) dsl_dataset_rollback(ds);
- }
- ra.err = dsl_sync_task_do(ds->ds_dir->dd_pool,
- replay_incremental_check, replay_incremental_sync,
- ds, drrb, 1);
- dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG);
- } else {
- /* full backup */
- dsl_dir_t *dd = NULL;
- const char *tail;
-
- /* can't restore full backup into topmost fs, for now */
- if (strrchr(drrb->drr_toname, '/') == NULL) {
- ra.err = EINVAL;
- goto out;
- }
-
- cp = strchr(tosnap, '@');
- *cp = '\0';
- ra.err = dsl_dir_open(tosnap, FTAG, &dd, &tail);
- *cp = '@';
- if (ra.err)
- goto out;
- if (tail == NULL) {
- ra.err = EEXIST;
- goto out;
- }
+ ra.td = td;
+ ra.fp = fp;
+ ra.voff = *voffp;
+ ra.bufsize = 1<<20;
+ ra.buf = kmem_alloc(ra.bufsize, KM_SLEEP);
- ra.err = dsl_sync_task_do(dd->dd_pool, replay_full_check,
- replay_full_sync, dd, drrb, 5);
- dsl_dir_close(dd, FTAG);
- }
- if (ra.err)
- goto out;
+ /* these were verified in dmu_recv_begin */
+ ASSERT(drc->drc_drrb->drr_version == DMU_BACKUP_STREAM_VERSION);
+ ASSERT(drc->drc_drrb->drr_type < DMU_OST_NUMTYPES);
/*
* Open the objset we are modifying.
*/
+ VERIFY(dmu_objset_open_ds(drc->drc_real_ds, DMU_OST_ANY, &os) == 0);
- cp = strchr(tosnap, '@');
- *cp = '\0';
- ra.err = dmu_objset_open(tosnap, DMU_OST_ANY,
- DS_MODE_PRIMARY | DS_MODE_INCONSISTENT, &os);
- *cp = '@';
- ASSERT3U(ra.err, ==, 0);
+ ASSERT(drc->drc_real_ds->ds_phys->ds_flags & DS_FLAG_INCONSISTENT);
/*
* Read records and process them.
*/
- pzc = ra.zc;
+ pcksum = ra.cksum;
while (ra.err == 0 &&
NULL != (drr = restore_read(&ra, sizeof (*drr)))) {
- if (SIGPENDING(td)) {
+ if (issig(JUSTLOOKING) && issig(FORREAL)) {
ra.err = EINTR;
goto out;
}
@@ -947,63 +1121,116 @@ dmu_recvbackup(char *tosnap, struct drr_begin *drrb, uint64_t *sizep,
* value, because the stored checksum is of
* everything before the DRR_END record.
*/
- if (drre.drr_checksum.zc_word[0] != 0 &&
- !ZIO_CHECKSUM_EQUAL(drre.drr_checksum, pzc)) {
+ if (!ZIO_CHECKSUM_EQUAL(drre.drr_checksum, pcksum))
ra.err = ECKSUM;
- goto out;
- }
-
- ra.err = dsl_sync_task_do(dmu_objset_ds(os)->
- ds_dir->dd_pool, replay_end_check, replay_end_sync,
- os, drrb, 3);
goto out;
}
default:
ra.err = EINVAL;
goto out;
}
- pzc = ra.zc;
+ pcksum = ra.cksum;
}
+ ASSERT(ra.err != 0);
out:
- if (os)
- dmu_objset_close(os);
+ dmu_objset_close(os);
- /*
- * Make sure we don't rollback/destroy unless we actually
- * processed the begin properly. 'os' will only be set if this
- * is the case.
- */
- if (ra.err && os && tosnap && strchr(tosnap, '@')) {
+ if (ra.err != 0) {
/*
* rollback or destroy what we created, so we don't
* leave it in the restoring state.
*/
- dsl_dataset_t *ds;
- int err;
-
- cp = strchr(tosnap, '@');
- *cp = '\0';
- err = dsl_dataset_open(tosnap,
- DS_MODE_EXCLUSIVE | DS_MODE_INCONSISTENT,
- FTAG, &ds);
- if (err == 0) {
- txg_wait_synced(ds->ds_dir->dd_pool, 0);
- if (drrb->drr_fromguid) {
- /* incremental: rollback to most recent snap */
- (void) dsl_dataset_rollback(ds);
- dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG);
- } else {
- /* full: destroy whole fs */
- dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG);
- (void) dsl_dataset_destroy(tosnap);
- }
- }
- *cp = '@';
+ txg_wait_synced(drc->drc_real_ds->ds_dir->dd_pool, 0);
+ dmu_recv_abort_cleanup(drc);
}
kmem_free(ra.buf, ra.bufsize);
- if (sizep)
- *sizep = ra.voff;
+ *voffp = ra.voff;
return (ra.err);
}
+
+struct recvendsyncarg {
+ char *tosnap;
+ uint64_t creation_time;
+ uint64_t toguid;
+};
+
+static int
+recv_end_check(void *arg1, void *arg2, dmu_tx_t *tx)
+{
+ dsl_dataset_t *ds = arg1;
+ struct recvendsyncarg *resa = arg2;
+
+ return (dsl_dataset_snapshot_check(ds, resa->tosnap, tx));
+}
+
+static void
+recv_end_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
+{
+ dsl_dataset_t *ds = arg1;
+ struct recvendsyncarg *resa = arg2;
+
+ dsl_dataset_snapshot_sync(ds, resa->tosnap, cr, tx);
+
+ /* set snapshot's creation time and guid */
+ dmu_buf_will_dirty(ds->ds_prev->ds_dbuf, tx);
+ ds->ds_prev->ds_phys->ds_creation_time = resa->creation_time;
+ ds->ds_prev->ds_phys->ds_guid = resa->toguid;
+ ds->ds_prev->ds_phys->ds_flags &= ~DS_FLAG_INCONSISTENT;
+
+ dmu_buf_will_dirty(ds->ds_dbuf, tx);
+ ds->ds_phys->ds_flags &= ~DS_FLAG_INCONSISTENT;
+}
+
+int
+dmu_recv_end(dmu_recv_cookie_t *drc)
+{
+ struct recvendsyncarg resa;
+ dsl_dataset_t *ds = drc->drc_logical_ds;
+ int err;
+
+ /*
+ * XXX hack; seems the ds is still dirty and
+ * dsl_pool_zil_clean() expects it to have a ds_user_ptr
+ * (and zil), but clone_swap() can close it.
+ */
+ txg_wait_synced(ds->ds_dir->dd_pool, 0);
+
+ if (ds != drc->drc_real_ds) {
+ /* we are doing an online recv */
+ if (dsl_dataset_tryown(ds, FALSE, dmu_recv_tag)) {
+ err = dsl_dataset_clone_swap(drc->drc_real_ds, ds,
+ drc->drc_force);
+ if (err)
+ dsl_dataset_disown(ds, dmu_recv_tag);
+ } else {
+ err = EBUSY;
+ dsl_dataset_rele(ds, dmu_recv_tag);
+ }
+ /* dsl_dataset_destroy() will disown the ds */
+ (void) dsl_dataset_destroy(drc->drc_real_ds, dmu_recv_tag);
+ if (err)
+ return (err);
+ }
+
+ resa.creation_time = drc->drc_drrb->drr_creation_time;
+ resa.toguid = drc->drc_drrb->drr_toguid;
+ resa.tosnap = drc->drc_tosnap;
+
+ err = dsl_sync_task_do(ds->ds_dir->dd_pool,
+ recv_end_check, recv_end_sync, ds, &resa, 3);
+ if (err) {
+ if (drc->drc_newfs) {
+ ASSERT(ds == drc->drc_real_ds);
+ (void) dsl_dataset_destroy(ds, dmu_recv_tag);
+ return (err);
+ } else {
+ (void) dsl_dataset_rollback(ds, DMU_OST_NONE);
+ }
+ }
+
+ /* release the hold from dmu_recv_begin */
+ dsl_dataset_disown(ds, dmu_recv_tag);
+ return (err);
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_traverse.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_traverse.c
index 3d2bc3e..43bf82e 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_traverse.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_traverse.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -35,6 +35,7 @@
#include <sys/spa.h>
#include <sys/zio.h>
#include <sys/dmu_impl.h>
+#include <sys/zvol.h>
#define BP_SPAN_SHIFT(level, width) ((level) * (width))
@@ -261,6 +262,16 @@ advance_block(zseg_t *zseg, dnode_phys_t *dnp, int rc, int advance)
return (EAGAIN);
}
+/*
+ * The traverse_callback function will call the function specified in th_func.
+ * In the event of an error the callee, specified by th_func, must return
+ * one of the following errors:
+ *
+ * EINTR - Indicates that the callee wants the traversal to
+ * abort immediately.
+ * ERESTART - The callee has acknowledged the error and would
+ * like to continue.
+ */
static int
traverse_callback(traverse_handle_t *th, zseg_t *zseg, traverse_blk_cache_t *bc)
{
@@ -603,7 +614,10 @@ traverse_segment(traverse_handle_t *th, zseg_t *zseg, blkptr_t *mosbp)
th->th_locked = 0;
}
- rc = traverse_read(th, bc, &dsp->ds_bp, dn);
+ if (BP_IS_HOLE(&dsp->ds_bp))
+ rc = ERESTART;
+ else
+ rc = traverse_read(th, bc, &dsp->ds_bp, dn);
if (rc != 0) {
if (rc == ERESTART)
@@ -722,6 +736,24 @@ traverse_dsl_dataset(dsl_dataset_t *ds, uint64_t txg_start, int advance,
}
int
+traverse_zvol(objset_t *os, int advance, blkptr_cb_t func, void *arg)
+{
+ spa_t *spa = dmu_objset_spa(os);
+ traverse_handle_t *th;
+ int err;
+
+ th = traverse_init(spa, func, arg, advance, ZIO_FLAG_CANFAIL);
+
+ traverse_add_dnode(th, 0, -1ULL, dmu_objset_id(os), ZVOL_OBJ);
+
+ while ((err = traverse_more(th)) == EAGAIN)
+ continue;
+
+ traverse_fini(th);
+ return (err);
+}
+
+int
traverse_more(traverse_handle_t *th)
{
zseg_t *zseg = list_head(&th->th_seglist);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_tx.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_tx.c
index 13fd8d4..000c3ce 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_tx.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_tx.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/dmu.h>
#include <sys/dmu_impl.h>
#include <sys/dbuf.h>
@@ -157,7 +155,7 @@ dmu_tx_check_ioerr(zio_t *zio, dnode_t *dn, int level, uint64_t blkid)
rw_exit(&dn->dn_struct_rwlock);
if (db == NULL)
return (EIO);
- err = dbuf_read(db, zio, DB_RF_CANFAIL);
+ err = dbuf_read(db, zio, DB_RF_CANFAIL | DB_RF_NOPREFETCH);
dbuf_rele(db, FTAG);
return (err);
}
@@ -294,6 +292,8 @@ dmu_tx_count_dnode(dmu_tx_hold_t *txh)
txh->txh_space_tooverwrite += space;
} else {
txh->txh_space_towrite += space;
+ if (dn && dn->dn_dbuf->db_blkptr)
+ txh->txh_space_tounref += space;
}
}
@@ -318,39 +318,25 @@ dmu_tx_hold_write(dmu_tx_t *tx, uint64_t object, uint64_t off, int len)
static void
dmu_tx_count_free(dmu_tx_hold_t *txh, uint64_t off, uint64_t len)
{
- uint64_t blkid, nblks;
- uint64_t space = 0;
+ uint64_t blkid, nblks, lastblk;
+ uint64_t space = 0, unref = 0, skipped = 0;
dnode_t *dn = txh->txh_dnode;
dsl_dataset_t *ds = dn->dn_objset->os_dsl_dataset;
spa_t *spa = txh->txh_tx->tx_pool->dp_spa;
- int dirty;
+ int epbs;
- /*
- * We don't need to use any locking to check for dirtyness
- * because it's OK if we get stale data -- the dnode may become
- * dirty immediately after our check anyway. This is just a
- * means to avoid the expensive count when we aren't sure we
- * need it. We need to be able to deal with a dirty dnode.
- */
- dirty = list_link_active(&dn->dn_dirty_link[0]) |
- list_link_active(&dn->dn_dirty_link[1]) |
- list_link_active(&dn->dn_dirty_link[2]) |
- list_link_active(&dn->dn_dirty_link[3]);
- if (dirty || dn->dn_assigned_txg || dn->dn_phys->dn_nlevels == 0)
+ if (dn->dn_nlevels == 0)
return;
/*
- * the struct_rwlock protects us against dn_phys->dn_nlevels
+ * The struct_rwlock protects us against dn_nlevels
* changing, in case (against all odds) we manage to dirty &
* sync out the changes after we check for being dirty.
- * also, dbuf_hold_impl() wants us to have the struct_rwlock.
- *
- * It's fine to use dn_datablkshift rather than the dn_phys
- * equivalent because if it is changing, maxblkid==0 and we will
- * bail.
+ * Also, dbuf_hold_level() wants us to have the struct_rwlock.
*/
rw_enter(&dn->dn_struct_rwlock, RW_READER);
- if (dn->dn_phys->dn_maxblkid == 0) {
+ epbs = dn->dn_indblkshift - SPA_BLKPTRSHIFT;
+ if (dn->dn_maxblkid == 0) {
if (off == 0 && len >= dn->dn_datablksz) {
blkid = 0;
nblks = 1;
@@ -360,78 +346,120 @@ dmu_tx_count_free(dmu_tx_hold_t *txh, uint64_t off, uint64_t len)
}
} else {
blkid = off >> dn->dn_datablkshift;
- nblks = (off + len) >> dn->dn_datablkshift;
+ nblks = (len + dn->dn_datablksz - 1) >> dn->dn_datablkshift;
- if (blkid >= dn->dn_phys->dn_maxblkid) {
+ if (blkid >= dn->dn_maxblkid) {
rw_exit(&dn->dn_struct_rwlock);
return;
}
- if (blkid + nblks > dn->dn_phys->dn_maxblkid)
- nblks = dn->dn_phys->dn_maxblkid - blkid;
+ if (blkid + nblks > dn->dn_maxblkid)
+ nblks = dn->dn_maxblkid - blkid;
- /* don't bother after 128,000 blocks */
- nblks = MIN(nblks, 128*1024);
}
-
- if (dn->dn_phys->dn_nlevels == 1) {
+ if (dn->dn_nlevels == 1) {
int i;
for (i = 0; i < nblks; i++) {
blkptr_t *bp = dn->dn_phys->dn_blkptr;
- ASSERT3U(blkid + i, <, dn->dn_phys->dn_nblkptr);
+ ASSERT3U(blkid + i, <, dn->dn_nblkptr);
bp += blkid + i;
if (dsl_dataset_block_freeable(ds, bp->blk_birth)) {
dprintf_bp(bp, "can free old%s", "");
space += bp_get_dasize(spa, bp);
}
+ unref += BP_GET_ASIZE(bp);
}
nblks = 0;
}
+ /*
+ * Add in memory requirements of higher-level indirects.
+ * This assumes a worst-possible scenario for dn_nlevels.
+ */
+ {
+ uint64_t blkcnt = 1 + ((nblks >> epbs) >> epbs);
+ int level = (dn->dn_nlevels > 1) ? 2 : 1;
+
+ while (level++ < DN_MAX_LEVELS) {
+ txh->txh_memory_tohold += blkcnt << dn->dn_indblkshift;
+ blkcnt = 1 + (blkcnt >> epbs);
+ }
+ ASSERT(blkcnt <= dn->dn_nblkptr);
+ }
+
+ lastblk = blkid + nblks - 1;
while (nblks) {
dmu_buf_impl_t *dbuf;
- int err, epbs, blkoff, tochk;
-
- epbs = dn->dn_indblkshift - SPA_BLKPTRSHIFT;
- blkoff = P2PHASE(blkid, 1<<epbs);
- tochk = MIN((1<<epbs) - blkoff, nblks);
-
- err = dbuf_hold_impl(dn, 1, blkid >> epbs, TRUE, FTAG, &dbuf);
- if (err == 0) {
- int i;
- blkptr_t *bp;
-
- err = dbuf_read(dbuf, NULL,
- DB_RF_HAVESTRUCT | DB_RF_CANFAIL);
- if (err != 0) {
- txh->txh_tx->tx_err = err;
- dbuf_rele(dbuf, FTAG);
- break;
- }
+ uint64_t ibyte, new_blkid;
+ int epb = 1 << epbs;
+ int err, i, blkoff, tochk;
+ blkptr_t *bp;
+
+ ibyte = blkid << dn->dn_datablkshift;
+ err = dnode_next_offset(dn,
+ DNODE_FIND_HAVELOCK, &ibyte, 2, 1, 0);
+ new_blkid = ibyte >> dn->dn_datablkshift;
+ if (err == ESRCH) {
+ skipped += (lastblk >> epbs) - (blkid >> epbs) + 1;
+ break;
+ }
+ if (err) {
+ txh->txh_tx->tx_err = err;
+ break;
+ }
+ if (new_blkid > lastblk) {
+ skipped += (lastblk >> epbs) - (blkid >> epbs) + 1;
+ break;
+ }
- bp = dbuf->db.db_data;
- bp += blkoff;
+ if (new_blkid > blkid) {
+ ASSERT((new_blkid >> epbs) > (blkid >> epbs));
+ skipped += (new_blkid >> epbs) - (blkid >> epbs) - 1;
+ nblks -= new_blkid - blkid;
+ blkid = new_blkid;
+ }
+ blkoff = P2PHASE(blkid, epb);
+ tochk = MIN(epb - blkoff, nblks);
- for (i = 0; i < tochk; i++) {
- if (dsl_dataset_block_freeable(ds,
- bp[i].blk_birth)) {
- dprintf_bp(&bp[i],
- "can free old%s", "");
- space += bp_get_dasize(spa, &bp[i]);
- }
- }
+ dbuf = dbuf_hold_level(dn, 1, blkid >> epbs, FTAG);
+
+ txh->txh_memory_tohold += dbuf->db.db_size;
+ if (txh->txh_memory_tohold > DMU_MAX_ACCESS) {
+ txh->txh_tx->tx_err = E2BIG;
dbuf_rele(dbuf, FTAG);
+ break;
}
- if (err && err != ENOENT) {
+ err = dbuf_read(dbuf, NULL, DB_RF_HAVESTRUCT | DB_RF_CANFAIL);
+ if (err != 0) {
txh->txh_tx->tx_err = err;
+ dbuf_rele(dbuf, FTAG);
break;
}
+ bp = dbuf->db.db_data;
+ bp += blkoff;
+
+ for (i = 0; i < tochk; i++) {
+ if (dsl_dataset_block_freeable(ds, bp[i].blk_birth)) {
+ dprintf_bp(&bp[i], "can free old%s", "");
+ space += bp_get_dasize(spa, &bp[i]);
+ }
+ unref += BP_GET_ASIZE(bp);
+ }
+ dbuf_rele(dbuf, FTAG);
+
blkid += tochk;
nblks -= tochk;
}
rw_exit(&dn->dn_struct_rwlock);
+ /* account for new level 1 indirect blocks that might show up */
+ if (skipped > 0) {
+ txh->txh_fudge += skipped << dn->dn_indblkshift;
+ skipped = MIN(skipped, DMU_MAX_DELETEBLKCNT >> epbs);
+ txh->txh_memory_tohold += skipped << dn->dn_indblkshift;
+ }
txh->txh_space_tofree += space;
+ txh->txh_space_tounref += unref;
}
void
@@ -466,7 +494,7 @@ dmu_tx_hold_free(dmu_tx_t *tx, uint64_t object, uint64_t off, uint64_t len)
/*
* For i/o error checking, read the first and last level-0
* blocks, and all the level-1 blocks. The above count_write's
- * will take care of the level-0 blocks.
+ * have already taken care of the level-0 blocks.
*/
if (dn->dn_nlevels > 1) {
shift = dn->dn_datablkshift + dn->dn_indblkshift -
@@ -478,7 +506,7 @@ dmu_tx_hold_free(dmu_tx_t *tx, uint64_t object, uint64_t off, uint64_t len)
NULL, NULL, ZIO_FLAG_CANFAIL);
for (i = start; i <= end; i++) {
uint64_t ibyte = i << shift;
- err = dnode_next_offset(dn, FALSE, &ibyte, 2, 1, 0);
+ err = dnode_next_offset(dn, 0, &ibyte, 2, 1, 0);
i = ibyte >> shift;
if (err == ESRCH)
break;
@@ -550,10 +578,13 @@ dmu_tx_hold_zap(dmu_tx_t *tx, uint64_t object, int add, char *name)
* the size will change between now and the dbuf dirty call.
*/
if (dsl_dataset_block_freeable(dn->dn_objset->os_dsl_dataset,
- dn->dn_phys->dn_blkptr[0].blk_birth))
+ dn->dn_phys->dn_blkptr[0].blk_birth)) {
txh->txh_space_tooverwrite += SPA_MAXBLOCKSIZE;
- else
+ } else {
txh->txh_space_towrite += SPA_MAXBLOCKSIZE;
+ txh->txh_space_tounref +=
+ BP_GET_ASIZE(dn->dn_phys->dn_blkptr);
+ }
return;
}
@@ -575,7 +606,7 @@ dmu_tx_hold_zap(dmu_tx_t *tx, uint64_t object, int add, char *name)
* 3 new blocks written if adding: new split leaf, 2 grown ptrtbl blocks
*/
dmu_tx_count_write(txh, dn->dn_maxblkid * dn->dn_datablksz,
- (3 + add ? 3 : 0) << dn->dn_datablkshift);
+ (3 + (add ? 3 : 0)) << dn->dn_datablkshift);
/*
* If the modified blocks are scattered to the four winds,
@@ -698,12 +729,13 @@ dmu_tx_dirty_buf(dmu_tx_t *tx, dmu_buf_impl_t *db)
match_offset = TRUE;
break;
case THT_FREE:
- if (blkid == beginblk &&
- (txh->txh_arg1 != 0 ||
- dn->dn_maxblkid == 0))
- match_offset = TRUE;
- if (blkid == endblk &&
- txh->txh_arg2 != DMU_OBJECT_END)
+ /*
+ * We will dirty all the level 1 blocks in
+ * the free range and perhaps the first and
+ * last level 0 block.
+ */
+ if (blkid >= beginblk && (blkid <= endblk ||
+ txh->txh_arg2 == DMU_OBJECT_END))
match_offset = TRUE;
break;
case THT_BONUS:
@@ -733,12 +765,32 @@ static int
dmu_tx_try_assign(dmu_tx_t *tx, uint64_t txg_how)
{
dmu_tx_hold_t *txh;
- uint64_t lsize, asize, fsize, towrite, tofree, tooverwrite;
+ spa_t *spa = tx->tx_pool->dp_spa;
+ uint64_t memory, asize, fsize, usize;
+ uint64_t towrite, tofree, tooverwrite, tounref, tohold, fudge;
ASSERT3U(tx->tx_txg, ==, 0);
+
if (tx->tx_err)
return (tx->tx_err);
+ if (spa_suspended(spa)) {
+ /*
+ * If the user has indicated a blocking failure mode
+ * then return ERESTART which will block in dmu_tx_wait().
+ * Otherwise, return EIO so that an error can get
+ * propagated back to the VOP calls.
+ *
+ * Note that we always honor the txg_how flag regardless
+ * of the failuremode setting.
+ */
+ if (spa_get_failmode(spa) == ZIO_FAILURE_MODE_CONTINUE &&
+ txg_how != TXG_WAIT)
+ return (EIO);
+
+ return (ERESTART);
+ }
+
tx->tx_txg = txg_hold_open(tx->tx_pool, &tx->tx_txgh);
tx->tx_needassign_txh = NULL;
@@ -748,7 +800,7 @@ dmu_tx_try_assign(dmu_tx_t *tx, uint64_t txg_how)
* dmu_tx_unassign() logic.
*/
- towrite = tofree = tooverwrite = 0;
+ towrite = tofree = tooverwrite = tounref = tohold = fudge = 0;
for (txh = list_head(&tx->tx_holds); txh;
txh = list_next(&tx->tx_holds, txh)) {
dnode_t *dn = txh->txh_dnode;
@@ -768,6 +820,9 @@ dmu_tx_try_assign(dmu_tx_t *tx, uint64_t txg_how)
towrite += txh->txh_space_towrite;
tofree += txh->txh_space_tofree;
tooverwrite += txh->txh_space_tooverwrite;
+ tounref += txh->txh_space_tounref;
+ tohold += txh->txh_memory_tohold;
+ fudge += txh->txh_fudge;
}
/*
@@ -788,22 +843,31 @@ dmu_tx_try_assign(dmu_tx_t *tx, uint64_t txg_how)
tooverwrite = tofree = 0;
}
- /*
- * Convert logical size to worst-case allocated size.
- */
+ /* needed allocation: worst-case estimate of write space */
+ asize = spa_get_asize(tx->tx_pool->dp_spa, towrite + tooverwrite);
+ /* freed space estimate: worst-case overwrite + free estimate */
fsize = spa_get_asize(tx->tx_pool->dp_spa, tooverwrite) + tofree;
- lsize = towrite + tooverwrite;
- asize = spa_get_asize(tx->tx_pool->dp_spa, lsize);
+ /* convert unrefd space to worst-case estimate */
+ usize = spa_get_asize(tx->tx_pool->dp_spa, tounref);
+ /* calculate memory footprint estimate */
+ memory = towrite + tooverwrite + tohold;
#ifdef ZFS_DEBUG
- tx->tx_space_towrite = asize;
+ /*
+ * Add in 'tohold' to account for our dirty holds on this memory
+ * XXX - the "fudge" factor is to account for skipped blocks that
+ * we missed because dnode_next_offset() misses in-core-only blocks.
+ */
+ tx->tx_space_towrite = asize +
+ spa_get_asize(tx->tx_pool->dp_spa, tohold + fudge);
tx->tx_space_tofree = tofree;
tx->tx_space_tooverwrite = tooverwrite;
+ tx->tx_space_tounref = tounref;
#endif
if (tx->tx_dir && asize != 0) {
- int err = dsl_dir_tempreserve_space(tx->tx_dir,
- lsize, asize, fsize, &tx->tx_tempreserve_cookie, tx);
+ int err = dsl_dir_tempreserve_space(tx->tx_dir, memory,
+ asize, fsize, usize, &tx->tx_tempreserve_cookie, tx);
if (err)
return (err);
}
@@ -885,10 +949,18 @@ dmu_tx_assign(dmu_tx_t *tx, uint64_t txg_how)
void
dmu_tx_wait(dmu_tx_t *tx)
{
+ spa_t *spa = tx->tx_pool->dp_spa;
+
ASSERT(tx->tx_txg == 0);
- ASSERT(tx->tx_lasttried_txg != 0);
- if (tx->tx_needassign_txh) {
+ /*
+ * It's possible that the pool has become active after this thread
+ * has tried to obtain a tx. If that's the case then his
+ * tx_lasttried_txg would not have been assigned.
+ */
+ if (spa_suspended(spa) || tx->tx_lasttried_txg == 0) {
+ txg_wait_synced(tx->tx_pool, spa_last_synced_txg(spa) + 1);
+ } else if (tx->tx_needassign_txh) {
dnode_t *dn = tx->tx_needassign_txh->txh_dnode;
mutex_enter(&dn->dn_mtx);
@@ -948,6 +1020,7 @@ dmu_tx_commit(dmu_tx_t *tx)
if (tx->tx_anyobj == FALSE)
txg_rele_to_sync(&tx->tx_txgh);
+ list_destroy(&tx->tx_holds);
#ifdef ZFS_DEBUG
dprintf("towrite=%llu written=%llu tofree=%llu freed=%llu\n",
tx->tx_space_towrite, refcount_count(&tx->tx_space_written),
@@ -975,6 +1048,7 @@ dmu_tx_abort(dmu_tx_t *tx)
if (dn != NULL)
dnode_rele(dn, tx);
}
+ list_destroy(&tx->tx_holds);
#ifdef ZFS_DEBUG
refcount_destroy_many(&tx->tx_space_written,
refcount_count(&tx->tx_space_written));
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_zfetch.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_zfetch.c
index b25cc89..8dba381 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_zfetch.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_zfetch.c
@@ -38,10 +38,6 @@
*/
int zfs_prefetch_disable = 0;
-SYSCTL_DECL(_vfs_zfs);
-TUNABLE_INT("vfs.zfs.prefetch_disable", &zfs_prefetch_disable);
-SYSCTL_INT(_vfs_zfs, OID_AUTO, prefetch_disable, CTLFLAG_RDTUN,
- &zfs_prefetch_disable, 0, "Disable prefetch");
/* max # of streams per zfetch */
uint32_t zfetch_max_streams = 8;
@@ -52,6 +48,25 @@ uint32_t zfetch_block_cap = 256;
/* number of bytes in a array_read at which we stop prefetching (1Mb) */
uint64_t zfetch_array_rd_sz = 1024 * 1024;
+SYSCTL_DECL(_vfs_zfs);
+TUNABLE_INT("vfs.zfs.prefetch_disable", &zfs_prefetch_disable);
+SYSCTL_INT(_vfs_zfs, OID_AUTO, prefetch_disable, CTLFLAG_RDTUN,
+ &zfs_prefetch_disable, 0, "Disable prefetch");
+SYSCTL_NODE(_vfs_zfs, OID_AUTO, zfetch, CTLFLAG_RW, 0, "ZFS ZFETCH");
+TUNABLE_INT("vfs.zfs.zfetch.max_streams", &zfetch_max_streams);
+SYSCTL_UINT(_vfs_zfs_zfetch, OID_AUTO, max_streams, CTLFLAG_RDTUN,
+ &zfetch_max_streams, 0, "Max # of streams per zfetch");
+TUNABLE_INT("vfs.zfs.zfetch.min_sec_reap", &zfetch_min_sec_reap);
+SYSCTL_UINT(_vfs_zfs_zfetch, OID_AUTO, min_sec_reap, CTLFLAG_RDTUN,
+ &zfetch_min_sec_reap, 0, "Min time before stream reclaim");
+TUNABLE_INT("vfs.zfs.zfetch.block_cap", &zfetch_block_cap);
+SYSCTL_UINT(_vfs_zfs_zfetch, OID_AUTO, block_cap, CTLFLAG_RDTUN,
+ &zfetch_block_cap, 0, "Max number of blocks to fetch at a time");
+TUNABLE_QUAD("vfs.zfs.zfetch.array_rd_sz", &zfetch_array_rd_sz);
+SYSCTL_QUAD(_vfs_zfs_zfetch, OID_AUTO, array_rd_sz, CTLFLAG_RDTUN,
+ &zfetch_array_rd_sz, 0,
+ "Number of bytes in a array_read at which we stop prefetching");
+
/* forward decls for static routines */
static int dmu_zfetch_colinear(zfetch_t *, zstream_t *);
static void dmu_zfetch_dofetch(zfetch_t *, zstream_t *);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dnode.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dnode.c
index ca50285..5adbc3c 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dnode.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dnode.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/zfs_context.h>
#include <sys/dbuf.h>
#include <sys/dnode.h>
@@ -242,6 +240,23 @@ free_range_compar(const void *node1, const void *node2)
else return (0);
}
+void
+dnode_setbonuslen(dnode_t *dn, int newsize, dmu_tx_t *tx)
+{
+ ASSERT3U(refcount_count(&dn->dn_holds), >=, 1);
+
+ dnode_setdirty(dn, tx);
+ rw_enter(&dn->dn_struct_rwlock, RW_WRITER);
+ ASSERT3U(newsize, <=, DN_MAX_BONUSLEN -
+ (dn->dn_nblkptr-1) * sizeof (blkptr_t));
+ dn->dn_bonuslen = newsize;
+ if (newsize == 0)
+ dn->dn_next_bonuslen[tx->tx_txg & TXG_MASK] = DN_ZERO_BONUSLEN;
+ else
+ dn->dn_next_bonuslen[tx->tx_txg & TXG_MASK] = dn->dn_bonuslen;
+ rw_exit(&dn->dn_struct_rwlock);
+}
+
static void
dnode_setdblksz(dnode_t *dn, int size)
{
@@ -285,6 +300,7 @@ dnode_create(objset_impl_t *os, dnode_phys_t *dnp, dmu_buf_impl_t *db,
list_insert_head(&os->os_dnodes, dn);
mutex_exit(&os->os_lock);
+ arc_space_consume(sizeof (dnode_t));
return (dn);
}
@@ -319,6 +335,7 @@ dnode_destroy(dnode_t *dn)
dn->dn_bonus = NULL;
}
kmem_cache_free(dnode_cache, dn);
+ arc_space_return(sizeof (dnode_t));
}
void
@@ -362,6 +379,7 @@ dnode_allocate(dnode_t *dn, dmu_object_type_t ot, int blocksize, int ibs,
for (i = 0; i < TXG_SIZE; i++) {
ASSERT3U(dn->dn_next_nlevels[i], ==, 0);
ASSERT3U(dn->dn_next_indblkshift[i], ==, 0);
+ ASSERT3U(dn->dn_next_bonuslen[i], ==, 0);
ASSERT3U(dn->dn_next_blksz[i], ==, 0);
ASSERT(!list_link_active(&dn->dn_dirty_link[i]));
ASSERT3P(list_head(&dn->dn_dirty_records[i]), ==, NULL);
@@ -389,6 +407,7 @@ dnode_allocate(dnode_t *dn, dmu_object_type_t ot, int blocksize, int ibs,
dnode_setdirty(dn, tx);
dn->dn_next_indblkshift[tx->tx_txg & TXG_MASK] = ibs;
+ dn->dn_next_bonuslen[tx->tx_txg & TXG_MASK] = dn->dn_bonuslen;
dn->dn_next_blksz[tx->tx_txg & TXG_MASK] = dn->dn_datablksz;
}
@@ -396,7 +415,7 @@ void
dnode_reallocate(dnode_t *dn, dmu_object_type_t ot, int blocksize,
dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx)
{
- int i;
+ int i, old_nblkptr;
dmu_buf_impl_t *db = NULL;
ASSERT3U(blocksize, >=, SPA_MINBLOCKSIZE);
@@ -413,7 +432,7 @@ dnode_reallocate(dnode_t *dn, dmu_object_type_t ot, int blocksize,
ASSERT(!list_link_active(&dn->dn_dirty_link[i]));
/* clean up any unreferenced dbufs */
- (void) dnode_evict_dbufs(dn, 0);
+ dnode_evict_dbufs(dn);
ASSERT3P(list_head(&dn->dn_dbufs), ==, NULL);
/*
@@ -436,38 +455,18 @@ dnode_reallocate(dnode_t *dn, dmu_object_type_t ot, int blocksize,
}
dnode_setdblksz(dn, blocksize);
dnode_setdirty(dn, tx);
+ dn->dn_next_bonuslen[tx->tx_txg&TXG_MASK] = bonuslen;
dn->dn_next_blksz[tx->tx_txg&TXG_MASK] = blocksize;
rw_exit(&dn->dn_struct_rwlock);
- if (db) {
+ if (db)
dbuf_rele(db, FTAG);
- db = NULL;
- }
/* change type */
dn->dn_type = ot;
- if (dn->dn_bonuslen != bonuslen) {
- /* change bonus size */
- if (bonuslen == 0)
- bonuslen = 1; /* XXX */
- rw_enter(&dn->dn_struct_rwlock, RW_WRITER);
- if (dn->dn_bonus == NULL)
- dn->dn_bonus = dbuf_create_bonus(dn);
- db = dn->dn_bonus;
- rw_exit(&dn->dn_struct_rwlock);
- if (refcount_add(&db->db_holds, FTAG) == 1)
- dnode_add_ref(dn, db);
- VERIFY(0 == dbuf_read(db, NULL, DB_RF_MUST_SUCCEED));
- mutex_enter(&db->db_mtx);
- ASSERT3U(db->db.db_size, ==, dn->dn_bonuslen);
- ASSERT(db->db.db_data != NULL);
- db->db.db_size = bonuslen;
- mutex_exit(&db->db_mtx);
- (void) dbuf_dirty(db, tx);
- }
-
/* change bonus size and type */
mutex_enter(&dn->dn_mtx);
+ old_nblkptr = dn->dn_nblkptr;
dn->dn_bonustype = bonustype;
dn->dn_bonuslen = bonuslen;
dn->dn_nblkptr = 1 + ((DN_MAX_BONUSLEN - bonuslen) >> SPA_BLKPTRSHIFT);
@@ -475,12 +474,15 @@ dnode_reallocate(dnode_t *dn, dmu_object_type_t ot, int blocksize,
dn->dn_compress = ZIO_COMPRESS_INHERIT;
ASSERT3U(dn->dn_nblkptr, <=, DN_MAX_NBLKPTR);
- /*
- * NB: we have to do the dbuf_rele after we've changed the
- * dn_bonuslen, for the sake of dbuf_verify().
- */
- if (db)
- dbuf_rele(db, FTAG);
+ /* XXX - for now, we can't make nblkptr smaller */
+ ASSERT3U(dn->dn_nblkptr, >=, old_nblkptr);
+
+ /* fix up the bonus db_size if dn_nblkptr has changed */
+ if (dn->dn_bonus && dn->dn_bonuslen != old_nblkptr) {
+ dn->dn_bonus->db.db_size =
+ DN_MAX_BONUSLEN - (dn->dn_nblkptr-1) * sizeof (blkptr_t);
+ ASSERT(dn->dn_bonuslen <= dn->dn_bonus->db.db_size);
+ }
dn->dn_allocated_txg = tx->tx_txg;
mutex_exit(&dn->dn_mtx);
@@ -559,6 +561,12 @@ dnode_hold_impl(objset_impl_t *os, uint64_t object, int flag,
dmu_buf_impl_t *db;
dnode_t **children_dnodes;
+ /*
+ * If you are holding the spa config lock as writer, you shouldn't
+ * be asking the DMU to do *anything*.
+ */
+ ASSERT(spa_config_held(os->os_spa, SCL_ALL, RW_WRITER) == 0);
+
if (object == 0 || object >= DN_MAX_OBJECT)
return (EINVAL);
@@ -602,9 +610,10 @@ dnode_hold_impl(objset_impl_t *os, uint64_t object, int flag,
}
if ((dn = children_dnodes[idx]) == NULL) {
+ dnode_phys_t *dnp = (dnode_phys_t *)db->db.db_data+idx;
dnode_t *winner;
- dn = dnode_create(os, (dnode_phys_t *)db->db.db_data+idx,
- db, object);
+
+ dn = dnode_create(os, dnp, db, object);
winner = atomic_cas_ptr(&children_dnodes[idx], NULL, dn);
if (winner != NULL) {
dnode_destroy(dn);
@@ -644,11 +653,22 @@ dnode_hold(objset_impl_t *os, uint64_t object, void *tag, dnode_t **dnp)
return (dnode_hold_impl(os, object, DNODE_MUST_BE_ALLOCATED, tag, dnp));
}
-void
+/*
+ * Can only add a reference if there is already at least one
+ * reference on the dnode. Returns FALSE if unable to add a
+ * new reference.
+ */
+boolean_t
dnode_add_ref(dnode_t *dn, void *tag)
{
- ASSERT(refcount_count(&dn->dn_holds) > 0);
- (void) refcount_add(&dn->dn_holds, tag);
+ mutex_enter(&dn->dn_mtx);
+ if (refcount_is_zero(&dn->dn_holds)) {
+ mutex_exit(&dn->dn_mtx);
+ return (FALSE);
+ }
+ VERIFY(1 < refcount_add(&dn->dn_holds, tag));
+ mutex_exit(&dn->dn_mtx);
+ return (TRUE);
}
void
@@ -656,7 +676,9 @@ dnode_rele(dnode_t *dn, void *tag)
{
uint64_t refs;
+ mutex_enter(&dn->dn_mtx);
refs = refcount_remove(&dn->dn_holds, tag);
+ mutex_exit(&dn->dn_mtx);
/* NOTE: the DNODE_DNODE does not have a dn_dbuf */
if (refs == 0 && dn->dn_dbuf)
dbuf_rele(dn->dn_dbuf, dn);
@@ -692,6 +714,7 @@ dnode_setdirty(dnode_t *dn, dmu_tx_t *tx)
ASSERT(!refcount_is_zero(&dn->dn_holds) || list_head(&dn->dn_dbufs));
ASSERT(dn->dn_datablksz != 0);
+ ASSERT3U(dn->dn_next_bonuslen[txg&TXG_MASK], ==, 0);
ASSERT3U(dn->dn_next_blksz[txg&TXG_MASK], ==, 0);
dprintf_ds(os->os_dsl_dataset, "obj=%llu txg=%llu\n",
@@ -714,7 +737,7 @@ dnode_setdirty(dnode_t *dn, dmu_tx_t *tx)
* dnode will hang around after we finish processing its
* children.
*/
- dnode_add_ref(dn, (void *)(uintptr_t)tx->tx_txg);
+ VERIFY(dnode_add_ref(dn, (void *)(uintptr_t)tx->tx_txg));
(void) dbuf_dirty(dn->dn_dbuf, tx);
@@ -762,7 +785,7 @@ int
dnode_set_blksz(dnode_t *dn, uint64_t size, int ibs, dmu_tx_t *tx)
{
dmu_buf_impl_t *db, *db_next;
- int have_db0 = FALSE;
+ int err;
if (size == 0)
size = SPA_MINBLOCKSIZE;
@@ -787,9 +810,7 @@ dnode_set_blksz(dnode_t *dn, uint64_t size, int ibs, dmu_tx_t *tx)
for (db = list_head(&dn->dn_dbufs); db; db = db_next) {
db_next = list_next(&dn->dn_dbufs, db);
- if (db->db_blkid == 0) {
- have_db0 = TRUE;
- } else if (db->db_blkid != DB_BONUS_BLKID) {
+ if (db->db_blkid != 0 && db->db_blkid != DB_BONUS_BLKID) {
mutex_exit(&dn->dn_dbufs_mtx);
goto fail;
}
@@ -799,12 +820,12 @@ dnode_set_blksz(dnode_t *dn, uint64_t size, int ibs, dmu_tx_t *tx)
if (ibs && dn->dn_nlevels != 1)
goto fail;
- db = NULL;
- if (!BP_IS_HOLE(&dn->dn_phys->dn_blkptr[0]) || have_db0) {
- /* obtain the old block */
- db = dbuf_hold(dn, 0, FTAG);
+ /* resize the old block */
+ err = dbuf_hold_impl(dn, 0, 0, TRUE, FTAG, &db);
+ if (err == 0)
dbuf_new_size(db, size, tx);
- }
+ else if (err != ENOENT)
+ goto fail;
dnode_setdblksz(dn, size);
dnode_setdirty(dn, tx);
@@ -813,7 +834,7 @@ dnode_set_blksz(dnode_t *dn, uint64_t size, int ibs, dmu_tx_t *tx)
dn->dn_indblkshift = ibs;
dn->dn_next_indblkshift[tx->tx_txg&TXG_MASK] = ibs;
}
-
+ /* rele after we have fixed the blocksize in the dnode */
if (db)
dbuf_rele(db, FTAG);
@@ -825,19 +846,32 @@ fail:
return (ENOTSUP);
}
+/* read-holding callers must not rely on the lock being continuously held */
void
-dnode_new_blkid(dnode_t *dn, uint64_t blkid, dmu_tx_t *tx)
+dnode_new_blkid(dnode_t *dn, uint64_t blkid, dmu_tx_t *tx, boolean_t have_read)
{
uint64_t txgoff = tx->tx_txg & TXG_MASK;
- int drop_struct_lock = FALSE;
int epbs, new_nlevels;
uint64_t sz;
ASSERT(blkid != DB_BONUS_BLKID);
- if (!RW_WRITE_HELD(&dn->dn_struct_rwlock)) {
- rw_enter(&dn->dn_struct_rwlock, RW_WRITER);
- drop_struct_lock = TRUE;
+ ASSERT(have_read ?
+ RW_READ_HELD(&dn->dn_struct_rwlock) :
+ RW_WRITE_HELD(&dn->dn_struct_rwlock));
+
+ /*
+ * if we have a read-lock, check to see if we need to do any work
+ * before upgrading to a write-lock.
+ */
+ if (have_read) {
+ if (blkid <= dn->dn_maxblkid)
+ return;
+
+ if (!rw_tryupgrade(&dn->dn_struct_rwlock)) {
+ rw_exit(&dn->dn_struct_rwlock);
+ rw_enter(&dn->dn_struct_rwlock, RW_WRITER);
+ }
}
if (blkid <= dn->dn_maxblkid)
@@ -889,8 +923,8 @@ dnode_new_blkid(dnode_t *dn, uint64_t blkid, dmu_tx_t *tx)
}
out:
- if (drop_struct_lock)
- rw_exit(&dn->dn_struct_rwlock);
+ if (have_read)
+ rw_downgrade(&dn->dn_struct_rwlock);
}
void
@@ -951,15 +985,15 @@ dnode_free_range(dnode_t *dn, uint64_t off, uint64_t len, dmu_tx_t *tx)
{
dmu_buf_impl_t *db;
uint64_t blkoff, blkid, nblks;
- int blksz, head;
+ int blksz, blkshift, head, tail;
int trunc = FALSE;
+ int epbs;
rw_enter(&dn->dn_struct_rwlock, RW_WRITER);
blksz = dn->dn_datablksz;
+ blkshift = dn->dn_datablkshift;
+ epbs = dn->dn_indblkshift - SPA_BLKPTRSHIFT;
- /* If the range is past the end of the file, this is a no-op */
- if (off >= blksz * (dn->dn_maxblkid+1))
- goto out;
if (len == -1ULL) {
len = UINT64_MAX - off;
trunc = TRUE;
@@ -971,11 +1005,18 @@ dnode_free_range(dnode_t *dn, uint64_t off, uint64_t len, dmu_tx_t *tx)
if (ISP2(blksz)) {
head = P2NPHASE(off, blksz);
blkoff = P2PHASE(off, blksz);
+ if ((off >> blkshift) > dn->dn_maxblkid)
+ goto out;
} else {
ASSERT(dn->dn_maxblkid == 0);
if (off == 0 && len >= blksz) {
- /* Freeing the whole block; don't do any head. */
- head = 0;
+ /* Freeing the whole block; fast-track this request */
+ blkid = 0;
+ nblks = 1;
+ goto done;
+ } else if (off >= blksz) {
+ /* Freeing past end-of-data */
+ goto out;
} else {
/* Freeing part of the block. */
head = blksz - off;
@@ -1008,88 +1049,95 @@ dnode_free_range(dnode_t *dn, uint64_t off, uint64_t len, dmu_tx_t *tx)
}
/* If the range was less than one block, we're done */
- if (len == 0 || off >= blksz * (dn->dn_maxblkid+1))
+ if (len == 0)
goto out;
- if (!ISP2(blksz)) {
- /*
- * They are freeing the whole block of a
- * non-power-of-two blocksize file. Skip all the messy
- * math.
- */
- ASSERT3U(off, ==, 0);
- ASSERT3U(len, >=, blksz);
- blkid = 0;
- nblks = 1;
- } else {
- int tail;
- int epbs = dn->dn_indblkshift - SPA_BLKPTRSHIFT;
- int blkshift = dn->dn_datablkshift;
-
- /* If the remaining range is past end of file, we're done */
- if (off > dn->dn_maxblkid << blkshift)
- goto out;
+ /* If the remaining range is past end of file, we're done */
+ if ((off >> blkshift) > dn->dn_maxblkid)
+ goto out;
- if (off + len == UINT64_MAX)
- tail = 0;
- else
- tail = P2PHASE(len, blksz);
-
- ASSERT3U(P2PHASE(off, blksz), ==, 0);
- /* zero out any partial block data at the end of the range */
- if (tail) {
- if (len < tail)
- tail = len;
- if (dbuf_hold_impl(dn, 0, dbuf_whichblock(dn, off+len),
- TRUE, FTAG, &db) == 0) {
- /* don't dirty if not on disk and not dirty */
- if (db->db_last_dirty ||
- (db->db_blkptr &&
- !BP_IS_HOLE(db->db_blkptr))) {
- rw_exit(&dn->dn_struct_rwlock);
- dbuf_will_dirty(db, tx);
- rw_enter(&dn->dn_struct_rwlock,
- RW_WRITER);
- bzero(db->db.db_data, tail);
- }
- dbuf_rele(db, FTAG);
+ ASSERT(ISP2(blksz));
+ if (trunc)
+ tail = 0;
+ else
+ tail = P2PHASE(len, blksz);
+
+ ASSERT3U(P2PHASE(off, blksz), ==, 0);
+ /* zero out any partial block data at the end of the range */
+ if (tail) {
+ if (len < tail)
+ tail = len;
+ if (dbuf_hold_impl(dn, 0, dbuf_whichblock(dn, off+len),
+ TRUE, FTAG, &db) == 0) {
+ /* don't dirty if not on disk and not dirty */
+ if (db->db_last_dirty ||
+ (db->db_blkptr && !BP_IS_HOLE(db->db_blkptr))) {
+ rw_exit(&dn->dn_struct_rwlock);
+ dbuf_will_dirty(db, tx);
+ rw_enter(&dn->dn_struct_rwlock, RW_WRITER);
+ bzero(db->db.db_data, tail);
}
- len -= tail;
+ dbuf_rele(db, FTAG);
}
- /* If the range did not include a full block, we are done */
- if (len == 0)
- goto out;
+ len -= tail;
+ }
- /* dirty the left indirects */
- if (dn->dn_nlevels > 1 && off != 0) {
- db = dbuf_hold_level(dn, 1,
- (off - head) >> (blkshift + epbs), FTAG);
+ /* If the range did not include a full block, we are done */
+ if (len == 0)
+ goto out;
+
+ ASSERT(IS_P2ALIGNED(off, blksz));
+ ASSERT(trunc || IS_P2ALIGNED(len, blksz));
+ blkid = off >> blkshift;
+ nblks = len >> blkshift;
+ if (trunc)
+ nblks += 1;
+
+ /*
+ * Read in and mark all the level-1 indirects dirty,
+ * so that they will stay in memory until syncing phase.
+ * Always dirty the first and last indirect to make sure
+ * we dirty all the partial indirects.
+ */
+ if (dn->dn_nlevels > 1) {
+ uint64_t i, first, last;
+ int shift = epbs + dn->dn_datablkshift;
+
+ first = blkid >> epbs;
+ if (db = dbuf_hold_level(dn, 1, first, FTAG)) {
dbuf_will_dirty(db, tx);
dbuf_rele(db, FTAG);
}
-
- /* dirty the right indirects */
- if (dn->dn_nlevels > 1 && !trunc) {
- db = dbuf_hold_level(dn, 1,
- (off + len + tail - 1) >> (blkshift + epbs), FTAG);
+ if (trunc)
+ last = dn->dn_maxblkid >> epbs;
+ else
+ last = (blkid + nblks - 1) >> epbs;
+ if (last > first && (db = dbuf_hold_level(dn, 1, last, FTAG))) {
dbuf_will_dirty(db, tx);
dbuf_rele(db, FTAG);
}
-
- /*
- * Finally, add this range to the dnode range list, we
- * will finish up this free operation in the syncing phase.
- */
- ASSERT(IS_P2ALIGNED(off, 1<<blkshift));
- ASSERT(off + len == UINT64_MAX ||
- IS_P2ALIGNED(len, 1<<blkshift));
- blkid = off >> blkshift;
- nblks = len >> blkshift;
-
- if (trunc)
- dn->dn_maxblkid = (blkid ? blkid - 1 : 0);
+ for (i = first + 1; i < last; i++) {
+ uint64_t ibyte = i << shift;
+ int err;
+
+ err = dnode_next_offset(dn,
+ DNODE_FIND_HAVELOCK, &ibyte, 1, 1, 0);
+ i = ibyte >> shift;
+ if (err == ESRCH || i >= last)
+ break;
+ ASSERT(err == 0);
+ db = dbuf_hold_level(dn, 1, i, FTAG);
+ if (db) {
+ dbuf_will_dirty(db, tx);
+ dbuf_rele(db, FTAG);
+ }
+ }
}
-
+done:
+ /*
+ * Add this range to the dnode range list.
+ * We will finish up this free operation in the syncing phase.
+ */
mutex_enter(&dn->dn_mtx);
dnode_clear_range(dn, blkid, nblks, tx);
{
@@ -1109,9 +1157,12 @@ dnode_free_range(dnode_t *dn, uint64_t off, uint64_t len, dmu_tx_t *tx)
}
mutex_exit(&dn->dn_mtx);
- dbuf_free_range(dn, blkid, nblks, tx);
+ dbuf_free_range(dn, blkid, blkid + nblks - 1, tx);
dnode_setdirty(dn, tx);
out:
+ if (trunc && dn->dn_maxblkid >= (off >> blkshift))
+ dn->dn_maxblkid = (off >> blkshift ? (off >> blkshift) - 1 : 0);
+
rw_exit(&dn->dn_struct_rwlock);
}
@@ -1179,7 +1230,7 @@ dnode_diduse_space(dnode_t *dn, int64_t delta)
ASSERT3U(space, >=, -delta); /* no underflow */
}
space += delta;
- if (spa_version(dn->dn_objset->os_spa) < ZFS_VERSION_DNODE_BYTES) {
+ if (spa_version(dn->dn_objset->os_spa) < SPA_VERSION_DNODE_BYTES) {
ASSERT((dn->dn_phys->dn_flags & DNODE_FLAG_USED_BYTES) == 0);
ASSERT3U(P2PHASE(space, 1<<DEV_BSHIFT), ==, 0);
dn->dn_phys->dn_used = space >> DEV_BSHIFT;
@@ -1211,7 +1262,7 @@ dnode_willuse_space(dnode_t *dn, int64_t space, dmu_tx_t *tx)
}
static int
-dnode_next_offset_level(dnode_t *dn, boolean_t hole, uint64_t *offset,
+dnode_next_offset_level(dnode_t *dn, int flags, uint64_t *offset,
int lvl, uint64_t blkfill, uint64_t txg)
{
dmu_buf_impl_t *db = NULL;
@@ -1219,11 +1270,16 @@ dnode_next_offset_level(dnode_t *dn, boolean_t hole, uint64_t *offset,
uint64_t epbs = dn->dn_phys->dn_indblkshift - SPA_BLKPTRSHIFT;
uint64_t epb = 1ULL << epbs;
uint64_t minfill, maxfill;
- int i, error, span;
+ boolean_t hole;
+ int i, inc, error, span;
dprintf("probing object %llu offset %llx level %d of %u\n",
dn->dn_object, *offset, lvl, dn->dn_phys->dn_nlevels);
+ hole = flags & DNODE_FIND_HOLE;
+ inc = (flags & DNODE_FIND_BACKWARDS) ? -1 : 1;
+ ASSERT(txg == 0 || !hole);
+
if (lvl == dn->dn_phys->dn_nlevels) {
error = 0;
epb = dn->dn_phys->dn_nblkptr;
@@ -1232,9 +1288,18 @@ dnode_next_offset_level(dnode_t *dn, boolean_t hole, uint64_t *offset,
uint64_t blkid = dbuf_whichblock(dn, *offset) >> (epbs * lvl);
error = dbuf_hold_impl(dn, lvl, blkid, TRUE, FTAG, &db);
if (error) {
- if (error == ENOENT)
- return (hole ? 0 : ESRCH);
- return (error);
+ if (error != ENOENT)
+ return (error);
+ if (hole)
+ return (0);
+ /*
+ * This can only happen when we are searching up
+ * the block tree for data. We don't really need to
+ * adjust the offset, as we will just end up looking
+ * at the pointer to this block in its parent, and its
+ * going to be unallocated, so we will skip over it.
+ */
+ return (ESRCH);
}
error = dbuf_read(db, NULL, DB_RF_CANFAIL | DB_RF_HAVESTRUCT);
if (error) {
@@ -1246,13 +1311,18 @@ dnode_next_offset_level(dnode_t *dn, boolean_t hole, uint64_t *offset,
if (db && txg &&
(db->db_blkptr == NULL || db->db_blkptr->blk_birth <= txg)) {
+ /*
+ * This can only happen when we are searching up the tree
+ * and these conditions mean that we need to keep climbing.
+ */
error = ESRCH;
} else if (lvl == 0) {
dnode_phys_t *dnp = data;
span = DNODE_SHIFT;
ASSERT(dn->dn_type == DMU_OT_DNODE);
- for (i = (*offset >> span) & (blkfill - 1); i < blkfill; i++) {
+ for (i = (*offset >> span) & (blkfill - 1);
+ i >= 0 && i < blkfill; i += inc) {
boolean_t newcontents = B_TRUE;
if (txg) {
int j;
@@ -1264,9 +1334,9 @@ dnode_next_offset_level(dnode_t *dn, boolean_t hole, uint64_t *offset,
}
if (!dnp[i].dn_type == hole && newcontents)
break;
- *offset += 1ULL << span;
+ *offset += (1ULL << span) * inc;
}
- if (i == blkfill)
+ if (i < 0 || i == blkfill)
error = ESRCH;
} else {
blkptr_t *bp = data;
@@ -1280,14 +1350,17 @@ dnode_next_offset_level(dnode_t *dn, boolean_t hole, uint64_t *offset,
minfill++;
for (i = (*offset >> span) & ((1ULL << epbs) - 1);
- i < epb; i++) {
+ i >= 0 && i < epb; i += inc) {
if (bp[i].blk_fill >= minfill &&
bp[i].blk_fill <= maxfill &&
- bp[i].blk_birth > txg)
+ (hole || bp[i].blk_birth > txg))
break;
- *offset += 1ULL << span;
+ if (inc < 0 && *offset < (1ULL << span))
+ *offset = 0;
+ else
+ *offset += (1ULL << span) * inc;
}
- if (i >= epb)
+ if (i < 0 || i == epb)
error = ESRCH;
}
@@ -1306,64 +1379,66 @@ dnode_next_offset_level(dnode_t *dn, boolean_t hole, uint64_t *offset,
*
* Examples:
*
- * dnode_next_offset(dn, hole, offset, 1, 1, 0);
- * Finds the next hole/data in a file.
+ * dnode_next_offset(dn, flags, offset, 1, 1, 0);
+ * Finds the next/previous hole/data in a file.
* Used in dmu_offset_next().
*
- * dnode_next_offset(mdn, hole, offset, 0, DNODES_PER_BLOCK, txg);
+ * dnode_next_offset(mdn, flags, offset, 0, DNODES_PER_BLOCK, txg);
* Finds the next free/allocated dnode an objset's meta-dnode.
* Only finds objects that have new contents since txg (ie.
* bonus buffer changes and content removal are ignored).
* Used in dmu_object_next().
*
- * dnode_next_offset(mdn, TRUE, offset, 2, DNODES_PER_BLOCK >> 2, 0);
+ * dnode_next_offset(mdn, DNODE_FIND_HOLE, offset, 2, DNODES_PER_BLOCK >> 2, 0);
* Finds the next L2 meta-dnode bp that's at most 1/4 full.
* Used in dmu_object_alloc().
*/
int
-dnode_next_offset(dnode_t *dn, boolean_t hole, uint64_t *offset,
+dnode_next_offset(dnode_t *dn, int flags, uint64_t *offset,
int minlvl, uint64_t blkfill, uint64_t txg)
{
+ uint64_t initial_offset = *offset;
int lvl, maxlvl;
int error = 0;
- uint64_t initial_offset = *offset;
- rw_enter(&dn->dn_struct_rwlock, RW_READER);
+ if (!(flags & DNODE_FIND_HAVELOCK))
+ rw_enter(&dn->dn_struct_rwlock, RW_READER);
if (dn->dn_phys->dn_nlevels == 0) {
- rw_exit(&dn->dn_struct_rwlock);
- return (ESRCH);
+ error = ESRCH;
+ goto out;
}
if (dn->dn_datablkshift == 0) {
if (*offset < dn->dn_datablksz) {
- if (hole)
+ if (flags & DNODE_FIND_HOLE)
*offset = dn->dn_datablksz;
} else {
error = ESRCH;
}
- rw_exit(&dn->dn_struct_rwlock);
- return (error);
+ goto out;
}
maxlvl = dn->dn_phys->dn_nlevels;
for (lvl = minlvl; lvl <= maxlvl; lvl++) {
error = dnode_next_offset_level(dn,
- hole, offset, lvl, blkfill, txg);
+ flags, offset, lvl, blkfill, txg);
if (error != ESRCH)
break;
}
- while (--lvl >= minlvl && error == 0) {
+ while (error == 0 && --lvl >= minlvl) {
error = dnode_next_offset_level(dn,
- hole, offset, lvl, blkfill, txg);
+ flags, offset, lvl, blkfill, txg);
}
- rw_exit(&dn->dn_struct_rwlock);
-
- if (error == 0 && initial_offset > *offset)
+ if (error == 0 && (flags & DNODE_FIND_BACKWARDS ?
+ initial_offset < *offset : initial_offset > *offset))
error = ESRCH;
+out:
+ if (!(flags & DNODE_FIND_HAVELOCK))
+ rw_exit(&dn->dn_struct_rwlock);
return (error);
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dnode_sync.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dnode_sync.c
index 9e8c7ad..a46d4e7 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dnode_sync.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dnode_sync.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -55,9 +55,8 @@ dnode_increase_indirection(dnode_t *dn, dmu_tx_t *tx)
ASSERT(db != NULL);
dn->dn_phys->dn_nlevels = new_level;
- dprintf("os=%p obj=%llu, increase to %d\n",
- dn->dn_objset, dn->dn_object,
- dn->dn_phys->dn_nlevels);
+ dprintf("os=%p obj=%llu, increase to %d\n", dn->dn_objset,
+ dn->dn_object, dn->dn_phys->dn_nlevels);
/* check for existing blkptrs in the dnode */
for (i = 0; i < nblkptr; i++)
@@ -110,25 +109,26 @@ dnode_increase_indirection(dnode_t *dn, dmu_tx_t *tx)
rw_exit(&dn->dn_struct_rwlock);
}
-static void
+static int
free_blocks(dnode_t *dn, blkptr_t *bp, int num, dmu_tx_t *tx)
{
- objset_impl_t *os = dn->dn_objset;
+ dsl_dataset_t *ds = dn->dn_objset->os_dsl_dataset;
uint64_t bytesfreed = 0;
- int i;
+ int i, blocks_freed = 0;
- dprintf("os=%p obj=%llx num=%d\n", os, dn->dn_object, num);
+ dprintf("ds=%p obj=%llx num=%d\n", ds, dn->dn_object, num);
for (i = 0; i < num; i++, bp++) {
if (BP_IS_HOLE(bp))
continue;
- bytesfreed += bp_get_dasize(os->os_spa, bp);
+ bytesfreed += dsl_dataset_block_kill(ds, bp, dn->dn_zio, tx);
ASSERT3U(bytesfreed, <=, DN_USED_BYTES(dn->dn_phys));
- dsl_dataset_block_kill(os->os_dsl_dataset, bp, dn->dn_zio, tx);
bzero(bp, sizeof (blkptr_t));
+ blocks_freed += 1;
}
dnode_diduse_space(dn, -bytesfreed);
+ return (blocks_freed);
}
#ifdef ZFS_DEBUG
@@ -160,7 +160,7 @@ free_verify(dmu_buf_impl_t *db, uint64_t start, uint64_t end, dmu_tx_t *tx)
rw_enter(&db->db_dnode->dn_struct_rwlock, RW_READER);
err = dbuf_hold_impl(db->db_dnode, db->db_level-1,
- (db->db_blkid << epbs) + i, TRUE, FTAG, &child);
+ (db->db_blkid << epbs) + i, TRUE, FTAG, &child);
rw_exit(&db->db_dnode->dn_struct_rwlock);
if (err == ENOENT)
continue;
@@ -178,7 +178,7 @@ free_verify(dmu_buf_impl_t *db, uint64_t start, uint64_t end, dmu_tx_t *tx)
if (buf[j] != 0) {
panic("freed data not zero: "
"child=%p i=%d off=%d num=%d\n",
- child, i, off, num);
+ (void *)child, i, off, num);
}
}
}
@@ -195,7 +195,7 @@ free_verify(dmu_buf_impl_t *db, uint64_t start, uint64_t end, dmu_tx_t *tx)
if (buf[j] != 0) {
panic("freed data not zero: "
"child=%p i=%d off=%d num=%d\n",
- child, i, off, num);
+ (void *)child, i, off, num);
}
}
}
@@ -206,6 +206,8 @@ free_verify(dmu_buf_impl_t *db, uint64_t start, uint64_t end, dmu_tx_t *tx)
}
#endif
+#define ALL -1
+
static int
free_children(dmu_buf_impl_t *db, uint64_t blkid, uint64_t nblks, int trunc,
dmu_tx_t *tx)
@@ -216,8 +218,18 @@ free_children(dmu_buf_impl_t *db, uint64_t blkid, uint64_t nblks, int trunc,
uint64_t start, end, dbstart, dbend, i;
int epbs, shift, err;
int all = TRUE;
+ int blocks_freed = 0;
+
+ /*
+ * There is a small possibility that this block will not be cached:
+ * 1 - if level > 1 and there are no children with level <= 1
+ * 2 - if we didn't get a dirty hold (because this block had just
+ * finished being written -- and so had no holds), and then this
+ * block got evicted before we got here.
+ */
+ if (db->db_state != DB_CACHED)
+ (void) dbuf_read(db, NULL, DB_RF_MUST_SUCCEED);
- (void) dbuf_read(db, NULL, DB_RF_MUST_SUCCEED);
arc_release(db->db_buf, db);
bp = (blkptr_t *)db->db.db_data;
@@ -241,10 +253,10 @@ free_children(dmu_buf_impl_t *db, uint64_t blkid, uint64_t nblks, int trunc,
if (db->db_level == 1) {
FREE_VERIFY(db, start, end, tx);
- free_blocks(dn, bp, end-start+1, tx);
+ blocks_freed = free_blocks(dn, bp, end-start+1, tx);
arc_buf_freeze(db->db_buf);
- ASSERT(all || db->db_last_dirty);
- return (all);
+ ASSERT(all || blocks_freed == 0 || db->db_last_dirty);
+ return (all ? ALL : blocks_freed);
}
for (i = start; i <= end; i++, bp++) {
@@ -255,9 +267,9 @@ free_children(dmu_buf_impl_t *db, uint64_t blkid, uint64_t nblks, int trunc,
ASSERT3U(err, ==, 0);
rw_exit(&dn->dn_struct_rwlock);
- if (free_children(subdb, blkid, nblks, trunc, tx)) {
+ if (free_children(subdb, blkid, nblks, trunc, tx) == ALL) {
ASSERT3P(subdb->db_blkptr, ==, bp);
- free_blocks(dn, bp, 1, tx);
+ blocks_freed += free_blocks(dn, bp, 1, tx);
} else {
all = FALSE;
}
@@ -274,8 +286,8 @@ free_children(dmu_buf_impl_t *db, uint64_t blkid, uint64_t nblks, int trunc,
ASSERT3U(bp->blk_birth, ==, 0);
}
#endif
- ASSERT(all || db->db_last_dirty);
- return (all);
+ ASSERT(all || blocks_freed == 0 || db->db_last_dirty);
+ return (all ? ALL : blocks_freed);
}
/*
@@ -305,15 +317,14 @@ dnode_sync_free_range(dnode_t *dn, uint64_t blkid, uint64_t nblks, dmu_tx_t *tx)
return;
}
ASSERT3U(blkid + nblks, <=, dn->dn_phys->dn_nblkptr);
- free_blocks(dn, bp + blkid, nblks, tx);
+ (void) free_blocks(dn, bp + blkid, nblks, tx);
if (trunc) {
uint64_t off = (dn->dn_phys->dn_maxblkid + 1) *
(dn->dn_phys->dn_datablkszsec << SPA_MINBLOCKSHIFT);
dn->dn_phys->dn_maxblkid = (blkid ? blkid - 1 : 0);
ASSERT(off < dn->dn_phys->dn_maxblkid ||
dn->dn_phys->dn_maxblkid == 0 ||
- dnode_next_offset(dn, FALSE, &off,
- 1, 1, 0) != 0);
+ dnode_next_offset(dn, 0, &off, 1, 1, 0) != 0);
}
return;
}
@@ -331,9 +342,9 @@ dnode_sync_free_range(dnode_t *dn, uint64_t blkid, uint64_t nblks, dmu_tx_t *tx)
ASSERT3U(err, ==, 0);
rw_exit(&dn->dn_struct_rwlock);
- if (free_children(db, blkid, nblks, trunc, tx)) {
+ if (free_children(db, blkid, nblks, trunc, tx) == ALL) {
ASSERT3P(db->db_blkptr, ==, bp);
- free_blocks(dn, bp, 1, tx);
+ (void) free_blocks(dn, bp, 1, tx);
}
dbuf_rele(db, FTAG);
}
@@ -343,15 +354,15 @@ dnode_sync_free_range(dnode_t *dn, uint64_t blkid, uint64_t nblks, dmu_tx_t *tx)
dn->dn_phys->dn_maxblkid = (blkid ? blkid - 1 : 0);
ASSERT(off < dn->dn_phys->dn_maxblkid ||
dn->dn_phys->dn_maxblkid == 0 ||
- dnode_next_offset(dn, FALSE, &off, 1, 1, 0) != 0);
+ dnode_next_offset(dn, 0, &off, 1, 1, 0) != 0);
}
}
/*
* Try to kick all the dnodes dbufs out of the cache...
*/
-int
-dnode_evict_dbufs(dnode_t *dn, int try)
+void
+dnode_evict_dbufs(dnode_t *dn)
{
int progress;
int pass = 0;
@@ -367,6 +378,7 @@ dnode_evict_dbufs(dnode_t *dn, int try)
for (; db != &marker; db = list_head(&dn->dn_dbufs)) {
list_remove(&dn->dn_dbufs, db);
list_insert_tail(&dn->dn_dbufs, db);
+ ASSERT3P(db->db_dnode, ==, dn);
mutex_enter(&db->db_mtx);
if (db->db_state == DB_EVICTING) {
@@ -375,7 +387,6 @@ dnode_evict_dbufs(dnode_t *dn, int try)
mutex_exit(&db->db_mtx);
} else if (refcount_is_zero(&db->db_holds)) {
progress = TRUE;
- ASSERT(!arc_released(db->db_buf));
dbuf_clear(db); /* exits db_mtx for us */
} else {
mutex_exit(&db->db_mtx);
@@ -397,21 +408,6 @@ dnode_evict_dbufs(dnode_t *dn, int try)
ASSERT(pass < 100); /* sanity check */
} while (progress);
- /*
- * This function works fine even if it can't evict everything.
- * If were only asked to try to evict everything then
- * return an error if we can't. Otherwise panic as the caller
- * expects total eviction.
- */
- if (list_head(&dn->dn_dbufs) != NULL) {
- if (try) {
- return (1);
- } else {
- panic("dangling dbufs (dn=%p, dbuf=%p)\n",
- dn, list_head(&dn->dn_dbufs));
- }
- }
-
rw_enter(&dn->dn_struct_rwlock, RW_WRITER);
if (dn->dn_bonus && refcount_is_zero(&dn->dn_bonus->db_holds)) {
mutex_enter(&dn->dn_bonus->db_mtx);
@@ -419,7 +415,6 @@ dnode_evict_dbufs(dnode_t *dn, int try)
dn->dn_bonus = NULL;
}
rw_exit(&dn->dn_struct_rwlock);
- return (0);
}
static void
@@ -460,8 +455,15 @@ dnode_sync_free(dnode_t *dn, dmu_tx_t *tx)
ASSERT(dmu_tx_is_syncing(tx));
+ /*
+ * Our contents should have been freed in dnode_sync() by the
+ * free range record inserted by the caller of dnode_free().
+ */
+ ASSERT3U(DN_USED_BYTES(dn->dn_phys), ==, 0);
+ ASSERT(BP_IS_HOLE(dn->dn_phys->dn_blkptr));
+
dnode_undirty_dbufs(&dn->dn_dirty_records[txgoff]);
- (void) dnode_evict_dbufs(dn, 0);
+ dnode_evict_dbufs(dn);
ASSERT3P(list_head(&dn->dn_dbufs), ==, NULL);
/*
@@ -479,10 +481,6 @@ dnode_sync_free(dnode_t *dn, dmu_tx_t *tx)
dn->dn_next_indblkshift[txgoff] = 0;
dn->dn_next_blksz[txgoff] = 0;
- /* free up all the blocks in the file. */
- dnode_sync_free_range(dn, 0, dn->dn_phys->dn_maxblkid+1, tx);
- ASSERT3U(DN_USED_BYTES(dn->dn_phys), ==, 0);
-
/* ASSERT(blkptrs are zero); */
ASSERT(dn->dn_phys->dn_type != DMU_OT_NONE);
ASSERT(dn->dn_type != DMU_OT_NONE);
@@ -496,6 +494,7 @@ dnode_sync_free(dnode_t *dn, dmu_tx_t *tx)
dn->dn_type = DMU_OT_NONE;
dn->dn_maxblkid = 0;
dn->dn_allocated_txg = 0;
+ dn->dn_free_txg = 0;
mutex_exit(&dn->dn_mtx);
ASSERT(dn->dn_object != DMU_META_DNODE_OBJECT);
@@ -558,7 +557,7 @@ dnode_sync(dnode_t *dn, dmu_tx_t *tx)
ASSERT(P2PHASE(dn->dn_next_blksz[txgoff],
SPA_MINBLOCKSIZE) == 0);
ASSERT(BP_IS_HOLE(&dnp->dn_blkptr[0]) ||
- list_head(list) != NULL ||
+ dn->dn_maxblkid == 0 || list_head(list) != NULL ||
dn->dn_next_blksz[txgoff] >> SPA_MINBLOCKSHIFT ==
dnp->dn_datablkszsec);
dnp->dn_datablkszsec =
@@ -566,6 +565,15 @@ dnode_sync(dnode_t *dn, dmu_tx_t *tx)
dn->dn_next_blksz[txgoff] = 0;
}
+ if (dn->dn_next_bonuslen[txgoff]) {
+ if (dn->dn_next_bonuslen[txgoff] == DN_ZERO_BONUSLEN)
+ dnp->dn_bonuslen = 0;
+ else
+ dnp->dn_bonuslen = dn->dn_next_bonuslen[txgoff];
+ ASSERT(dnp->dn_bonuslen <= DN_MAX_BONUSLEN);
+ dn->dn_next_bonuslen[txgoff] = 0;
+ }
+
if (dn->dn_next_indblkshift[txgoff]) {
ASSERT(dnp->dn_nlevels == 1);
dnp->dn_indblkshift = dn->dn_next_indblkshift[txgoff];
@@ -583,20 +591,14 @@ dnode_sync(dnode_t *dn, dmu_tx_t *tx)
mutex_exit(&dn->dn_mtx);
/* process all the "freed" ranges in the file */
- if (dn->dn_free_txg == 0 || dn->dn_free_txg > tx->tx_txg) {
- for (rp = avl_last(&dn->dn_ranges[txgoff]); rp != NULL;
- rp = AVL_PREV(&dn->dn_ranges[txgoff], rp))
- dnode_sync_free_range(dn,
- rp->fr_blkid, rp->fr_nblks, tx);
+ while (rp = avl_last(&dn->dn_ranges[txgoff])) {
+ dnode_sync_free_range(dn, rp->fr_blkid, rp->fr_nblks, tx);
+ /* grab the mutex so we don't race with dnode_block_freed() */
+ mutex_enter(&dn->dn_mtx);
+ avl_remove(&dn->dn_ranges[txgoff], rp);
+ mutex_exit(&dn->dn_mtx);
+ kmem_free(rp, sizeof (free_range_t));
}
- mutex_enter(&dn->dn_mtx);
- for (rp = avl_first(&dn->dn_ranges[txgoff]); rp; ) {
- free_range_t *last = rp;
- rp = AVL_NEXT(&dn->dn_ranges[txgoff], rp);
- avl_remove(&dn->dn_ranges[txgoff], last);
- kmem_free(last, sizeof (free_range_t));
- }
- mutex_exit(&dn->dn_mtx);
if (dn->dn_free_txg > 0 && dn->dn_free_txg <= tx->tx_txg) {
dnode_sync_free(dn, tx);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c
index 7d4689f..20d8ec8 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/dmu_objset.h>
#include <sys/dsl_dataset.h>
#include <sys/dsl_dir.h>
@@ -38,35 +36,44 @@
#include <sys/unique.h>
#include <sys/zfs_context.h>
#include <sys/zfs_ioctl.h>
+#include <sys/spa.h>
+#include <sys/zfs_znode.h>
+#include <sys/sunddi.h>
+
+static char *dsl_reaper = "the grim reaper";
static dsl_checkfunc_t dsl_dataset_destroy_begin_check;
static dsl_syncfunc_t dsl_dataset_destroy_begin_sync;
static dsl_checkfunc_t dsl_dataset_rollback_check;
static dsl_syncfunc_t dsl_dataset_rollback_sync;
-static dsl_checkfunc_t dsl_dataset_destroy_check;
-static dsl_syncfunc_t dsl_dataset_destroy_sync;
+static dsl_syncfunc_t dsl_dataset_set_reservation_sync;
#define DS_REF_MAX (1ULL << 62)
#define DSL_DEADLIST_BLOCKSIZE SPA_MAXBLOCKSIZE
+#define DSL_DATASET_IS_DESTROYED(ds) ((ds)->ds_owner == dsl_reaper)
+
+
/*
- * We use weighted reference counts to express the various forms of exclusion
- * between different open modes. A STANDARD open is 1 point, an EXCLUSIVE open
- * is DS_REF_MAX, and a PRIMARY open is little more than half of an EXCLUSIVE.
- * This makes the exclusion logic simple: the total refcnt for all opens cannot
- * exceed DS_REF_MAX. For example, EXCLUSIVE opens are exclusive because their
- * weight (DS_REF_MAX) consumes the entire refcnt space. PRIMARY opens consume
- * just over half of the refcnt space, so there can't be more than one, but it
- * can peacefully coexist with any number of STANDARD opens.
+ * Figure out how much of this delta should be propogated to the dsl_dir
+ * layer. If there's a refreservation, that space has already been
+ * partially accounted for in our ancestors.
*/
-static uint64_t ds_refcnt_weight[DS_MODE_LEVELS] = {
- 0, /* DS_MODE_NONE - invalid */
- 1, /* DS_MODE_STANDARD - unlimited number */
- (DS_REF_MAX >> 1) + 1, /* DS_MODE_PRIMARY - only one of these */
- DS_REF_MAX /* DS_MODE_EXCLUSIVE - no other opens */
-};
+static int64_t
+parent_delta(dsl_dataset_t *ds, int64_t delta)
+{
+ uint64_t old_bytes, new_bytes;
+ if (ds->ds_reserved == 0)
+ return (delta);
+
+ old_bytes = MAX(ds->ds_phys->ds_unique_bytes, ds->ds_reserved);
+ new_bytes = MAX(ds->ds_phys->ds_unique_bytes + delta, ds->ds_reserved);
+
+ ASSERT3U(ABS((int64_t)(new_bytes - old_bytes)), <=, ABS(delta));
+ return (new_bytes - old_bytes);
+}
void
dsl_dataset_block_born(dsl_dataset_t *ds, blkptr_t *bp, dmu_tx_t *tx)
@@ -74,6 +81,7 @@ dsl_dataset_block_born(dsl_dataset_t *ds, blkptr_t *bp, dmu_tx_t *tx)
int used = bp_get_dasize(tx->tx_pool->dp_spa, bp);
int compressed = BP_GET_PSIZE(bp);
int uncompressed = BP_GET_UCSIZE(bp);
+ int64_t delta;
dprintf_bp(bp, "born, ds=%p\n", ds);
@@ -89,23 +97,28 @@ dsl_dataset_block_born(dsl_dataset_t *ds, blkptr_t *bp, dmu_tx_t *tx)
* dsl_dir.
*/
ASSERT3U(compressed, ==, uncompressed); /* it's all metadata */
- dsl_dir_diduse_space(tx->tx_pool->dp_mos_dir,
+ dsl_dir_diduse_space(tx->tx_pool->dp_mos_dir, DD_USED_HEAD,
used, compressed, uncompressed, tx);
dsl_dir_dirty(tx->tx_pool->dp_mos_dir, tx);
return;
}
dmu_buf_will_dirty(ds->ds_dbuf, tx);
+ mutex_enter(&ds->ds_dir->dd_lock);
mutex_enter(&ds->ds_lock);
+ delta = parent_delta(ds, used);
ds->ds_phys->ds_used_bytes += used;
ds->ds_phys->ds_compressed_bytes += compressed;
ds->ds_phys->ds_uncompressed_bytes += uncompressed;
ds->ds_phys->ds_unique_bytes += used;
mutex_exit(&ds->ds_lock);
- dsl_dir_diduse_space(ds->ds_dir,
- used, compressed, uncompressed, tx);
+ dsl_dir_diduse_space(ds->ds_dir, DD_USED_HEAD, delta,
+ compressed, uncompressed, tx);
+ dsl_dir_transfer_space(ds->ds_dir, used - delta,
+ DD_USED_REFRSRV, DD_USED_HEAD, tx);
+ mutex_exit(&ds->ds_dir->dd_lock);
}
-void
+int
dsl_dataset_block_kill(dsl_dataset_t *ds, blkptr_t *bp, zio_t *pio,
dmu_tx_t *tx)
{
@@ -113,10 +126,11 @@ dsl_dataset_block_kill(dsl_dataset_t *ds, blkptr_t *bp, zio_t *pio,
int compressed = BP_GET_PSIZE(bp);
int uncompressed = BP_GET_UCSIZE(bp);
+ ASSERT(pio != NULL);
ASSERT(dmu_tx_is_syncing(tx));
/* No block pointer => nothing to free */
if (BP_IS_HOLE(bp))
- return;
+ return (0);
ASSERT(used > 0);
if (ds == NULL) {
@@ -125,51 +139,59 @@ dsl_dataset_block_kill(dsl_dataset_t *ds, blkptr_t *bp, zio_t *pio,
* Account for the meta-objset space in its placeholder
* dataset.
*/
- err = arc_free(pio, tx->tx_pool->dp_spa,
- tx->tx_txg, bp, NULL, NULL, pio ? ARC_NOWAIT: ARC_WAIT);
+ err = dsl_free(pio, tx->tx_pool,
+ tx->tx_txg, bp, NULL, NULL, ARC_NOWAIT);
ASSERT(err == 0);
- dsl_dir_diduse_space(tx->tx_pool->dp_mos_dir,
+ dsl_dir_diduse_space(tx->tx_pool->dp_mos_dir, DD_USED_HEAD,
-used, -compressed, -uncompressed, tx);
dsl_dir_dirty(tx->tx_pool->dp_mos_dir, tx);
- return;
+ return (used);
}
ASSERT3P(tx->tx_pool, ==, ds->ds_dir->dd_pool);
+ ASSERT(!dsl_dataset_is_snapshot(ds));
dmu_buf_will_dirty(ds->ds_dbuf, tx);
if (bp->blk_birth > ds->ds_phys->ds_prev_snap_txg) {
int err;
+ int64_t delta;
dprintf_bp(bp, "freeing: %s", "");
- err = arc_free(pio, tx->tx_pool->dp_spa,
- tx->tx_txg, bp, NULL, NULL, pio ? ARC_NOWAIT: ARC_WAIT);
+ err = dsl_free(pio, tx->tx_pool,
+ tx->tx_txg, bp, NULL, NULL, ARC_NOWAIT);
ASSERT(err == 0);
+ mutex_enter(&ds->ds_dir->dd_lock);
mutex_enter(&ds->ds_lock);
- /* XXX unique_bytes is not accurate for head datasets */
- /* ASSERT3U(ds->ds_phys->ds_unique_bytes, >=, used); */
+ ASSERT(ds->ds_phys->ds_unique_bytes >= used ||
+ !DS_UNIQUE_IS_ACCURATE(ds));
+ delta = parent_delta(ds, -used);
ds->ds_phys->ds_unique_bytes -= used;
mutex_exit(&ds->ds_lock);
- dsl_dir_diduse_space(ds->ds_dir,
- -used, -compressed, -uncompressed, tx);
+ dsl_dir_diduse_space(ds->ds_dir, DD_USED_HEAD,
+ delta, -compressed, -uncompressed, tx);
+ dsl_dir_transfer_space(ds->ds_dir, -used - delta,
+ DD_USED_REFRSRV, DD_USED_HEAD, tx);
+ mutex_exit(&ds->ds_dir->dd_lock);
} else {
dprintf_bp(bp, "putting on dead list: %s", "");
VERIFY(0 == bplist_enqueue(&ds->ds_deadlist, bp, tx));
+ ASSERT3U(ds->ds_prev->ds_object, ==,
+ ds->ds_phys->ds_prev_snap_obj);
+ ASSERT(ds->ds_prev->ds_phys->ds_num_children > 0);
/* if (bp->blk_birth > prev prev snap txg) prev unique += bs */
- if (ds->ds_phys->ds_prev_snap_obj != 0) {
- ASSERT3U(ds->ds_prev->ds_object, ==,
- ds->ds_phys->ds_prev_snap_obj);
- ASSERT(ds->ds_prev->ds_phys->ds_num_children > 0);
- if (ds->ds_prev->ds_phys->ds_next_snap_obj ==
- ds->ds_object && bp->blk_birth >
- ds->ds_prev->ds_phys->ds_prev_snap_txg) {
- dmu_buf_will_dirty(ds->ds_prev->ds_dbuf, tx);
- mutex_enter(&ds->ds_prev->ds_lock);
- ds->ds_prev->ds_phys->ds_unique_bytes +=
- used;
- mutex_exit(&ds->ds_prev->ds_lock);
- }
+ if (ds->ds_prev->ds_phys->ds_next_snap_obj ==
+ ds->ds_object && bp->blk_birth >
+ ds->ds_prev->ds_phys->ds_prev_snap_txg) {
+ dmu_buf_will_dirty(ds->ds_prev->ds_dbuf, tx);
+ mutex_enter(&ds->ds_prev->ds_lock);
+ ds->ds_prev->ds_phys->ds_unique_bytes += used;
+ mutex_exit(&ds->ds_prev->ds_lock);
+ }
+ if (bp->blk_birth > ds->ds_origin_txg) {
+ dsl_dir_transfer_space(ds->ds_dir, used,
+ DD_USED_HEAD, DD_USED_SNAP, tx);
}
}
mutex_enter(&ds->ds_lock);
@@ -180,6 +202,8 @@ dsl_dataset_block_kill(dsl_dataset_t *ds, blkptr_t *bp, zio_t *pio,
ASSERT3U(ds->ds_phys->ds_uncompressed_bytes, >=, uncompressed);
ds->ds_phys->ds_uncompressed_bytes -= uncompressed;
mutex_exit(&ds->ds_lock);
+
+ return (used);
}
uint64_t
@@ -216,32 +240,38 @@ static void
dsl_dataset_evict(dmu_buf_t *db, void *dsv)
{
dsl_dataset_t *ds = dsv;
- dsl_pool_t *dp = ds->ds_dir->dd_pool;
- /* open_refcount == DS_REF_MAX when deleting */
- ASSERT(ds->ds_open_refcount == 0 ||
- ds->ds_open_refcount == DS_REF_MAX);
+ ASSERT(ds->ds_owner == NULL || DSL_DATASET_IS_DESTROYED(ds));
dprintf_ds(ds, "evicting %s\n", "");
- unique_remove(ds->ds_phys->ds_fsid_guid);
+ unique_remove(ds->ds_fsid_guid);
if (ds->ds_user_ptr != NULL)
ds->ds_user_evict_func(ds, ds->ds_user_ptr);
if (ds->ds_prev) {
- dsl_dataset_close(ds->ds_prev, DS_MODE_NONE, ds);
+ dsl_dataset_drop_ref(ds->ds_prev, ds);
ds->ds_prev = NULL;
}
bplist_close(&ds->ds_deadlist);
- dsl_dir_close(ds->ds_dir, ds);
+ if (ds->ds_dir)
+ dsl_dir_close(ds->ds_dir, ds);
- if (list_link_active(&ds->ds_synced_link))
- list_remove(&dp->dp_synced_objsets, ds);
+ ASSERT(!list_link_active(&ds->ds_synced_link));
+ if (mutex_owned(&ds->ds_lock))
+ mutex_exit(&ds->ds_lock);
mutex_destroy(&ds->ds_lock);
+ if (mutex_owned(&ds->ds_opening_lock))
+ mutex_exit(&ds->ds_opening_lock);
+ mutex_destroy(&ds->ds_opening_lock);
+ if (mutex_owned(&ds->ds_deadlist.bpl_lock))
+ mutex_exit(&ds->ds_deadlist.bpl_lock);
mutex_destroy(&ds->ds_deadlist.bpl_lock);
+ rw_destroy(&ds->ds_rwlock);
+ cv_destroy(&ds->ds_exclusive_cv);
kmem_free(ds, sizeof (dsl_dataset_t));
}
@@ -266,16 +296,54 @@ dsl_dataset_get_snapname(dsl_dataset_t *ds)
return (err);
headphys = headdbuf->db_data;
err = zap_value_search(dp->dp_meta_objset,
- headphys->ds_snapnames_zapobj, ds->ds_object, ds->ds_snapname);
+ headphys->ds_snapnames_zapobj, ds->ds_object, 0, ds->ds_snapname);
dmu_buf_rele(headdbuf, FTAG);
return (err);
}
-int
-dsl_dataset_open_obj(dsl_pool_t *dp, uint64_t dsobj, const char *snapname,
- int mode, void *tag, dsl_dataset_t **dsp)
+static int
+dsl_dataset_snap_lookup(dsl_dataset_t *ds, const char *name, uint64_t *value)
+{
+ objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
+ uint64_t snapobj = ds->ds_phys->ds_snapnames_zapobj;
+ matchtype_t mt;
+ int err;
+
+ if (ds->ds_phys->ds_flags & DS_FLAG_CI_DATASET)
+ mt = MT_FIRST;
+ else
+ mt = MT_EXACT;
+
+ err = zap_lookup_norm(mos, snapobj, name, 8, 1,
+ value, mt, NULL, 0, NULL);
+ if (err == ENOTSUP && mt == MT_FIRST)
+ err = zap_lookup(mos, snapobj, name, 8, 1, value);
+ return (err);
+}
+
+static int
+dsl_dataset_snap_remove(dsl_dataset_t *ds, char *name, dmu_tx_t *tx)
+{
+ objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
+ uint64_t snapobj = ds->ds_phys->ds_snapnames_zapobj;
+ matchtype_t mt;
+ int err;
+
+ if (ds->ds_phys->ds_flags & DS_FLAG_CI_DATASET)
+ mt = MT_FIRST;
+ else
+ mt = MT_EXACT;
+
+ err = zap_remove_norm(mos, snapobj, name, mt, tx);
+ if (err == ENOTSUP && mt == MT_FIRST)
+ err = zap_remove(mos, snapobj, name, tx);
+ return (err);
+}
+
+static int
+dsl_dataset_get_ref(dsl_pool_t *dp, uint64_t dsobj, void *tag,
+ dsl_dataset_t **dsp)
{
- uint64_t weight = ds_refcnt_weight[DS_MODE_LEVEL(mode)];
objset_t *mos = dp->dp_meta_objset;
dmu_buf_t *dbuf;
dsl_dataset_t *ds;
@@ -297,8 +365,11 @@ dsl_dataset_open_obj(dsl_pool_t *dp, uint64_t dsobj, const char *snapname,
ds->ds_phys = dbuf->db_data;
mutex_init(&ds->ds_lock, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&ds->ds_opening_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&ds->ds_deadlist.bpl_lock, NULL, MUTEX_DEFAULT,
NULL);
+ rw_init(&ds->ds_rwlock, 0, 0, 0);
+ cv_init(&ds->ds_exclusive_cv, NULL, CV_DEFAULT, NULL);
err = bplist_open(&ds->ds_deadlist,
mos, ds->ds_phys->ds_deadlist_obj);
@@ -312,42 +383,65 @@ dsl_dataset_open_obj(dsl_pool_t *dp, uint64_t dsobj, const char *snapname,
* just opened it.
*/
mutex_destroy(&ds->ds_lock);
+ mutex_destroy(&ds->ds_opening_lock);
mutex_destroy(&ds->ds_deadlist.bpl_lock);
+ rw_destroy(&ds->ds_rwlock);
+ cv_destroy(&ds->ds_exclusive_cv);
kmem_free(ds, sizeof (dsl_dataset_t));
dmu_buf_rele(dbuf, tag);
return (err);
}
- if (ds->ds_dir->dd_phys->dd_head_dataset_obj == dsobj) {
+ if (!dsl_dataset_is_snapshot(ds)) {
ds->ds_snapname[0] = '\0';
if (ds->ds_phys->ds_prev_snap_obj) {
- err = dsl_dataset_open_obj(dp,
- ds->ds_phys->ds_prev_snap_obj, NULL,
- DS_MODE_NONE, ds, &ds->ds_prev);
+ err = dsl_dataset_get_ref(dp,
+ ds->ds_phys->ds_prev_snap_obj,
+ ds, &ds->ds_prev);
}
- } else {
- if (snapname) {
-#ifdef ZFS_DEBUG
- dsl_dataset_phys_t *headphys;
- dmu_buf_t *headdbuf;
- err = dmu_bonus_hold(mos,
- ds->ds_dir->dd_phys->dd_head_dataset_obj,
- FTAG, &headdbuf);
+
+ if (err == 0 && dsl_dir_is_clone(ds->ds_dir)) {
+ dsl_dataset_t *origin;
+
+ err = dsl_dataset_hold_obj(dp,
+ ds->ds_dir->dd_phys->dd_origin_obj,
+ FTAG, &origin);
if (err == 0) {
- headphys = headdbuf->db_data;
- uint64_t foundobj;
- err = zap_lookup(dp->dp_meta_objset,
- headphys->ds_snapnames_zapobj,
- snapname, sizeof (foundobj), 1,
- &foundobj);
- ASSERT3U(foundobj, ==, dsobj);
- dmu_buf_rele(headdbuf, FTAG);
+ ds->ds_origin_txg =
+ origin->ds_phys->ds_creation_txg;
+ dsl_dataset_rele(origin, FTAG);
}
-#endif
- (void) strcat(ds->ds_snapname, snapname);
- } else if (zfs_flags & ZFS_DEBUG_SNAPNAMES) {
- err = dsl_dataset_get_snapname(ds);
}
+ } else if (zfs_flags & ZFS_DEBUG_SNAPNAMES) {
+ err = dsl_dataset_get_snapname(ds);
+ }
+
+ if (err == 0 && !dsl_dataset_is_snapshot(ds)) {
+ /*
+ * In sync context, we're called with either no lock
+ * or with the write lock. If we're not syncing,
+ * we're always called with the read lock held.
+ */
+ boolean_t need_lock =
+ !RW_WRITE_HELD(&dp->dp_config_rwlock) &&
+ dsl_pool_sync_context(dp);
+
+ if (need_lock)
+ rw_enter(&dp->dp_config_rwlock, RW_READER);
+
+ err = dsl_prop_get_ds(ds,
+ "refreservation", sizeof (uint64_t), 1,
+ &ds->ds_reserved, NULL);
+ if (err == 0) {
+ err = dsl_prop_get_ds(ds,
+ "refquota", sizeof (uint64_t), 1,
+ &ds->ds_quota, NULL);
+ }
+
+ if (need_lock)
+ rw_exit(&dp->dp_config_rwlock);
+ } else {
+ ds->ds_reserved = ds->ds_quota = 0;
}
if (err == 0) {
@@ -356,13 +450,14 @@ dsl_dataset_open_obj(dsl_pool_t *dp, uint64_t dsobj, const char *snapname,
}
if (err || winner) {
bplist_close(&ds->ds_deadlist);
- if (ds->ds_prev) {
- dsl_dataset_close(ds->ds_prev,
- DS_MODE_NONE, ds);
- }
+ if (ds->ds_prev)
+ dsl_dataset_drop_ref(ds->ds_prev, ds);
dsl_dir_close(ds->ds_dir, ds);
mutex_destroy(&ds->ds_lock);
+ mutex_destroy(&ds->ds_opening_lock);
mutex_destroy(&ds->ds_deadlist.bpl_lock);
+ rw_destroy(&ds->ds_rwlock);
+ cv_destroy(&ds->ds_exclusive_cv);
kmem_free(ds, sizeof (dsl_dataset_t));
if (err) {
dmu_buf_rele(dbuf, tag);
@@ -370,101 +465,175 @@ dsl_dataset_open_obj(dsl_pool_t *dp, uint64_t dsobj, const char *snapname,
}
ds = winner;
} else {
- uint64_t new =
+ ds->ds_fsid_guid =
unique_insert(ds->ds_phys->ds_fsid_guid);
- if (new != ds->ds_phys->ds_fsid_guid) {
- /* XXX it won't necessarily be synced... */
- ds->ds_phys->ds_fsid_guid = new;
- }
}
}
ASSERT3P(ds->ds_dbuf, ==, dbuf);
ASSERT3P(ds->ds_phys, ==, dbuf->db_data);
-
+ ASSERT(ds->ds_phys->ds_prev_snap_obj != 0 ||
+ spa_version(dp->dp_spa) < SPA_VERSION_ORIGIN ||
+ dp->dp_origin_snap == NULL || ds == dp->dp_origin_snap);
mutex_enter(&ds->ds_lock);
- if ((DS_MODE_LEVEL(mode) == DS_MODE_PRIMARY &&
- (ds->ds_phys->ds_flags & DS_FLAG_INCONSISTENT) &&
- !DS_MODE_IS_INCONSISTENT(mode)) ||
- (ds->ds_open_refcount + weight > DS_REF_MAX)) {
+ if (!dsl_pool_sync_context(dp) && DSL_DATASET_IS_DESTROYED(ds)) {
mutex_exit(&ds->ds_lock);
- dsl_dataset_close(ds, DS_MODE_NONE, tag);
- return (EBUSY);
+ dmu_buf_rele(ds->ds_dbuf, tag);
+ return (ENOENT);
}
- ds->ds_open_refcount += weight;
mutex_exit(&ds->ds_lock);
-
*dsp = ds;
return (0);
}
+static int
+dsl_dataset_hold_ref(dsl_dataset_t *ds, void *tag)
+{
+ dsl_pool_t *dp = ds->ds_dir->dd_pool;
+
+ /*
+ * In syncing context we don't want the rwlock lock: there
+ * may be an existing writer waiting for sync phase to
+ * finish. We don't need to worry about such writers, since
+ * sync phase is single-threaded, so the writer can't be
+ * doing anything while we are active.
+ */
+ if (dsl_pool_sync_context(dp)) {
+ ASSERT(!DSL_DATASET_IS_DESTROYED(ds));
+ return (0);
+ }
+
+ /*
+ * Normal users will hold the ds_rwlock as a READER until they
+ * are finished (i.e., call dsl_dataset_rele()). "Owners" will
+ * drop their READER lock after they set the ds_owner field.
+ *
+ * If the dataset is being destroyed, the destroy thread will
+ * obtain a WRITER lock for exclusive access after it's done its
+ * open-context work and then change the ds_owner to
+ * dsl_reaper once destruction is assured. So threads
+ * may block here temporarily, until the "destructability" of
+ * the dataset is determined.
+ */
+ ASSERT(!RW_WRITE_HELD(&dp->dp_config_rwlock));
+ mutex_enter(&ds->ds_lock);
+ while (!rw_tryenter(&ds->ds_rwlock, RW_READER)) {
+ rw_exit(&dp->dp_config_rwlock);
+ cv_wait(&ds->ds_exclusive_cv, &ds->ds_lock);
+ if (DSL_DATASET_IS_DESTROYED(ds)) {
+ mutex_exit(&ds->ds_lock);
+ dsl_dataset_drop_ref(ds, tag);
+ rw_enter(&dp->dp_config_rwlock, RW_READER);
+ return (ENOENT);
+ }
+ rw_enter(&dp->dp_config_rwlock, RW_READER);
+ }
+ mutex_exit(&ds->ds_lock);
+ return (0);
+}
+
+int
+dsl_dataset_hold_obj(dsl_pool_t *dp, uint64_t dsobj, void *tag,
+ dsl_dataset_t **dsp)
+{
+ int err = dsl_dataset_get_ref(dp, dsobj, tag, dsp);
+
+ if (err)
+ return (err);
+ return (dsl_dataset_hold_ref(*dsp, tag));
+}
+
int
-dsl_dataset_open_spa(spa_t *spa, const char *name, int mode,
- void *tag, dsl_dataset_t **dsp)
+dsl_dataset_own_obj(dsl_pool_t *dp, uint64_t dsobj, int flags, void *owner,
+ dsl_dataset_t **dsp)
+{
+ int err = dsl_dataset_hold_obj(dp, dsobj, owner, dsp);
+
+ ASSERT(DS_MODE_TYPE(flags) != DS_MODE_USER);
+
+ if (err)
+ return (err);
+ if (!dsl_dataset_tryown(*dsp, DS_MODE_IS_INCONSISTENT(flags), owner)) {
+ dsl_dataset_rele(*dsp, owner);
+ return (EBUSY);
+ }
+ return (0);
+}
+
+int
+dsl_dataset_hold(const char *name, void *tag, dsl_dataset_t **dsp)
{
dsl_dir_t *dd;
dsl_pool_t *dp;
- const char *tail;
+ const char *snapname;
uint64_t obj;
- dsl_dataset_t *ds = NULL;
int err = 0;
- err = dsl_dir_open_spa(spa, name, FTAG, &dd, &tail);
+ err = dsl_dir_open_spa(NULL, name, FTAG, &dd, &snapname);
if (err)
return (err);
dp = dd->dd_pool;
obj = dd->dd_phys->dd_head_dataset_obj;
rw_enter(&dp->dp_config_rwlock, RW_READER);
- if (obj == 0) {
- /* A dataset with no associated objset */
+ if (obj)
+ err = dsl_dataset_get_ref(dp, obj, tag, dsp);
+ else
err = ENOENT;
+ if (err)
goto out;
- }
- if (tail != NULL) {
- objset_t *mos = dp->dp_meta_objset;
+ err = dsl_dataset_hold_ref(*dsp, tag);
- err = dsl_dataset_open_obj(dp, obj, NULL,
- DS_MODE_NONE, tag, &ds);
- if (err)
- goto out;
- obj = ds->ds_phys->ds_snapnames_zapobj;
- dsl_dataset_close(ds, DS_MODE_NONE, tag);
- ds = NULL;
+ /* we may be looking for a snapshot */
+ if (err == 0 && snapname != NULL) {
+ dsl_dataset_t *ds = NULL;
- if (tail[0] != '@') {
+ if (*snapname++ != '@') {
+ dsl_dataset_rele(*dsp, tag);
err = ENOENT;
goto out;
}
- tail++;
- /* Look for a snapshot */
- if (!DS_MODE_IS_READONLY(mode)) {
- err = EROFS;
- goto out;
+ dprintf("looking for snapshot '%s'\n", snapname);
+ err = dsl_dataset_snap_lookup(*dsp, snapname, &obj);
+ if (err == 0)
+ err = dsl_dataset_get_ref(dp, obj, tag, &ds);
+ dsl_dataset_rele(*dsp, tag);
+
+ ASSERT3U((err == 0), ==, (ds != NULL));
+
+ if (ds) {
+ mutex_enter(&ds->ds_lock);
+ if (ds->ds_snapname[0] == 0)
+ (void) strlcpy(ds->ds_snapname, snapname,
+ sizeof (ds->ds_snapname));
+ mutex_exit(&ds->ds_lock);
+ err = dsl_dataset_hold_ref(ds, tag);
+ *dsp = err ? NULL : ds;
}
- dprintf("looking for snapshot '%s'\n", tail);
- err = zap_lookup(mos, obj, tail, 8, 1, &obj);
- if (err)
- goto out;
}
- err = dsl_dataset_open_obj(dp, obj, tail, mode, tag, &ds);
-
out:
rw_exit(&dp->dp_config_rwlock);
dsl_dir_close(dd, FTAG);
-
- ASSERT3U((err == 0), ==, (ds != NULL));
- /* ASSERT(ds == NULL || strcmp(name, ds->ds_name) == 0); */
-
- *dsp = ds;
return (err);
}
int
-dsl_dataset_open(const char *name, int mode, void *tag, dsl_dataset_t **dsp)
+dsl_dataset_own(const char *name, int flags, void *owner, dsl_dataset_t **dsp)
{
- return (dsl_dataset_open_spa(NULL, name, mode, tag, dsp));
+ int err = dsl_dataset_hold(name, owner, dsp);
+ if (err)
+ return (err);
+ if ((*dsp)->ds_phys->ds_num_children > 0 &&
+ !DS_MODE_IS_READONLY(flags)) {
+ dsl_dataset_rele(*dsp, owner);
+ return (EROFS);
+ }
+ if (!dsl_dataset_tryown(*dsp, DS_MODE_IS_INCONSISTENT(flags), owner)) {
+ dsl_dataset_rele(*dsp, owner);
+ return (EBUSY);
+ }
+ return (0);
}
void
@@ -477,11 +646,11 @@ dsl_dataset_name(dsl_dataset_t *ds, char *name)
VERIFY(0 == dsl_dataset_get_snapname(ds));
if (ds->ds_snapname[0]) {
(void) strcat(name, "@");
+ /*
+ * We use a "recursive" mutex so that we
+ * can call dprintf_ds() with ds_lock held.
+ */
if (!MUTEX_HELD(&ds->ds_lock)) {
- /*
- * We use a "recursive" mutex so that we
- * can call dprintf_ds() with ds_lock held.
- */
mutex_enter(&ds->ds_lock);
(void) strcat(name, ds->ds_snapname);
mutex_exit(&ds->ds_lock);
@@ -505,7 +674,6 @@ dsl_dataset_namelen(dsl_dataset_t *ds)
if (ds->ds_snapname[0]) {
++result; /* adding one for the @-sign */
if (!MUTEX_HELD(&ds->ds_lock)) {
- /* see dsl_datset_name */
mutex_enter(&ds->ds_lock);
result += strlen(ds->ds_snapname);
mutex_exit(&ds->ds_lock);
@@ -519,119 +687,160 @@ dsl_dataset_namelen(dsl_dataset_t *ds)
}
void
-dsl_dataset_close(dsl_dataset_t *ds, int mode, void *tag)
+dsl_dataset_drop_ref(dsl_dataset_t *ds, void *tag)
{
- uint64_t weight = ds_refcnt_weight[DS_MODE_LEVEL(mode)];
+ dmu_buf_rele(ds->ds_dbuf, tag);
+}
+
+void
+dsl_dataset_rele(dsl_dataset_t *ds, void *tag)
+{
+ if (!dsl_pool_sync_context(ds->ds_dir->dd_pool)) {
+ rw_exit(&ds->ds_rwlock);
+ }
+ dsl_dataset_drop_ref(ds, tag);
+}
+
+void
+dsl_dataset_disown(dsl_dataset_t *ds, void *owner)
+{
+ ASSERT((ds->ds_owner == owner && ds->ds_dbuf) ||
+ (DSL_DATASET_IS_DESTROYED(ds) && ds->ds_dbuf == NULL));
+
mutex_enter(&ds->ds_lock);
- ASSERT3U(ds->ds_open_refcount, >=, weight);
- ds->ds_open_refcount -= weight;
- dprintf_ds(ds, "closing mode %u refcount now 0x%llx\n",
- mode, ds->ds_open_refcount);
+ ds->ds_owner = NULL;
+ if (RW_WRITE_HELD(&ds->ds_rwlock)) {
+ rw_exit(&ds->ds_rwlock);
+ cv_broadcast(&ds->ds_exclusive_cv);
+ }
mutex_exit(&ds->ds_lock);
+ if (ds->ds_dbuf)
+ dsl_dataset_drop_ref(ds, owner);
+ else
+ dsl_dataset_evict(ds->ds_dbuf, ds);
+}
- dmu_buf_rele(ds->ds_dbuf, tag);
+boolean_t
+dsl_dataset_tryown(dsl_dataset_t *ds, boolean_t inconsistentok, void *owner)
+{
+ boolean_t gotit = FALSE;
+
+ mutex_enter(&ds->ds_lock);
+ if (ds->ds_owner == NULL &&
+ (!DS_IS_INCONSISTENT(ds) || inconsistentok)) {
+ ds->ds_owner = owner;
+ if (!dsl_pool_sync_context(ds->ds_dir->dd_pool))
+ rw_exit(&ds->ds_rwlock);
+ gotit = TRUE;
+ }
+ mutex_exit(&ds->ds_lock);
+ return (gotit);
}
void
-dsl_dataset_create_root(dsl_pool_t *dp, uint64_t *ddobjp, dmu_tx_t *tx)
+dsl_dataset_make_exclusive(dsl_dataset_t *ds, void *owner)
{
- objset_t *mos = dp->dp_meta_objset;
+ ASSERT3P(owner, ==, ds->ds_owner);
+ if (!RW_WRITE_HELD(&ds->ds_rwlock))
+ rw_enter(&ds->ds_rwlock, RW_WRITER);
+}
+
+uint64_t
+dsl_dataset_create_sync_dd(dsl_dir_t *dd, dsl_dataset_t *origin,
+ uint64_t flags, dmu_tx_t *tx)
+{
+ dsl_pool_t *dp = dd->dd_pool;
dmu_buf_t *dbuf;
dsl_dataset_phys_t *dsphys;
- dsl_dataset_t *ds;
uint64_t dsobj;
- dsl_dir_t *dd;
+ objset_t *mos = dp->dp_meta_objset;
- dsl_dir_create_root(mos, ddobjp, tx);
- VERIFY(0 == dsl_dir_open_obj(dp, *ddobjp, NULL, FTAG, &dd));
+ if (origin == NULL)
+ origin = dp->dp_origin_snap;
+
+ ASSERT(origin == NULL || origin->ds_dir->dd_pool == dp);
+ ASSERT(origin == NULL || origin->ds_phys->ds_num_children > 0);
+ ASSERT(dmu_tx_is_syncing(tx));
+ ASSERT(dd->dd_phys->dd_head_dataset_obj == 0);
dsobj = dmu_object_alloc(mos, DMU_OT_DSL_DATASET, 0,
DMU_OT_DSL_DATASET, sizeof (dsl_dataset_phys_t), tx);
VERIFY(0 == dmu_bonus_hold(mos, dsobj, FTAG, &dbuf));
dmu_buf_will_dirty(dbuf, tx);
dsphys = dbuf->db_data;
+ bzero(dsphys, sizeof (dsl_dataset_phys_t));
dsphys->ds_dir_obj = dd->dd_object;
+ dsphys->ds_flags = flags;
dsphys->ds_fsid_guid = unique_create();
- unique_remove(dsphys->ds_fsid_guid); /* it isn't open yet */
(void) random_get_pseudo_bytes((void*)&dsphys->ds_guid,
sizeof (dsphys->ds_guid));
dsphys->ds_snapnames_zapobj =
- zap_create(mos, DMU_OT_DSL_DS_SNAP_MAP, DMU_OT_NONE, 0, tx);
+ zap_create_norm(mos, U8_TEXTPREP_TOUPPER, DMU_OT_DSL_DS_SNAP_MAP,
+ DMU_OT_NONE, 0, tx);
dsphys->ds_creation_time = gethrestime_sec();
- dsphys->ds_creation_txg = tx->tx_txg;
+ dsphys->ds_creation_txg = tx->tx_txg == TXG_INITIAL ? 1 : tx->tx_txg;
dsphys->ds_deadlist_obj =
bplist_create(mos, DSL_DEADLIST_BLOCKSIZE, tx);
+
+ if (origin) {
+ dsphys->ds_prev_snap_obj = origin->ds_object;
+ dsphys->ds_prev_snap_txg =
+ origin->ds_phys->ds_creation_txg;
+ dsphys->ds_used_bytes =
+ origin->ds_phys->ds_used_bytes;
+ dsphys->ds_compressed_bytes =
+ origin->ds_phys->ds_compressed_bytes;
+ dsphys->ds_uncompressed_bytes =
+ origin->ds_phys->ds_uncompressed_bytes;
+ dsphys->ds_bp = origin->ds_phys->ds_bp;
+ dsphys->ds_flags |= origin->ds_phys->ds_flags;
+
+ dmu_buf_will_dirty(origin->ds_dbuf, tx);
+ origin->ds_phys->ds_num_children++;
+
+ if (spa_version(dp->dp_spa) >= SPA_VERSION_NEXT_CLONES) {
+ if (origin->ds_phys->ds_next_clones_obj == 0) {
+ origin->ds_phys->ds_next_clones_obj =
+ zap_create(mos,
+ DMU_OT_NEXT_CLONES, DMU_OT_NONE, 0, tx);
+ }
+ VERIFY(0 == zap_add_int(mos,
+ origin->ds_phys->ds_next_clones_obj,
+ dsobj, tx));
+ }
+
+ dmu_buf_will_dirty(dd->dd_dbuf, tx);
+ dd->dd_phys->dd_origin_obj = origin->ds_object;
+ }
+
+ if (spa_version(dp->dp_spa) >= SPA_VERSION_UNIQUE_ACCURATE)
+ dsphys->ds_flags |= DS_FLAG_UNIQUE_ACCURATE;
+
dmu_buf_rele(dbuf, FTAG);
dmu_buf_will_dirty(dd->dd_dbuf, tx);
dd->dd_phys->dd_head_dataset_obj = dsobj;
- dsl_dir_close(dd, FTAG);
- VERIFY(0 ==
- dsl_dataset_open_obj(dp, dsobj, NULL, DS_MODE_NONE, FTAG, &ds));
- (void) dmu_objset_create_impl(dp->dp_spa, ds,
- &ds->ds_phys->ds_bp, DMU_OST_ZFS, tx);
- dsl_dataset_close(ds, DS_MODE_NONE, FTAG);
+ return (dsobj);
}
uint64_t
-dsl_dataset_create_sync(dsl_dir_t *pdd,
- const char *lastname, dsl_dataset_t *clone_parent, dmu_tx_t *tx)
+dsl_dataset_create_sync(dsl_dir_t *pdd, const char *lastname,
+ dsl_dataset_t *origin, uint64_t flags, cred_t *cr, dmu_tx_t *tx)
{
dsl_pool_t *dp = pdd->dd_pool;
- dmu_buf_t *dbuf;
- dsl_dataset_phys_t *dsphys;
uint64_t dsobj, ddobj;
- objset_t *mos = dp->dp_meta_objset;
dsl_dir_t *dd;
- ASSERT(clone_parent == NULL || clone_parent->ds_dir->dd_pool == dp);
- ASSERT(clone_parent == NULL ||
- clone_parent->ds_phys->ds_num_children > 0);
ASSERT(lastname[0] != '@');
- ASSERT(dmu_tx_is_syncing(tx));
- ddobj = dsl_dir_create_sync(pdd, lastname, tx);
+ ddobj = dsl_dir_create_sync(dp, pdd, lastname, tx);
VERIFY(0 == dsl_dir_open_obj(dp, ddobj, lastname, FTAG, &dd));
- dsobj = dmu_object_alloc(mos, DMU_OT_DSL_DATASET, 0,
- DMU_OT_DSL_DATASET, sizeof (dsl_dataset_phys_t), tx);
- VERIFY(0 == dmu_bonus_hold(mos, dsobj, FTAG, &dbuf));
- dmu_buf_will_dirty(dbuf, tx);
- dsphys = dbuf->db_data;
- dsphys->ds_dir_obj = dd->dd_object;
- dsphys->ds_fsid_guid = unique_create();
- unique_remove(dsphys->ds_fsid_guid); /* it isn't open yet */
- (void) random_get_pseudo_bytes((void*)&dsphys->ds_guid,
- sizeof (dsphys->ds_guid));
- dsphys->ds_snapnames_zapobj =
- zap_create(mos, DMU_OT_DSL_DS_SNAP_MAP, DMU_OT_NONE, 0, tx);
- dsphys->ds_creation_time = gethrestime_sec();
- dsphys->ds_creation_txg = tx->tx_txg;
- dsphys->ds_deadlist_obj =
- bplist_create(mos, DSL_DEADLIST_BLOCKSIZE, tx);
- if (clone_parent) {
- dsphys->ds_prev_snap_obj = clone_parent->ds_object;
- dsphys->ds_prev_snap_txg =
- clone_parent->ds_phys->ds_creation_txg;
- dsphys->ds_used_bytes =
- clone_parent->ds_phys->ds_used_bytes;
- dsphys->ds_compressed_bytes =
- clone_parent->ds_phys->ds_compressed_bytes;
- dsphys->ds_uncompressed_bytes =
- clone_parent->ds_phys->ds_uncompressed_bytes;
- dsphys->ds_bp = clone_parent->ds_phys->ds_bp;
+ dsobj = dsl_dataset_create_sync_dd(dd, origin, flags, tx);
- dmu_buf_will_dirty(clone_parent->ds_dbuf, tx);
- clone_parent->ds_phys->ds_num_children++;
+ dsl_deleg_set_create_perms(dd, tx, cr);
- dmu_buf_will_dirty(dd->dd_dbuf, tx);
- dd->dd_phys->dd_clone_parent_obj = clone_parent->ds_object;
- }
- dmu_buf_rele(dbuf, FTAG);
-
- dmu_buf_will_dirty(dd->dd_dbuf, tx);
- dd->dd_phys->dd_head_dataset_obj = dsobj;
dsl_dir_close(dd, FTAG);
return (dsobj);
@@ -653,21 +862,24 @@ dsl_snapshot_destroy_one(char *name, void *arg)
(void) strcat(name, "@");
(void) strcat(name, da->snapname);
- err = dsl_dataset_open(name,
- DS_MODE_EXCLUSIVE | DS_MODE_READONLY | DS_MODE_INCONSISTENT,
+ err = dsl_dataset_own(name, DS_MODE_READONLY | DS_MODE_INCONSISTENT,
da->dstg, &ds);
cp = strchr(name, '@');
*cp = '\0';
- if (err == ENOENT)
- return (0);
- if (err) {
+ if (err == 0) {
+ dsl_dataset_make_exclusive(ds, da->dstg);
+ if (ds->ds_user_ptr) {
+ ds->ds_user_evict_func(ds, ds->ds_user_ptr);
+ ds->ds_user_ptr = NULL;
+ }
+ dsl_sync_task_create(da->dstg, dsl_dataset_destroy_check,
+ dsl_dataset_destroy_sync, ds, da->dstg, 0);
+ } else if (err == ENOENT) {
+ err = 0;
+ } else {
(void) strcpy(da->failed, name);
- return (err);
}
-
- dsl_sync_task_create(da->dstg, dsl_dataset_destroy_check,
- dsl_dataset_destroy_sync, ds, da->dstg, 0);
- return (0);
+ return (err);
}
/*
@@ -681,16 +893,8 @@ dsl_snapshots_destroy(char *fsname, char *snapname)
struct destroyarg da;
dsl_sync_task_t *dst;
spa_t *spa;
- char *cp;
- cp = strchr(fsname, '/');
- if (cp) {
- *cp = '\0';
- err = spa_open(fsname, &spa, FTAG);
- *cp = '/';
- } else {
- err = spa_open(fsname, &spa, FTAG);
- }
+ err = spa_open(fsname, &spa, FTAG);
if (err)
return (err);
da.dstg = dsl_sync_task_group_create(spa_get_dsl(spa));
@@ -706,17 +910,14 @@ dsl_snapshots_destroy(char *fsname, char *snapname)
for (dst = list_head(&da.dstg->dstg_tasks); dst;
dst = list_next(&da.dstg->dstg_tasks, dst)) {
dsl_dataset_t *ds = dst->dst_arg1;
+ /*
+ * Return the file system name that triggered the error
+ */
if (dst->dst_err) {
dsl_dataset_name(ds, fsname);
- cp = strchr(fsname, '@');
- *cp = '\0';
+ *strchr(fsname, '@') = '\0';
}
- /*
- * If it was successful, destroy_sync would have
- * closed the ds
- */
- if (err)
- dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, da.dstg);
+ dsl_dataset_disown(ds, da.dstg);
}
dsl_sync_task_group_destroy(da.dstg);
@@ -724,36 +925,33 @@ dsl_snapshots_destroy(char *fsname, char *snapname)
return (err);
}
+/*
+ * ds must be opened as OWNER. On return (whether successful or not),
+ * ds will be closed and caller can no longer dereference it.
+ */
int
-dsl_dataset_destroy(const char *name)
+dsl_dataset_destroy(dsl_dataset_t *ds, void *tag)
{
int err;
dsl_sync_task_group_t *dstg;
objset_t *os;
- dsl_dataset_t *ds;
dsl_dir_t *dd;
uint64_t obj;
- if (strchr(name, '@')) {
+ if (dsl_dataset_is_snapshot(ds)) {
/* Destroying a snapshot is simpler */
- err = dsl_dataset_open(name,
- DS_MODE_EXCLUSIVE | DS_MODE_READONLY | DS_MODE_INCONSISTENT,
- FTAG, &ds);
- if (err)
- return (err);
+ dsl_dataset_make_exclusive(ds, tag);
+
+ if (ds->ds_user_ptr) {
+ ds->ds_user_evict_func(ds, ds->ds_user_ptr);
+ ds->ds_user_ptr = NULL;
+ }
err = dsl_sync_task_do(ds->ds_dir->dd_pool,
dsl_dataset_destroy_check, dsl_dataset_destroy_sync,
- ds, FTAG, 0);
- if (err)
- dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG);
- return (err);
+ ds, tag, 0);
+ goto out;
}
- err = dmu_objset_open(name, DMU_OST_ANY,
- DS_MODE_EXCLUSIVE | DS_MODE_INCONSISTENT, &os);
- if (err)
- return (err);
- ds = os->os->os_dsl_dataset;
dd = ds->ds_dir;
/*
@@ -762,10 +960,12 @@ dsl_dataset_destroy(const char *name)
*/
err = dsl_sync_task_do(dd->dd_pool, dsl_dataset_destroy_begin_check,
dsl_dataset_destroy_begin_sync, ds, NULL, 0);
- if (err) {
- dmu_objset_close(os);
- return (err);
- }
+ if (err)
+ goto out;
+
+ err = dmu_objset_open_ds(ds, DMU_OST_ANY, &os);
+ if (err)
+ goto out;
/*
* remove the objects in open context, so that we won't
@@ -773,66 +973,73 @@ dsl_dataset_destroy(const char *name)
*/
for (obj = 0; err == 0; err = dmu_object_next(os, &obj, FALSE,
ds->ds_phys->ds_prev_snap_txg)) {
- dmu_tx_t *tx = dmu_tx_create(os);
- dmu_tx_hold_free(tx, obj, 0, DMU_OBJECT_END);
- dmu_tx_hold_bonus(tx, obj);
- err = dmu_tx_assign(tx, TXG_WAIT);
- if (err) {
- /*
- * Perhaps there is not enough disk
- * space. Just deal with it from
- * dsl_dataset_destroy_sync().
- */
- dmu_tx_abort(tx);
- continue;
- }
- VERIFY(0 == dmu_object_free(os, obj, tx));
- dmu_tx_commit(tx);
+ /*
+ * Ignore errors, if there is not enough disk space
+ * we will deal with it in dsl_dataset_destroy_sync().
+ */
+ (void) dmu_free_object(os, obj);
}
- /* Make sure it's not dirty before we finish destroying it. */
- txg_wait_synced(dd->dd_pool, 0);
dmu_objset_close(os);
if (err != ESRCH)
- return (err);
+ goto out;
+
+ rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
+ err = dsl_dir_open_obj(dd->dd_pool, dd->dd_object, NULL, FTAG, &dd);
+ rw_exit(&dd->dd_pool->dp_config_rwlock);
- err = dsl_dataset_open(name,
- DS_MODE_EXCLUSIVE | DS_MODE_READONLY | DS_MODE_INCONSISTENT,
- FTAG, &ds);
if (err)
- return (err);
+ goto out;
- err = dsl_dir_open(name, FTAG, &dd, NULL);
- if (err) {
- dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG);
- return (err);
+ if (ds->ds_user_ptr) {
+ /*
+ * We need to sync out all in-flight IO before we try
+ * to evict (the dataset evict func is trying to clear
+ * the cached entries for this dataset in the ARC).
+ */
+ txg_wait_synced(dd->dd_pool, 0);
}
/*
* Blow away the dsl_dir + head dataset.
*/
+ dsl_dataset_make_exclusive(ds, tag);
+ if (ds->ds_user_ptr) {
+ ds->ds_user_evict_func(ds, ds->ds_user_ptr);
+ ds->ds_user_ptr = NULL;
+ }
dstg = dsl_sync_task_group_create(ds->ds_dir->dd_pool);
dsl_sync_task_create(dstg, dsl_dataset_destroy_check,
- dsl_dataset_destroy_sync, ds, FTAG, 0);
+ dsl_dataset_destroy_sync, ds, tag, 0);
dsl_sync_task_create(dstg, dsl_dir_destroy_check,
dsl_dir_destroy_sync, dd, FTAG, 0);
err = dsl_sync_task_group_wait(dstg);
dsl_sync_task_group_destroy(dstg);
- /* if it is successful, *destroy_sync will close the ds+dd */
- if (err) {
- dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG);
+ /* if it is successful, dsl_dir_destroy_sync will close the dd */
+ if (err)
dsl_dir_close(dd, FTAG);
- }
+out:
+ dsl_dataset_disown(ds, tag);
return (err);
}
int
-dsl_dataset_rollback(dsl_dataset_t *ds)
+dsl_dataset_rollback(dsl_dataset_t *ds, dmu_objset_type_t ost)
{
- ASSERT3U(ds->ds_open_refcount, ==, DS_REF_MAX);
- return (dsl_sync_task_do(ds->ds_dir->dd_pool,
+ int err;
+
+ ASSERT(ds->ds_owner);
+
+ dsl_dataset_make_exclusive(ds, ds->ds_owner);
+ err = dsl_sync_task_do(ds->ds_dir->dd_pool,
dsl_dataset_rollback_check, dsl_dataset_rollback_sync,
- ds, NULL, 0));
+ ds, &ost, 0);
+ /* drop exclusive access */
+ mutex_enter(&ds->ds_lock);
+ rw_exit(&ds->ds_rwlock);
+ cv_broadcast(&ds->ds_exclusive_cv);
+ mutex_exit(&ds->ds_lock);
+ return (err);
}
void *
@@ -904,14 +1111,56 @@ dsl_dataset_dirty(dsl_dataset_t *ds, dmu_tx_t *tx)
}
}
+/*
+ * The unique space in the head dataset can be calculated by subtracting
+ * the space used in the most recent snapshot, that is still being used
+ * in this file system, from the space currently in use. To figure out
+ * the space in the most recent snapshot still in use, we need to take
+ * the total space used in the snapshot and subtract out the space that
+ * has been freed up since the snapshot was taken.
+ */
+static void
+dsl_dataset_recalc_head_uniq(dsl_dataset_t *ds)
+{
+ uint64_t mrs_used;
+ uint64_t dlused, dlcomp, dluncomp;
+
+ ASSERT(ds->ds_object == ds->ds_dir->dd_phys->dd_head_dataset_obj);
+
+ if (ds->ds_phys->ds_prev_snap_obj != 0)
+ mrs_used = ds->ds_prev->ds_phys->ds_used_bytes;
+ else
+ mrs_used = 0;
+
+ VERIFY(0 == bplist_space(&ds->ds_deadlist, &dlused, &dlcomp,
+ &dluncomp));
+
+ ASSERT3U(dlused, <=, mrs_used);
+ ds->ds_phys->ds_unique_bytes =
+ ds->ds_phys->ds_used_bytes - (mrs_used - dlused);
+
+ if (!DS_UNIQUE_IS_ACCURATE(ds) &&
+ spa_version(ds->ds_dir->dd_pool->dp_spa) >=
+ SPA_VERSION_UNIQUE_ACCURATE)
+ ds->ds_phys->ds_flags |= DS_FLAG_UNIQUE_ACCURATE;
+}
+
+static uint64_t
+dsl_dataset_unique(dsl_dataset_t *ds)
+{
+ if (!DS_UNIQUE_IS_ACCURATE(ds) && !dsl_dataset_is_snapshot(ds))
+ dsl_dataset_recalc_head_uniq(ds);
+
+ return (ds->ds_phys->ds_unique_bytes);
+}
+
struct killarg {
- uint64_t *usedp;
- uint64_t *compressedp;
- uint64_t *uncompressedp;
+ dsl_dataset_t *ds;
zio_t *zio;
dmu_tx_t *tx;
};
+/* ARGSUSED */
static int
kill_blkptr(traverse_blk_cache_t *bc, spa_t *spa, void *arg)
{
@@ -920,16 +1169,9 @@ kill_blkptr(traverse_blk_cache_t *bc, spa_t *spa, void *arg)
ASSERT3U(bc->bc_errno, ==, 0);
- /*
- * Since this callback is not called concurrently, no lock is
- * needed on the accounting values.
- */
- *ka->usedp += bp_get_dasize(spa, bp);
- *ka->compressedp += BP_GET_PSIZE(bp);
- *ka->uncompressedp += BP_GET_UCSIZE(bp);
- /* XXX check for EIO? */
- (void) arc_free(ka->zio, spa, ka->tx->tx_txg, bp, NULL, NULL,
- ARC_NOWAIT);
+ ASSERT3U(bp->blk_birth, >, ka->ds->ds_phys->ds_prev_snap_txg);
+ (void) dsl_dataset_block_kill(ka->ds, bp, ka->zio, ka->tx);
+
return (0);
}
@@ -938,14 +1180,12 @@ static int
dsl_dataset_rollback_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
dsl_dataset_t *ds = arg1;
+ dmu_objset_type_t *ost = arg2;
/*
- * There must be a previous snapshot. I suppose we could roll
- * it back to being empty (and re-initialize the upper (ZPL)
- * layer). But for now there's no way to do this via the user
- * interface.
+ * We can only roll back to emptyness if it is a ZPL objset.
*/
- if (ds->ds_phys->ds_prev_snap_txg == 0)
+ if (*ost != DMU_OST_ZFS && ds->ds_phys->ds_prev_snap_txg == 0)
return (EINVAL);
/*
@@ -966,13 +1206,44 @@ dsl_dataset_rollback_check(void *arg1, void *arg2, dmu_tx_t *tx)
/* ARGSUSED */
static void
-dsl_dataset_rollback_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+dsl_dataset_rollback_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
dsl_dataset_t *ds = arg1;
+ dmu_objset_type_t *ost = arg2;
objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
dmu_buf_will_dirty(ds->ds_dbuf, tx);
+ /*
+ * Before the roll back destroy the zil.
+ */
+ if (ds->ds_user_ptr != NULL) {
+ zil_rollback_destroy(
+ ((objset_impl_t *)ds->ds_user_ptr)->os_zil, tx);
+
+ /*
+ * We need to make sure that the objset_impl_t is reopened after
+ * we do the rollback, otherwise it will have the wrong
+ * objset_phys_t. Normally this would happen when this
+ * dataset-open is closed, thus causing the
+ * dataset to be immediately evicted. But when doing "zfs recv
+ * -F", we reopen the objset before that, so that there is no
+ * window where the dataset is closed and inconsistent.
+ */
+ ds->ds_user_evict_func(ds, ds->ds_user_ptr);
+ ds->ds_user_ptr = NULL;
+ }
+
+ /* Transfer space that was freed since last snap back to the head. */
+ {
+ uint64_t used;
+
+ VERIFY(0 == bplist_space_birthrange(&ds->ds_deadlist,
+ ds->ds_origin_txg, UINT64_MAX, &used));
+ dsl_dir_transfer_space(ds->ds_dir, used,
+ DD_USED_SNAP, DD_USED_HEAD, tx);
+ }
+
/* Zero out the deadlist. */
bplist_close(&ds->ds_deadlist);
bplist_destroy(mos, ds->ds_phys->ds_deadlist_obj, tx);
@@ -984,39 +1255,65 @@ dsl_dataset_rollback_sync(void *arg1, void *arg2, dmu_tx_t *tx)
{
/* Free blkptrs that we gave birth to */
zio_t *zio;
- uint64_t used = 0, compressed = 0, uncompressed = 0;
struct killarg ka;
zio = zio_root(tx->tx_pool->dp_spa, NULL, NULL,
ZIO_FLAG_MUSTSUCCEED);
- ka.usedp = &used;
- ka.compressedp = &compressed;
- ka.uncompressedp = &uncompressed;
+ ka.ds = ds;
ka.zio = zio;
ka.tx = tx;
(void) traverse_dsl_dataset(ds, ds->ds_phys->ds_prev_snap_txg,
ADVANCE_POST, kill_blkptr, &ka);
(void) zio_wait(zio);
-
- dsl_dir_diduse_space(ds->ds_dir,
- -used, -compressed, -uncompressed, tx);
}
- /* Change our contents to that of the prev snapshot */
- ASSERT3U(ds->ds_prev->ds_object, ==, ds->ds_phys->ds_prev_snap_obj);
- ds->ds_phys->ds_bp = ds->ds_prev->ds_phys->ds_bp;
- ds->ds_phys->ds_used_bytes = ds->ds_prev->ds_phys->ds_used_bytes;
- ds->ds_phys->ds_compressed_bytes =
- ds->ds_prev->ds_phys->ds_compressed_bytes;
- ds->ds_phys->ds_uncompressed_bytes =
- ds->ds_prev->ds_phys->ds_uncompressed_bytes;
- ds->ds_phys->ds_flags = ds->ds_prev->ds_phys->ds_flags;
- ds->ds_phys->ds_unique_bytes = 0;
+ ASSERT(!(ds->ds_phys->ds_flags & DS_FLAG_UNIQUE_ACCURATE) ||
+ ds->ds_phys->ds_unique_bytes == 0);
+
+ if (ds->ds_prev && ds->ds_prev != ds->ds_dir->dd_pool->dp_origin_snap) {
+ /* Change our contents to that of the prev snapshot */
+
+ ASSERT3U(ds->ds_prev->ds_object, ==,
+ ds->ds_phys->ds_prev_snap_obj);
+ ASSERT3U(ds->ds_phys->ds_used_bytes, <=,
+ ds->ds_prev->ds_phys->ds_used_bytes);
+
+ ds->ds_phys->ds_bp = ds->ds_prev->ds_phys->ds_bp;
+ ds->ds_phys->ds_used_bytes =
+ ds->ds_prev->ds_phys->ds_used_bytes;
+ ds->ds_phys->ds_compressed_bytes =
+ ds->ds_prev->ds_phys->ds_compressed_bytes;
+ ds->ds_phys->ds_uncompressed_bytes =
+ ds->ds_prev->ds_phys->ds_uncompressed_bytes;
+ ds->ds_phys->ds_flags = ds->ds_prev->ds_phys->ds_flags;
+
+ if (ds->ds_prev->ds_phys->ds_next_snap_obj == ds->ds_object) {
+ dmu_buf_will_dirty(ds->ds_prev->ds_dbuf, tx);
+ ds->ds_prev->ds_phys->ds_unique_bytes = 0;
+ }
+ } else {
+ objset_impl_t *osi;
+
+ ASSERT3U(ds->ds_phys->ds_used_bytes, ==, 0);
+ ASSERT3U(ds->ds_phys->ds_compressed_bytes, ==, 0);
+ ASSERT3U(ds->ds_phys->ds_uncompressed_bytes, ==, 0);
- if (ds->ds_prev->ds_phys->ds_next_snap_obj == ds->ds_object) {
- dmu_buf_will_dirty(ds->ds_prev->ds_dbuf, tx);
- ds->ds_prev->ds_phys->ds_unique_bytes = 0;
+ bzero(&ds->ds_phys->ds_bp, sizeof (blkptr_t));
+ ds->ds_phys->ds_flags = 0;
+ ds->ds_phys->ds_unique_bytes = 0;
+ if (spa_version(ds->ds_dir->dd_pool->dp_spa) >=
+ SPA_VERSION_UNIQUE_ACCURATE)
+ ds->ds_phys->ds_flags |= DS_FLAG_UNIQUE_ACCURATE;
+
+ osi = dmu_objset_create_impl(ds->ds_dir->dd_pool->dp_spa, ds,
+ &ds->ds_phys->ds_bp, *ost, tx);
+#ifdef _KERNEL
+ zfs_create_fs(&osi->os, kcred, NULL, tx);
+#endif
}
+
+ spa_history_internal_log(LOG_DS_ROLLBACK, ds->ds_dir->dd_pool->dp_spa,
+ tx, cr, "dataset = %llu", ds->ds_object);
}
/* ARGSUSED */
@@ -1024,6 +1321,9 @@ static int
dsl_dataset_destroy_begin_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
dsl_dataset_t *ds = arg1;
+ objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
+ uint64_t count;
+ int err;
/*
* Can't delete a head dataset if there are snapshots of it.
@@ -1034,26 +1334,44 @@ dsl_dataset_destroy_begin_check(void *arg1, void *arg2, dmu_tx_t *tx)
ds->ds_prev->ds_phys->ds_next_snap_obj == ds->ds_object)
return (EINVAL);
+ /*
+ * This is really a dsl_dir thing, but check it here so that
+ * we'll be less likely to leave this dataset inconsistent &
+ * nearly destroyed.
+ */
+ err = zap_count(mos, ds->ds_dir->dd_phys->dd_child_dir_zapobj, &count);
+ if (err)
+ return (err);
+ if (count != 0)
+ return (EEXIST);
+
return (0);
}
/* ARGSUSED */
static void
-dsl_dataset_destroy_begin_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+dsl_dataset_destroy_begin_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
dsl_dataset_t *ds = arg1;
+ dsl_pool_t *dp = ds->ds_dir->dd_pool;
/* Mark it as inconsistent on-disk, in case we crash */
dmu_buf_will_dirty(ds->ds_dbuf, tx);
ds->ds_phys->ds_flags |= DS_FLAG_INCONSISTENT;
+
+ spa_history_internal_log(LOG_DS_DESTROY_BEGIN, dp->dp_spa, tx,
+ cr, "dataset = %llu", ds->ds_object);
}
/* ARGSUSED */
-static int
+int
dsl_dataset_destroy_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
dsl_dataset_t *ds = arg1;
+ /* we have an owner hold, so noone else can destroy us */
+ ASSERT(!DSL_DATASET_IS_DESTROYED(ds));
+
/* Can't delete a branch point. */
if (ds->ds_phys->ds_num_children > 1)
return (EEXIST);
@@ -1078,11 +1396,50 @@ dsl_dataset_destroy_check(void *arg1, void *arg2, dmu_tx_t *tx)
return (0);
}
+struct refsarg {
+ kmutex_t lock;
+ boolean_t gone;
+ kcondvar_t cv;
+};
+
+/* ARGSUSED */
+static void
+dsl_dataset_refs_gone(dmu_buf_t *db, void *argv)
+{
+ struct refsarg *arg = argv;
+
+ mutex_enter(&arg->lock);
+ arg->gone = TRUE;
+ cv_signal(&arg->cv);
+ mutex_exit(&arg->lock);
+}
+
static void
-dsl_dataset_destroy_sync(void *arg1, void *tag, dmu_tx_t *tx)
+dsl_dataset_drain_refs(dsl_dataset_t *ds, void *tag)
+{
+ struct refsarg arg;
+
+ mutex_init(&arg.lock, NULL, MUTEX_DEFAULT, NULL);
+ cv_init(&arg.cv, NULL, CV_DEFAULT, NULL);
+ arg.gone = FALSE;
+ (void) dmu_buf_update_user(ds->ds_dbuf, ds, &arg, &ds->ds_phys,
+ dsl_dataset_refs_gone);
+ dmu_buf_rele(ds->ds_dbuf, tag);
+ mutex_enter(&arg.lock);
+ while (!arg.gone)
+ cv_wait(&arg.cv, &arg.lock);
+ ASSERT(arg.gone);
+ mutex_exit(&arg.lock);
+ ds->ds_dbuf = NULL;
+ ds->ds_phys = NULL;
+ mutex_destroy(&arg.lock);
+ cv_destroy(&arg.cv);
+}
+
+void
+dsl_dataset_destroy_sync(void *arg1, void *tag, cred_t *cr, dmu_tx_t *tx)
{
dsl_dataset_t *ds = arg1;
- uint64_t used = 0, compressed = 0, uncompressed = 0;
zio_t *zio;
int err;
int after_branch_point = FALSE;
@@ -1091,29 +1448,53 @@ dsl_dataset_destroy_sync(void *arg1, void *tag, dmu_tx_t *tx)
dsl_dataset_t *ds_prev = NULL;
uint64_t obj;
- ASSERT3U(ds->ds_open_refcount, ==, DS_REF_MAX);
+ ASSERT(ds->ds_owner);
ASSERT3U(ds->ds_phys->ds_num_children, <=, 1);
ASSERT(ds->ds_prev == NULL ||
ds->ds_prev->ds_phys->ds_next_snap_obj != ds->ds_object);
ASSERT3U(ds->ds_phys->ds_bp.blk_birth, <=, tx->tx_txg);
+ /* signal any waiters that this dataset is going away */
+ mutex_enter(&ds->ds_lock);
+ ds->ds_owner = dsl_reaper;
+ cv_broadcast(&ds->ds_exclusive_cv);
+ mutex_exit(&ds->ds_lock);
+
+ /* Remove our reservation */
+ if (ds->ds_reserved != 0) {
+ uint64_t val = 0;
+ dsl_dataset_set_reservation_sync(ds, &val, cr, tx);
+ ASSERT3U(ds->ds_reserved, ==, 0);
+ }
+
ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock));
+ dsl_pool_ds_destroyed(ds, tx);
+
obj = ds->ds_object;
if (ds->ds_phys->ds_prev_snap_obj != 0) {
if (ds->ds_prev) {
ds_prev = ds->ds_prev;
} else {
- VERIFY(0 == dsl_dataset_open_obj(dp,
- ds->ds_phys->ds_prev_snap_obj, NULL,
- DS_MODE_NONE, FTAG, &ds_prev));
+ VERIFY(0 == dsl_dataset_hold_obj(dp,
+ ds->ds_phys->ds_prev_snap_obj, FTAG, &ds_prev));
}
after_branch_point =
(ds_prev->ds_phys->ds_next_snap_obj != obj);
dmu_buf_will_dirty(ds_prev->ds_dbuf, tx);
if (after_branch_point &&
+ ds_prev->ds_phys->ds_next_clones_obj != 0) {
+ VERIFY(0 == zap_remove_int(mos,
+ ds_prev->ds_phys->ds_next_clones_obj, obj, tx));
+ if (ds->ds_phys->ds_next_snap_obj != 0) {
+ VERIFY(0 == zap_add_int(mos,
+ ds_prev->ds_phys->ds_next_clones_obj,
+ ds->ds_phys->ds_next_snap_obj, tx));
+ }
+ }
+ if (after_branch_point &&
ds->ds_phys->ds_next_snap_obj == 0) {
/* This clone is toast. */
ASSERT(ds_prev->ds_phys->ds_num_children > 1);
@@ -1130,14 +1511,15 @@ dsl_dataset_destroy_sync(void *arg1, void *tag, dmu_tx_t *tx)
blkptr_t bp;
dsl_dataset_t *ds_next;
uint64_t itor = 0;
+ uint64_t old_unique;
+ int64_t used = 0, compressed = 0, uncompressed = 0;
- spa_scrub_restart(dp->dp_spa, tx->tx_txg);
-
- VERIFY(0 == dsl_dataset_open_obj(dp,
- ds->ds_phys->ds_next_snap_obj, NULL,
- DS_MODE_NONE, FTAG, &ds_next));
+ VERIFY(0 == dsl_dataset_hold_obj(dp,
+ ds->ds_phys->ds_next_snap_obj, FTAG, &ds_next));
ASSERT3U(ds_next->ds_phys->ds_prev_snap_obj, ==, obj);
+ old_unique = dsl_dataset_unique(ds_next);
+
dmu_buf_will_dirty(ds_next->ds_dbuf, tx);
ds_next->ds_phys->ds_prev_snap_obj =
ds->ds_phys->ds_prev_snap_obj;
@@ -1154,8 +1536,7 @@ dsl_dataset_destroy_sync(void *arg1, void *tag, dmu_tx_t *tx)
*
* XXX we're doing this long task with the config lock held
*/
- while (bplist_iterate(&ds_next->ds_deadlist, &itor,
- &bp) == 0) {
+ while (bplist_iterate(&ds_next->ds_deadlist, &itor, &bp) == 0) {
if (bp.blk_birth <= ds->ds_phys->ds_prev_snap_txg) {
VERIFY(0 == bplist_enqueue(&ds->ds_deadlist,
&bp, tx));
@@ -1170,16 +1551,23 @@ dsl_dataset_destroy_sync(void *arg1, void *tag, dmu_tx_t *tx)
compressed += BP_GET_PSIZE(&bp);
uncompressed += BP_GET_UCSIZE(&bp);
/* XXX check return value? */
- (void) arc_free(zio, dp->dp_spa, tx->tx_txg,
+ (void) dsl_free(zio, dp, tx->tx_txg,
&bp, NULL, NULL, ARC_NOWAIT);
}
}
+ ASSERT3U(used, ==, ds->ds_phys->ds_unique_bytes);
+
+ /* change snapused */
+ dsl_dir_diduse_space(ds->ds_dir, DD_USED_SNAP,
+ -used, -compressed, -uncompressed, tx);
+
/* free next's deadlist */
bplist_close(&ds_next->ds_deadlist);
bplist_destroy(mos, ds_next->ds_phys->ds_deadlist_obj, tx);
/* set next's deadlist to our deadlist */
+ bplist_close(&ds->ds_deadlist);
ds_next->ds_phys->ds_deadlist_obj =
ds->ds_phys->ds_deadlist_obj;
VERIFY(0 == bplist_open(&ds_next->ds_deadlist, mos,
@@ -1200,51 +1588,50 @@ dsl_dataset_destroy_sync(void *arg1, void *tag, dmu_tx_t *tx)
* config lock held
*/
dsl_dataset_t *ds_after_next;
+ uint64_t space;
- VERIFY(0 == dsl_dataset_open_obj(dp,
- ds_next->ds_phys->ds_next_snap_obj, NULL,
- DS_MODE_NONE, FTAG, &ds_after_next));
- itor = 0;
- while (bplist_iterate(&ds_after_next->ds_deadlist,
- &itor, &bp) == 0) {
- if (bp.blk_birth >
- ds->ds_phys->ds_prev_snap_txg &&
- bp.blk_birth <=
- ds->ds_phys->ds_creation_txg) {
- ds_next->ds_phys->ds_unique_bytes +=
- bp_get_dasize(dp->dp_spa, &bp);
- }
- }
+ VERIFY(0 == dsl_dataset_hold_obj(dp,
+ ds_next->ds_phys->ds_next_snap_obj,
+ FTAG, &ds_after_next));
+
+ VERIFY(0 ==
+ bplist_space_birthrange(&ds_after_next->ds_deadlist,
+ ds->ds_phys->ds_prev_snap_txg,
+ ds->ds_phys->ds_creation_txg, &space));
+ ds_next->ds_phys->ds_unique_bytes += space;
- dsl_dataset_close(ds_after_next, DS_MODE_NONE, FTAG);
+ dsl_dataset_rele(ds_after_next, FTAG);
ASSERT3P(ds_next->ds_prev, ==, NULL);
} else {
- /*
- * It would be nice to update the head dataset's
- * unique. To do so we would have to traverse
- * it for blocks born after ds_prev, which is
- * pretty expensive just to maintain something
- * for debugging purposes.
- */
ASSERT3P(ds_next->ds_prev, ==, ds);
- dsl_dataset_close(ds_next->ds_prev, DS_MODE_NONE,
- ds_next);
+ dsl_dataset_drop_ref(ds_next->ds_prev, ds_next);
+ ds_next->ds_prev = NULL;
if (ds_prev) {
- VERIFY(0 == dsl_dataset_open_obj(dp,
- ds->ds_phys->ds_prev_snap_obj, NULL,
- DS_MODE_NONE, ds_next, &ds_next->ds_prev));
- } else {
- ds_next->ds_prev = NULL;
+ VERIFY(0 == dsl_dataset_get_ref(dp,
+ ds->ds_phys->ds_prev_snap_obj,
+ ds_next, &ds_next->ds_prev));
}
- }
- dsl_dataset_close(ds_next, DS_MODE_NONE, FTAG);
- /*
- * NB: unique_bytes is not accurate for head objsets
- * because we don't update it when we delete the most
- * recent snapshot -- see above comment.
- */
- ASSERT3U(used, ==, ds->ds_phys->ds_unique_bytes);
+ dsl_dataset_recalc_head_uniq(ds_next);
+
+ /*
+ * Reduce the amount of our unconsmed refreservation
+ * being charged to our parent by the amount of
+ * new unique data we have gained.
+ */
+ if (old_unique < ds_next->ds_reserved) {
+ int64_t mrsdelta;
+ uint64_t new_unique =
+ ds_next->ds_phys->ds_unique_bytes;
+
+ ASSERT(old_unique <= new_unique);
+ mrsdelta = MIN(new_unique - old_unique,
+ ds_next->ds_reserved - old_unique);
+ dsl_dir_diduse_space(ds->ds_dir,
+ DD_USED_REFRSRV, -mrsdelta, 0, 0, tx);
+ }
+ }
+ dsl_dataset_rele(ds_next, FTAG);
} else {
/*
* There's no next snapshot, so this is a head dataset.
@@ -1263,76 +1650,106 @@ dsl_dataset_destroy_sync(void *arg1, void *tag, dmu_tx_t *tx)
* Free everything that we point to (that's born after
* the previous snapshot, if we are a clone)
*
- * XXX we're doing this long task with the config lock held
+ * NB: this should be very quick, because we already
+ * freed all the objects in open context.
*/
- ka.usedp = &used;
- ka.compressedp = &compressed;
- ka.uncompressedp = &uncompressed;
+ ka.ds = ds;
ka.zio = zio;
ka.tx = tx;
err = traverse_dsl_dataset(ds, ds->ds_phys->ds_prev_snap_txg,
ADVANCE_POST, kill_blkptr, &ka);
ASSERT3U(err, ==, 0);
+ ASSERT(spa_version(dp->dp_spa) < SPA_VERSION_UNIQUE_ACCURATE ||
+ ds->ds_phys->ds_unique_bytes == 0);
}
err = zio_wait(zio);
ASSERT3U(err, ==, 0);
- dsl_dir_diduse_space(ds->ds_dir, -used, -compressed, -uncompressed, tx);
-
- if (ds->ds_phys->ds_snapnames_zapobj) {
- err = zap_destroy(mos, ds->ds_phys->ds_snapnames_zapobj, tx);
- ASSERT(err == 0);
- }
-
if (ds->ds_dir->dd_phys->dd_head_dataset_obj == ds->ds_object) {
- /* Erase the link in the dataset */
+ /* Erase the link in the dir */
dmu_buf_will_dirty(ds->ds_dir->dd_dbuf, tx);
ds->ds_dir->dd_phys->dd_head_dataset_obj = 0;
- /*
- * dsl_dir_sync_destroy() called us, they'll destroy
- * the dataset.
- */
+ ASSERT(ds->ds_phys->ds_snapnames_zapobj != 0);
+ err = zap_destroy(mos, ds->ds_phys->ds_snapnames_zapobj, tx);
+ ASSERT(err == 0);
} else {
/* remove from snapshot namespace */
dsl_dataset_t *ds_head;
- VERIFY(0 == dsl_dataset_open_obj(dp,
- ds->ds_dir->dd_phys->dd_head_dataset_obj, NULL,
- DS_MODE_NONE, FTAG, &ds_head));
+ ASSERT(ds->ds_phys->ds_snapnames_zapobj == 0);
+ VERIFY(0 == dsl_dataset_hold_obj(dp,
+ ds->ds_dir->dd_phys->dd_head_dataset_obj, FTAG, &ds_head));
VERIFY(0 == dsl_dataset_get_snapname(ds));
#ifdef ZFS_DEBUG
{
uint64_t val;
- err = zap_lookup(mos,
- ds_head->ds_phys->ds_snapnames_zapobj,
- ds->ds_snapname, 8, 1, &val);
+
+ err = dsl_dataset_snap_lookup(ds_head,
+ ds->ds_snapname, &val);
ASSERT3U(err, ==, 0);
ASSERT3U(val, ==, obj);
}
#endif
- err = zap_remove(mos, ds_head->ds_phys->ds_snapnames_zapobj,
- ds->ds_snapname, tx);
+ err = dsl_dataset_snap_remove(ds_head, ds->ds_snapname, tx);
ASSERT(err == 0);
- dsl_dataset_close(ds_head, DS_MODE_NONE, FTAG);
+ dsl_dataset_rele(ds_head, FTAG);
}
if (ds_prev && ds->ds_prev != ds_prev)
- dsl_dataset_close(ds_prev, DS_MODE_NONE, FTAG);
-
- spa_clear_bootfs(dp->dp_spa, ds->ds_object, tx);
- dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, tag);
+ dsl_dataset_rele(ds_prev, FTAG);
+
+ spa_prop_clear_bootfs(dp->dp_spa, ds->ds_object, tx);
+ spa_history_internal_log(LOG_DS_DESTROY, dp->dp_spa, tx,
+ cr, "dataset = %llu", ds->ds_object);
+
+ if (ds->ds_phys->ds_next_clones_obj != 0) {
+ uint64_t count;
+ ASSERT(0 == zap_count(mos,
+ ds->ds_phys->ds_next_clones_obj, &count) && count == 0);
+ VERIFY(0 == dmu_object_free(mos,
+ ds->ds_phys->ds_next_clones_obj, tx));
+ }
+ if (ds->ds_phys->ds_props_obj != 0)
+ VERIFY(0 == zap_destroy(mos, ds->ds_phys->ds_props_obj, tx));
+ dsl_dir_close(ds->ds_dir, ds);
+ ds->ds_dir = NULL;
+ dsl_dataset_drain_refs(ds, tag);
VERIFY(0 == dmu_object_free(mos, obj, tx));
+}
+static int
+dsl_dataset_snapshot_reserve_space(dsl_dataset_t *ds, dmu_tx_t *tx)
+{
+ uint64_t asize;
+
+ if (!dmu_tx_is_syncing(tx))
+ return (0);
+
+ /*
+ * If there's an fs-only reservation, any blocks that might become
+ * owned by the snapshot dataset must be accommodated by space
+ * outside of the reservation.
+ */
+ asize = MIN(dsl_dataset_unique(ds), ds->ds_reserved);
+ if (asize > dsl_dir_space_available(ds->ds_dir, NULL, 0, FALSE))
+ return (ENOSPC);
+
+ /*
+ * Propogate any reserved space for this snapshot to other
+ * snapshot checks in this sync group.
+ */
+ if (asize > 0)
+ dsl_dir_willuse_space(ds->ds_dir, asize, tx);
+
+ return (0);
}
/* ARGSUSED */
int
dsl_dataset_snapshot_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
- objset_t *os = arg1;
- dsl_dataset_t *ds = os->os->os_dsl_dataset;
+ dsl_dataset_t *ds = arg1;
const char *snapname = arg2;
- objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
int err;
uint64_t value;
@@ -1346,8 +1763,7 @@ dsl_dataset_snapshot_check(void *arg1, void *arg2, dmu_tx_t *tx)
/*
* Check for conflicting name snapshot name.
*/
- err = zap_lookup(mos, ds->ds_phys->ds_snapnames_zapobj,
- snapname, 8, 1, &value);
+ err = dsl_dataset_snap_lookup(ds, snapname, &value);
if (err == 0)
return (EEXIST);
if (err != ENOENT)
@@ -1360,34 +1776,44 @@ dsl_dataset_snapshot_check(void *arg1, void *arg2, dmu_tx_t *tx)
if (dsl_dataset_namelen(ds) + 1 + strlen(snapname) >= MAXNAMELEN)
return (ENAMETOOLONG);
+ err = dsl_dataset_snapshot_reserve_space(ds, tx);
+ if (err)
+ return (err);
+
ds->ds_trysnap_txg = tx->tx_txg;
return (0);
}
void
-dsl_dataset_snapshot_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+dsl_dataset_snapshot_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
- objset_t *os = arg1;
- dsl_dataset_t *ds = os->os->os_dsl_dataset;
+ dsl_dataset_t *ds = arg1;
const char *snapname = arg2;
dsl_pool_t *dp = ds->ds_dir->dd_pool;
dmu_buf_t *dbuf;
dsl_dataset_phys_t *dsphys;
- uint64_t dsobj;
+ uint64_t dsobj, crtxg;
objset_t *mos = dp->dp_meta_objset;
int err;
- spa_scrub_restart(dp->dp_spa, tx->tx_txg);
ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock));
+ /*
+ * The origin's ds_creation_txg has to be < TXG_INITIAL
+ */
+ if (strcmp(snapname, ORIGIN_DIR_NAME) == 0)
+ crtxg = 1;
+ else
+ crtxg = tx->tx_txg;
+
dsobj = dmu_object_alloc(mos, DMU_OT_DSL_DATASET, 0,
DMU_OT_DSL_DATASET, sizeof (dsl_dataset_phys_t), tx);
VERIFY(0 == dmu_bonus_hold(mos, dsobj, FTAG, &dbuf));
dmu_buf_will_dirty(dbuf, tx);
dsphys = dbuf->db_data;
+ bzero(dsphys, sizeof (dsl_dataset_phys_t));
dsphys->ds_dir_obj = ds->ds_dir->dd_object;
dsphys->ds_fsid_guid = unique_create();
- unique_remove(dsphys->ds_fsid_guid); /* it isn't open yet */
(void) random_get_pseudo_bytes((void*)&dsphys->ds_guid,
sizeof (dsphys->ds_guid));
dsphys->ds_prev_snap_obj = ds->ds_phys->ds_prev_snap_obj;
@@ -1395,7 +1821,7 @@ dsl_dataset_snapshot_sync(void *arg1, void *arg2, dmu_tx_t *tx)
dsphys->ds_next_snap_obj = ds->ds_object;
dsphys->ds_num_children = 1;
dsphys->ds_creation_time = gethrestime_sec();
- dsphys->ds_creation_txg = tx->tx_txg;
+ dsphys->ds_creation_txg = crtxg;
dsphys->ds_deadlist_obj = ds->ds_phys->ds_deadlist_obj;
dsphys->ds_used_bytes = ds->ds_phys->ds_used_bytes;
dsphys->ds_compressed_bytes = ds->ds_phys->ds_compressed_bytes;
@@ -1406,6 +1832,8 @@ dsl_dataset_snapshot_sync(void *arg1, void *arg2, dmu_tx_t *tx)
ASSERT3U(ds->ds_prev != 0, ==, ds->ds_phys->ds_prev_snap_obj != 0);
if (ds->ds_prev) {
+ uint64_t next_clones_obj =
+ ds->ds_prev->ds_phys->ds_next_clones_obj;
ASSERT(ds->ds_prev->ds_phys->ds_next_snap_obj ==
ds->ds_object ||
ds->ds_prev->ds_phys->ds_num_children > 1);
@@ -1414,15 +1842,33 @@ dsl_dataset_snapshot_sync(void *arg1, void *arg2, dmu_tx_t *tx)
ASSERT3U(ds->ds_phys->ds_prev_snap_txg, ==,
ds->ds_prev->ds_phys->ds_creation_txg);
ds->ds_prev->ds_phys->ds_next_snap_obj = dsobj;
+ } else if (next_clones_obj != 0) {
+ VERIFY3U(0, ==, zap_remove_int(mos,
+ next_clones_obj, dsphys->ds_next_snap_obj, tx));
+ VERIFY3U(0, ==, zap_add_int(mos,
+ next_clones_obj, dsobj, tx));
}
}
+ /*
+ * If we have a reference-reservation on this dataset, we will
+ * need to increase the amount of refreservation being charged
+ * since our unique space is going to zero.
+ */
+ if (ds->ds_reserved) {
+ int64_t add = MIN(dsl_dataset_unique(ds), ds->ds_reserved);
+ dsl_dir_diduse_space(ds->ds_dir, DD_USED_REFRSRV,
+ add, 0, 0, tx);
+ }
+
bplist_close(&ds->ds_deadlist);
dmu_buf_will_dirty(ds->ds_dbuf, tx);
- ASSERT3U(ds->ds_phys->ds_prev_snap_txg, <, dsphys->ds_creation_txg);
+ ASSERT3U(ds->ds_phys->ds_prev_snap_txg, <, tx->tx_txg);
ds->ds_phys->ds_prev_snap_obj = dsobj;
- ds->ds_phys->ds_prev_snap_txg = dsphys->ds_creation_txg;
+ ds->ds_phys->ds_prev_snap_txg = crtxg;
ds->ds_phys->ds_unique_bytes = 0;
+ if (spa_version(dp->dp_spa) >= SPA_VERSION_UNIQUE_ACCURATE)
+ ds->ds_phys->ds_flags |= DS_FLAG_UNIQUE_ACCURATE;
ds->ds_phys->ds_deadlist_obj =
bplist_create(mos, DSL_DEADLIST_BLOCKSIZE, tx);
VERIFY(0 == bplist_open(&ds->ds_deadlist, mos,
@@ -1434,10 +1880,14 @@ dsl_dataset_snapshot_sync(void *arg1, void *arg2, dmu_tx_t *tx)
ASSERT(err == 0);
if (ds->ds_prev)
- dsl_dataset_close(ds->ds_prev, DS_MODE_NONE, ds);
- VERIFY(0 == dsl_dataset_open_obj(dp,
- ds->ds_phys->ds_prev_snap_obj, snapname,
- DS_MODE_NONE, ds, &ds->ds_prev));
+ dsl_dataset_drop_ref(ds->ds_prev, ds);
+ VERIFY(0 == dsl_dataset_get_ref(dp,
+ ds->ds_phys->ds_prev_snap_obj, ds, &ds->ds_prev));
+
+ dsl_pool_ds_snapshotted(ds, tx);
+
+ spa_history_internal_log(LOG_DS_SNAPSHOT, dp->dp_spa, tx, cr,
+ "dataset = %llu", dsobj);
}
void
@@ -1447,22 +1897,38 @@ dsl_dataset_sync(dsl_dataset_t *ds, zio_t *zio, dmu_tx_t *tx)
ASSERT(ds->ds_user_ptr != NULL);
ASSERT(ds->ds_phys->ds_next_snap_obj == 0);
+ /*
+ * in case we had to change ds_fsid_guid when we opened it,
+ * sync it out now.
+ */
+ dmu_buf_will_dirty(ds->ds_dbuf, tx);
+ ds->ds_phys->ds_fsid_guid = ds->ds_fsid_guid;
+
dsl_dir_dirty(ds->ds_dir, tx);
dmu_objset_sync(ds->ds_user_ptr, zio, tx);
- /* Unneeded? bplist_close(&ds->ds_deadlist); */
}
void
dsl_dataset_stats(dsl_dataset_t *ds, nvlist_t *nv)
{
+ uint64_t refd, avail, uobjs, aobjs;
+
dsl_dir_stats(ds->ds_dir, nv);
+ dsl_dataset_space(ds, &refd, &avail, &uobjs, &aobjs);
+ dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_AVAILABLE, avail);
+ dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFERENCED, refd);
+
dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_CREATION,
ds->ds_phys->ds_creation_time);
dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_CREATETXG,
ds->ds_phys->ds_creation_txg);
- dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFERENCED,
- ds->ds_phys->ds_used_bytes);
+ dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFQUOTA,
+ ds->ds_quota);
+ dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFRESERVATION,
+ ds->ds_reserved);
+ dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_GUID,
+ ds->ds_phys->ds_guid);
if (ds->ds_phys->ds_next_snap_obj) {
/*
@@ -1483,29 +1949,29 @@ dsl_dataset_fast_stat(dsl_dataset_t *ds, dmu_objset_stats_t *stat)
{
stat->dds_creation_txg = ds->ds_phys->ds_creation_txg;
stat->dds_inconsistent = ds->ds_phys->ds_flags & DS_FLAG_INCONSISTENT;
+ stat->dds_guid = ds->ds_phys->ds_guid;
if (ds->ds_phys->ds_next_snap_obj) {
stat->dds_is_snapshot = B_TRUE;
stat->dds_num_clones = ds->ds_phys->ds_num_children - 1;
}
/* clone origin is really a dsl_dir thing... */
- if (ds->ds_dir->dd_phys->dd_clone_parent_obj) {
+ rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER);
+ if (dsl_dir_is_clone(ds->ds_dir)) {
dsl_dataset_t *ods;
- rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER);
- VERIFY(0 == dsl_dataset_open_obj(ds->ds_dir->dd_pool,
- ds->ds_dir->dd_phys->dd_clone_parent_obj,
- NULL, DS_MODE_NONE, FTAG, &ods));
- dsl_dataset_name(ods, stat->dds_clone_of);
- dsl_dataset_close(ods, DS_MODE_NONE, FTAG);
- rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
+ VERIFY(0 == dsl_dataset_get_ref(ds->ds_dir->dd_pool,
+ ds->ds_dir->dd_phys->dd_origin_obj, FTAG, &ods));
+ dsl_dataset_name(ods, stat->dds_origin);
+ dsl_dataset_drop_ref(ods, FTAG);
}
+ rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
}
uint64_t
dsl_dataset_fsid_guid(dsl_dataset_t *ds)
{
- return (ds->ds_phys->ds_fsid_guid);
+ return (ds->ds_fsid_guid);
}
void
@@ -1515,10 +1981,37 @@ dsl_dataset_space(dsl_dataset_t *ds,
{
*refdbytesp = ds->ds_phys->ds_used_bytes;
*availbytesp = dsl_dir_space_available(ds->ds_dir, NULL, 0, TRUE);
+ if (ds->ds_reserved > ds->ds_phys->ds_unique_bytes)
+ *availbytesp += ds->ds_reserved - ds->ds_phys->ds_unique_bytes;
+ if (ds->ds_quota != 0) {
+ /*
+ * Adjust available bytes according to refquota
+ */
+ if (*refdbytesp < ds->ds_quota)
+ *availbytesp = MIN(*availbytesp,
+ ds->ds_quota - *refdbytesp);
+ else
+ *availbytesp = 0;
+ }
*usedobjsp = ds->ds_phys->ds_bp.blk_fill;
*availobjsp = DN_MAX_OBJECT - *usedobjsp;
}
+boolean_t
+dsl_dataset_modified_since_lastsnap(dsl_dataset_t *ds)
+{
+ dsl_pool_t *dp = ds->ds_dir->dd_pool;
+
+ ASSERT(RW_LOCK_HELD(&dp->dp_config_rwlock) ||
+ dsl_pool_sync_context(dp));
+ if (ds->ds_prev == NULL)
+ return (B_FALSE);
+ if (ds->ds_phys->ds_bp.blk_birth >
+ ds->ds_prev->ds_phys->ds_creation_txg)
+ return (B_TRUE);
+ return (B_FALSE);
+}
+
/* ARGSUSED */
static int
dsl_dataset_snapshot_rename_check(void *arg1, void *arg2, dmu_tx_t *tx)
@@ -1526,20 +2019,18 @@ dsl_dataset_snapshot_rename_check(void *arg1, void *arg2, dmu_tx_t *tx)
dsl_dataset_t *ds = arg1;
char *newsnapname = arg2;
dsl_dir_t *dd = ds->ds_dir;
- objset_t *mos = dd->dd_pool->dp_meta_objset;
dsl_dataset_t *hds;
uint64_t val;
int err;
- err = dsl_dataset_open_obj(dd->dd_pool,
- dd->dd_phys->dd_head_dataset_obj, NULL, DS_MODE_NONE, FTAG, &hds);
+ err = dsl_dataset_hold_obj(dd->dd_pool,
+ dd->dd_phys->dd_head_dataset_obj, FTAG, &hds);
if (err)
return (err);
/* new name better not be in use */
- err = zap_lookup(mos, hds->ds_phys->ds_snapnames_zapobj,
- newsnapname, 8, 1, &val);
- dsl_dataset_close(hds, DS_MODE_NONE, FTAG);
+ err = dsl_dataset_snap_lookup(hds, newsnapname, &val);
+ dsl_dataset_rele(hds, FTAG);
if (err == 0)
err = EEXIST;
@@ -1554,10 +2045,11 @@ dsl_dataset_snapshot_rename_check(void *arg1, void *arg2, dmu_tx_t *tx)
}
static void
-dsl_dataset_snapshot_rename_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+dsl_dataset_snapshot_rename_sync(void *arg1, void *arg2,
+ cred_t *cr, dmu_tx_t *tx)
{
dsl_dataset_t *ds = arg1;
- char *newsnapname = arg2;
+ const char *newsnapname = arg2;
dsl_dir_t *dd = ds->ds_dir;
objset_t *mos = dd->dd_pool->dp_meta_objset;
dsl_dataset_t *hds;
@@ -1565,12 +2057,11 @@ dsl_dataset_snapshot_rename_sync(void *arg1, void *arg2, dmu_tx_t *tx)
ASSERT(ds->ds_phys->ds_next_snap_obj != 0);
- VERIFY(0 == dsl_dataset_open_obj(dd->dd_pool,
- dd->dd_phys->dd_head_dataset_obj, NULL, DS_MODE_NONE, FTAG, &hds));
+ VERIFY(0 == dsl_dataset_hold_obj(dd->dd_pool,
+ dd->dd_phys->dd_head_dataset_obj, FTAG, &hds));
VERIFY(0 == dsl_dataset_get_snapname(ds));
- err = zap_remove(mos, hds->ds_phys->ds_snapnames_zapobj,
- ds->ds_snapname, tx);
+ err = dsl_dataset_snap_remove(hds, ds->ds_snapname, tx);
ASSERT3U(err, ==, 0);
mutex_enter(&ds->ds_lock);
(void) strcpy(ds->ds_snapname, newsnapname);
@@ -1579,10 +2070,12 @@ dsl_dataset_snapshot_rename_sync(void *arg1, void *arg2, dmu_tx_t *tx)
ds->ds_snapname, 8, 1, &ds->ds_object, tx);
ASSERT3U(err, ==, 0);
- dsl_dataset_close(hds, DS_MODE_NONE, FTAG);
+ spa_history_internal_log(LOG_DS_RENAME, dd->dd_pool->dp_spa, tx,
+ cr, "dataset = %llu", ds->ds_object);
+ dsl_dataset_rele(hds, FTAG);
}
-struct renamearg {
+struct renamesnaparg {
dsl_sync_task_group_t *dstg;
char failed[MAXPATHLEN];
char *oldsnap;
@@ -1592,7 +2085,7 @@ struct renamearg {
static int
dsl_snapshot_rename_one(char *name, void *arg)
{
- struct renamearg *ra = arg;
+ struct renamesnaparg *ra = arg;
dsl_dataset_t *ds = NULL;
char *cp;
int err;
@@ -1600,25 +2093,33 @@ dsl_snapshot_rename_one(char *name, void *arg)
cp = name + strlen(name);
*cp = '@';
(void) strcpy(cp + 1, ra->oldsnap);
- err = dsl_dataset_open(name, DS_MODE_READONLY | DS_MODE_STANDARD,
- ra->dstg, &ds);
+
+ /*
+ * For recursive snapshot renames the parent won't be changing
+ * so we just pass name for both the to/from argument.
+ */
+ err = zfs_secpolicy_rename_perms(name, name, CRED());
if (err == ENOENT) {
- *cp = '\0';
return (0);
- }
- if (err) {
+ } else if (err) {
(void) strcpy(ra->failed, name);
- *cp = '\0';
- dsl_dataset_close(ds, DS_MODE_STANDARD, ra->dstg);
return (err);
}
#ifdef _KERNEL
- /* for all filesystems undergoing rename, we'll need to unmount it */
+ /*
+ * For all filesystems undergoing rename, we'll need to unmount it.
+ */
(void) zfs_unmount_snap(name, NULL);
#endif
-
+ err = dsl_dataset_hold(name, ra->dstg, &ds);
*cp = '\0';
+ if (err == ENOENT) {
+ return (0);
+ } else if (err) {
+ (void) strcpy(ra->failed, name);
+ return (err);
+ }
dsl_sync_task_create(ra->dstg, dsl_dataset_snapshot_rename_check,
dsl_dataset_snapshot_rename_sync, ds, ra->newsnap, 0);
@@ -1630,7 +2131,7 @@ static int
dsl_recursive_rename(char *oldname, const char *newname)
{
int err;
- struct renamearg *ra;
+ struct renamesnaparg *ra;
dsl_sync_task_t *dst;
spa_t *spa;
char *cp, *fsname = spa_strdup(oldname);
@@ -1640,19 +2141,12 @@ dsl_recursive_rename(char *oldname, const char *newname)
cp = strchr(fsname, '@');
*cp = '\0';
- cp = strchr(fsname, '/');
- if (cp) {
- *cp = '\0';
- err = spa_open(fsname, &spa, FTAG);
- *cp = '/';
- } else {
- err = spa_open(fsname, &spa, FTAG);
- }
+ err = spa_open(fsname, &spa, FTAG);
if (err) {
kmem_free(fsname, len + 1);
return (err);
}
- ra = kmem_alloc(sizeof (struct renamearg), KM_SLEEP);
+ ra = kmem_alloc(sizeof (struct renamesnaparg), KM_SLEEP);
ra->dstg = dsl_sync_task_group_create(spa_get_dsl(spa));
ra->oldsnap = strchr(oldname, '@') + 1;
@@ -1675,21 +2169,32 @@ dsl_recursive_rename(char *oldname, const char *newname)
(void) strcat(ra->failed, "@");
(void) strcat(ra->failed, ra->newsnap);
}
- dsl_dataset_close(ds, DS_MODE_STANDARD, ra->dstg);
+ dsl_dataset_rele(ds, ra->dstg);
}
- (void) strcpy(oldname, ra->failed);
+ if (err)
+ (void) strcpy(oldname, ra->failed);
dsl_sync_task_group_destroy(ra->dstg);
- kmem_free(ra, sizeof (struct renamearg));
+ kmem_free(ra, sizeof (struct renamesnaparg));
spa_close(spa, FTAG);
return (err);
}
+static int
+dsl_valid_rename(char *oldname, void *arg)
+{
+ int delta = *(int *)arg;
+
+ if (strlen(oldname) + delta >= MAXNAMELEN)
+ return (ENAMETOOLONG);
+
+ return (0);
+}
+
#pragma weak dmu_objset_rename = dsl_dataset_rename
int
-dsl_dataset_rename(char *oldname, const char *newname,
- boolean_t recursive)
+dsl_dataset_rename(char *oldname, const char *newname, boolean_t recursive)
{
dsl_dir_t *dd;
dsl_dataset_t *ds;
@@ -1700,7 +2205,15 @@ dsl_dataset_rename(char *oldname, const char *newname,
if (err)
return (err);
if (tail == NULL) {
- err = dsl_dir_rename(dd, newname);
+ int delta = strlen(newname) - strlen(oldname);
+
+ /* if we're growing, validate child name lengths */
+ if (delta > 0)
+ err = dmu_objset_find(oldname, dsl_valid_rename,
+ &delta, DS_FIND_CHILDREN | DS_FIND_SNAPSHOTS);
+
+ if (!err)
+ err = dsl_dir_rename(dd, newname);
dsl_dir_close(dd, FTAG);
return (err);
}
@@ -1723,8 +2236,7 @@ dsl_dataset_rename(char *oldname, const char *newname,
if (recursive) {
err = dsl_recursive_rename(oldname, newname);
} else {
- err = dsl_dataset_open(oldname,
- DS_MODE_READONLY | DS_MODE_STANDARD, FTAG, &ds);
+ err = dsl_dataset_hold(oldname, FTAG, &ds);
if (err)
return (err);
@@ -1732,278 +2244,640 @@ dsl_dataset_rename(char *oldname, const char *newname,
dsl_dataset_snapshot_rename_check,
dsl_dataset_snapshot_rename_sync, ds, (char *)tail, 1);
- dsl_dataset_close(ds, DS_MODE_STANDARD, FTAG);
+ dsl_dataset_rele(ds, FTAG);
}
return (err);
}
+struct promotenode {
+ list_node_t link;
+ dsl_dataset_t *ds;
+};
+
struct promotearg {
- uint64_t used, comp, uncomp, unique;
- uint64_t newnext_obj, snapnames_obj;
+ list_t shared_snaps, origin_snaps, clone_snaps;
+ dsl_dataset_t *origin_origin, *origin_head;
+ uint64_t used, comp, uncomp, unique, cloneusedsnap, originusedsnap;
};
+static int snaplist_space(list_t *l, uint64_t mintxg, uint64_t *spacep);
+
+/* ARGSUSED */
static int
dsl_dataset_promote_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
dsl_dataset_t *hds = arg1;
struct promotearg *pa = arg2;
- dsl_dir_t *dd = hds->ds_dir;
- dsl_pool_t *dp = hds->ds_dir->dd_pool;
- dsl_dir_t *pdd = NULL;
- dsl_dataset_t *ds = NULL;
- dsl_dataset_t *pivot_ds = NULL;
- dsl_dataset_t *newnext_ds = NULL;
+ struct promotenode *snap = list_head(&pa->shared_snaps);
+ dsl_dataset_t *origin_ds = snap->ds;
int err;
- char *name = NULL;
- uint64_t itor = 0;
- blkptr_t bp;
-
- bzero(pa, sizeof (*pa));
- /* Check that it is a clone */
- if (dd->dd_phys->dd_clone_parent_obj == 0)
+ /* Check that it is a real clone */
+ if (!dsl_dir_is_clone(hds->ds_dir))
return (EINVAL);
/* Since this is so expensive, don't do the preliminary check */
if (!dmu_tx_is_syncing(tx))
return (0);
- if (err = dsl_dataset_open_obj(dp,
- dd->dd_phys->dd_clone_parent_obj,
- NULL, DS_MODE_EXCLUSIVE, FTAG, &pivot_ds))
- goto out;
- pdd = pivot_ds->ds_dir;
-
- {
- dsl_dataset_t *phds;
- if (err = dsl_dataset_open_obj(dd->dd_pool,
- pdd->dd_phys->dd_head_dataset_obj,
- NULL, DS_MODE_NONE, FTAG, &phds))
- goto out;
- pa->snapnames_obj = phds->ds_phys->ds_snapnames_zapobj;
- dsl_dataset_close(phds, DS_MODE_NONE, FTAG);
- }
-
- if (hds->ds_phys->ds_flags & DS_FLAG_NOPROMOTE) {
- err = EXDEV;
- goto out;
- }
-
- /* find pivot point's new next ds */
- VERIFY(0 == dsl_dataset_open_obj(dd->dd_pool, hds->ds_object,
- NULL, DS_MODE_NONE, FTAG, &newnext_ds));
- while (newnext_ds->ds_phys->ds_prev_snap_obj != pivot_ds->ds_object) {
- dsl_dataset_t *prev;
-
- if (err = dsl_dataset_open_obj(dd->dd_pool,
- newnext_ds->ds_phys->ds_prev_snap_obj,
- NULL, DS_MODE_NONE, FTAG, &prev))
- goto out;
- dsl_dataset_close(newnext_ds, DS_MODE_NONE, FTAG);
- newnext_ds = prev;
- }
- pa->newnext_obj = newnext_ds->ds_object;
+ if (hds->ds_phys->ds_flags & DS_FLAG_NOPROMOTE)
+ return (EXDEV);
- /* compute pivot point's new unique space */
- while ((err = bplist_iterate(&newnext_ds->ds_deadlist,
- &itor, &bp)) == 0) {
- if (bp.blk_birth > pivot_ds->ds_phys->ds_prev_snap_txg)
- pa->unique += bp_get_dasize(dd->dd_pool->dp_spa, &bp);
- }
- if (err != ENOENT)
- goto out;
+ /* compute origin's new unique space */
+ snap = list_tail(&pa->clone_snaps);
+ ASSERT3U(snap->ds->ds_phys->ds_prev_snap_obj, ==, origin_ds->ds_object);
+ err = bplist_space_birthrange(&snap->ds->ds_deadlist,
+ origin_ds->ds_phys->ds_prev_snap_txg, UINT64_MAX, &pa->unique);
+ if (err)
+ return (err);
- /* Walk the snapshots that we are moving */
- name = kmem_alloc(MAXPATHLEN, KM_SLEEP);
- ds = pivot_ds;
- /* CONSTCOND */
- while (TRUE) {
+ /*
+ * Walk the snapshots that we are moving
+ *
+ * Compute space to transfer. Consider the incremental changes
+ * to used for each snapshot:
+ * (my used) = (prev's used) + (blocks born) - (blocks killed)
+ * So each snapshot gave birth to:
+ * (blocks born) = (my used) - (prev's used) + (blocks killed)
+ * So a sequence would look like:
+ * (uN - u(N-1) + kN) + ... + (u1 - u0 + k1) + (u0 - 0 + k0)
+ * Which simplifies to:
+ * uN + kN + kN-1 + ... + k1 + k0
+ * Note however, if we stop before we reach the ORIGIN we get:
+ * uN + kN + kN-1 + ... + kM - uM-1
+ */
+ pa->used = origin_ds->ds_phys->ds_used_bytes;
+ pa->comp = origin_ds->ds_phys->ds_compressed_bytes;
+ pa->uncomp = origin_ds->ds_phys->ds_uncompressed_bytes;
+ for (snap = list_head(&pa->shared_snaps); snap;
+ snap = list_next(&pa->shared_snaps, snap)) {
uint64_t val, dlused, dlcomp, dluncomp;
- dsl_dataset_t *prev;
+ dsl_dataset_t *ds = snap->ds;
/* Check that the snapshot name does not conflict */
- dsl_dataset_name(ds, name);
- err = zap_lookup(dd->dd_pool->dp_meta_objset,
- hds->ds_phys->ds_snapnames_zapobj, ds->ds_snapname,
- 8, 1, &val);
- if (err != ENOENT) {
- if (err == 0)
- err = EEXIST;
- goto out;
- }
-
- /*
- * compute space to transfer. Each snapshot gave birth to:
- * (my used) - (prev's used) + (deadlist's used)
- */
- pa->used += ds->ds_phys->ds_used_bytes;
- pa->comp += ds->ds_phys->ds_compressed_bytes;
- pa->uncomp += ds->ds_phys->ds_uncompressed_bytes;
+ VERIFY(0 == dsl_dataset_get_snapname(ds));
+ err = dsl_dataset_snap_lookup(hds, ds->ds_snapname, &val);
+ if (err == 0)
+ return (EEXIST);
+ if (err != ENOENT)
+ return (err);
- /* If we reach the first snapshot, we're done. */
+ /* The very first snapshot does not have a deadlist */
if (ds->ds_phys->ds_prev_snap_obj == 0)
- break;
+ continue;
if (err = bplist_space(&ds->ds_deadlist,
&dlused, &dlcomp, &dluncomp))
- goto out;
- if (err = dsl_dataset_open_obj(dd->dd_pool,
- ds->ds_phys->ds_prev_snap_obj, NULL, DS_MODE_EXCLUSIVE,
- FTAG, &prev))
- goto out;
- pa->used += dlused - prev->ds_phys->ds_used_bytes;
- pa->comp += dlcomp - prev->ds_phys->ds_compressed_bytes;
- pa->uncomp += dluncomp - prev->ds_phys->ds_uncompressed_bytes;
+ return (err);
+ pa->used += dlused;
+ pa->comp += dlcomp;
+ pa->uncomp += dluncomp;
+ }
- /*
- * We could be a clone of a clone. If we reach our
- * parent's branch point, we're done.
- */
- if (prev->ds_phys->ds_next_snap_obj != ds->ds_object) {
- dsl_dataset_close(prev, DS_MODE_EXCLUSIVE, FTAG);
- break;
- }
- if (ds != pivot_ds)
- dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG);
- ds = prev;
+ /*
+ * If we are a clone of a clone then we never reached ORIGIN,
+ * so we need to subtract out the clone origin's used space.
+ */
+ if (pa->origin_origin) {
+ pa->used -= pa->origin_origin->ds_phys->ds_used_bytes;
+ pa->comp -= pa->origin_origin->ds_phys->ds_compressed_bytes;
+ pa->uncomp -= pa->origin_origin->ds_phys->ds_uncompressed_bytes;
}
/* Check that there is enough space here */
- err = dsl_dir_transfer_possible(pdd, dd, pa->used);
+ err = dsl_dir_transfer_possible(origin_ds->ds_dir, hds->ds_dir,
+ pa->used);
+ if (err)
+ return (err);
-out:
- if (ds && ds != pivot_ds)
- dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG);
- if (pivot_ds)
- dsl_dataset_close(pivot_ds, DS_MODE_EXCLUSIVE, FTAG);
- if (newnext_ds)
- dsl_dataset_close(newnext_ds, DS_MODE_NONE, FTAG);
- if (name)
- kmem_free(name, MAXPATHLEN);
- return (err);
+ /*
+ * Compute the amounts of space that will be used by snapshots
+ * after the promotion (for both origin and clone). For each,
+ * it is the amount of space that will be on all of their
+ * deadlists (that was not born before their new origin).
+ */
+ if (hds->ds_dir->dd_phys->dd_flags & DD_FLAG_USED_BREAKDOWN) {
+ uint64_t space;
+
+ /*
+ * Note, typically this will not be a clone of a clone,
+ * so snap->ds->ds_origin_txg will be < TXG_INITIAL, so
+ * these snaplist_space() -> bplist_space_birthrange()
+ * calls will be fast because they do not have to
+ * iterate over all bps.
+ */
+ snap = list_head(&pa->origin_snaps);
+ err = snaplist_space(&pa->shared_snaps,
+ snap->ds->ds_origin_txg, &pa->cloneusedsnap);
+ if (err)
+ return (err);
+
+ err = snaplist_space(&pa->clone_snaps,
+ snap->ds->ds_origin_txg, &space);
+ if (err)
+ return (err);
+ pa->cloneusedsnap += space;
+ }
+ if (origin_ds->ds_dir->dd_phys->dd_flags & DD_FLAG_USED_BREAKDOWN) {
+ err = snaplist_space(&pa->origin_snaps,
+ origin_ds->ds_phys->ds_creation_txg, &pa->originusedsnap);
+ if (err)
+ return (err);
+ }
+
+ return (0);
}
static void
-dsl_dataset_promote_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+dsl_dataset_promote_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
dsl_dataset_t *hds = arg1;
struct promotearg *pa = arg2;
+ struct promotenode *snap = list_head(&pa->shared_snaps);
+ dsl_dataset_t *origin_ds = snap->ds;
+ dsl_dataset_t *origin_head;
dsl_dir_t *dd = hds->ds_dir;
dsl_pool_t *dp = hds->ds_dir->dd_pool;
- dsl_dir_t *pdd = NULL;
- dsl_dataset_t *ds, *pivot_ds;
- char *name;
+ dsl_dir_t *odd = NULL;
+ uint64_t oldnext_obj;
+ int64_t delta;
- ASSERT(dd->dd_phys->dd_clone_parent_obj != 0);
ASSERT(0 == (hds->ds_phys->ds_flags & DS_FLAG_NOPROMOTE));
- VERIFY(0 == dsl_dataset_open_obj(dp,
- dd->dd_phys->dd_clone_parent_obj,
- NULL, DS_MODE_EXCLUSIVE, FTAG, &pivot_ds));
+ snap = list_head(&pa->origin_snaps);
+ origin_head = snap->ds;
+
/*
- * We need to explicitly open pdd, since pivot_ds's pdd will be
+ * We need to explicitly open odd, since origin_ds's dd will be
* changing.
*/
- VERIFY(0 == dsl_dir_open_obj(dp, pivot_ds->ds_dir->dd_object,
- NULL, FTAG, &pdd));
+ VERIFY(0 == dsl_dir_open_obj(dp, origin_ds->ds_dir->dd_object,
+ NULL, FTAG, &odd));
+
+ /* change origin's next snap */
+ dmu_buf_will_dirty(origin_ds->ds_dbuf, tx);
+ oldnext_obj = origin_ds->ds_phys->ds_next_snap_obj;
+ snap = list_tail(&pa->clone_snaps);
+ ASSERT3U(snap->ds->ds_phys->ds_prev_snap_obj, ==, origin_ds->ds_object);
+ origin_ds->ds_phys->ds_next_snap_obj = snap->ds->ds_object;
+
+ /* change the origin's next clone */
+ if (origin_ds->ds_phys->ds_next_clones_obj) {
+ VERIFY3U(0, ==, zap_remove_int(dp->dp_meta_objset,
+ origin_ds->ds_phys->ds_next_clones_obj,
+ origin_ds->ds_phys->ds_next_snap_obj, tx));
+ VERIFY3U(0, ==, zap_add_int(dp->dp_meta_objset,
+ origin_ds->ds_phys->ds_next_clones_obj,
+ oldnext_obj, tx));
+ }
- /* move snapshots to this dir */
- name = kmem_alloc(MAXPATHLEN, KM_SLEEP);
- ds = pivot_ds;
- /* CONSTCOND */
- while (TRUE) {
- dsl_dataset_t *prev;
+ /* change origin */
+ dmu_buf_will_dirty(dd->dd_dbuf, tx);
+ ASSERT3U(dd->dd_phys->dd_origin_obj, ==, origin_ds->ds_object);
+ dd->dd_phys->dd_origin_obj = odd->dd_phys->dd_origin_obj;
+ hds->ds_origin_txg = origin_head->ds_origin_txg;
+ dmu_buf_will_dirty(odd->dd_dbuf, tx);
+ odd->dd_phys->dd_origin_obj = origin_ds->ds_object;
+ origin_head->ds_origin_txg = origin_ds->ds_phys->ds_creation_txg;
+ /* move snapshots to this dir */
+ for (snap = list_head(&pa->shared_snaps); snap;
+ snap = list_next(&pa->shared_snaps, snap)) {
+ dsl_dataset_t *ds = snap->ds;
+
+ /* unregister props as dsl_dir is changing */
+ if (ds->ds_user_ptr) {
+ ds->ds_user_evict_func(ds, ds->ds_user_ptr);
+ ds->ds_user_ptr = NULL;
+ }
/* move snap name entry */
- dsl_dataset_name(ds, name);
- VERIFY(0 == zap_remove(dp->dp_meta_objset,
- pa->snapnames_obj, ds->ds_snapname, tx));
+ VERIFY(0 == dsl_dataset_get_snapname(ds));
+ VERIFY(0 == dsl_dataset_snap_remove(origin_head,
+ ds->ds_snapname, tx));
VERIFY(0 == zap_add(dp->dp_meta_objset,
hds->ds_phys->ds_snapnames_zapobj, ds->ds_snapname,
8, 1, &ds->ds_object, tx));
-
/* change containing dsl_dir */
dmu_buf_will_dirty(ds->ds_dbuf, tx);
- ASSERT3U(ds->ds_phys->ds_dir_obj, ==, pdd->dd_object);
+ ASSERT3U(ds->ds_phys->ds_dir_obj, ==, odd->dd_object);
ds->ds_phys->ds_dir_obj = dd->dd_object;
- ASSERT3P(ds->ds_dir, ==, pdd);
+ ASSERT3P(ds->ds_dir, ==, odd);
dsl_dir_close(ds->ds_dir, ds);
VERIFY(0 == dsl_dir_open_obj(dp, dd->dd_object,
NULL, ds, &ds->ds_dir));
ASSERT3U(dsl_prop_numcb(ds), ==, 0);
+ }
- if (ds->ds_phys->ds_prev_snap_obj == 0)
- break;
+ /*
+ * Change space accounting.
+ * Note, pa->*usedsnap and dd_used_breakdown[SNAP] will either
+ * both be valid, or both be 0 (resulting in delta == 0). This
+ * is true for each of {clone,origin} independently.
+ */
+
+ delta = pa->cloneusedsnap -
+ dd->dd_phys->dd_used_breakdown[DD_USED_SNAP];
+ ASSERT3S(delta, >=, 0);
+ ASSERT3U(pa->used, >=, delta);
+ dsl_dir_diduse_space(dd, DD_USED_SNAP, delta, 0, 0, tx);
+ dsl_dir_diduse_space(dd, DD_USED_HEAD,
+ pa->used - delta, pa->comp, pa->uncomp, tx);
+
+ delta = pa->originusedsnap -
+ odd->dd_phys->dd_used_breakdown[DD_USED_SNAP];
+ ASSERT3S(delta, <=, 0);
+ ASSERT3U(pa->used, >=, -delta);
+ dsl_dir_diduse_space(odd, DD_USED_SNAP, delta, 0, 0, tx);
+ dsl_dir_diduse_space(odd, DD_USED_HEAD,
+ -pa->used - delta, -pa->comp, -pa->uncomp, tx);
+
+ origin_ds->ds_phys->ds_unique_bytes = pa->unique;
+
+ /* log history record */
+ spa_history_internal_log(LOG_DS_PROMOTE, dd->dd_pool->dp_spa, tx,
+ cr, "dataset = %llu", hds->ds_object);
+
+ dsl_dir_close(odd, FTAG);
+}
+
+static char *snaplist_tag = "snaplist";
+/*
+ * Make a list of dsl_dataset_t's for the snapshots between first_obj
+ * (exclusive) and last_obj (inclusive). The list will be in reverse
+ * order (last_obj will be the list_head()). If first_obj == 0, do all
+ * snapshots back to this dataset's origin.
+ */
+static int
+snaplist_make(dsl_pool_t *dp, boolean_t own,
+ uint64_t first_obj, uint64_t last_obj, list_t *l)
+{
+ uint64_t obj = last_obj;
+
+ ASSERT(RW_LOCK_HELD(&dp->dp_config_rwlock));
+
+ list_create(l, sizeof (struct promotenode),
+ offsetof(struct promotenode, link));
- VERIFY(0 == dsl_dataset_open_obj(dp,
- ds->ds_phys->ds_prev_snap_obj, NULL, DS_MODE_EXCLUSIVE,
- FTAG, &prev));
+ while (obj != first_obj) {
+ dsl_dataset_t *ds;
+ struct promotenode *snap;
+ int err;
- if (prev->ds_phys->ds_next_snap_obj != ds->ds_object) {
- dsl_dataset_close(prev, DS_MODE_EXCLUSIVE, FTAG);
- break;
+ if (own) {
+ err = dsl_dataset_own_obj(dp, obj,
+ 0, snaplist_tag, &ds);
+ if (err == 0)
+ dsl_dataset_make_exclusive(ds, snaplist_tag);
+ } else {
+ err = dsl_dataset_hold_obj(dp, obj, snaplist_tag, &ds);
+ }
+ if (err == ENOENT) {
+ /* lost race with snapshot destroy */
+ struct promotenode *last = list_tail(l);
+ ASSERT(obj != last->ds->ds_phys->ds_prev_snap_obj);
+ obj = last->ds->ds_phys->ds_prev_snap_obj;
+ continue;
+ } else if (err) {
+ return (err);
}
- if (ds != pivot_ds)
- dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG);
- ds = prev;
+
+ if (first_obj == 0)
+ first_obj = ds->ds_dir->dd_phys->dd_origin_obj;
+
+ snap = kmem_alloc(sizeof (struct promotenode), KM_SLEEP);
+ snap->ds = ds;
+ list_insert_tail(l, snap);
+ obj = ds->ds_phys->ds_prev_snap_obj;
}
- if (ds != pivot_ds)
- dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG);
- /* change pivot point's next snap */
- dmu_buf_will_dirty(pivot_ds->ds_dbuf, tx);
- pivot_ds->ds_phys->ds_next_snap_obj = pa->newnext_obj;
+ return (0);
+}
- /* change clone_parent-age */
- dmu_buf_will_dirty(dd->dd_dbuf, tx);
- ASSERT3U(dd->dd_phys->dd_clone_parent_obj, ==, pivot_ds->ds_object);
- dd->dd_phys->dd_clone_parent_obj = pdd->dd_phys->dd_clone_parent_obj;
- dmu_buf_will_dirty(pdd->dd_dbuf, tx);
- pdd->dd_phys->dd_clone_parent_obj = pivot_ds->ds_object;
+static int
+snaplist_space(list_t *l, uint64_t mintxg, uint64_t *spacep)
+{
+ struct promotenode *snap;
- /* change space accounting */
- dsl_dir_diduse_space(pdd, -pa->used, -pa->comp, -pa->uncomp, tx);
- dsl_dir_diduse_space(dd, pa->used, pa->comp, pa->uncomp, tx);
- pivot_ds->ds_phys->ds_unique_bytes = pa->unique;
+ *spacep = 0;
+ for (snap = list_head(l); snap; snap = list_next(l, snap)) {
+ uint64_t used;
+ int err = bplist_space_birthrange(&snap->ds->ds_deadlist,
+ mintxg, UINT64_MAX, &used);
+ if (err)
+ return (err);
+ *spacep += used;
+ }
+ return (0);
+}
- dsl_dir_close(pdd, FTAG);
- dsl_dataset_close(pivot_ds, DS_MODE_EXCLUSIVE, FTAG);
- kmem_free(name, MAXPATHLEN);
+static void
+snaplist_destroy(list_t *l, boolean_t own)
+{
+ struct promotenode *snap;
+
+ if (!list_link_active(&l->list_head))
+ return;
+
+ while ((snap = list_tail(l)) != NULL) {
+ list_remove(l, snap);
+ if (own)
+ dsl_dataset_disown(snap->ds, snaplist_tag);
+ else
+ dsl_dataset_rele(snap->ds, snaplist_tag);
+ kmem_free(snap, sizeof (struct promotenode));
+ }
+ list_destroy(l);
}
+/*
+ * Promote a clone. Nomenclature note:
+ * "clone" or "cds": the original clone which is being promoted
+ * "origin" or "ods": the snapshot which is originally clone's origin
+ * "origin head" or "ohds": the dataset which is the head
+ * (filesystem/volume) for the origin
+ * "origin origin": the origin of the origin's filesystem (typically
+ * NULL, indicating that the clone is not a clone of a clone).
+ */
int
dsl_dataset_promote(const char *name)
{
dsl_dataset_t *ds;
- int err;
+ dsl_dir_t *dd;
+ dsl_pool_t *dp;
dmu_object_info_t doi;
- struct promotearg pa;
+ struct promotearg pa = { 0 };
+ struct promotenode *snap;
+ int err;
- err = dsl_dataset_open(name, DS_MODE_NONE, FTAG, &ds);
+ err = dsl_dataset_hold(name, FTAG, &ds);
if (err)
return (err);
+ dd = ds->ds_dir;
+ dp = dd->dd_pool;
- err = dmu_object_info(ds->ds_dir->dd_pool->dp_meta_objset,
+ err = dmu_object_info(dp->dp_meta_objset,
ds->ds_phys->ds_snapnames_zapobj, &doi);
if (err) {
- dsl_dataset_close(ds, DS_MODE_NONE, FTAG);
+ dsl_dataset_rele(ds, FTAG);
return (err);
}
+ if (dsl_dataset_is_snapshot(ds) || dd->dd_phys->dd_origin_obj == 0) {
+ dsl_dataset_rele(ds, FTAG);
+ return (EINVAL);
+ }
+
+ /*
+ * We are going to inherit all the snapshots taken before our
+ * origin (i.e., our new origin will be our parent's origin).
+ * Take ownership of them so that we can rename them into our
+ * namespace.
+ */
+ rw_enter(&dp->dp_config_rwlock, RW_READER);
+
+ err = snaplist_make(dp, B_TRUE, 0, dd->dd_phys->dd_origin_obj,
+ &pa.shared_snaps);
+ if (err != 0)
+ goto out;
+
+ err = snaplist_make(dp, B_FALSE, 0, ds->ds_object, &pa.clone_snaps);
+ if (err != 0)
+ goto out;
+
+ snap = list_head(&pa.shared_snaps);
+ ASSERT3U(snap->ds->ds_object, ==, dd->dd_phys->dd_origin_obj);
+ err = snaplist_make(dp, B_FALSE, dd->dd_phys->dd_origin_obj,
+ snap->ds->ds_dir->dd_phys->dd_head_dataset_obj, &pa.origin_snaps);
+ if (err != 0)
+ goto out;
+
+ if (dsl_dir_is_clone(snap->ds->ds_dir)) {
+ err = dsl_dataset_own_obj(dp,
+ snap->ds->ds_dir->dd_phys->dd_origin_obj,
+ 0, FTAG, &pa.origin_origin);
+ if (err != 0)
+ goto out;
+ }
+
+out:
+ rw_exit(&dp->dp_config_rwlock);
+
/*
* Add in 128x the snapnames zapobj size, since we will be moving
* a bunch of snapnames to the promoted ds, and dirtying their
* bonus buffers.
*/
- err = dsl_sync_task_do(ds->ds_dir->dd_pool,
- dsl_dataset_promote_check,
- dsl_dataset_promote_sync, ds, &pa, 2 + 2 * doi.doi_physical_blks);
- dsl_dataset_close(ds, DS_MODE_NONE, FTAG);
+ if (err == 0) {
+ err = dsl_sync_task_do(dp, dsl_dataset_promote_check,
+ dsl_dataset_promote_sync, ds, &pa,
+ 2 + 2 * doi.doi_physical_blks);
+ }
+
+ snaplist_destroy(&pa.shared_snaps, B_TRUE);
+ snaplist_destroy(&pa.clone_snaps, B_FALSE);
+ snaplist_destroy(&pa.origin_snaps, B_FALSE);
+ if (pa.origin_origin)
+ dsl_dataset_disown(pa.origin_origin, FTAG);
+ dsl_dataset_rele(ds, FTAG);
return (err);
}
+struct cloneswaparg {
+ dsl_dataset_t *cds; /* clone dataset */
+ dsl_dataset_t *ohds; /* origin's head dataset */
+ boolean_t force;
+ int64_t unused_refres_delta; /* change in unconsumed refreservation */
+};
+
+/* ARGSUSED */
+static int
+dsl_dataset_clone_swap_check(void *arg1, void *arg2, dmu_tx_t *tx)
+{
+ struct cloneswaparg *csa = arg1;
+
+ /* they should both be heads */
+ if (dsl_dataset_is_snapshot(csa->cds) ||
+ dsl_dataset_is_snapshot(csa->ohds))
+ return (EINVAL);
+
+ /* the branch point should be just before them */
+ if (csa->cds->ds_prev != csa->ohds->ds_prev)
+ return (EINVAL);
+
+ /* cds should be the clone */
+ if (csa->cds->ds_prev->ds_phys->ds_next_snap_obj !=
+ csa->ohds->ds_object)
+ return (EINVAL);
+
+ /* the clone should be a child of the origin */
+ if (csa->cds->ds_dir->dd_parent != csa->ohds->ds_dir)
+ return (EINVAL);
+
+ /* ohds shouldn't be modified unless 'force' */
+ if (!csa->force && dsl_dataset_modified_since_lastsnap(csa->ohds))
+ return (ETXTBSY);
+
+ /* adjust amount of any unconsumed refreservation */
+ csa->unused_refres_delta =
+ (int64_t)MIN(csa->ohds->ds_reserved,
+ csa->ohds->ds_phys->ds_unique_bytes) -
+ (int64_t)MIN(csa->ohds->ds_reserved,
+ csa->cds->ds_phys->ds_unique_bytes);
+
+ if (csa->unused_refres_delta > 0 &&
+ csa->unused_refres_delta >
+ dsl_dir_space_available(csa->ohds->ds_dir, NULL, 0, TRUE))
+ return (ENOSPC);
+
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+dsl_dataset_clone_swap_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
+{
+ struct cloneswaparg *csa = arg1;
+ dsl_pool_t *dp = csa->cds->ds_dir->dd_pool;
+
+ ASSERT(csa->cds->ds_reserved == 0);
+ ASSERT(csa->cds->ds_quota == csa->ohds->ds_quota);
+
+ dmu_buf_will_dirty(csa->cds->ds_dbuf, tx);
+ dmu_buf_will_dirty(csa->ohds->ds_dbuf, tx);
+ dmu_buf_will_dirty(csa->cds->ds_prev->ds_dbuf, tx);
+
+ if (csa->cds->ds_user_ptr != NULL) {
+ csa->cds->ds_user_evict_func(csa->cds, csa->cds->ds_user_ptr);
+ csa->cds->ds_user_ptr = NULL;
+ }
+
+ if (csa->ohds->ds_user_ptr != NULL) {
+ csa->ohds->ds_user_evict_func(csa->ohds,
+ csa->ohds->ds_user_ptr);
+ csa->ohds->ds_user_ptr = NULL;
+ }
+
+ /* reset origin's unique bytes */
+ VERIFY(0 == bplist_space_birthrange(&csa->cds->ds_deadlist,
+ csa->cds->ds_prev->ds_phys->ds_prev_snap_txg, UINT64_MAX,
+ &csa->cds->ds_prev->ds_phys->ds_unique_bytes));
+
+ /* swap blkptrs */
+ {
+ blkptr_t tmp;
+ tmp = csa->ohds->ds_phys->ds_bp;
+ csa->ohds->ds_phys->ds_bp = csa->cds->ds_phys->ds_bp;
+ csa->cds->ds_phys->ds_bp = tmp;
+ }
+
+ /* set dd_*_bytes */
+ {
+ int64_t dused, dcomp, duncomp;
+ uint64_t cdl_used, cdl_comp, cdl_uncomp;
+ uint64_t odl_used, odl_comp, odl_uncomp;
+
+ ASSERT3U(csa->cds->ds_dir->dd_phys->
+ dd_used_breakdown[DD_USED_SNAP], ==, 0);
+
+ VERIFY(0 == bplist_space(&csa->cds->ds_deadlist, &cdl_used,
+ &cdl_comp, &cdl_uncomp));
+ VERIFY(0 == bplist_space(&csa->ohds->ds_deadlist, &odl_used,
+ &odl_comp, &odl_uncomp));
+
+ dused = csa->cds->ds_phys->ds_used_bytes + cdl_used -
+ (csa->ohds->ds_phys->ds_used_bytes + odl_used);
+ dcomp = csa->cds->ds_phys->ds_compressed_bytes + cdl_comp -
+ (csa->ohds->ds_phys->ds_compressed_bytes + odl_comp);
+ duncomp = csa->cds->ds_phys->ds_uncompressed_bytes +
+ cdl_uncomp -
+ (csa->ohds->ds_phys->ds_uncompressed_bytes + odl_uncomp);
+
+ dsl_dir_diduse_space(csa->ohds->ds_dir, DD_USED_HEAD,
+ dused, dcomp, duncomp, tx);
+ dsl_dir_diduse_space(csa->cds->ds_dir, DD_USED_HEAD,
+ -dused, -dcomp, -duncomp, tx);
+
+ /*
+ * The difference in the space used by snapshots is the
+ * difference in snapshot space due to the head's
+ * deadlist (since that's the only thing that's
+ * changing that affects the snapused).
+ */
+ VERIFY(0 == bplist_space_birthrange(&csa->cds->ds_deadlist,
+ csa->ohds->ds_origin_txg, UINT64_MAX, &cdl_used));
+ VERIFY(0 == bplist_space_birthrange(&csa->ohds->ds_deadlist,
+ csa->ohds->ds_origin_txg, UINT64_MAX, &odl_used));
+ dsl_dir_transfer_space(csa->ohds->ds_dir, cdl_used - odl_used,
+ DD_USED_HEAD, DD_USED_SNAP, tx);
+ }
+
+#define SWITCH64(x, y) \
+ { \
+ uint64_t __tmp = (x); \
+ (x) = (y); \
+ (y) = __tmp; \
+ }
+
+ /* swap ds_*_bytes */
+ SWITCH64(csa->ohds->ds_phys->ds_used_bytes,
+ csa->cds->ds_phys->ds_used_bytes);
+ SWITCH64(csa->ohds->ds_phys->ds_compressed_bytes,
+ csa->cds->ds_phys->ds_compressed_bytes);
+ SWITCH64(csa->ohds->ds_phys->ds_uncompressed_bytes,
+ csa->cds->ds_phys->ds_uncompressed_bytes);
+ SWITCH64(csa->ohds->ds_phys->ds_unique_bytes,
+ csa->cds->ds_phys->ds_unique_bytes);
+
+ /* apply any parent delta for change in unconsumed refreservation */
+ dsl_dir_diduse_space(csa->ohds->ds_dir, DD_USED_REFRSRV,
+ csa->unused_refres_delta, 0, 0, tx);
+
+ /* swap deadlists */
+ bplist_close(&csa->cds->ds_deadlist);
+ bplist_close(&csa->ohds->ds_deadlist);
+ SWITCH64(csa->ohds->ds_phys->ds_deadlist_obj,
+ csa->cds->ds_phys->ds_deadlist_obj);
+ VERIFY(0 == bplist_open(&csa->cds->ds_deadlist, dp->dp_meta_objset,
+ csa->cds->ds_phys->ds_deadlist_obj));
+ VERIFY(0 == bplist_open(&csa->ohds->ds_deadlist, dp->dp_meta_objset,
+ csa->ohds->ds_phys->ds_deadlist_obj));
+}
+
+/*
+ * Swap 'clone' with its origin head file system. Used at the end
+ * of "online recv" to swizzle the file system to the new version.
+ */
+int
+dsl_dataset_clone_swap(dsl_dataset_t *clone, dsl_dataset_t *origin_head,
+ boolean_t force)
+{
+ struct cloneswaparg csa;
+ int error;
+
+ ASSERT(clone->ds_owner);
+ ASSERT(origin_head->ds_owner);
+retry:
+ /* Need exclusive access for the swap */
+ rw_enter(&clone->ds_rwlock, RW_WRITER);
+ if (!rw_tryenter(&origin_head->ds_rwlock, RW_WRITER)) {
+ rw_exit(&clone->ds_rwlock);
+ rw_enter(&origin_head->ds_rwlock, RW_WRITER);
+ if (!rw_tryenter(&clone->ds_rwlock, RW_WRITER)) {
+ rw_exit(&origin_head->ds_rwlock);
+ goto retry;
+ }
+ }
+ csa.cds = clone;
+ csa.ohds = origin_head;
+ csa.force = force;
+ error = dsl_sync_task_do(clone->ds_dir->dd_pool,
+ dsl_dataset_clone_swap_check,
+ dsl_dataset_clone_swap_sync, &csa, NULL, 9);
+ return (error);
+}
+
/*
* Given a pool name and a dataset object number in that pool,
* return the name of that dataset.
@@ -2013,23 +2887,220 @@ dsl_dsobj_to_dsname(char *pname, uint64_t obj, char *buf)
{
spa_t *spa;
dsl_pool_t *dp;
- dsl_dataset_t *ds = NULL;
+ dsl_dataset_t *ds;
int error;
if ((error = spa_open(pname, &spa, FTAG)) != 0)
return (error);
dp = spa_get_dsl(spa);
rw_enter(&dp->dp_config_rwlock, RW_READER);
- if ((error = dsl_dataset_open_obj(dp, obj,
- NULL, DS_MODE_NONE, FTAG, &ds)) != 0) {
- rw_exit(&dp->dp_config_rwlock);
- spa_close(spa, FTAG);
- return (error);
+ if ((error = dsl_dataset_hold_obj(dp, obj, FTAG, &ds)) == 0) {
+ dsl_dataset_name(ds, buf);
+ dsl_dataset_rele(ds, FTAG);
}
- dsl_dataset_name(ds, buf);
- dsl_dataset_close(ds, DS_MODE_NONE, FTAG);
rw_exit(&dp->dp_config_rwlock);
spa_close(spa, FTAG);
+ return (error);
+}
+
+int
+dsl_dataset_check_quota(dsl_dataset_t *ds, boolean_t check_quota,
+ uint64_t asize, uint64_t inflight, uint64_t *used, uint64_t *ref_rsrv)
+{
+ int error = 0;
+
+ ASSERT3S(asize, >, 0);
+
+ /*
+ * *ref_rsrv is the portion of asize that will come from any
+ * unconsumed refreservation space.
+ */
+ *ref_rsrv = 0;
+
+ mutex_enter(&ds->ds_lock);
+ /*
+ * Make a space adjustment for reserved bytes.
+ */
+ if (ds->ds_reserved > ds->ds_phys->ds_unique_bytes) {
+ ASSERT3U(*used, >=,
+ ds->ds_reserved - ds->ds_phys->ds_unique_bytes);
+ *used -= (ds->ds_reserved - ds->ds_phys->ds_unique_bytes);
+ *ref_rsrv =
+ asize - MIN(asize, parent_delta(ds, asize + inflight));
+ }
+
+ if (!check_quota || ds->ds_quota == 0) {
+ mutex_exit(&ds->ds_lock);
+ return (0);
+ }
+ /*
+ * If they are requesting more space, and our current estimate
+ * is over quota, they get to try again unless the actual
+ * on-disk is over quota and there are no pending changes (which
+ * may free up space for us).
+ */
+ if (ds->ds_phys->ds_used_bytes + inflight >= ds->ds_quota) {
+ if (inflight > 0 || ds->ds_phys->ds_used_bytes < ds->ds_quota)
+ error = ERESTART;
+ else
+ error = EDQUOT;
+ }
+ mutex_exit(&ds->ds_lock);
+
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+dsl_dataset_set_quota_check(void *arg1, void *arg2, dmu_tx_t *tx)
+{
+ dsl_dataset_t *ds = arg1;
+ uint64_t *quotap = arg2;
+ uint64_t new_quota = *quotap;
+
+ if (spa_version(ds->ds_dir->dd_pool->dp_spa) < SPA_VERSION_REFQUOTA)
+ return (ENOTSUP);
+
+ if (new_quota == 0)
+ return (0);
+
+ if (new_quota < ds->ds_phys->ds_used_bytes ||
+ new_quota < ds->ds_reserved)
+ return (ENOSPC);
+
return (0);
}
+
+/* ARGSUSED */
+void
+dsl_dataset_set_quota_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
+{
+ dsl_dataset_t *ds = arg1;
+ uint64_t *quotap = arg2;
+ uint64_t new_quota = *quotap;
+
+ dmu_buf_will_dirty(ds->ds_dbuf, tx);
+
+ ds->ds_quota = new_quota;
+
+ dsl_prop_set_uint64_sync(ds->ds_dir, "refquota", new_quota, cr, tx);
+
+ spa_history_internal_log(LOG_DS_REFQUOTA, ds->ds_dir->dd_pool->dp_spa,
+ tx, cr, "%lld dataset = %llu ",
+ (longlong_t)new_quota, ds->ds_object);
+}
+
+int
+dsl_dataset_set_quota(const char *dsname, uint64_t quota)
+{
+ dsl_dataset_t *ds;
+ int err;
+
+ err = dsl_dataset_hold(dsname, FTAG, &ds);
+ if (err)
+ return (err);
+
+ if (quota != ds->ds_quota) {
+ /*
+ * If someone removes a file, then tries to set the quota, we
+ * want to make sure the file freeing takes effect.
+ */
+ txg_wait_open(ds->ds_dir->dd_pool, 0);
+
+ err = dsl_sync_task_do(ds->ds_dir->dd_pool,
+ dsl_dataset_set_quota_check, dsl_dataset_set_quota_sync,
+ ds, &quota, 0);
+ }
+ dsl_dataset_rele(ds, FTAG);
+ return (err);
+}
+
+static int
+dsl_dataset_set_reservation_check(void *arg1, void *arg2, dmu_tx_t *tx)
+{
+ dsl_dataset_t *ds = arg1;
+ uint64_t *reservationp = arg2;
+ uint64_t new_reservation = *reservationp;
+ int64_t delta;
+ uint64_t unique;
+
+ if (new_reservation > INT64_MAX)
+ return (EOVERFLOW);
+
+ if (spa_version(ds->ds_dir->dd_pool->dp_spa) <
+ SPA_VERSION_REFRESERVATION)
+ return (ENOTSUP);
+
+ if (dsl_dataset_is_snapshot(ds))
+ return (EINVAL);
+
+ /*
+ * If we are doing the preliminary check in open context, the
+ * space estimates may be inaccurate.
+ */
+ if (!dmu_tx_is_syncing(tx))
+ return (0);
+
+ mutex_enter(&ds->ds_lock);
+ unique = dsl_dataset_unique(ds);
+ delta = MAX(unique, new_reservation) - MAX(unique, ds->ds_reserved);
+ mutex_exit(&ds->ds_lock);
+
+ if (delta > 0 &&
+ delta > dsl_dir_space_available(ds->ds_dir, NULL, 0, TRUE))
+ return (ENOSPC);
+ if (delta > 0 && ds->ds_quota > 0 &&
+ new_reservation > ds->ds_quota)
+ return (ENOSPC);
+
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+dsl_dataset_set_reservation_sync(void *arg1, void *arg2, cred_t *cr,
+ dmu_tx_t *tx)
+{
+ dsl_dataset_t *ds = arg1;
+ uint64_t *reservationp = arg2;
+ uint64_t new_reservation = *reservationp;
+ uint64_t unique;
+ int64_t delta;
+
+ dmu_buf_will_dirty(ds->ds_dbuf, tx);
+
+ mutex_enter(&ds->ds_dir->dd_lock);
+ mutex_enter(&ds->ds_lock);
+ unique = dsl_dataset_unique(ds);
+ delta = MAX(0, (int64_t)(new_reservation - unique)) -
+ MAX(0, (int64_t)(ds->ds_reserved - unique));
+ ds->ds_reserved = new_reservation;
+ mutex_exit(&ds->ds_lock);
+
+ dsl_dir_diduse_space(ds->ds_dir, DD_USED_REFRSRV, delta, 0, 0, tx);
+ mutex_exit(&ds->ds_dir->dd_lock);
+ dsl_prop_set_uint64_sync(ds->ds_dir, "refreservation",
+ new_reservation, cr, tx);
+
+ spa_history_internal_log(LOG_DS_REFRESERV,
+ ds->ds_dir->dd_pool->dp_spa, tx, cr, "%lld dataset = %llu",
+ (longlong_t)new_reservation, ds->ds_object);
+}
+
+int
+dsl_dataset_set_reservation(const char *dsname, uint64_t reservation)
+{
+ dsl_dataset_t *ds;
+ int err;
+
+ err = dsl_dataset_hold(dsname, FTAG, &ds);
+ if (err)
+ return (err);
+
+ err = dsl_sync_task_do(ds->ds_dir->dd_pool,
+ dsl_dataset_set_reservation_check,
+ dsl_dataset_set_reservation_sync, ds, &reservation, 0);
+ dsl_dataset_rele(ds, FTAG);
+ return (err);
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_deleg.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_deleg.c
new file mode 100644
index 0000000..2ce16fe
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_deleg.c
@@ -0,0 +1,735 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL 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.
+ */
+
+/*
+ * DSL permissions are stored in a two level zap attribute
+ * mechanism. The first level identifies the "class" of
+ * entry. The class is identified by the first 2 letters of
+ * the attribute. The second letter "l" or "d" identifies whether
+ * it is a local or descendent permission. The first letter
+ * identifies the type of entry.
+ *
+ * ul$<id> identifies permissions 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>
+ *
+ * Each of the above entities points to another zap attribute that contains one
+ * attribute for each allowed permission, such as create, destroy,...
+ * All of the "upper" case class types will specify permission set names
+ * rather than permissions.
+ *
+ * Basically it looks something like this:
+ * ul$12 -> ZAP OBJ -> permissions...
+ *
+ * The ZAP OBJ is referred to as the jump object.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/dmu.h>
+#include <sys/dmu_objset.h>
+#include <sys/dmu_tx.h>
+#include <sys/dsl_dataset.h>
+#include <sys/dsl_dir.h>
+#include <sys/dsl_prop.h>
+#include <sys/dsl_synctask.h>
+#include <sys/dsl_deleg.h>
+#include <sys/spa.h>
+#include <sys/spa_impl.h>
+#include <sys/zio_checksum.h> /* for the default checksum value */
+#include <sys/zap.h>
+#include <sys/fs/zfs.h>
+#include <sys/cred.h>
+#include <sys/sunddi.h>
+
+#include "zfs_deleg.h"
+
+/*
+ * Validate that user is allowed to delegate specified permissions.
+ *
+ * In order to delegate "create" you must have "create"
+ * and "allow".
+ */
+int
+dsl_deleg_can_allow(char *ddname, nvlist_t *nvp, cred_t *cr)
+{
+ nvpair_t *whopair = NULL;
+ int error;
+
+ if ((error = dsl_deleg_access(ddname, ZFS_DELEG_PERM_ALLOW, cr)) != 0)
+ return (error);
+
+ while (whopair = nvlist_next_nvpair(nvp, whopair)) {
+ nvlist_t *perms;
+ nvpair_t *permpair = NULL;
+
+ VERIFY(nvpair_value_nvlist(whopair, &perms) == 0);
+
+ while (permpair = nvlist_next_nvpair(perms, permpair)) {
+ const char *perm = nvpair_name(permpair);
+
+ if (strcmp(perm, ZFS_DELEG_PERM_ALLOW) == 0)
+ return (EPERM);
+
+ if ((error = dsl_deleg_access(ddname, perm, cr)) != 0)
+ return (error);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Validate that user is allowed to unallow specified permissions. They
+ * must have the 'allow' permission, and even then can only unallow
+ * perms for their uid.
+ */
+int
+dsl_deleg_can_unallow(char *ddname, nvlist_t *nvp, cred_t *cr)
+{
+ nvpair_t *whopair = NULL;
+ int error;
+ char idstr[32];
+
+ if ((error = dsl_deleg_access(ddname, ZFS_DELEG_PERM_ALLOW, cr)) != 0)
+ return (error);
+
+ (void) snprintf(idstr, sizeof (idstr), "%lld",
+ (longlong_t)crgetuid(cr));
+
+ while (whopair = nvlist_next_nvpair(nvp, whopair)) {
+ zfs_deleg_who_type_t type = nvpair_name(whopair)[0];
+
+ if (type != ZFS_DELEG_USER &&
+ type != ZFS_DELEG_USER_SETS)
+ return (EPERM);
+
+ if (strcmp(idstr, &nvpair_name(whopair)[3]) != 0)
+ return (EPERM);
+ }
+ return (0);
+}
+
+static void
+dsl_deleg_set_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
+{
+ dsl_dir_t *dd = arg1;
+ nvlist_t *nvp = arg2;
+ objset_t *mos = dd->dd_pool->dp_meta_objset;
+ nvpair_t *whopair = NULL;
+ uint64_t zapobj = dd->dd_phys->dd_deleg_zapobj;
+
+ if (zapobj == 0) {
+ dmu_buf_will_dirty(dd->dd_dbuf, tx);
+ zapobj = dd->dd_phys->dd_deleg_zapobj = zap_create(mos,
+ DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
+ }
+
+ while (whopair = nvlist_next_nvpair(nvp, whopair)) {
+ const char *whokey = nvpair_name(whopair);
+ nvlist_t *perms;
+ nvpair_t *permpair = NULL;
+ uint64_t jumpobj;
+
+ VERIFY(nvpair_value_nvlist(whopair, &perms) == 0);
+
+ if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) != 0) {
+ jumpobj = zap_create(mos, DMU_OT_DSL_PERMS,
+ DMU_OT_NONE, 0, tx);
+ VERIFY(zap_update(mos, zapobj,
+ whokey, 8, 1, &jumpobj, tx) == 0);
+ }
+
+ while (permpair = nvlist_next_nvpair(perms, permpair)) {
+ const char *perm = nvpair_name(permpair);
+ uint64_t n = 0;
+
+ VERIFY(zap_update(mos, jumpobj,
+ perm, 8, 1, &n, tx) == 0);
+ spa_history_internal_log(LOG_DS_PERM_UPDATE,
+ dd->dd_pool->dp_spa, tx, cr,
+ "%s %s dataset = %llu", whokey, perm,
+ dd->dd_phys->dd_head_dataset_obj);
+ }
+ }
+}
+
+static void
+dsl_deleg_unset_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
+{
+ dsl_dir_t *dd = arg1;
+ nvlist_t *nvp = arg2;
+ objset_t *mos = dd->dd_pool->dp_meta_objset;
+ nvpair_t *whopair = NULL;
+ uint64_t zapobj = dd->dd_phys->dd_deleg_zapobj;
+
+ if (zapobj == 0)
+ return;
+
+ while (whopair = nvlist_next_nvpair(nvp, whopair)) {
+ const char *whokey = nvpair_name(whopair);
+ nvlist_t *perms;
+ nvpair_t *permpair = NULL;
+ uint64_t jumpobj;
+
+ if (nvpair_value_nvlist(whopair, &perms) != 0) {
+ if (zap_lookup(mos, zapobj, whokey, 8,
+ 1, &jumpobj) == 0) {
+ (void) zap_remove(mos, zapobj, whokey, tx);
+ VERIFY(0 == zap_destroy(mos, jumpobj, tx));
+ }
+ spa_history_internal_log(LOG_DS_PERM_WHO_REMOVE,
+ dd->dd_pool->dp_spa, tx, cr,
+ "%s dataset = %llu", whokey,
+ dd->dd_phys->dd_head_dataset_obj);
+ continue;
+ }
+
+ if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) != 0)
+ continue;
+
+ while (permpair = nvlist_next_nvpair(perms, permpair)) {
+ const char *perm = nvpair_name(permpair);
+ uint64_t n = 0;
+
+ (void) zap_remove(mos, jumpobj, perm, tx);
+ if (zap_count(mos, jumpobj, &n) == 0 && n == 0) {
+ (void) zap_remove(mos, zapobj,
+ whokey, tx);
+ VERIFY(0 == zap_destroy(mos,
+ jumpobj, tx));
+ }
+ spa_history_internal_log(LOG_DS_PERM_REMOVE,
+ dd->dd_pool->dp_spa, tx, cr,
+ "%s %s dataset = %llu", whokey, perm,
+ dd->dd_phys->dd_head_dataset_obj);
+ }
+ }
+}
+
+int
+dsl_deleg_set(const char *ddname, nvlist_t *nvp, boolean_t unset)
+{
+ dsl_dir_t *dd;
+ int error;
+ nvpair_t *whopair = NULL;
+ int blocks_modified = 0;
+
+ error = dsl_dir_open(ddname, FTAG, &dd, NULL);
+ if (error)
+ return (error);
+
+ if (spa_version(dmu_objset_spa(dd->dd_pool->dp_meta_objset)) <
+ SPA_VERSION_DELEGATED_PERMS) {
+ dsl_dir_close(dd, FTAG);
+ return (ENOTSUP);
+ }
+
+ while (whopair = nvlist_next_nvpair(nvp, whopair))
+ blocks_modified++;
+
+ error = dsl_sync_task_do(dd->dd_pool, NULL,
+ unset ? dsl_deleg_unset_sync : dsl_deleg_set_sync,
+ dd, nvp, blocks_modified);
+ dsl_dir_close(dd, FTAG);
+
+ return (error);
+}
+
+/*
+ * Find all 'allow' permissions from a given point and then continue
+ * traversing up to the root.
+ *
+ * This function constructs an nvlist of nvlists.
+ * each setpoint is an nvlist composed of an nvlist of an nvlist
+ * of the individual * users/groups/everyone/create
+ * permissions.
+ *
+ * The nvlist will look like this.
+ *
+ * { source fsname -> { whokeys { permissions,...}, ...}}
+ *
+ * The fsname nvpairs will be arranged in a bottom up order. For example,
+ * if we have the following structure a/b/c then the nvpairs for the fsnames
+ * will be ordered a/b/c, a/b, a.
+ */
+int
+dsl_deleg_get(const char *ddname, nvlist_t **nvp)
+{
+ dsl_dir_t *dd, *startdd;
+ dsl_pool_t *dp;
+ int error;
+ objset_t *mos;
+
+ error = dsl_dir_open(ddname, FTAG, &startdd, NULL);
+ if (error)
+ return (error);
+
+ dp = startdd->dd_pool;
+ mos = dp->dp_meta_objset;
+
+ VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
+
+ rw_enter(&dp->dp_config_rwlock, RW_READER);
+ for (dd = startdd; dd != NULL; dd = dd->dd_parent) {
+ zap_cursor_t basezc;
+ zap_attribute_t baseza;
+ nvlist_t *sp_nvp;
+ uint64_t n;
+ char source[MAXNAMELEN];
+
+ if (dd->dd_phys->dd_deleg_zapobj &&
+ (zap_count(mos, dd->dd_phys->dd_deleg_zapobj,
+ &n) == 0) && n) {
+ VERIFY(nvlist_alloc(&sp_nvp,
+ NV_UNIQUE_NAME, KM_SLEEP) == 0);
+ } else {
+ continue;
+ }
+
+ for (zap_cursor_init(&basezc, mos,
+ dd->dd_phys->dd_deleg_zapobj);
+ zap_cursor_retrieve(&basezc, &baseza) == 0;
+ zap_cursor_advance(&basezc)) {
+ zap_cursor_t zc;
+ zap_attribute_t za;
+ nvlist_t *perms_nvp;
+
+ ASSERT(baseza.za_integer_length == 8);
+ ASSERT(baseza.za_num_integers == 1);
+
+ VERIFY(nvlist_alloc(&perms_nvp,
+ NV_UNIQUE_NAME, KM_SLEEP) == 0);
+ for (zap_cursor_init(&zc, mos, baseza.za_first_integer);
+ zap_cursor_retrieve(&zc, &za) == 0;
+ zap_cursor_advance(&zc)) {
+ VERIFY(nvlist_add_boolean(perms_nvp,
+ za.za_name) == 0);
+ }
+ zap_cursor_fini(&zc);
+ VERIFY(nvlist_add_nvlist(sp_nvp, baseza.za_name,
+ perms_nvp) == 0);
+ nvlist_free(perms_nvp);
+ }
+
+ zap_cursor_fini(&basezc);
+
+ dsl_dir_name(dd, source);
+ VERIFY(nvlist_add_nvlist(*nvp, source, sp_nvp) == 0);
+ nvlist_free(sp_nvp);
+ }
+ rw_exit(&dp->dp_config_rwlock);
+
+ dsl_dir_close(startdd, FTAG);
+ return (0);
+}
+
+/*
+ * Routines for dsl_deleg_access() -- access checking.
+ */
+typedef struct perm_set {
+ avl_node_t p_node;
+ boolean_t p_matched;
+ char p_setname[ZFS_MAX_DELEG_NAME];
+} perm_set_t;
+
+static int
+perm_set_compare(const void *arg1, const void *arg2)
+{
+ const perm_set_t *node1 = arg1;
+ const perm_set_t *node2 = arg2;
+ int val;
+
+ val = strcmp(node1->p_setname, node2->p_setname);
+ if (val == 0)
+ return (0);
+ return (val > 0 ? 1 : -1);
+}
+
+/*
+ * Determine whether a specified permission exists.
+ *
+ * First the base attribute has to be retrieved. i.e. ul$12
+ * Once the base object has been retrieved the actual permission
+ * is lookup up in the zap object the base object points to.
+ *
+ * Return 0 if permission exists, ENOENT if there is no whokey, EPERM if
+ * there is no perm in that jumpobj.
+ */
+static int
+dsl_check_access(objset_t *mos, uint64_t zapobj,
+ char type, char checkflag, void *valp, const char *perm)
+{
+ int error;
+ uint64_t jumpobj, zero;
+ char whokey[ZFS_MAX_DELEG_NAME];
+
+ zfs_deleg_whokey(whokey, type, checkflag, valp);
+ error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj);
+ if (error == 0) {
+ error = zap_lookup(mos, jumpobj, perm, 8, 1, &zero);
+ if (error == ENOENT)
+ error = EPERM;
+ }
+ return (error);
+}
+
+/*
+ * check a specified user/group for a requested permission
+ */
+static int
+dsl_check_user_access(objset_t *mos, uint64_t zapobj, const char *perm,
+ int checkflag, cred_t *cr)
+{
+ const gid_t *gids;
+ int ngids;
+ int i;
+ uint64_t id;
+
+ /* check for user */
+ id = crgetuid(cr);
+ if (dsl_check_access(mos, zapobj,
+ ZFS_DELEG_USER, checkflag, &id, perm) == 0)
+ return (0);
+
+ /* check for users primary group */
+ id = crgetgid(cr);
+ if (dsl_check_access(mos, zapobj,
+ ZFS_DELEG_GROUP, checkflag, &id, perm) == 0)
+ return (0);
+
+ /* check for everyone entry */
+ id = -1;
+ if (dsl_check_access(mos, zapobj,
+ ZFS_DELEG_EVERYONE, checkflag, &id, perm) == 0)
+ return (0);
+
+ /* check each supplemental group user is a member of */
+ ngids = crgetngroups(cr);
+ gids = crgetgroups(cr);
+ for (i = 0; i != ngids; i++) {
+ id = gids[i];
+ if (dsl_check_access(mos, zapobj,
+ ZFS_DELEG_GROUP, checkflag, &id, perm) == 0)
+ return (0);
+ }
+
+ return (EPERM);
+}
+
+/*
+ * Iterate over the sets specified in the specified zapobj
+ * and load them into the permsets avl tree.
+ */
+static int
+dsl_load_sets(objset_t *mos, uint64_t zapobj,
+ char type, char checkflag, void *valp, avl_tree_t *avl)
+{
+ zap_cursor_t zc;
+ zap_attribute_t za;
+ perm_set_t *permnode;
+ avl_index_t idx;
+ uint64_t jumpobj;
+ int error;
+ char whokey[ZFS_MAX_DELEG_NAME];
+
+ zfs_deleg_whokey(whokey, type, checkflag, valp);
+
+ error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj);
+ if (error != 0)
+ return (error);
+
+ for (zap_cursor_init(&zc, mos, jumpobj);
+ zap_cursor_retrieve(&zc, &za) == 0;
+ zap_cursor_advance(&zc)) {
+ permnode = kmem_alloc(sizeof (perm_set_t), KM_SLEEP);
+ (void) strlcpy(permnode->p_setname, za.za_name,
+ sizeof (permnode->p_setname));
+ permnode->p_matched = B_FALSE;
+
+ if (avl_find(avl, permnode, &idx) == NULL) {
+ avl_insert(avl, permnode, idx);
+ } else {
+ kmem_free(permnode, sizeof (perm_set_t));
+ }
+ }
+ zap_cursor_fini(&zc);
+ return (0);
+}
+
+/*
+ * Load all permissions user based on cred belongs to.
+ */
+static void
+dsl_load_user_sets(objset_t *mos, uint64_t zapobj, avl_tree_t *avl,
+ char checkflag, cred_t *cr)
+{
+ const gid_t *gids;
+ int ngids, i;
+ uint64_t id;
+
+ id = crgetuid(cr);
+ (void) dsl_load_sets(mos, zapobj,
+ ZFS_DELEG_USER_SETS, checkflag, &id, avl);
+
+ id = crgetgid(cr);
+ (void) dsl_load_sets(mos, zapobj,
+ ZFS_DELEG_GROUP_SETS, checkflag, &id, avl);
+
+ (void) dsl_load_sets(mos, zapobj,
+ ZFS_DELEG_EVERYONE_SETS, checkflag, NULL, avl);
+
+ ngids = crgetngroups(cr);
+ gids = crgetgroups(cr);
+ for (i = 0; i != ngids; i++) {
+ id = gids[i];
+ (void) dsl_load_sets(mos, zapobj,
+ ZFS_DELEG_GROUP_SETS, checkflag, &id, avl);
+ }
+}
+
+/*
+ * Check if user has requested permission.
+ */
+int
+dsl_deleg_access(const char *dsname, const char *perm, cred_t *cr)
+{
+ dsl_dataset_t *ds;
+ dsl_dir_t *dd;
+ dsl_pool_t *dp;
+ void *cookie;
+ int error;
+ char checkflag = ZFS_DELEG_LOCAL;
+ objset_t *mos;
+ avl_tree_t permsets;
+ perm_set_t *setnode;
+
+ error = dsl_dataset_hold(dsname, FTAG, &ds);
+ if (error)
+ return (error);
+
+ dp = ds->ds_dir->dd_pool;
+ mos = dp->dp_meta_objset;
+
+ if (dsl_delegation_on(mos) == B_FALSE) {
+ dsl_dataset_rele(ds, FTAG);
+ return (ECANCELED);
+ }
+
+ if (spa_version(dmu_objset_spa(dp->dp_meta_objset)) <
+ SPA_VERSION_DELEGATED_PERMS) {
+ dsl_dataset_rele(ds, FTAG);
+ return (EPERM);
+ }
+
+ avl_create(&permsets, perm_set_compare, sizeof (perm_set_t),
+ offsetof(perm_set_t, p_node));
+
+ rw_enter(&dp->dp_config_rwlock, RW_READER);
+ for (dd = ds->ds_dir; dd != NULL; dd = dd->dd_parent,
+ checkflag = ZFS_DELEG_DESCENDENT) {
+ uint64_t zapobj;
+ boolean_t expanded;
+
+ /*
+ * If not in global zone then make sure
+ * the zoned property is set
+ */
+ if (!INGLOBALZONE(curthread)) {
+ uint64_t zoned;
+
+ if (dsl_prop_get_dd(dd,
+ zfs_prop_to_name(ZFS_PROP_ZONED),
+ 8, 1, &zoned, NULL) != 0)
+ break;
+ if (!zoned)
+ break;
+ }
+ zapobj = dd->dd_phys->dd_deleg_zapobj;
+
+ if (zapobj == 0)
+ continue;
+
+ dsl_load_user_sets(mos, zapobj, &permsets, checkflag, cr);
+again:
+ expanded = B_FALSE;
+ for (setnode = avl_first(&permsets); setnode;
+ setnode = AVL_NEXT(&permsets, setnode)) {
+ if (setnode->p_matched == B_TRUE)
+ continue;
+
+ /* See if this set directly grants this permission */
+ error = dsl_check_access(mos, zapobj,
+ ZFS_DELEG_NAMED_SET, 0, setnode->p_setname, perm);
+ if (error == 0)
+ goto success;
+ if (error == EPERM)
+ setnode->p_matched = B_TRUE;
+
+ /* See if this set includes other sets */
+ error = dsl_load_sets(mos, zapobj,
+ ZFS_DELEG_NAMED_SET_SETS, 0,
+ setnode->p_setname, &permsets);
+ if (error == 0)
+ setnode->p_matched = expanded = B_TRUE;
+ }
+ /*
+ * If we expanded any sets, that will define more sets,
+ * which we need to check.
+ */
+ if (expanded)
+ goto again;
+
+ error = dsl_check_user_access(mos, zapobj, perm, checkflag, cr);
+ if (error == 0)
+ goto success;
+ }
+ error = EPERM;
+success:
+ rw_exit(&dp->dp_config_rwlock);
+ dsl_dataset_rele(ds, FTAG);
+
+ cookie = NULL;
+ while ((setnode = avl_destroy_nodes(&permsets, &cookie)) != NULL)
+ kmem_free(setnode, sizeof (perm_set_t));
+
+ return (error);
+}
+
+/*
+ * Other routines.
+ */
+
+static void
+copy_create_perms(dsl_dir_t *dd, uint64_t pzapobj,
+ boolean_t dosets, uint64_t uid, dmu_tx_t *tx)
+{
+ objset_t *mos = dd->dd_pool->dp_meta_objset;
+ uint64_t jumpobj, pjumpobj;
+ uint64_t zapobj = dd->dd_phys->dd_deleg_zapobj;
+ zap_cursor_t zc;
+ zap_attribute_t za;
+ char whokey[ZFS_MAX_DELEG_NAME];
+
+ zfs_deleg_whokey(whokey,
+ dosets ? ZFS_DELEG_CREATE_SETS : ZFS_DELEG_CREATE,
+ ZFS_DELEG_LOCAL, NULL);
+ if (zap_lookup(mos, pzapobj, whokey, 8, 1, &pjumpobj) != 0)
+ return;
+
+ if (zapobj == 0) {
+ dmu_buf_will_dirty(dd->dd_dbuf, tx);
+ zapobj = dd->dd_phys->dd_deleg_zapobj = zap_create(mos,
+ DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
+ }
+
+ zfs_deleg_whokey(whokey,
+ dosets ? ZFS_DELEG_USER_SETS : ZFS_DELEG_USER,
+ ZFS_DELEG_LOCAL, &uid);
+ if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) == ENOENT) {
+ jumpobj = zap_create(mos, DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
+ VERIFY(zap_add(mos, zapobj, whokey, 8, 1, &jumpobj, tx) == 0);
+ }
+
+ for (zap_cursor_init(&zc, mos, pjumpobj);
+ zap_cursor_retrieve(&zc, &za) == 0;
+ zap_cursor_advance(&zc)) {
+ uint64_t zero = 0;
+ ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1);
+
+ VERIFY(zap_update(mos, jumpobj, za.za_name,
+ 8, 1, &zero, tx) == 0);
+ }
+ zap_cursor_fini(&zc);
+}
+
+/*
+ * set all create time permission on new dataset.
+ */
+void
+dsl_deleg_set_create_perms(dsl_dir_t *sdd, dmu_tx_t *tx, cred_t *cr)
+{
+ dsl_dir_t *dd;
+ uint64_t uid = crgetuid(cr);
+
+ if (spa_version(dmu_objset_spa(sdd->dd_pool->dp_meta_objset)) <
+ SPA_VERSION_DELEGATED_PERMS)
+ return;
+
+ for (dd = sdd->dd_parent; dd != NULL; dd = dd->dd_parent) {
+ uint64_t pzapobj = dd->dd_phys->dd_deleg_zapobj;
+
+ if (pzapobj == 0)
+ continue;
+
+ copy_create_perms(sdd, pzapobj, B_FALSE, uid, tx);
+ copy_create_perms(sdd, pzapobj, B_TRUE, uid, tx);
+ }
+}
+
+int
+dsl_deleg_destroy(objset_t *mos, uint64_t zapobj, dmu_tx_t *tx)
+{
+ zap_cursor_t zc;
+ zap_attribute_t za;
+
+ if (zapobj == 0)
+ return (0);
+
+ for (zap_cursor_init(&zc, mos, zapobj);
+ zap_cursor_retrieve(&zc, &za) == 0;
+ zap_cursor_advance(&zc)) {
+ ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1);
+ VERIFY(0 == zap_destroy(mos, za.za_first_integer, tx));
+ }
+ zap_cursor_fini(&zc);
+ VERIFY(0 == zap_destroy(mos, zapobj, tx));
+ return (0);
+}
+
+boolean_t
+dsl_delegation_on(objset_t *os)
+{
+ return (os->os->os_spa->spa_delegation);
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dir.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dir.c
index 5e563b6..48d87f9 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dir.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dir.c
@@ -19,26 +19,28 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/dmu.h>
+#include <sys/dmu_objset.h>
#include <sys/dmu_tx.h>
#include <sys/dsl_dataset.h>
#include <sys/dsl_dir.h>
#include <sys/dsl_prop.h>
#include <sys/dsl_synctask.h>
+#include <sys/dsl_deleg.h>
#include <sys/spa.h>
#include <sys/zap.h>
#include <sys/zio.h>
#include <sys/arc.h>
+#include <sys/sunddi.h>
#include "zfs_namecheck.h"
-static uint64_t dsl_dir_estimated_space(dsl_dir_t *dd);
-static void dsl_dir_set_reservation_sync(void *arg1, void *arg2, dmu_tx_t *tx);
+static uint64_t dsl_dir_space_towrite(dsl_dir_t *dd);
+static void dsl_dir_set_reservation_sync(void *arg1, void *arg2,
+ cred_t *cr, dmu_tx_t *tx);
/* ARGSUSED */
@@ -55,8 +57,6 @@ dsl_dir_evict(dmu_buf_t *db, void *arg)
ASSERT(dd->dd_space_towrite[t] == 0);
}
- ASSERT3U(dd->dd_used_bytes, ==, dd->dd_phys->dd_used_bytes);
-
if (dd->dd_parent)
dsl_dir_close(dd->dd_parent, dd);
@@ -91,9 +91,9 @@ dsl_dir_open_obj(dsl_pool_t *dp, uint64_t ddobj,
dmu_object_info_t doi;
dmu_object_info_from_db(dbuf, &doi);
ASSERT3U(doi.doi_type, ==, DMU_OT_DSL_DIR);
+ ASSERT3U(doi.doi_bonus_size, >=, sizeof (dsl_dir_phys_t));
}
#endif
- /* XXX assert bonus buffer size is correct */
if (dd == NULL) {
dsl_dir_t *winner;
int err;
@@ -103,7 +103,6 @@ dsl_dir_open_obj(dsl_pool_t *dp, uint64_t ddobj,
dd->dd_dbuf = dbuf;
dd->dd_pool = dp;
dd->dd_phys = dbuf->db_data;
- dd->dd_used_bytes = dd->dd_phys->dd_used_bytes;
mutex_init(&dd->dd_lock, NULL, MUTEX_DEFAULT, NULL);
list_create(&dd->dd_prop_cbs, sizeof (dsl_prop_cb_record_t),
@@ -112,36 +111,25 @@ dsl_dir_open_obj(dsl_pool_t *dp, uint64_t ddobj,
if (dd->dd_phys->dd_parent_obj) {
err = dsl_dir_open_obj(dp, dd->dd_phys->dd_parent_obj,
NULL, dd, &dd->dd_parent);
- if (err) {
- mutex_destroy(&dd->dd_lock);
- kmem_free(dd, sizeof (dsl_dir_t));
- dmu_buf_rele(dbuf, tag);
- return (err);
- }
+ if (err)
+ goto errout;
if (tail) {
#ifdef ZFS_DEBUG
uint64_t foundobj;
err = zap_lookup(dp->dp_meta_objset,
- dd->dd_parent->dd_phys->
- dd_child_dir_zapobj,
+ dd->dd_parent->dd_phys->dd_child_dir_zapobj,
tail, sizeof (foundobj), 1, &foundobj);
ASSERT(err || foundobj == ddobj);
#endif
(void) strcpy(dd->dd_myname, tail);
} else {
err = zap_value_search(dp->dp_meta_objset,
- dd->dd_parent->dd_phys->
- dd_child_dir_zapobj,
- ddobj, dd->dd_myname);
- }
- if (err) {
- dsl_dir_close(dd->dd_parent, dd);
- mutex_destroy(&dd->dd_lock);
- kmem_free(dd, sizeof (dsl_dir_t));
- dmu_buf_rele(dbuf, tag);
- return (err);
+ dd->dd_parent->dd_phys->dd_child_dir_zapobj,
+ ddobj, 0, dd->dd_myname);
}
+ if (err)
+ goto errout;
} else {
(void) strcpy(dd->dd_myname, spa_name(dp->dp_spa));
}
@@ -174,6 +162,15 @@ dsl_dir_open_obj(dsl_pool_t *dp, uint64_t ddobj,
ASSERT3P(dd->dd_dbuf, ==, dbuf);
*ddp = dd;
return (0);
+
+errout:
+ if (dd->dd_parent)
+ dsl_dir_close(dd->dd_parent, dd);
+ mutex_destroy(&dd->dd_lock);
+ kmem_free(dd, sizeof (dsl_dir_t));
+ dmu_buf_rele(dbuf, tag);
+ return (err);
+
}
void
@@ -404,27 +401,37 @@ dsl_dir_open(const char *name, void *tag, dsl_dir_t **ddp, const char **tailp)
}
uint64_t
-dsl_dir_create_sync(dsl_dir_t *pds, const char *name, dmu_tx_t *tx)
+dsl_dir_create_sync(dsl_pool_t *dp, dsl_dir_t *pds, const char *name,
+ dmu_tx_t *tx)
{
- objset_t *mos = pds->dd_pool->dp_meta_objset;
+ objset_t *mos = dp->dp_meta_objset;
uint64_t ddobj;
dsl_dir_phys_t *dsphys;
dmu_buf_t *dbuf;
ddobj = dmu_object_alloc(mos, DMU_OT_DSL_DIR, 0,
DMU_OT_DSL_DIR, sizeof (dsl_dir_phys_t), tx);
- VERIFY(0 == zap_add(mos, pds->dd_phys->dd_child_dir_zapobj,
- name, sizeof (uint64_t), 1, &ddobj, tx));
+ if (pds) {
+ VERIFY(0 == zap_add(mos, pds->dd_phys->dd_child_dir_zapobj,
+ name, sizeof (uint64_t), 1, &ddobj, tx));
+ } else {
+ /* it's the root dir */
+ VERIFY(0 == zap_add(mos, DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_ROOT_DATASET, sizeof (uint64_t), 1, &ddobj, tx));
+ }
VERIFY(0 == dmu_bonus_hold(mos, ddobj, FTAG, &dbuf));
dmu_buf_will_dirty(dbuf, tx);
dsphys = dbuf->db_data;
dsphys->dd_creation_time = gethrestime_sec();
- dsphys->dd_parent_obj = pds->dd_object;
+ if (pds)
+ dsphys->dd_parent_obj = pds->dd_object;
dsphys->dd_props_zapobj = zap_create(mos,
DMU_OT_DSL_PROPS, DMU_OT_NONE, 0, tx);
dsphys->dd_child_dir_zapobj = zap_create(mos,
DMU_OT_DSL_DIR_CHILD_MAP, DMU_OT_NONE, 0, tx);
+ if (spa_version(dp->dp_spa) >= SPA_VERSION_USED_BREAKDOWN)
+ dsphys->dd_flags |= DD_FLAG_USED_BREAKDOWN;
dmu_buf_rele(dbuf, FTAG);
return (ddobj);
@@ -461,23 +468,27 @@ dsl_dir_destroy_check(void *arg1, void *arg2, dmu_tx_t *tx)
}
void
-dsl_dir_destroy_sync(void *arg1, void *tag, dmu_tx_t *tx)
+dsl_dir_destroy_sync(void *arg1, void *tag, cred_t *cr, dmu_tx_t *tx)
{
dsl_dir_t *dd = arg1;
objset_t *mos = dd->dd_pool->dp_meta_objset;
uint64_t val, obj;
+ dd_used_t t;
ASSERT(RW_WRITE_HELD(&dd->dd_pool->dp_config_rwlock));
ASSERT(dd->dd_phys->dd_head_dataset_obj == 0);
/* Remove our reservation. */
val = 0;
- dsl_dir_set_reservation_sync(dd, &val, tx);
- ASSERT3U(dd->dd_used_bytes, ==, 0);
+ dsl_dir_set_reservation_sync(dd, &val, cr, tx);
+ ASSERT3U(dd->dd_phys->dd_used_bytes, ==, 0);
ASSERT3U(dd->dd_phys->dd_reserved, ==, 0);
+ for (t = 0; t < DD_USED_NUM; t++)
+ ASSERT3U(dd->dd_phys->dd_used_breakdown[t], ==, 0);
VERIFY(0 == zap_destroy(mos, dd->dd_phys->dd_child_dir_zapobj, tx));
VERIFY(0 == zap_destroy(mos, dd->dd_phys->dd_props_zapobj, tx));
+ VERIFY(0 == dsl_deleg_destroy(mos, dd->dd_phys->dd_deleg_zapobj, tx));
VERIFY(0 == zap_remove(mos,
dd->dd_parent->dd_phys->dd_child_dir_zapobj, dd->dd_myname, tx));
@@ -486,65 +497,53 @@ dsl_dir_destroy_sync(void *arg1, void *tag, dmu_tx_t *tx)
VERIFY(0 == dmu_object_free(mos, obj, tx));
}
-void
-dsl_dir_create_root(objset_t *mos, uint64_t *ddobjp, dmu_tx_t *tx)
+boolean_t
+dsl_dir_is_clone(dsl_dir_t *dd)
{
- dsl_dir_phys_t *dsp;
- dmu_buf_t *dbuf;
- int error;
-
- *ddobjp = dmu_object_alloc(mos, DMU_OT_DSL_DIR, 0,
- DMU_OT_DSL_DIR, sizeof (dsl_dir_phys_t), tx);
-
- error = zap_add(mos, DMU_POOL_DIRECTORY_OBJECT, DMU_POOL_ROOT_DATASET,
- sizeof (uint64_t), 1, ddobjp, tx);
- ASSERT3U(error, ==, 0);
-
- VERIFY(0 == dmu_bonus_hold(mos, *ddobjp, FTAG, &dbuf));
- dmu_buf_will_dirty(dbuf, tx);
- dsp = dbuf->db_data;
-
- dsp->dd_creation_time = gethrestime_sec();
- dsp->dd_props_zapobj = zap_create(mos,
- DMU_OT_DSL_PROPS, DMU_OT_NONE, 0, tx);
- dsp->dd_child_dir_zapobj = zap_create(mos,
- DMU_OT_DSL_DIR_CHILD_MAP, DMU_OT_NONE, 0, tx);
-
- dmu_buf_rele(dbuf, FTAG);
+ return (dd->dd_phys->dd_origin_obj &&
+ (dd->dd_pool->dp_origin_snap == NULL ||
+ dd->dd_phys->dd_origin_obj !=
+ dd->dd_pool->dp_origin_snap->ds_object));
}
void
dsl_dir_stats(dsl_dir_t *dd, nvlist_t *nv)
{
- dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_AVAILABLE,
- dsl_dir_space_available(dd, NULL, 0, TRUE));
-
mutex_enter(&dd->dd_lock);
- dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USED, dd->dd_used_bytes);
- dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_QUOTA,
- dd->dd_phys->dd_quota);
+ dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USED,
+ dd->dd_phys->dd_used_bytes);
+ dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_QUOTA, dd->dd_phys->dd_quota);
dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_RESERVATION,
dd->dd_phys->dd_reserved);
dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_COMPRESSRATIO,
dd->dd_phys->dd_compressed_bytes == 0 ? 100 :
(dd->dd_phys->dd_uncompressed_bytes * 100 /
dd->dd_phys->dd_compressed_bytes));
+ if (dd->dd_phys->dd_flags & DD_FLAG_USED_BREAKDOWN) {
+ dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USEDSNAP,
+ dd->dd_phys->dd_used_breakdown[DD_USED_SNAP]);
+ dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USEDDS,
+ dd->dd_phys->dd_used_breakdown[DD_USED_HEAD]);
+ dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USEDREFRESERV,
+ dd->dd_phys->dd_used_breakdown[DD_USED_REFRSRV]);
+ dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USEDCHILD,
+ dd->dd_phys->dd_used_breakdown[DD_USED_CHILD] +
+ dd->dd_phys->dd_used_breakdown[DD_USED_CHILD_RSRV]);
+ }
mutex_exit(&dd->dd_lock);
- if (dd->dd_phys->dd_clone_parent_obj) {
+ rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
+ if (dsl_dir_is_clone(dd)) {
dsl_dataset_t *ds;
char buf[MAXNAMELEN];
- rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
- VERIFY(0 == dsl_dataset_open_obj(dd->dd_pool,
- dd->dd_phys->dd_clone_parent_obj,
- NULL, DS_MODE_NONE, FTAG, &ds));
+ VERIFY(0 == dsl_dataset_hold_obj(dd->dd_pool,
+ dd->dd_phys->dd_origin_obj, FTAG, &ds));
dsl_dataset_name(ds, buf);
- dsl_dataset_close(ds, DS_MODE_NONE, FTAG);
- rw_exit(&dd->dd_pool->dp_config_rwlock);
-
+ dsl_dataset_rele(ds, FTAG);
dsl_prop_nvlist_add_string(nv, ZFS_PROP_ORIGIN, buf);
}
+ rw_exit(&dd->dd_pool->dp_config_rwlock);
}
void
@@ -580,7 +579,6 @@ dsl_dir_sync(dsl_dir_t *dd, dmu_tx_t *tx)
dprintf_dd(dd, "txg=%llu towrite=%lluK\n", tx->tx_txg,
dd->dd_space_towrite[tx->tx_txg&TXG_MASK] / 1024);
dd->dd_space_towrite[tx->tx_txg&TXG_MASK] = 0;
- dd->dd_phys->dd_used_bytes = dd->dd_used_bytes;
mutex_exit(&dd->dd_lock);
/* release the hold from dsl_dir_dirty */
@@ -588,15 +586,13 @@ dsl_dir_sync(dsl_dir_t *dd, dmu_tx_t *tx)
}
static uint64_t
-dsl_dir_estimated_space(dsl_dir_t *dd)
+dsl_dir_space_towrite(dsl_dir_t *dd)
{
- int64_t space;
+ uint64_t space = 0;
int i;
ASSERT(MUTEX_HELD(&dd->dd_lock));
- space = dd->dd_phys->dd_used_bytes;
- ASSERT(space >= 0);
for (i = 0; i < TXG_SIZE; i++) {
space += dd->dd_space_towrite[i&TXG_MASK];
ASSERT3U(dd->dd_space_towrite[i&TXG_MASK], >=, 0);
@@ -630,13 +626,9 @@ dsl_dir_space_available(dsl_dir_t *dd,
mutex_enter(&dd->dd_lock);
if (dd->dd_phys->dd_quota != 0)
quota = dd->dd_phys->dd_quota;
- if (ondiskonly) {
- used = dd->dd_used_bytes;
- } else {
- used = dsl_dir_estimated_space(dd);
- }
- if (dd == ancestor)
- used += delta;
+ used = dd->dd_phys->dd_used_bytes;
+ if (!ondiskonly)
+ used += dsl_dir_space_towrite(dd);
if (dd->dd_parent == NULL) {
uint64_t poolsize = dsl_pool_adjustedsize(dd->dd_pool, FALSE);
@@ -651,6 +643,14 @@ dsl_dir_space_available(dsl_dir_t *dd,
parentspace += dd->dd_phys->dd_reserved - used;
}
+ if (dd == ancestor) {
+ ASSERT(delta <= 0);
+ ASSERT(used >= -delta);
+ used += delta;
+ if (parentspace != UINT64_MAX)
+ parentspace -= delta;
+ }
+
if (used > quota) {
/* over quota */
myspace = 0;
@@ -678,50 +678,68 @@ dsl_dir_space_available(dsl_dir_t *dd,
struct tempreserve {
list_node_t tr_node;
+ dsl_pool_t *tr_dp;
dsl_dir_t *tr_ds;
uint64_t tr_size;
};
-/*
- * Reserve space in this dsl_dir, to be used in this tx's txg.
- * After the space has been dirtied (and thus
- * dsl_dir_willuse_space() has been called), the reservation should
- * be canceled, using dsl_dir_tempreserve_clear().
- */
static int
-dsl_dir_tempreserve_impl(dsl_dir_t *dd,
- uint64_t asize, boolean_t netfree, list_t *tr_list, dmu_tx_t *tx)
+dsl_dir_tempreserve_impl(dsl_dir_t *dd, uint64_t asize, boolean_t netfree,
+ boolean_t ignorequota, boolean_t checkrefquota, list_t *tr_list,
+ dmu_tx_t *tx, boolean_t first)
{
uint64_t txg = tx->tx_txg;
- uint64_t est_used, quota, parent_rsrv;
- int edquot = EDQUOT;
+ uint64_t est_inflight, used_on_disk, quota, parent_rsrv;
+ struct tempreserve *tr;
+ int enospc = EDQUOT;
int txgidx = txg & TXG_MASK;
int i;
- struct tempreserve *tr;
+ uint64_t ref_rsrv = 0;
ASSERT3U(txg, !=, 0);
- ASSERT3S(asize, >=, 0);
+ ASSERT3S(asize, >, 0);
mutex_enter(&dd->dd_lock);
+
/*
* Check against the dsl_dir's quota. We don't add in the delta
* when checking for over-quota because they get one free hit.
*/
- est_used = dsl_dir_estimated_space(dd);
+ est_inflight = dsl_dir_space_towrite(dd);
for (i = 0; i < TXG_SIZE; i++)
- est_used += dd->dd_tempreserved[i];
+ est_inflight += dd->dd_tempreserved[i];
+ used_on_disk = dd->dd_phys->dd_used_bytes;
- quota = UINT64_MAX;
+ /*
+ * On the first iteration, fetch the dataset's used-on-disk and
+ * refreservation values. Also, if checkrefquota is set, test if
+ * allocating this space would exceed the dataset's refquota.
+ */
+ if (first && tx->tx_objset) {
+ int error;
+ dsl_dataset_t *ds = tx->tx_objset->os->os_dsl_dataset;
+
+ error = dsl_dataset_check_quota(ds, checkrefquota,
+ asize, est_inflight, &used_on_disk, &ref_rsrv);
+ if (error) {
+ mutex_exit(&dd->dd_lock);
+ return (error);
+ }
+ }
- if (dd->dd_phys->dd_quota)
+ /*
+ * If this transaction will result in a net free of space,
+ * we want to let it through.
+ */
+ if (ignorequota || netfree || dd->dd_phys->dd_quota == 0)
+ quota = UINT64_MAX;
+ else
quota = dd->dd_phys->dd_quota;
/*
- * If this transaction will result in a net free of space, we want
- * to let it through, but we have to be careful: the space that it
- * frees won't become available until *after* this txg syncs.
- * Therefore, to ensure that it's possible to remove files from
- * a full pool without inducing transient overcommits, we throttle
+ * Adjust the quota against the actual pool size at the root.
+ * To ensure that it's possible to remove files from a full
+ * pool without inducing transient overcommits, we throttle
* netfree transactions against a quota that is slightly larger,
* but still within the pool's allocation slop. In cases where
* we're very close to full, this will allow a steady trickle of
@@ -731,47 +749,45 @@ dsl_dir_tempreserve_impl(dsl_dir_t *dd,
uint64_t poolsize = dsl_pool_adjustedsize(dd->dd_pool, netfree);
if (poolsize < quota) {
quota = poolsize;
- edquot = ENOSPC;
+ enospc = ENOSPC;
}
- } else if (netfree) {
- quota = UINT64_MAX;
}
/*
* If they are requesting more space, and our current estimate
- * is over quota. They get to try again unless the actual
+ * is over quota, they get to try again unless the actual
* on-disk is over quota and there are no pending changes (which
* may free up space for us).
*/
- if (asize > 0 && est_used > quota) {
- if (dd->dd_space_towrite[txg & TXG_MASK] != 0 ||
- dd->dd_space_towrite[(txg-1) & TXG_MASK] != 0 ||
- dd->dd_space_towrite[(txg-2) & TXG_MASK] != 0 ||
- dd->dd_used_bytes < quota)
- edquot = ERESTART;
- dprintf_dd(dd, "failing: used=%lluK est_used = %lluK "
+ if (used_on_disk + est_inflight > quota) {
+ if (est_inflight > 0 || used_on_disk < quota)
+ enospc = ERESTART;
+ dprintf_dd(dd, "failing: used=%lluK inflight = %lluK "
"quota=%lluK tr=%lluK err=%d\n",
- dd->dd_used_bytes>>10, est_used>>10,
- quota>>10, asize>>10, edquot);
+ used_on_disk>>10, est_inflight>>10,
+ quota>>10, asize>>10, enospc);
mutex_exit(&dd->dd_lock);
- return (edquot);
+ return (enospc);
}
/* We need to up our estimated delta before dropping dd_lock */
dd->dd_tempreserved[txgidx] += asize;
- parent_rsrv = parent_delta(dd, est_used, asize);
+ parent_rsrv = parent_delta(dd, used_on_disk + est_inflight,
+ asize - ref_rsrv);
mutex_exit(&dd->dd_lock);
- tr = kmem_alloc(sizeof (struct tempreserve), KM_SLEEP);
+ tr = kmem_zalloc(sizeof (struct tempreserve), KM_SLEEP);
tr->tr_ds = dd;
tr->tr_size = asize;
list_insert_tail(tr_list, tr);
/* see if it's OK with our parent */
if (dd->dd_parent && parent_rsrv) {
+ boolean_t ismos = (dd->dd_phys->dd_head_dataset_obj == 0);
+
return (dsl_dir_tempreserve_impl(dd->dd_parent,
- parent_rsrv, netfree, tr_list, tx));
+ parent_rsrv, netfree, ismos, TRUE, tr_list, tx, FALSE));
} else {
return (0);
}
@@ -779,42 +795,62 @@ dsl_dir_tempreserve_impl(dsl_dir_t *dd,
/*
* Reserve space in this dsl_dir, to be used in this tx's txg.
- * After the space has been dirtied (and thus
- * dsl_dir_willuse_space() has been called), the reservation should
- * be canceled, using dsl_dir_tempreserve_clear().
+ * After the space has been dirtied (and dsl_dir_willuse_space()
+ * has been called), the reservation should be canceled, using
+ * dsl_dir_tempreserve_clear().
*/
int
-dsl_dir_tempreserve_space(dsl_dir_t *dd, uint64_t lsize,
- uint64_t asize, uint64_t fsize, void **tr_cookiep, dmu_tx_t *tx)
+dsl_dir_tempreserve_space(dsl_dir_t *dd, uint64_t lsize, uint64_t asize,
+ uint64_t fsize, uint64_t usize, void **tr_cookiep, dmu_tx_t *tx)
{
- int err = 0;
+ int err;
list_t *tr_list;
+ if (asize == 0) {
+ *tr_cookiep = NULL;
+ return (0);
+ }
+
tr_list = kmem_alloc(sizeof (list_t), KM_SLEEP);
list_create(tr_list, sizeof (struct tempreserve),
offsetof(struct tempreserve, tr_node));
- ASSERT3S(asize, >=, 0);
+ ASSERT3S(asize, >, 0);
ASSERT3S(fsize, >=, 0);
- err = dsl_dir_tempreserve_impl(dd, asize, fsize >= asize,
- tr_list, tx);
-
+ err = arc_tempreserve_space(lsize, tx->tx_txg);
if (err == 0) {
struct tempreserve *tr;
- err = arc_tempreserve_space(lsize);
- if (err == 0) {
- tr = kmem_alloc(sizeof (struct tempreserve), KM_SLEEP);
- tr->tr_ds = NULL;
- tr->tr_size = lsize;
- list_insert_tail(tr_list, tr);
+ tr = kmem_zalloc(sizeof (struct tempreserve), KM_SLEEP);
+ tr->tr_size = lsize;
+ list_insert_tail(tr_list, tr);
+
+ err = dsl_pool_tempreserve_space(dd->dd_pool, asize, tx);
+ } else {
+ if (err == EAGAIN) {
+ txg_delay(dd->dd_pool, tx->tx_txg, 1);
+ err = ERESTART;
}
+ dsl_pool_memory_pressure(dd->dd_pool);
+ }
+
+ if (err == 0) {
+ struct tempreserve *tr;
+
+ tr = kmem_zalloc(sizeof (struct tempreserve), KM_SLEEP);
+ tr->tr_dp = dd->dd_pool;
+ tr->tr_size = asize;
+ list_insert_tail(tr_list, tr);
+
+ err = dsl_dir_tempreserve_impl(dd, asize, fsize >= asize,
+ FALSE, asize > usize, tr_list, tx, TRUE);
}
if (err)
dsl_dir_tempreserve_clear(tr_list, tx);
else
*tr_cookiep = tr_list;
+
return (err);
}
@@ -831,15 +867,20 @@ dsl_dir_tempreserve_clear(void *tr_cookie, dmu_tx_t *tx)
ASSERT3U(tx->tx_txg, !=, 0);
+ if (tr_cookie == NULL)
+ return;
+
while (tr = list_head(tr_list)) {
- if (tr->tr_ds == NULL) {
- arc_tempreserve_clear(tr->tr_size);
- } else {
+ if (tr->tr_dp) {
+ dsl_pool_tempreserve_clear(tr->tr_dp, tr->tr_size, tx);
+ } else if (tr->tr_ds) {
mutex_enter(&tr->tr_ds->dd_lock);
ASSERT3U(tr->tr_ds->dd_tempreserved[txgidx], >=,
tr->tr_size);
tr->tr_ds->dd_tempreserved[txgidx] -= tr->tr_size;
mutex_exit(&tr->tr_ds->dd_lock);
+ } else {
+ arc_tempreserve_clear(tr->tr_size);
}
list_remove(tr_list, tr);
kmem_free(tr, sizeof (struct tempreserve));
@@ -848,13 +889,8 @@ dsl_dir_tempreserve_clear(void *tr_cookie, dmu_tx_t *tx)
kmem_free(tr_list, sizeof (list_t));
}
-/*
- * Call in open context when we think we're going to write/free space,
- * eg. when dirtying data. Be conservative (ie. OK to write less than
- * this or free more than this, but don't write more or free less).
- */
-void
-dsl_dir_willuse_space(dsl_dir_t *dd, int64_t space, dmu_tx_t *tx)
+static void
+dsl_dir_willuse_space_impl(dsl_dir_t *dd, int64_t space, dmu_tx_t *tx)
{
int64_t parent_space;
uint64_t est_used;
@@ -863,7 +899,7 @@ dsl_dir_willuse_space(dsl_dir_t *dd, int64_t space, dmu_tx_t *tx)
if (space > 0)
dd->dd_space_towrite[tx->tx_txg & TXG_MASK] += space;
- est_used = dsl_dir_estimated_space(dd);
+ est_used = dsl_dir_space_towrite(dd) + dd->dd_phys->dd_used_bytes;
parent_space = parent_delta(dd, est_used, space);
mutex_exit(&dd->dd_lock);
@@ -872,39 +908,96 @@ dsl_dir_willuse_space(dsl_dir_t *dd, int64_t space, dmu_tx_t *tx)
/* XXX this is potentially expensive and unnecessary... */
if (parent_space && dd->dd_parent)
- dsl_dir_willuse_space(dd->dd_parent, parent_space, tx);
+ dsl_dir_willuse_space_impl(dd->dd_parent, parent_space, tx);
+}
+
+/*
+ * Call in open context when we think we're going to write/free space,
+ * eg. when dirtying data. Be conservative (ie. OK to write less than
+ * this or free more than this, but don't write more or free less).
+ */
+void
+dsl_dir_willuse_space(dsl_dir_t *dd, int64_t space, dmu_tx_t *tx)
+{
+ dsl_pool_willuse_space(dd->dd_pool, space, tx);
+ dsl_dir_willuse_space_impl(dd, space, tx);
}
/* call from syncing context when we actually write/free space for this dd */
void
-dsl_dir_diduse_space(dsl_dir_t *dd,
+dsl_dir_diduse_space(dsl_dir_t *dd, dd_used_t type,
int64_t used, int64_t compressed, int64_t uncompressed, dmu_tx_t *tx)
{
int64_t accounted_delta;
+ boolean_t needlock = !MUTEX_HELD(&dd->dd_lock);
ASSERT(dmu_tx_is_syncing(tx));
+ ASSERT(type < DD_USED_NUM);
dsl_dir_dirty(dd, tx);
- mutex_enter(&dd->dd_lock);
- accounted_delta = parent_delta(dd, dd->dd_used_bytes, used);
- ASSERT(used >= 0 || dd->dd_used_bytes >= -used);
+ if (needlock)
+ mutex_enter(&dd->dd_lock);
+ accounted_delta = parent_delta(dd, dd->dd_phys->dd_used_bytes, used);
+ ASSERT(used >= 0 || dd->dd_phys->dd_used_bytes >= -used);
ASSERT(compressed >= 0 ||
dd->dd_phys->dd_compressed_bytes >= -compressed);
ASSERT(uncompressed >= 0 ||
dd->dd_phys->dd_uncompressed_bytes >= -uncompressed);
- dd->dd_used_bytes += used;
+ dd->dd_phys->dd_used_bytes += used;
dd->dd_phys->dd_uncompressed_bytes += uncompressed;
dd->dd_phys->dd_compressed_bytes += compressed;
- mutex_exit(&dd->dd_lock);
+
+ if (dd->dd_phys->dd_flags & DD_FLAG_USED_BREAKDOWN) {
+ ASSERT(used > 0 ||
+ dd->dd_phys->dd_used_breakdown[type] >= -used);
+ dd->dd_phys->dd_used_breakdown[type] += used;
+#ifdef DEBUG
+ dd_used_t t;
+ uint64_t u = 0;
+ for (t = 0; t < DD_USED_NUM; t++)
+ u += dd->dd_phys->dd_used_breakdown[t];
+ ASSERT3U(u, ==, dd->dd_phys->dd_used_bytes);
+#endif
+ }
+ if (needlock)
+ mutex_exit(&dd->dd_lock);
if (dd->dd_parent != NULL) {
- dsl_dir_diduse_space(dd->dd_parent,
+ dsl_dir_diduse_space(dd->dd_parent, DD_USED_CHILD,
accounted_delta, compressed, uncompressed, tx);
+ dsl_dir_transfer_space(dd->dd_parent,
+ used - accounted_delta,
+ DD_USED_CHILD_RSRV, DD_USED_CHILD, tx);
}
}
-/* ARGSUSED */
+void
+dsl_dir_transfer_space(dsl_dir_t *dd, int64_t delta,
+ dd_used_t oldtype, dd_used_t newtype, dmu_tx_t *tx)
+{
+ boolean_t needlock = !MUTEX_HELD(&dd->dd_lock);
+
+ ASSERT(dmu_tx_is_syncing(tx));
+ ASSERT(oldtype < DD_USED_NUM);
+ ASSERT(newtype < DD_USED_NUM);
+
+ if (delta == 0 || !(dd->dd_phys->dd_flags & DD_FLAG_USED_BREAKDOWN))
+ return;
+
+ dsl_dir_dirty(dd, tx);
+ if (needlock)
+ mutex_enter(&dd->dd_lock);
+ ASSERT(delta > 0 ?
+ dd->dd_phys->dd_used_breakdown[oldtype] >= delta :
+ dd->dd_phys->dd_used_breakdown[newtype] >= -delta);
+ ASSERT(dd->dd_phys->dd_used_bytes >= ABS(delta));
+ dd->dd_phys->dd_used_breakdown[oldtype] -= delta;
+ dd->dd_phys->dd_used_breakdown[newtype] += delta;
+ if (needlock)
+ mutex_exit(&dd->dd_lock);
+}
+
static int
dsl_dir_set_quota_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
@@ -921,22 +1014,22 @@ dsl_dir_set_quota_check(void *arg1, void *arg2, dmu_tx_t *tx)
/*
* If we are doing the preliminary check in open context, and
* there are pending changes, then don't fail it, since the
- * pending changes could under-estimat the amount of space to be
+ * pending changes could under-estimate the amount of space to be
* freed up.
*/
- towrite = dd->dd_space_towrite[0] + dd->dd_space_towrite[1] +
- dd->dd_space_towrite[2] + dd->dd_space_towrite[3];
+ towrite = dsl_dir_space_towrite(dd);
if ((dmu_tx_is_syncing(tx) || towrite == 0) &&
(new_quota < dd->dd_phys->dd_reserved ||
- new_quota < dsl_dir_estimated_space(dd))) {
+ new_quota < dd->dd_phys->dd_used_bytes + towrite)) {
err = ENOSPC;
}
mutex_exit(&dd->dd_lock);
return (err);
}
+/* ARGSUSED */
static void
-dsl_dir_set_quota_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+dsl_dir_set_quota_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
dsl_dir_t *dd = arg1;
uint64_t *quotap = arg2;
@@ -947,6 +1040,10 @@ dsl_dir_set_quota_sync(void *arg1, void *arg2, dmu_tx_t *tx)
mutex_enter(&dd->dd_lock);
dd->dd_phys->dd_quota = new_quota;
mutex_exit(&dd->dd_lock);
+
+ spa_history_internal_log(LOG_DS_QUOTA, dd->dd_pool->dp_spa,
+ tx, cr, "%lld dataset = %llu ",
+ (longlong_t)new_quota, dd->dd_phys->dd_head_dataset_obj);
}
int
@@ -958,20 +1055,22 @@ dsl_dir_set_quota(const char *ddname, uint64_t quota)
err = dsl_dir_open(ddname, FTAG, &dd, NULL);
if (err)
return (err);
- /*
- * If someone removes a file, then tries to set the quota, we
- * want to make sure the file freeing takes effect.
- */
- txg_wait_open(dd->dd_pool, 0);
- err = dsl_sync_task_do(dd->dd_pool, dsl_dir_set_quota_check,
- dsl_dir_set_quota_sync, dd, &quota, 0);
+ if (quota != dd->dd_phys->dd_quota) {
+ /*
+ * If someone removes a file, then tries to set the quota, we
+ * want to make sure the file freeing takes effect.
+ */
+ txg_wait_open(dd->dd_pool, 0);
+
+ err = dsl_sync_task_do(dd->dd_pool, dsl_dir_set_quota_check,
+ dsl_dir_set_quota_sync, dd, &quota, 0);
+ }
dsl_dir_close(dd, FTAG);
return (err);
}
-/* ARGSUSED */
-static int
+int
dsl_dir_set_reservation_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
dsl_dir_t *dd = arg1;
@@ -991,7 +1090,7 @@ dsl_dir_set_reservation_check(void *arg1, void *arg2, dmu_tx_t *tx)
return (0);
mutex_enter(&dd->dd_lock);
- used = dd->dd_used_bytes;
+ used = dd->dd_phys->dd_used_bytes;
delta = MAX(used, new_reservation) -
MAX(used, dd->dd_phys->dd_reserved);
mutex_exit(&dd->dd_lock);
@@ -1011,8 +1110,9 @@ dsl_dir_set_reservation_check(void *arg1, void *arg2, dmu_tx_t *tx)
return (0);
}
+/* ARGSUSED */
static void
-dsl_dir_set_reservation_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+dsl_dir_set_reservation_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
dsl_dir_t *dd = arg1;
uint64_t *reservationp = arg2;
@@ -1020,19 +1120,24 @@ dsl_dir_set_reservation_sync(void *arg1, void *arg2, dmu_tx_t *tx)
uint64_t used;
int64_t delta;
+ dmu_buf_will_dirty(dd->dd_dbuf, tx);
+
mutex_enter(&dd->dd_lock);
- used = dd->dd_used_bytes;
+ used = dd->dd_phys->dd_used_bytes;
delta = MAX(used, new_reservation) -
MAX(used, dd->dd_phys->dd_reserved);
- mutex_exit(&dd->dd_lock);
-
- dmu_buf_will_dirty(dd->dd_dbuf, tx);
dd->dd_phys->dd_reserved = new_reservation;
if (dd->dd_parent != NULL) {
/* Roll up this additional usage into our ancestors */
- dsl_dir_diduse_space(dd->dd_parent, delta, 0, 0, tx);
+ dsl_dir_diduse_space(dd->dd_parent, DD_USED_CHILD_RSRV,
+ delta, 0, 0, tx);
}
+ mutex_exit(&dd->dd_lock);
+
+ spa_history_internal_log(LOG_DS_RESERVATION, dd->dd_pool->dp_spa,
+ tx, cr, "%lld dataset = %llu",
+ (longlong_t)new_reservation, dd->dd_phys->dd_head_dataset_obj);
}
int
@@ -1074,7 +1179,7 @@ would_change(dsl_dir_t *dd, int64_t delta, dsl_dir_t *ancestor)
return (delta);
mutex_enter(&dd->dd_lock);
- delta = parent_delta(dd, dd->dd_used_bytes, delta);
+ delta = parent_delta(dd, dd->dd_phys->dd_used_bytes, delta);
mutex_exit(&dd->dd_lock);
return (would_change(dd->dd_parent, delta, ancestor));
}
@@ -1084,7 +1189,7 @@ struct renamearg {
const char *mynewname;
};
-/* ARGSUSED */
+/*ARGSUSED*/
static int
dsl_dir_rename_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
@@ -1110,7 +1215,7 @@ dsl_dir_rename_check(void *arg1, void *arg2, dmu_tx_t *tx)
if (ra->newparent != dd->dd_parent) {
/* is there enough space? */
uint64_t myspace =
- MAX(dd->dd_used_bytes, dd->dd_phys->dd_reserved);
+ MAX(dd->dd_phys->dd_used_bytes, dd->dd_phys->dd_reserved);
/* no rename into our descendant */
if (closest_common_ancestor(dd, ra->newparent) == dd)
@@ -1125,7 +1230,7 @@ dsl_dir_rename_check(void *arg1, void *arg2, dmu_tx_t *tx)
}
static void
-dsl_dir_rename_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+dsl_dir_rename_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
dsl_dir_t *dd = arg1;
struct renamearg *ra = arg2;
@@ -1136,15 +1241,24 @@ dsl_dir_rename_sync(void *arg1, void *arg2, dmu_tx_t *tx)
ASSERT(dmu_buf_refcount(dd->dd_dbuf) <= 2);
if (ra->newparent != dd->dd_parent) {
- uint64_t myspace =
- MAX(dd->dd_used_bytes, dd->dd_phys->dd_reserved);
-
- dsl_dir_diduse_space(dd->dd_parent, -myspace,
+ dsl_dir_diduse_space(dd->dd_parent, DD_USED_CHILD,
+ -dd->dd_phys->dd_used_bytes,
-dd->dd_phys->dd_compressed_bytes,
-dd->dd_phys->dd_uncompressed_bytes, tx);
- dsl_dir_diduse_space(ra->newparent, myspace,
+ dsl_dir_diduse_space(ra->newparent, DD_USED_CHILD,
+ dd->dd_phys->dd_used_bytes,
dd->dd_phys->dd_compressed_bytes,
dd->dd_phys->dd_uncompressed_bytes, tx);
+
+ if (dd->dd_phys->dd_reserved > dd->dd_phys->dd_used_bytes) {
+ uint64_t unused_rsrv = dd->dd_phys->dd_reserved -
+ dd->dd_phys->dd_used_bytes;
+
+ dsl_dir_diduse_space(dd->dd_parent, DD_USED_CHILD_RSRV,
+ -unused_rsrv, 0, 0, tx);
+ dsl_dir_diduse_space(ra->newparent, DD_USED_CHILD_RSRV,
+ unused_rsrv, 0, 0, tx);
+ }
}
dmu_buf_will_dirty(dd->dd_dbuf, tx);
@@ -1164,6 +1278,9 @@ dsl_dir_rename_sync(void *arg1, void *arg2, dmu_tx_t *tx)
err = zap_add(mos, ra->newparent->dd_phys->dd_child_dir_zapobj,
dd->dd_myname, 8, 1, &dd->dd_object, tx);
ASSERT3U(err, ==, 0);
+
+ spa_history_internal_log(LOG_DS_RENAME, dd->dd_pool->dp_spa,
+ tx, cr, "dataset = %llu", dd->dd_phys->dd_head_dataset_obj);
}
int
@@ -1189,7 +1306,6 @@ dsl_dir_rename(dsl_dir_t *dd, const char *newname)
goto out;
}
-
err = dsl_sync_task_do(dd->dd_pool,
dsl_dir_rename_check, dsl_dir_rename_sync, dd, &ra, 3);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_pool.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_pool.c
index 00abf7e..4585dc8 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_pool.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_pool.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/dsl_pool.h>
#include <sys/dsl_dataset.h>
#include <sys/dsl_dir.h>
@@ -36,20 +34,36 @@
#include <sys/zio.h>
#include <sys/zfs_context.h>
#include <sys/fs/zfs.h>
+#include <sys/zfs_znode.h>
+#include <sys/spa_impl.h>
+
+int zfs_no_write_throttle = 0;
+int zfs_write_limit_shift = 3; /* 1/8th of physical memory */
+int zfs_txg_synctime = 5; /* target secs to sync a txg */
+
+uint64_t zfs_write_limit_min = 32 << 20; /* min write limit is 32MB */
+uint64_t zfs_write_limit_max = 0; /* max data payload per txg */
+uint64_t zfs_write_limit_inflated = 0;
+uint64_t zfs_write_limit_override = 0;
+extern uint64_t zfs_write_limit_min;
+
+kmutex_t zfs_write_limit_lock;
+
+static pgcnt_t old_physmem = 0;
static int
-dsl_pool_open_mos_dir(dsl_pool_t *dp, dsl_dir_t **ddp)
+dsl_pool_open_special_dir(dsl_pool_t *dp, const char *name, dsl_dir_t **ddp)
{
uint64_t obj;
int err;
err = zap_lookup(dp->dp_meta_objset,
dp->dp_root_dir->dd_phys->dd_child_dir_zapobj,
- MOS_DIR_NAME, sizeof (obj), 1, &obj);
+ name, sizeof (obj), 1, &obj);
if (err)
return (err);
- return (dsl_dir_open_obj(dp, obj, MOS_DIR_NAME, dp, ddp));
+ return (dsl_dir_open_obj(dp, obj, name, dp, ddp));
}
static dsl_pool_t *
@@ -62,6 +76,7 @@ dsl_pool_open_impl(spa_t *spa, uint64_t txg)
dp->dp_spa = spa;
dp->dp_meta_rootbp = *bp;
rw_init(&dp->dp_config_rwlock, NULL, RW_DEFAULT, NULL);
+ dp->dp_write_limit = zfs_write_limit_min;
txg_init(dp, txg);
txg_list_create(&dp->dp_dirty_datasets,
@@ -70,9 +85,12 @@ dsl_pool_open_impl(spa_t *spa, uint64_t txg)
offsetof(dsl_dir_t, dd_dirty_link));
txg_list_create(&dp->dp_sync_tasks,
offsetof(dsl_sync_task_group_t, dstg_node));
- list_create(&dp->dp_synced_objsets, sizeof (dsl_dataset_t),
+ list_create(&dp->dp_synced_datasets, sizeof (dsl_dataset_t),
offsetof(dsl_dataset_t, ds_synced_link));
+ mutex_init(&dp->dp_lock, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&dp->dp_scrub_cancel_lock, NULL, MUTEX_DEFAULT, NULL);
+
return (dp);
}
@@ -81,9 +99,11 @@ dsl_pool_open(spa_t *spa, uint64_t txg, dsl_pool_t **dpp)
{
int err;
dsl_pool_t *dp = dsl_pool_open_impl(spa, txg);
+ dsl_dir_t *dd;
+ dsl_dataset_t *ds;
objset_impl_t *osi;
- rw_enter(&dp->dp_config_rwlock, RW_READER);
+ rw_enter(&dp->dp_config_rwlock, RW_WRITER);
err = dmu_objset_open_impl(spa, NULL, &dp->dp_meta_rootbp, &osi);
if (err)
goto out;
@@ -100,10 +120,73 @@ dsl_pool_open(spa_t *spa, uint64_t txg, dsl_pool_t **dpp)
if (err)
goto out;
- err = dsl_pool_open_mos_dir(dp, &dp->dp_mos_dir);
+ err = dsl_pool_open_special_dir(dp, MOS_DIR_NAME, &dp->dp_mos_dir);
if (err)
goto out;
+ if (spa_version(spa) >= SPA_VERSION_ORIGIN) {
+ err = dsl_pool_open_special_dir(dp, ORIGIN_DIR_NAME, &dd);
+ if (err)
+ goto out;
+ err = dsl_dataset_hold_obj(dp, dd->dd_phys->dd_head_dataset_obj,
+ FTAG, &ds);
+ if (err)
+ goto out;
+ err = dsl_dataset_hold_obj(dp, ds->ds_phys->ds_prev_snap_obj,
+ dp, &dp->dp_origin_snap);
+ if (err)
+ goto out;
+ dsl_dataset_rele(ds, FTAG);
+ dsl_dir_close(dd, dp);
+ }
+
+ /* get scrub status */
+ err = zap_lookup(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_SCRUB_FUNC, sizeof (uint32_t), 1,
+ &dp->dp_scrub_func);
+ if (err == 0) {
+ err = zap_lookup(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_SCRUB_QUEUE, sizeof (uint64_t), 1,
+ &dp->dp_scrub_queue_obj);
+ if (err)
+ goto out;
+ err = zap_lookup(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_SCRUB_MIN_TXG, sizeof (uint64_t), 1,
+ &dp->dp_scrub_min_txg);
+ if (err)
+ goto out;
+ err = zap_lookup(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_SCRUB_MAX_TXG, sizeof (uint64_t), 1,
+ &dp->dp_scrub_max_txg);
+ if (err)
+ goto out;
+ err = zap_lookup(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_SCRUB_BOOKMARK, sizeof (uint64_t), 4,
+ &dp->dp_scrub_bookmark);
+ if (err)
+ goto out;
+ err = zap_lookup(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_SCRUB_ERRORS, sizeof (uint64_t), 1,
+ &spa->spa_scrub_errors);
+ if (err)
+ goto out;
+ if (spa_version(spa) < SPA_VERSION_DSL_SCRUB) {
+ /*
+ * A new-type scrub was in progress on an old
+ * pool. Restart from the beginning, since the
+ * old software may have changed the pool in the
+ * meantime.
+ */
+ dsl_pool_scrub_restart(dp);
+ }
+ } else {
+ /*
+ * It's OK if there is no scrub in progress (and if
+ * there was an I/O error, ignore it).
+ */
+ err = 0;
+ }
+
out:
rw_exit(&dp->dp_config_rwlock);
if (err)
@@ -117,7 +200,15 @@ out:
void
dsl_pool_close(dsl_pool_t *dp)
{
- /* drop our reference from dsl_pool_open() */
+ /* drop our references from dsl_pool_open() */
+
+ /*
+ * Since we held the origin_snap from "syncing" context (which
+ * includes pool-opening context), it actually only got a "ref"
+ * and not a hold, so just drop that here.
+ */
+ if (dp->dp_origin_snap)
+ dsl_dataset_drop_ref(dp->dp_origin_snap, dp);
if (dp->dp_mos_dir)
dsl_dir_close(dp->dp_mos_dir, dp);
if (dp->dp_root_dir)
@@ -130,20 +221,27 @@ dsl_pool_close(dsl_pool_t *dp)
txg_list_destroy(&dp->dp_dirty_datasets);
txg_list_destroy(&dp->dp_dirty_dirs);
txg_list_destroy(&dp->dp_sync_tasks);
- list_destroy(&dp->dp_synced_objsets);
+ list_destroy(&dp->dp_synced_datasets);
- arc_flush();
+ arc_flush(dp->dp_spa);
txg_fini(dp);
rw_destroy(&dp->dp_config_rwlock);
+ mutex_destroy(&dp->dp_lock);
+ mutex_destroy(&dp->dp_scrub_cancel_lock);
kmem_free(dp, sizeof (dsl_pool_t));
}
dsl_pool_t *
-dsl_pool_create(spa_t *spa, uint64_t txg)
+dsl_pool_create(spa_t *spa, nvlist_t *zplprops, uint64_t txg)
{
int err;
dsl_pool_t *dp = dsl_pool_open_impl(spa, txg);
dmu_tx_t *tx = dmu_tx_create_assigned(dp, txg);
+ objset_impl_t *osip;
+ dsl_dataset_t *ds;
+ uint64_t dsobj;
+
+ /* create and open the MOS (meta-objset) */
dp->dp_meta_objset = &dmu_objset_create_impl(spa,
NULL, &dp->dp_meta_rootbp, DMU_OST_META, tx)->os;
@@ -153,13 +251,29 @@ dsl_pool_create(spa_t *spa, uint64_t txg)
ASSERT3U(err, ==, 0);
/* create and open the root dir */
- dsl_dataset_create_root(dp, &dp->dp_root_dir_obj, tx);
+ dp->dp_root_dir_obj = dsl_dir_create_sync(dp, NULL, NULL, tx);
VERIFY(0 == dsl_dir_open_obj(dp, dp->dp_root_dir_obj,
NULL, dp, &dp->dp_root_dir));
/* create and open the meta-objset dir */
- (void) dsl_dir_create_sync(dp->dp_root_dir, MOS_DIR_NAME, tx);
- VERIFY(0 == dsl_pool_open_mos_dir(dp, &dp->dp_mos_dir));
+ (void) dsl_dir_create_sync(dp, dp->dp_root_dir, MOS_DIR_NAME, tx);
+ VERIFY(0 == dsl_pool_open_special_dir(dp,
+ MOS_DIR_NAME, &dp->dp_mos_dir));
+
+ if (spa_version(spa) >= SPA_VERSION_DSL_SCRUB)
+ dsl_pool_create_origin(dp, tx);
+
+ /* create the root dataset */
+ dsobj = dsl_dataset_create_sync_dd(dp->dp_root_dir, NULL, 0, tx);
+
+ /* create the root objset */
+ VERIFY(0 == dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds));
+ osip = dmu_objset_create_impl(dp->dp_spa, ds,
+ dsl_dataset_get_blkptr(ds), DMU_OST_ZFS, tx);
+#ifdef _KERNEL
+ zfs_create_fs(&osip->os, kcred, zplprops, tx);
+#endif
+ dsl_dataset_rele(ds, FTAG);
dmu_tx_commit(tx);
@@ -175,26 +289,42 @@ dsl_pool_sync(dsl_pool_t *dp, uint64_t txg)
dsl_dataset_t *ds;
dsl_sync_task_group_t *dstg;
objset_impl_t *mosi = dp->dp_meta_objset->os;
+ hrtime_t start, write_time;
+ uint64_t data_written;
int err;
tx = dmu_tx_create_assigned(dp, txg);
+ dp->dp_read_overhead = 0;
zio = zio_root(dp->dp_spa, NULL, NULL, ZIO_FLAG_MUSTSUCCEED);
while (ds = txg_list_remove(&dp->dp_dirty_datasets, txg)) {
if (!list_link_active(&ds->ds_synced_link))
- list_insert_tail(&dp->dp_synced_objsets, ds);
+ list_insert_tail(&dp->dp_synced_datasets, ds);
else
dmu_buf_rele(ds->ds_dbuf, ds);
dsl_dataset_sync(ds, zio, tx);
}
+ DTRACE_PROBE(pool_sync__1setup);
+
+ start = gethrtime();
err = zio_wait(zio);
+ write_time = gethrtime() - start;
ASSERT(err == 0);
+ DTRACE_PROBE(pool_sync__2rootzio);
while (dstg = txg_list_remove(&dp->dp_sync_tasks, txg))
dsl_sync_task_group_sync(dstg, tx);
+ DTRACE_PROBE(pool_sync__3task);
+
+ start = gethrtime();
while (dd = txg_list_remove(&dp->dp_dirty_dirs, txg))
dsl_dir_sync(dd, tx);
+ write_time += gethrtime() - start;
+
+ if (spa_sync_pass(dp->dp_spa) == 1)
+ dsl_pool_scrub_sync(dp, tx);
+ start = gethrtime();
if (list_head(&mosi->os_dirty_dnodes[txg & TXG_MASK]) != NULL ||
list_head(&mosi->os_free_dnodes[txg & TXG_MASK]) != NULL) {
zio = zio_root(dp->dp_spa, NULL, NULL, ZIO_FLAG_MUSTSUCCEED);
@@ -204,8 +334,51 @@ dsl_pool_sync(dsl_pool_t *dp, uint64_t txg)
dprintf_bp(&dp->dp_meta_rootbp, "meta objset rootbp is %s", "");
spa_set_rootblkptr(dp->dp_spa, &dp->dp_meta_rootbp);
}
+ write_time += gethrtime() - start;
+ DTRACE_PROBE2(pool_sync__4io, hrtime_t, write_time,
+ hrtime_t, dp->dp_read_overhead);
+ write_time -= dp->dp_read_overhead;
dmu_tx_commit(tx);
+
+ data_written = dp->dp_space_towrite[txg & TXG_MASK];
+ dp->dp_space_towrite[txg & TXG_MASK] = 0;
+ ASSERT(dp->dp_tempreserved[txg & TXG_MASK] == 0);
+
+ /*
+ * If the write limit max has not been explicitly set, set it
+ * to a fraction of available physical memory (default 1/8th).
+ * Note that we must inflate the limit because the spa
+ * inflates write sizes to account for data replication.
+ * Check this each sync phase to catch changing memory size.
+ */
+ if (physmem != old_physmem && zfs_write_limit_shift) {
+ mutex_enter(&zfs_write_limit_lock);
+ old_physmem = physmem;
+ zfs_write_limit_max = ptob(physmem) >> zfs_write_limit_shift;
+ zfs_write_limit_inflated = MAX(zfs_write_limit_min,
+ spa_get_asize(dp->dp_spa, zfs_write_limit_max));
+ mutex_exit(&zfs_write_limit_lock);
+ }
+
+ /*
+ * Attempt to keep the sync time consistent by adjusting the
+ * amount of write traffic allowed into each transaction group.
+ * Weight the throughput calculation towards the current value:
+ * thru = 3/4 old_thru + 1/4 new_thru
+ */
+ ASSERT(zfs_write_limit_min > 0);
+ if (data_written > zfs_write_limit_min / 8 && write_time > 0) {
+ uint64_t throughput = (data_written * NANOSEC) / write_time;
+ if (dp->dp_throughput)
+ dp->dp_throughput = throughput / 4 +
+ 3 * dp->dp_throughput / 4;
+ else
+ dp->dp_throughput = throughput;
+ dp->dp_write_limit = MIN(zfs_write_limit_inflated,
+ MAX(zfs_write_limit_min,
+ dp->dp_throughput * zfs_txg_synctime));
+ }
}
void
@@ -213,8 +386,8 @@ dsl_pool_zil_clean(dsl_pool_t *dp)
{
dsl_dataset_t *ds;
- while (ds = list_head(&dp->dp_synced_objsets)) {
- list_remove(&dp->dp_synced_objsets, ds);
+ while (ds = list_head(&dp->dp_synced_datasets)) {
+ list_remove(&dp->dp_synced_datasets, ds);
ASSERT(ds->ds_user_ptr != NULL);
zil_clean(((objset_impl_t *)ds->ds_user_ptr)->os_zil);
dmu_buf_rele(ds->ds_dbuf, ds);
@@ -254,3 +427,187 @@ dsl_pool_adjustedsize(dsl_pool_t *dp, boolean_t netfree)
return (space - resv);
}
+
+int
+dsl_pool_tempreserve_space(dsl_pool_t *dp, uint64_t space, dmu_tx_t *tx)
+{
+ uint64_t reserved = 0;
+ uint64_t write_limit = (zfs_write_limit_override ?
+ zfs_write_limit_override : dp->dp_write_limit);
+
+ if (zfs_no_write_throttle) {
+ atomic_add_64(&dp->dp_tempreserved[tx->tx_txg & TXG_MASK],
+ space);
+ return (0);
+ }
+
+ /*
+ * Check to see if we have exceeded the maximum allowed IO for
+ * this transaction group. We can do this without locks since
+ * a little slop here is ok. Note that we do the reserved check
+ * with only half the requested reserve: this is because the
+ * reserve requests are worst-case, and we really don't want to
+ * throttle based off of worst-case estimates.
+ */
+ if (write_limit > 0) {
+ reserved = dp->dp_space_towrite[tx->tx_txg & TXG_MASK]
+ + dp->dp_tempreserved[tx->tx_txg & TXG_MASK] / 2;
+
+ if (reserved && reserved > write_limit)
+ return (ERESTART);
+ }
+
+ atomic_add_64(&dp->dp_tempreserved[tx->tx_txg & TXG_MASK], space);
+
+ /*
+ * If this transaction group is over 7/8ths capacity, delay
+ * the caller 1 clock tick. This will slow down the "fill"
+ * rate until the sync process can catch up with us.
+ */
+ if (reserved && reserved > (write_limit - (write_limit >> 3)))
+ txg_delay(dp, tx->tx_txg, 1);
+
+ return (0);
+}
+
+void
+dsl_pool_tempreserve_clear(dsl_pool_t *dp, int64_t space, dmu_tx_t *tx)
+{
+ ASSERT(dp->dp_tempreserved[tx->tx_txg & TXG_MASK] >= space);
+ atomic_add_64(&dp->dp_tempreserved[tx->tx_txg & TXG_MASK], -space);
+}
+
+void
+dsl_pool_memory_pressure(dsl_pool_t *dp)
+{
+ uint64_t space_inuse = 0;
+ int i;
+
+ if (dp->dp_write_limit == zfs_write_limit_min)
+ return;
+
+ for (i = 0; i < TXG_SIZE; i++) {
+ space_inuse += dp->dp_space_towrite[i];
+ space_inuse += dp->dp_tempreserved[i];
+ }
+ dp->dp_write_limit = MAX(zfs_write_limit_min,
+ MIN(dp->dp_write_limit, space_inuse / 4));
+}
+
+void
+dsl_pool_willuse_space(dsl_pool_t *dp, int64_t space, dmu_tx_t *tx)
+{
+ if (space > 0) {
+ mutex_enter(&dp->dp_lock);
+ dp->dp_space_towrite[tx->tx_txg & TXG_MASK] += space;
+ mutex_exit(&dp->dp_lock);
+ }
+}
+
+/* ARGSUSED */
+static int
+upgrade_clones_cb(spa_t *spa, uint64_t dsobj, const char *dsname, void *arg)
+{
+ dmu_tx_t *tx = arg;
+ dsl_dataset_t *ds, *prev = NULL;
+ int err;
+ dsl_pool_t *dp = spa_get_dsl(spa);
+
+ err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
+ if (err)
+ return (err);
+
+ while (ds->ds_phys->ds_prev_snap_obj != 0) {
+ err = dsl_dataset_hold_obj(dp, ds->ds_phys->ds_prev_snap_obj,
+ FTAG, &prev);
+ if (err) {
+ dsl_dataset_rele(ds, FTAG);
+ return (err);
+ }
+
+ if (prev->ds_phys->ds_next_snap_obj != ds->ds_object)
+ break;
+ dsl_dataset_rele(ds, FTAG);
+ ds = prev;
+ prev = NULL;
+ }
+
+ if (prev == NULL) {
+ prev = dp->dp_origin_snap;
+
+ /*
+ * The $ORIGIN can't have any data, or the accounting
+ * will be wrong.
+ */
+ ASSERT(prev->ds_phys->ds_bp.blk_birth == 0);
+
+ /* The origin doesn't get attached to itself */
+ if (ds->ds_object == prev->ds_object) {
+ dsl_dataset_rele(ds, FTAG);
+ return (0);
+ }
+
+ dmu_buf_will_dirty(ds->ds_dbuf, tx);
+ ds->ds_phys->ds_prev_snap_obj = prev->ds_object;
+ ds->ds_phys->ds_prev_snap_txg = prev->ds_phys->ds_creation_txg;
+
+ dmu_buf_will_dirty(ds->ds_dir->dd_dbuf, tx);
+ ds->ds_dir->dd_phys->dd_origin_obj = prev->ds_object;
+
+ dmu_buf_will_dirty(prev->ds_dbuf, tx);
+ prev->ds_phys->ds_num_children++;
+
+ if (ds->ds_phys->ds_next_snap_obj == 0) {
+ ASSERT(ds->ds_prev == NULL);
+ VERIFY(0 == dsl_dataset_hold_obj(dp,
+ ds->ds_phys->ds_prev_snap_obj, ds, &ds->ds_prev));
+ }
+ }
+
+ ASSERT(ds->ds_dir->dd_phys->dd_origin_obj == prev->ds_object);
+ ASSERT(ds->ds_phys->ds_prev_snap_obj == prev->ds_object);
+
+ if (prev->ds_phys->ds_next_clones_obj == 0) {
+ prev->ds_phys->ds_next_clones_obj =
+ zap_create(dp->dp_meta_objset,
+ DMU_OT_NEXT_CLONES, DMU_OT_NONE, 0, tx);
+ }
+ VERIFY(0 == zap_add_int(dp->dp_meta_objset,
+ prev->ds_phys->ds_next_clones_obj, ds->ds_object, tx));
+
+ dsl_dataset_rele(ds, FTAG);
+ if (prev != dp->dp_origin_snap)
+ dsl_dataset_rele(prev, FTAG);
+ return (0);
+}
+
+void
+dsl_pool_upgrade_clones(dsl_pool_t *dp, dmu_tx_t *tx)
+{
+ ASSERT(dmu_tx_is_syncing(tx));
+ ASSERT(dp->dp_origin_snap != NULL);
+
+ (void) dmu_objset_find_spa(dp->dp_spa, NULL, upgrade_clones_cb,
+ tx, DS_FIND_CHILDREN);
+}
+
+void
+dsl_pool_create_origin(dsl_pool_t *dp, dmu_tx_t *tx)
+{
+ uint64_t dsobj;
+ dsl_dataset_t *ds;
+
+ ASSERT(dmu_tx_is_syncing(tx));
+ ASSERT(dp->dp_origin_snap == NULL);
+
+ /* create the origin dir, ds, & snap-ds */
+ rw_enter(&dp->dp_config_rwlock, RW_WRITER);
+ dsobj = dsl_dataset_create_sync(dp->dp_root_dir, ORIGIN_DIR_NAME,
+ NULL, 0, kcred, tx);
+ VERIFY(0 == dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds));
+ dsl_dataset_snapshot_sync(ds, ORIGIN_DIR_NAME, kcred, tx);
+ VERIFY(0 == dsl_dataset_hold_obj(dp, ds->ds_phys->ds_prev_snap_obj,
+ dp, &dp->dp_origin_snap));
+ dsl_dataset_rele(ds, FTAG);
+ rw_exit(&dp->dp_config_rwlock);
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_prop.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_prop.c
index 2fff66d..212acbb 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_prop.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_prop.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -44,14 +44,20 @@ dodefault(const char *propname, int intsz, int numint, void *buf)
{
zfs_prop_t prop;
- if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL ||
- zfs_prop_readonly(prop))
+ /*
+ * The setonce properties are read-only, BUT they still
+ * have a default value that can be used as the initial
+ * value.
+ */
+ if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL ||
+ (zfs_prop_readonly(prop) && !zfs_prop_setonce(prop)))
return (ENOENT);
- if (zfs_prop_get_type(prop) == prop_type_string) {
+ if (zfs_prop_get_type(prop) == PROP_TYPE_STRING) {
if (intsz != 1)
return (EOVERFLOW);
- (void) strncpy(buf, zfs_prop_default_string(prop), numint);
+ (void) strncpy(buf, zfs_prop_default_string(prop),
+ numint);
} else {
if (intsz != 8 || numint < 1)
return (EOVERFLOW);
@@ -62,13 +68,16 @@ dodefault(const char *propname, int intsz, int numint, void *buf)
return (0);
}
-static int
-dsl_prop_get_impl(dsl_dir_t *dd, const char *propname,
+int
+dsl_prop_get_dd(dsl_dir_t *dd, const char *propname,
int intsz, int numint, void *buf, char *setpoint)
{
int err = ENOENT;
+ objset_t *mos = dd->dd_pool->dp_meta_objset;
zfs_prop_t prop;
+ ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock));
+
if (setpoint)
setpoint[0] = '\0';
@@ -79,7 +88,6 @@ dsl_prop_get_impl(dsl_dir_t *dd, const char *propname,
* ouside this loop.
*/
for (; dd != NULL; dd = dd->dd_parent) {
- objset_t *mos = dd->dd_pool->dp_meta_objset;
ASSERT(RW_LOCK_HELD(&dd->dd_pool->dp_config_rwlock));
err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj,
propname, intsz, numint, buf);
@@ -92,8 +100,7 @@ dsl_prop_get_impl(dsl_dir_t *dd, const char *propname,
/*
* Break out of this loop for non-inheritable properties.
*/
- if (prop != ZFS_PROP_INVAL &&
- !zfs_prop_inheritable(prop))
+ if (prop != ZPROP_INVAL && !zfs_prop_inheritable(prop))
break;
}
if (err == ENOENT)
@@ -102,6 +109,26 @@ dsl_prop_get_impl(dsl_dir_t *dd, const char *propname,
return (err);
}
+int
+dsl_prop_get_ds(dsl_dataset_t *ds, const char *propname,
+ int intsz, int numint, void *buf, char *setpoint)
+{
+ ASSERT(RW_LOCK_HELD(&ds->ds_dir->dd_pool->dp_config_rwlock));
+
+ if (ds->ds_phys->ds_props_obj) {
+ int err = zap_lookup(ds->ds_dir->dd_pool->dp_meta_objset,
+ ds->ds_phys->ds_props_obj, propname, intsz, numint, buf);
+ if (err != ENOENT) {
+ if (setpoint)
+ dsl_dataset_name(ds, setpoint);
+ return (err);
+ }
+ }
+
+ return (dsl_prop_get_dd(ds->ds_dir, propname,
+ intsz, numint, buf, setpoint));
+}
+
/*
* Register interest in the named property. We'll call the callback
* once to notify it of the current property value, and again each time
@@ -114,18 +141,20 @@ dsl_prop_register(dsl_dataset_t *ds, const char *propname,
dsl_prop_changed_cb_t *callback, void *cbarg)
{
dsl_dir_t *dd = ds->ds_dir;
+ dsl_pool_t *dp = dd->dd_pool;
uint64_t value;
dsl_prop_cb_record_t *cbr;
int err;
int need_rwlock;
- need_rwlock = !RW_WRITE_HELD(&dd->dd_pool->dp_config_rwlock);
+ need_rwlock = !RW_WRITE_HELD(&dp->dp_config_rwlock);
if (need_rwlock)
- rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
+ rw_enter(&dp->dp_config_rwlock, RW_READER);
- err = dsl_prop_get_impl(dd, propname, 8, 1, &value, NULL);
+ err = dsl_prop_get_ds(ds, propname, 8, 1, &value, NULL);
if (err != 0) {
- rw_exit(&dd->dd_pool->dp_config_rwlock);
+ if (need_rwlock)
+ rw_exit(&dp->dp_config_rwlock);
return (err);
}
@@ -141,46 +170,30 @@ dsl_prop_register(dsl_dataset_t *ds, const char *propname,
cbr->cbr_func(cbr->cbr_arg, value);
- VERIFY(0 == dsl_dir_open_obj(dd->dd_pool, dd->dd_object,
+ VERIFY(0 == dsl_dir_open_obj(dp, dd->dd_object,
NULL, cbr, &dd));
if (need_rwlock)
- rw_exit(&dd->dd_pool->dp_config_rwlock);
- /* Leave dataset open until this callback is unregistered */
+ rw_exit(&dp->dp_config_rwlock);
+ /* Leave dir open until this callback is unregistered */
return (0);
}
int
-dsl_prop_get_ds(dsl_dir_t *dd, const char *propname,
- int intsz, int numints, void *buf, char *setpoint)
-{
- int err;
-
- rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
- err = dsl_prop_get_impl(dd, propname, intsz, numints, buf, setpoint);
- rw_exit(&dd->dd_pool->dp_config_rwlock);
-
- return (err);
-}
-
-int
-dsl_prop_get(const char *ddname, const char *propname,
+dsl_prop_get(const char *dsname, const char *propname,
int intsz, int numints, void *buf, char *setpoint)
{
- dsl_dir_t *dd;
- const char *tail;
+ dsl_dataset_t *ds;
int err;
- err = dsl_dir_open(ddname, FTAG, &dd, &tail);
+ err = dsl_dataset_hold(dsname, FTAG, &ds);
if (err)
return (err);
- if (tail && tail[0] != '@') {
- dsl_dir_close(dd, FTAG);
- return (ENOENT);
- }
- err = dsl_prop_get_ds(dd, propname, intsz, numints, buf, setpoint);
+ rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER);
+ err = dsl_prop_get_ds(ds, propname, intsz, numints, buf, setpoint);
+ rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
- dsl_dir_close(dd, FTAG);
+ dsl_dataset_rele(ds, FTAG);
return (err);
}
@@ -264,8 +277,9 @@ dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj,
dsl_prop_cb_record_t *cbr;
objset_t *mos = dp->dp_meta_objset;
zap_cursor_t zc;
- zap_attribute_t za;
+ zap_attribute_t *za;
int err;
+ uint64_t dummyval;
ASSERT(RW_WRITE_HELD(&dp->dp_config_rwlock));
err = dsl_dir_open_obj(dp, ddobj, NULL, FTAG, &dd);
@@ -278,7 +292,7 @@ dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj,
* being inherited here or below; stop the recursion.
*/
err = zap_lookup(mos, dd->dd_phys->dd_props_zapobj, propname,
- 8, 1, &value);
+ 8, 1, &dummyval);
if (err == 0) {
dsl_dir_close(dd, FTAG);
return;
@@ -287,22 +301,34 @@ dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj,
}
mutex_enter(&dd->dd_lock);
- for (cbr = list_head(&dd->dd_prop_cbs);
- cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) {
- if (strcmp(cbr->cbr_propname, propname) == 0) {
- cbr->cbr_func(cbr->cbr_arg, value);
- }
+ for (cbr = list_head(&dd->dd_prop_cbs); cbr;
+ cbr = list_next(&dd->dd_prop_cbs, cbr)) {
+ uint64_t propobj = cbr->cbr_ds->ds_phys->ds_props_obj;
+
+ if (strcmp(cbr->cbr_propname, propname) != 0)
+ continue;
+
+ /*
+ * If the property is set on this ds, then it is not
+ * inherited here; don't call the callback.
+ */
+ if (propobj && 0 == zap_lookup(mos, propobj, propname,
+ 8, 1, &dummyval))
+ continue;
+
+ cbr->cbr_func(cbr->cbr_arg, value);
}
mutex_exit(&dd->dd_lock);
+ za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP);
for (zap_cursor_init(&zc, mos,
dd->dd_phys->dd_child_dir_zapobj);
- zap_cursor_retrieve(&zc, &za) == 0;
+ zap_cursor_retrieve(&zc, za) == 0;
zap_cursor_advance(&zc)) {
- /* XXX recursion could blow stack; esp. za! */
- dsl_prop_changed_notify(dp, za.za_first_integer,
+ dsl_prop_changed_notify(dp, za->za_first_integer,
propname, value, FALSE);
}
+ kmem_free(za, sizeof (zap_attribute_t));
zap_cursor_fini(&zc);
dsl_dir_close(dd, FTAG);
}
@@ -316,22 +342,37 @@ struct prop_set_arg {
static void
-dsl_prop_set_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+dsl_prop_set_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
- dsl_dir_t *dd = arg1;
+ dsl_dataset_t *ds = arg1;
struct prop_set_arg *psa = arg2;
- objset_t *mos = dd->dd_pool->dp_meta_objset;
- uint64_t zapobj = dd->dd_phys->dd_props_zapobj;
- uint64_t intval;
+ objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
+ uint64_t zapobj, intval;
int isint;
+ char valbuf[32];
+ char *valstr;
isint = (dodefault(psa->name, 8, 1, &intval) == 0);
+ if (dsl_dataset_is_snapshot(ds)) {
+ ASSERT(spa_version(ds->ds_dir->dd_pool->dp_spa) >=
+ SPA_VERSION_SNAP_PROPS);
+ if (ds->ds_phys->ds_props_obj == 0) {
+ dmu_buf_will_dirty(ds->ds_dbuf, tx);
+ ds->ds_phys->ds_props_obj =
+ zap_create(mos,
+ DMU_OT_DSL_PROPS, DMU_OT_NONE, 0, tx);
+ }
+ zapobj = ds->ds_phys->ds_props_obj;
+ } else {
+ zapobj = ds->ds_dir->dd_phys->dd_props_zapobj;
+ }
+
if (psa->numints == 0) {
int err = zap_remove(mos, zapobj, psa->name, tx);
ASSERT(err == 0 || err == ENOENT);
if (isint) {
- VERIFY(0 == dsl_prop_get_impl(dd->dd_parent,
+ VERIFY(0 == dsl_prop_get_ds(ds,
psa->name, 8, 1, &intval, NULL));
}
} else {
@@ -342,32 +383,63 @@ dsl_prop_set_sync(void *arg1, void *arg2, dmu_tx_t *tx)
}
if (isint) {
- dsl_prop_changed_notify(dd->dd_pool,
- dd->dd_object, psa->name, intval, TRUE);
+ if (dsl_dataset_is_snapshot(ds)) {
+ dsl_prop_cb_record_t *cbr;
+ /*
+ * It's a snapshot; nothing can inherit this
+ * property, so just look for callbacks on this
+ * ds here.
+ */
+ mutex_enter(&ds->ds_dir->dd_lock);
+ for (cbr = list_head(&ds->ds_dir->dd_prop_cbs); cbr;
+ cbr = list_next(&ds->ds_dir->dd_prop_cbs, cbr)) {
+ if (cbr->cbr_ds == ds &&
+ strcmp(cbr->cbr_propname, psa->name) == 0)
+ cbr->cbr_func(cbr->cbr_arg, intval);
+ }
+ mutex_exit(&ds->ds_dir->dd_lock);
+ } else {
+ dsl_prop_changed_notify(ds->ds_dir->dd_pool,
+ ds->ds_dir->dd_object, psa->name, intval, TRUE);
+ }
+ }
+ if (isint) {
+ (void) snprintf(valbuf, sizeof (valbuf),
+ "%lld", (longlong_t)intval);
+ valstr = valbuf;
+ } else {
+ valstr = (char *)psa->buf;
}
+ spa_history_internal_log((psa->numints == 0) ? LOG_DS_INHERIT :
+ LOG_DS_PROPSET, ds->ds_dir->dd_pool->dp_spa, tx, cr,
+ "%s=%s dataset = %llu", psa->name, valstr, ds->ds_object);
}
-int
-dsl_prop_set_dd(dsl_dir_t *dd, const char *propname,
- int intsz, int numints, const void *buf)
+void
+dsl_prop_set_uint64_sync(dsl_dir_t *dd, const char *name, uint64_t val,
+ cred_t *cr, dmu_tx_t *tx)
{
- struct prop_set_arg psa;
+ objset_t *mos = dd->dd_pool->dp_meta_objset;
+ uint64_t zapobj = dd->dd_phys->dd_props_zapobj;
- psa.name = propname;
- psa.intsz = intsz;
- psa.numints = numints;
- psa.buf = buf;
+ ASSERT(dmu_tx_is_syncing(tx));
+
+ VERIFY(0 == zap_update(mos, zapobj, name, sizeof (val), 1, &val, tx));
+
+ dsl_prop_changed_notify(dd->dd_pool, dd->dd_object, name, val, TRUE);
- return (dsl_sync_task_do(dd->dd_pool,
- NULL, dsl_prop_set_sync, dd, &psa, 2));
+ spa_history_internal_log(LOG_DS_PROPSET, dd->dd_pool->dp_spa, tx, cr,
+ "%s=%llu dataset = %llu", name, (u_longlong_t)val,
+ dd->dd_phys->dd_head_dataset_obj);
}
int
-dsl_prop_set(const char *ddname, const char *propname,
+dsl_prop_set(const char *dsname, const char *propname,
int intsz, int numints, const void *buf)
{
- dsl_dir_t *dd;
+ dsl_dataset_t *ds;
int err;
+ struct prop_set_arg psa;
/*
* We must do these checks before we get to the syncfunc, since
@@ -378,11 +450,24 @@ dsl_prop_set(const char *ddname, const char *propname,
if (intsz * numints >= ZAP_MAXVALUELEN)
return (E2BIG);
- err = dsl_dir_open(ddname, FTAG, &dd, NULL);
+ err = dsl_dataset_hold(dsname, FTAG, &ds);
if (err)
return (err);
- err = dsl_prop_set_dd(dd, propname, intsz, numints, buf);
- dsl_dir_close(dd, FTAG);
+
+ if (dsl_dataset_is_snapshot(ds) &&
+ spa_version(ds->ds_dir->dd_pool->dp_spa) < SPA_VERSION_SNAP_PROPS) {
+ dsl_dataset_rele(ds, FTAG);
+ return (ENOTSUP);
+ }
+
+ psa.name = propname;
+ psa.intsz = intsz;
+ psa.numints = numints;
+ psa.buf = buf;
+ err = dsl_sync_task_do(ds->ds_dir->dd_pool,
+ NULL, dsl_prop_set_sync, ds, &psa, 2);
+
+ dsl_dataset_rele(ds, FTAG);
return (err);
}
@@ -390,45 +475,55 @@ dsl_prop_set(const char *ddname, const char *propname,
* Iterate over all properties for this dataset and return them in an nvlist.
*/
int
-dsl_prop_get_all(objset_t *os, nvlist_t **nvp)
+dsl_prop_get_all(objset_t *os, nvlist_t **nvp, boolean_t local)
{
dsl_dataset_t *ds = os->os->os_dsl_dataset;
dsl_dir_t *dd = ds->ds_dir;
+ boolean_t snapshot = dsl_dataset_is_snapshot(ds);
int err = 0;
- dsl_pool_t *dp;
- objset_t *mos;
-
- if (dsl_dataset_is_snapshot(ds)) {
- VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
- return (0);
- }
+ dsl_pool_t *dp = dd->dd_pool;
+ objset_t *mos = dp->dp_meta_objset;
+ uint64_t propobj = ds->ds_phys->ds_props_obj;
VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
- dp = dd->dd_pool;
- mos = dp->dp_meta_objset;
+ if (local && snapshot && !propobj)
+ return (0);
rw_enter(&dp->dp_config_rwlock, RW_READER);
- for (; dd != NULL; dd = dd->dd_parent) {
+ while (dd != NULL) {
char setpoint[MAXNAMELEN];
zap_cursor_t zc;
zap_attribute_t za;
+ dsl_dir_t *dd_next;
+
+ if (propobj) {
+ dsl_dataset_name(ds, setpoint);
+ dd_next = dd;
+ } else {
+ dsl_dir_name(dd, setpoint);
+ propobj = dd->dd_phys->dd_props_zapobj;
+ dd_next = dd->dd_parent;
+ }
- dsl_dir_name(dd, setpoint);
-
- for (zap_cursor_init(&zc, mos, dd->dd_phys->dd_props_zapobj);
+ for (zap_cursor_init(&zc, mos, propobj);
(err = zap_cursor_retrieve(&zc, &za)) == 0;
zap_cursor_advance(&zc)) {
nvlist_t *propval;
- zfs_prop_t prop;
- /*
- * Skip non-inheritable properties.
- */
- if ((prop = zfs_name_to_prop(za.za_name)) !=
- ZFS_PROP_INVAL && !zfs_prop_inheritable(prop) &&
- dd != ds->ds_dir)
+ zfs_prop_t prop = zfs_name_to_prop(za.za_name);
+
+ /* Skip non-inheritable properties. */
+ if (prop != ZPROP_INVAL &&
+ !zfs_prop_inheritable(prop) &&
+ (dd != ds->ds_dir || (snapshot && dd != dd_next)))
continue;
+ /* Skip properties not valid for this type. */
+ if (snapshot && prop != ZPROP_INVAL &&
+ !zfs_prop_valid_for_type(prop, ZFS_TYPE_SNAPSHOT))
+ continue;
+
+ /* Skip properties already defined */
if (nvlist_lookup_nvlist(*nvp, za.za_name,
&propval) == 0)
continue;
@@ -441,28 +536,26 @@ dsl_prop_get_all(objset_t *os, nvlist_t **nvp)
*/
char *tmp = kmem_alloc(za.za_num_integers,
KM_SLEEP);
- err = zap_lookup(mos,
- dd->dd_phys->dd_props_zapobj,
- za.za_name, 1, za.za_num_integers,
- tmp);
+ err = zap_lookup(mos, propobj,
+ za.za_name, 1, za.za_num_integers, tmp);
if (err != 0) {
kmem_free(tmp, za.za_num_integers);
break;
}
- VERIFY(nvlist_add_string(propval,
- ZFS_PROP_VALUE, tmp) == 0);
+ VERIFY(nvlist_add_string(propval, ZPROP_VALUE,
+ tmp) == 0);
kmem_free(tmp, za.za_num_integers);
} else {
/*
* Integer property
*/
ASSERT(za.za_integer_length == 8);
- (void) nvlist_add_uint64(propval,
- ZFS_PROP_VALUE, za.za_first_integer);
+ (void) nvlist_add_uint64(propval, ZPROP_VALUE,
+ za.za_first_integer);
}
- VERIFY(nvlist_add_string(propval,
- ZFS_PROP_SOURCE, setpoint) == 0);
+ VERIFY(nvlist_add_string(propval, ZPROP_SOURCE,
+ setpoint) == 0);
VERIFY(nvlist_add_nvlist(*nvp, za.za_name,
propval) == 0);
nvlist_free(propval);
@@ -472,6 +565,14 @@ dsl_prop_get_all(objset_t *os, nvlist_t **nvp)
if (err != ENOENT)
break;
err = 0;
+ /*
+ * If we are just after the props that have been set
+ * locally, then we are done after the first iteration.
+ */
+ if (local)
+ break;
+ dd = dd_next;
+ propobj = 0;
}
rw_exit(&dp->dp_config_rwlock);
@@ -484,7 +585,7 @@ dsl_prop_nvlist_add_uint64(nvlist_t *nv, zfs_prop_t prop, uint64_t value)
nvlist_t *propval;
VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
- VERIFY(nvlist_add_uint64(propval, ZFS_PROP_VALUE, value) == 0);
+ VERIFY(nvlist_add_uint64(propval, ZPROP_VALUE, value) == 0);
VERIFY(nvlist_add_nvlist(nv, zfs_prop_to_name(prop), propval) == 0);
nvlist_free(propval);
}
@@ -495,7 +596,7 @@ dsl_prop_nvlist_add_string(nvlist_t *nv, zfs_prop_t prop, const char *value)
nvlist_t *propval;
VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
- VERIFY(nvlist_add_string(propval, ZFS_PROP_VALUE, value) == 0);
+ VERIFY(nvlist_add_string(propval, ZPROP_VALUE, value) == 0);
VERIFY(nvlist_add_nvlist(nv, zfs_prop_to_name(prop), propval) == 0);
nvlist_free(propval);
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_scrub.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_scrub.c
new file mode 100644
index 0000000..5f675b7
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_scrub.c
@@ -0,0 +1,929 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL 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.
+ */
+
+#include <sys/dsl_pool.h>
+#include <sys/dsl_dataset.h>
+#include <sys/dsl_prop.h>
+#include <sys/dsl_dir.h>
+#include <sys/dsl_synctask.h>
+#include <sys/dnode.h>
+#include <sys/dmu_tx.h>
+#include <sys/dmu_objset.h>
+#include <sys/arc.h>
+#include <sys/zap.h>
+#include <sys/zio.h>
+#include <sys/zfs_context.h>
+#include <sys/fs/zfs.h>
+#include <sys/zfs_znode.h>
+#include <sys/spa_impl.h>
+#include <sys/vdev_impl.h>
+#include <sys/zil_impl.h>
+
+typedef int (scrub_cb_t)(dsl_pool_t *, const blkptr_t *, const zbookmark_t *);
+
+static scrub_cb_t dsl_pool_scrub_clean_cb;
+static dsl_syncfunc_t dsl_pool_scrub_cancel_sync;
+
+int zfs_scrub_min_time = 1; /* scrub for at least 1 sec each txg */
+int zfs_resilver_min_time = 3; /* resilver for at least 3 sec each txg */
+boolean_t zfs_no_scrub_io = B_FALSE; /* set to disable scrub i/o */
+
+extern int zfs_txg_timeout;
+
+static scrub_cb_t *scrub_funcs[SCRUB_FUNC_NUMFUNCS] = {
+ NULL,
+ dsl_pool_scrub_clean_cb
+};
+
+#define SET_BOOKMARK(zb, objset, object, level, blkid) \
+{ \
+ (zb)->zb_objset = objset; \
+ (zb)->zb_object = object; \
+ (zb)->zb_level = level; \
+ (zb)->zb_blkid = blkid; \
+}
+
+/* ARGSUSED */
+static void
+dsl_pool_scrub_setup_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
+{
+ dsl_pool_t *dp = arg1;
+ enum scrub_func *funcp = arg2;
+ dmu_object_type_t ot = 0;
+ boolean_t complete = B_FALSE;
+
+ dsl_pool_scrub_cancel_sync(dp, &complete, cr, tx);
+
+ ASSERT(dp->dp_scrub_func == SCRUB_FUNC_NONE);
+ ASSERT(*funcp > SCRUB_FUNC_NONE);
+ ASSERT(*funcp < SCRUB_FUNC_NUMFUNCS);
+
+ dp->dp_scrub_min_txg = 0;
+ dp->dp_scrub_max_txg = tx->tx_txg;
+
+ if (*funcp == SCRUB_FUNC_CLEAN) {
+ vdev_t *rvd = dp->dp_spa->spa_root_vdev;
+
+ /* rewrite all disk labels */
+ vdev_config_dirty(rvd);
+
+ if (vdev_resilver_needed(rvd,
+ &dp->dp_scrub_min_txg, &dp->dp_scrub_max_txg)) {
+ spa_event_notify(dp->dp_spa, NULL,
+ ESC_ZFS_RESILVER_START);
+ dp->dp_scrub_max_txg = MIN(dp->dp_scrub_max_txg,
+ tx->tx_txg);
+ }
+
+ /* zero out the scrub stats in all vdev_stat_t's */
+ vdev_scrub_stat_update(rvd,
+ dp->dp_scrub_min_txg ? POOL_SCRUB_RESILVER :
+ POOL_SCRUB_EVERYTHING, B_FALSE);
+
+ dp->dp_spa->spa_scrub_started = B_TRUE;
+ }
+
+ /* back to the generic stuff */
+
+ if (spa_version(dp->dp_spa) < SPA_VERSION_DSL_SCRUB)
+ ot = DMU_OT_ZAP_OTHER;
+
+ dp->dp_scrub_func = *funcp;
+ dp->dp_scrub_queue_obj = zap_create(dp->dp_meta_objset,
+ ot ? ot : DMU_OT_SCRUB_QUEUE, DMU_OT_NONE, 0, tx);
+ bzero(&dp->dp_scrub_bookmark, sizeof (zbookmark_t));
+ dp->dp_scrub_restart = B_FALSE;
+ dp->dp_spa->spa_scrub_errors = 0;
+
+ VERIFY(0 == zap_add(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_SCRUB_FUNC, sizeof (uint32_t), 1,
+ &dp->dp_scrub_func, tx));
+ VERIFY(0 == zap_add(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_SCRUB_QUEUE, sizeof (uint64_t), 1,
+ &dp->dp_scrub_queue_obj, tx));
+ VERIFY(0 == zap_add(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_SCRUB_MIN_TXG, sizeof (uint64_t), 1,
+ &dp->dp_scrub_min_txg, tx));
+ VERIFY(0 == zap_add(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_SCRUB_MAX_TXG, sizeof (uint64_t), 1,
+ &dp->dp_scrub_max_txg, tx));
+ VERIFY(0 == zap_add(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_SCRUB_BOOKMARK, sizeof (uint64_t), 4,
+ &dp->dp_scrub_bookmark, tx));
+ VERIFY(0 == zap_add(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_SCRUB_ERRORS, sizeof (uint64_t), 1,
+ &dp->dp_spa->spa_scrub_errors, tx));
+
+ spa_history_internal_log(LOG_POOL_SCRUB, dp->dp_spa, tx, cr,
+ "func=%u mintxg=%llu maxtxg=%llu",
+ *funcp, dp->dp_scrub_min_txg, dp->dp_scrub_max_txg);
+}
+
+int
+dsl_pool_scrub_setup(dsl_pool_t *dp, enum scrub_func func)
+{
+ return (dsl_sync_task_do(dp, NULL,
+ dsl_pool_scrub_setup_sync, dp, &func, 0));
+}
+
+/* ARGSUSED */
+static void
+dsl_pool_scrub_cancel_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
+{
+ dsl_pool_t *dp = arg1;
+ boolean_t *completep = arg2;
+
+ if (dp->dp_scrub_func == SCRUB_FUNC_NONE)
+ return;
+
+ mutex_enter(&dp->dp_scrub_cancel_lock);
+
+ if (dp->dp_scrub_restart) {
+ dp->dp_scrub_restart = B_FALSE;
+ *completep = B_FALSE;
+ }
+
+ /* XXX this is scrub-clean specific */
+ mutex_enter(&dp->dp_spa->spa_scrub_lock);
+ while (dp->dp_spa->spa_scrub_inflight > 0) {
+ cv_wait(&dp->dp_spa->spa_scrub_io_cv,
+ &dp->dp_spa->spa_scrub_lock);
+ }
+ mutex_exit(&dp->dp_spa->spa_scrub_lock);
+ dp->dp_spa->spa_scrub_started = B_FALSE;
+ dp->dp_spa->spa_scrub_active = B_FALSE;
+
+ dp->dp_scrub_func = SCRUB_FUNC_NONE;
+ VERIFY(0 == dmu_object_free(dp->dp_meta_objset,
+ dp->dp_scrub_queue_obj, tx));
+ dp->dp_scrub_queue_obj = 0;
+ bzero(&dp->dp_scrub_bookmark, sizeof (zbookmark_t));
+
+ VERIFY(0 == zap_remove(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_SCRUB_QUEUE, tx));
+ VERIFY(0 == zap_remove(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_SCRUB_MIN_TXG, tx));
+ VERIFY(0 == zap_remove(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_SCRUB_MAX_TXG, tx));
+ VERIFY(0 == zap_remove(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_SCRUB_BOOKMARK, tx));
+ VERIFY(0 == zap_remove(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_SCRUB_FUNC, tx));
+ VERIFY(0 == zap_remove(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_SCRUB_ERRORS, tx));
+
+ spa_history_internal_log(LOG_POOL_SCRUB_DONE, dp->dp_spa, tx, cr,
+ "complete=%u", *completep);
+
+ /* below is scrub-clean specific */
+ vdev_scrub_stat_update(dp->dp_spa->spa_root_vdev, POOL_SCRUB_NONE,
+ *completep);
+ /*
+ * If the scrub/resilver completed, update all DTLs to reflect this.
+ * Whether it succeeded or not, vacate all temporary scrub DTLs.
+ */
+ vdev_dtl_reassess(dp->dp_spa->spa_root_vdev, tx->tx_txg,
+ *completep ? dp->dp_scrub_max_txg : 0, B_TRUE);
+ if (dp->dp_scrub_min_txg && *completep)
+ spa_event_notify(dp->dp_spa, NULL, ESC_ZFS_RESILVER_FINISH);
+ spa_errlog_rotate(dp->dp_spa);
+
+ /*
+ * We may have finished replacing a device.
+ * Let the async thread assess this and handle the detach.
+ */
+ spa_async_request(dp->dp_spa, SPA_ASYNC_RESILVER_DONE);
+
+ dp->dp_scrub_min_txg = dp->dp_scrub_max_txg = 0;
+ mutex_exit(&dp->dp_scrub_cancel_lock);
+}
+
+int
+dsl_pool_scrub_cancel(dsl_pool_t *dp)
+{
+ boolean_t complete = B_FALSE;
+
+ return (dsl_sync_task_do(dp, NULL,
+ dsl_pool_scrub_cancel_sync, dp, &complete, 3));
+}
+
+int
+dsl_free(zio_t *pio, dsl_pool_t *dp, uint64_t txg, const blkptr_t *bpp,
+ zio_done_func_t *done, void *private, uint32_t arc_flags)
+{
+ /*
+ * This function will be used by bp-rewrite wad to intercept frees.
+ */
+ return (arc_free(pio, dp->dp_spa, txg, (blkptr_t *)bpp,
+ done, private, arc_flags));
+}
+
+static boolean_t
+bookmark_is_zero(const zbookmark_t *zb)
+{
+ return (zb->zb_objset == 0 && zb->zb_object == 0 &&
+ zb->zb_level == 0 && zb->zb_blkid == 0);
+}
+
+/* dnp is the dnode for zb1->zb_object */
+static boolean_t
+bookmark_is_before(dnode_phys_t *dnp, const zbookmark_t *zb1,
+ const zbookmark_t *zb2)
+{
+ uint64_t zb1nextL0, zb2thisobj;
+
+ ASSERT(zb1->zb_objset == zb2->zb_objset);
+ ASSERT(zb1->zb_object != -1ULL);
+ ASSERT(zb2->zb_level == 0);
+
+ /*
+ * A bookmark in the deadlist is considered to be after
+ * everything else.
+ */
+ if (zb2->zb_object == -1ULL)
+ return (B_TRUE);
+
+ /* The objset_phys_t isn't before anything. */
+ if (dnp == NULL)
+ return (B_FALSE);
+
+ zb1nextL0 = (zb1->zb_blkid + 1) <<
+ ((zb1->zb_level) * (dnp->dn_indblkshift - SPA_BLKPTRSHIFT));
+
+ zb2thisobj = zb2->zb_object ? zb2->zb_object :
+ zb2->zb_blkid << (DNODE_BLOCK_SHIFT - DNODE_SHIFT);
+
+ if (zb1->zb_object == 0) {
+ uint64_t nextobj = zb1nextL0 *
+ (dnp->dn_datablkszsec << SPA_MINBLOCKSHIFT) >> DNODE_SHIFT;
+ return (nextobj <= zb2thisobj);
+ }
+
+ if (zb1->zb_object < zb2thisobj)
+ return (B_TRUE);
+ if (zb1->zb_object > zb2thisobj)
+ return (B_FALSE);
+ if (zb2->zb_object == 0)
+ return (B_FALSE);
+ return (zb1nextL0 <= zb2->zb_blkid);
+}
+
+static boolean_t
+scrub_pause(dsl_pool_t *dp, const zbookmark_t *zb)
+{
+ int elapsed_ticks;
+ int mintime;
+
+ if (dp->dp_scrub_pausing)
+ return (B_TRUE); /* we're already pausing */
+
+ if (!bookmark_is_zero(&dp->dp_scrub_bookmark))
+ return (B_FALSE); /* we're resuming */
+
+ /* We only know how to resume from level-0 blocks. */
+ if (zb->zb_level != 0)
+ return (B_FALSE);
+
+ mintime = dp->dp_scrub_isresilver ? zfs_resilver_min_time :
+ zfs_scrub_min_time;
+ elapsed_ticks = lbolt64 - dp->dp_scrub_start_time;
+ if (elapsed_ticks > hz * zfs_txg_timeout ||
+ (elapsed_ticks > hz * mintime && txg_sync_waiting(dp))) {
+ dprintf("pausing at %llx/%llx/%llx/%llx\n",
+ (longlong_t)zb->zb_objset, (longlong_t)zb->zb_object,
+ (longlong_t)zb->zb_level, (longlong_t)zb->zb_blkid);
+ dp->dp_scrub_pausing = B_TRUE;
+ dp->dp_scrub_bookmark = *zb;
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
+typedef struct zil_traverse_arg {
+ dsl_pool_t *zta_dp;
+ zil_header_t *zta_zh;
+} zil_traverse_arg_t;
+
+/* ARGSUSED */
+static void
+traverse_zil_block(zilog_t *zilog, blkptr_t *bp, void *arg, uint64_t claim_txg)
+{
+ zil_traverse_arg_t *zta = arg;
+ dsl_pool_t *dp = zta->zta_dp;
+ zil_header_t *zh = zta->zta_zh;
+ zbookmark_t zb;
+
+ if (bp->blk_birth <= dp->dp_scrub_min_txg)
+ return;
+
+ if (claim_txg == 0 && bp->blk_birth >= spa_first_txg(dp->dp_spa))
+ return;
+
+ zb.zb_objset = zh->zh_log.blk_cksum.zc_word[ZIL_ZC_OBJSET];
+ zb.zb_object = 0;
+ zb.zb_level = -1;
+ zb.zb_blkid = bp->blk_cksum.zc_word[ZIL_ZC_SEQ];
+ VERIFY(0 == scrub_funcs[dp->dp_scrub_func](dp, bp, &zb));
+}
+
+/* ARGSUSED */
+static void
+traverse_zil_record(zilog_t *zilog, lr_t *lrc, void *arg, uint64_t claim_txg)
+{
+ if (lrc->lrc_txtype == TX_WRITE) {
+ zil_traverse_arg_t *zta = arg;
+ dsl_pool_t *dp = zta->zta_dp;
+ zil_header_t *zh = zta->zta_zh;
+ lr_write_t *lr = (lr_write_t *)lrc;
+ blkptr_t *bp = &lr->lr_blkptr;
+ zbookmark_t zb;
+
+ if (bp->blk_birth <= dp->dp_scrub_min_txg)
+ return;
+
+ if (claim_txg == 0 || bp->blk_birth < claim_txg)
+ return;
+
+ zb.zb_objset = zh->zh_log.blk_cksum.zc_word[ZIL_ZC_OBJSET];
+ zb.zb_object = lr->lr_foid;
+ zb.zb_level = BP_GET_LEVEL(bp);
+ zb.zb_blkid = lr->lr_offset / BP_GET_LSIZE(bp);
+ VERIFY(0 == scrub_funcs[dp->dp_scrub_func](dp, bp, &zb));
+ }
+}
+
+static void
+traverse_zil(dsl_pool_t *dp, zil_header_t *zh)
+{
+ uint64_t claim_txg = zh->zh_claim_txg;
+ zil_traverse_arg_t zta = { dp, zh };
+ zilog_t *zilog;
+
+ /*
+ * We only want to visit blocks that have been claimed but not yet
+ * replayed (or, in read-only mode, blocks that *would* be claimed).
+ */
+ if (claim_txg == 0 && (spa_mode & FWRITE))
+ return;
+
+ zilog = zil_alloc(dp->dp_meta_objset, zh);
+
+ (void) zil_parse(zilog, traverse_zil_block, traverse_zil_record, &zta,
+ claim_txg);
+
+ zil_free(zilog);
+}
+
+static void
+scrub_visitbp(dsl_pool_t *dp, dnode_phys_t *dnp,
+ arc_buf_t *pbuf, blkptr_t *bp, const zbookmark_t *zb)
+{
+ int err;
+ arc_buf_t *buf = NULL;
+
+ if (bp->blk_birth == 0)
+ return;
+
+ if (bp->blk_birth <= dp->dp_scrub_min_txg)
+ return;
+
+ if (scrub_pause(dp, zb))
+ return;
+
+ if (!bookmark_is_zero(&dp->dp_scrub_bookmark)) {
+ /*
+ * If we already visited this bp & everything below (in
+ * a prior txg), don't bother doing it again.
+ */
+ if (bookmark_is_before(dnp, zb, &dp->dp_scrub_bookmark))
+ return;
+
+ /*
+ * If we found the block we're trying to resume from, or
+ * we went past it to a different object, zero it out to
+ * indicate that it's OK to start checking for pausing
+ * again.
+ */
+ if (bcmp(zb, &dp->dp_scrub_bookmark, sizeof (*zb)) == 0 ||
+ zb->zb_object > dp->dp_scrub_bookmark.zb_object) {
+ dprintf("resuming at %llx/%llx/%llx/%llx\n",
+ (longlong_t)zb->zb_objset,
+ (longlong_t)zb->zb_object,
+ (longlong_t)zb->zb_level,
+ (longlong_t)zb->zb_blkid);
+ bzero(&dp->dp_scrub_bookmark, sizeof (*zb));
+ }
+ }
+
+ if (BP_GET_LEVEL(bp) > 0) {
+ uint32_t flags = ARC_WAIT;
+ int i;
+ blkptr_t *cbp;
+ int epb = BP_GET_LSIZE(bp) >> SPA_BLKPTRSHIFT;
+
+ err = arc_read(NULL, dp->dp_spa, bp, pbuf,
+ arc_getbuf_func, &buf,
+ ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, &flags, zb);
+ if (err) {
+ mutex_enter(&dp->dp_spa->spa_scrub_lock);
+ dp->dp_spa->spa_scrub_errors++;
+ mutex_exit(&dp->dp_spa->spa_scrub_lock);
+ return;
+ }
+ cbp = buf->b_data;
+
+ for (i = 0; i < epb; i++, cbp++) {
+ zbookmark_t czb;
+
+ SET_BOOKMARK(&czb, zb->zb_objset, zb->zb_object,
+ zb->zb_level - 1,
+ zb->zb_blkid * epb + i);
+ scrub_visitbp(dp, dnp, buf, cbp, &czb);
+ }
+ } else if (BP_GET_TYPE(bp) == DMU_OT_DNODE) {
+ uint32_t flags = ARC_WAIT;
+ dnode_phys_t *child_dnp;
+ int i, j;
+ int epb = BP_GET_LSIZE(bp) >> DNODE_SHIFT;
+
+ err = arc_read(NULL, dp->dp_spa, bp, pbuf,
+ arc_getbuf_func, &buf,
+ ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, &flags, zb);
+ if (err) {
+ mutex_enter(&dp->dp_spa->spa_scrub_lock);
+ dp->dp_spa->spa_scrub_errors++;
+ mutex_exit(&dp->dp_spa->spa_scrub_lock);
+ return;
+ }
+ child_dnp = buf->b_data;
+
+ for (i = 0; i < epb; i++, child_dnp++) {
+ for (j = 0; j < child_dnp->dn_nblkptr; j++) {
+ zbookmark_t czb;
+
+ SET_BOOKMARK(&czb, zb->zb_objset,
+ zb->zb_blkid * epb + i,
+ child_dnp->dn_nlevels - 1, j);
+ scrub_visitbp(dp, child_dnp, buf,
+ &child_dnp->dn_blkptr[j], &czb);
+ }
+ }
+ } else if (BP_GET_TYPE(bp) == DMU_OT_OBJSET) {
+ uint32_t flags = ARC_WAIT;
+ objset_phys_t *osp;
+ int j;
+
+ err = arc_read_nolock(NULL, dp->dp_spa, bp,
+ arc_getbuf_func, &buf,
+ ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, &flags, zb);
+ if (err) {
+ mutex_enter(&dp->dp_spa->spa_scrub_lock);
+ dp->dp_spa->spa_scrub_errors++;
+ mutex_exit(&dp->dp_spa->spa_scrub_lock);
+ return;
+ }
+
+ osp = buf->b_data;
+
+ traverse_zil(dp, &osp->os_zil_header);
+
+ for (j = 0; j < osp->os_meta_dnode.dn_nblkptr; j++) {
+ zbookmark_t czb;
+
+ SET_BOOKMARK(&czb, zb->zb_objset, 0,
+ osp->os_meta_dnode.dn_nlevels - 1, j);
+ scrub_visitbp(dp, &osp->os_meta_dnode, buf,
+ &osp->os_meta_dnode.dn_blkptr[j], &czb);
+ }
+ }
+
+ (void) scrub_funcs[dp->dp_scrub_func](dp, bp, zb);
+ if (buf)
+ (void) arc_buf_remove_ref(buf, &buf);
+}
+
+static void
+scrub_visit_rootbp(dsl_pool_t *dp, dsl_dataset_t *ds, blkptr_t *bp)
+{
+ zbookmark_t zb;
+
+ SET_BOOKMARK(&zb, ds ? ds->ds_object : 0, 0, -1, 0);
+ scrub_visitbp(dp, NULL, NULL, bp, &zb);
+}
+
+void
+dsl_pool_ds_destroyed(dsl_dataset_t *ds, dmu_tx_t *tx)
+{
+ dsl_pool_t *dp = ds->ds_dir->dd_pool;
+
+ if (dp->dp_scrub_func == SCRUB_FUNC_NONE)
+ return;
+
+ if (dp->dp_scrub_bookmark.zb_objset == ds->ds_object) {
+ SET_BOOKMARK(&dp->dp_scrub_bookmark, -1, 0, 0, 0);
+ } else if (zap_remove_int(dp->dp_meta_objset, dp->dp_scrub_queue_obj,
+ ds->ds_object, tx) != 0) {
+ return;
+ }
+
+ if (ds->ds_phys->ds_next_snap_obj != 0) {
+ VERIFY(zap_add_int(dp->dp_meta_objset, dp->dp_scrub_queue_obj,
+ ds->ds_phys->ds_next_snap_obj, tx) == 0);
+ }
+ ASSERT3U(ds->ds_phys->ds_num_children, <=, 1);
+}
+
+void
+dsl_pool_ds_snapshotted(dsl_dataset_t *ds, dmu_tx_t *tx)
+{
+ dsl_pool_t *dp = ds->ds_dir->dd_pool;
+
+ if (dp->dp_scrub_func == SCRUB_FUNC_NONE)
+ return;
+
+ ASSERT(ds->ds_phys->ds_prev_snap_obj != 0);
+
+ if (dp->dp_scrub_bookmark.zb_objset == ds->ds_object) {
+ dp->dp_scrub_bookmark.zb_objset =
+ ds->ds_phys->ds_prev_snap_obj;
+ } else if (zap_remove_int(dp->dp_meta_objset, dp->dp_scrub_queue_obj,
+ ds->ds_object, tx) == 0) {
+ VERIFY(zap_add_int(dp->dp_meta_objset, dp->dp_scrub_queue_obj,
+ ds->ds_phys->ds_prev_snap_obj, tx) == 0);
+ }
+}
+
+struct enqueue_clones_arg {
+ dmu_tx_t *tx;
+ uint64_t originobj;
+};
+
+/* ARGSUSED */
+static int
+enqueue_clones_cb(spa_t *spa, uint64_t dsobj, const char *dsname, void *arg)
+{
+ struct enqueue_clones_arg *eca = arg;
+ dsl_dataset_t *ds;
+ int err;
+ dsl_pool_t *dp;
+
+ err = dsl_dataset_hold_obj(spa->spa_dsl_pool, dsobj, FTAG, &ds);
+ if (err)
+ return (err);
+ dp = ds->ds_dir->dd_pool;
+
+ if (ds->ds_dir->dd_phys->dd_origin_obj == eca->originobj) {
+ while (ds->ds_phys->ds_prev_snap_obj != eca->originobj) {
+ dsl_dataset_t *prev;
+ err = dsl_dataset_hold_obj(dp,
+ ds->ds_phys->ds_prev_snap_obj, FTAG, &prev);
+
+ dsl_dataset_rele(ds, FTAG);
+ if (err)
+ return (err);
+ ds = prev;
+ }
+ VERIFY(zap_add_int(dp->dp_meta_objset, dp->dp_scrub_queue_obj,
+ ds->ds_object, eca->tx) == 0);
+ }
+ dsl_dataset_rele(ds, FTAG);
+ return (0);
+}
+
+static void
+scrub_visitds(dsl_pool_t *dp, uint64_t dsobj, dmu_tx_t *tx)
+{
+ dsl_dataset_t *ds;
+ uint64_t min_txg_save;
+
+ VERIFY3U(0, ==, dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds));
+
+ /*
+ * Iterate over the bps in this ds.
+ */
+ min_txg_save = dp->dp_scrub_min_txg;
+ dp->dp_scrub_min_txg =
+ MAX(dp->dp_scrub_min_txg, ds->ds_phys->ds_prev_snap_txg);
+ scrub_visit_rootbp(dp, ds, &ds->ds_phys->ds_bp);
+ dp->dp_scrub_min_txg = min_txg_save;
+
+ if (dp->dp_scrub_pausing)
+ goto out;
+
+ /*
+ * Add descendent datasets to work queue.
+ */
+ if (ds->ds_phys->ds_next_snap_obj != 0) {
+ VERIFY(zap_add_int(dp->dp_meta_objset, dp->dp_scrub_queue_obj,
+ ds->ds_phys->ds_next_snap_obj, tx) == 0);
+ }
+ if (ds->ds_phys->ds_num_children > 1) {
+ if (spa_version(dp->dp_spa) < SPA_VERSION_DSL_SCRUB) {
+ struct enqueue_clones_arg eca;
+ eca.tx = tx;
+ eca.originobj = ds->ds_object;
+
+ (void) dmu_objset_find_spa(ds->ds_dir->dd_pool->dp_spa,
+ NULL, enqueue_clones_cb, &eca, DS_FIND_CHILDREN);
+ } else {
+ VERIFY(zap_join(dp->dp_meta_objset,
+ ds->ds_phys->ds_next_clones_obj,
+ dp->dp_scrub_queue_obj, tx) == 0);
+ }
+ }
+
+out:
+ dsl_dataset_rele(ds, FTAG);
+}
+
+/* ARGSUSED */
+static int
+enqueue_cb(spa_t *spa, uint64_t dsobj, const char *dsname, void *arg)
+{
+ dmu_tx_t *tx = arg;
+ dsl_dataset_t *ds;
+ int err;
+ dsl_pool_t *dp;
+
+ err = dsl_dataset_hold_obj(spa->spa_dsl_pool, dsobj, FTAG, &ds);
+ if (err)
+ return (err);
+
+ dp = ds->ds_dir->dd_pool;
+
+ while (ds->ds_phys->ds_prev_snap_obj != 0) {
+ dsl_dataset_t *prev;
+ err = dsl_dataset_hold_obj(dp, ds->ds_phys->ds_prev_snap_obj,
+ FTAG, &prev);
+ if (err) {
+ dsl_dataset_rele(ds, FTAG);
+ return (err);
+ }
+
+ /*
+ * If this is a clone, we don't need to worry about it for now.
+ */
+ if (prev->ds_phys->ds_next_snap_obj != ds->ds_object) {
+ dsl_dataset_rele(ds, FTAG);
+ dsl_dataset_rele(prev, FTAG);
+ return (0);
+ }
+ dsl_dataset_rele(ds, FTAG);
+ ds = prev;
+ }
+
+ VERIFY(zap_add_int(dp->dp_meta_objset, dp->dp_scrub_queue_obj,
+ ds->ds_object, tx) == 0);
+ dsl_dataset_rele(ds, FTAG);
+ return (0);
+}
+
+void
+dsl_pool_scrub_sync(dsl_pool_t *dp, dmu_tx_t *tx)
+{
+ zap_cursor_t zc;
+ zap_attribute_t za;
+ boolean_t complete = B_TRUE;
+
+ if (dp->dp_scrub_func == SCRUB_FUNC_NONE)
+ return;
+
+ /* If the spa is not fully loaded, don't bother. */
+ if (dp->dp_spa->spa_load_state != SPA_LOAD_NONE)
+ return;
+
+ if (dp->dp_scrub_restart) {
+ enum scrub_func func = dp->dp_scrub_func;
+ dp->dp_scrub_restart = B_FALSE;
+ dsl_pool_scrub_setup_sync(dp, &func, kcred, tx);
+ }
+
+ if (dp->dp_spa->spa_root_vdev->vdev_stat.vs_scrub_type == 0) {
+ /*
+ * We must have resumed after rebooting; reset the vdev
+ * stats to know that we're doing a scrub (although it
+ * will think we're just starting now).
+ */
+ vdev_scrub_stat_update(dp->dp_spa->spa_root_vdev,
+ dp->dp_scrub_min_txg ? POOL_SCRUB_RESILVER :
+ POOL_SCRUB_EVERYTHING, B_FALSE);
+ }
+
+ dp->dp_scrub_pausing = B_FALSE;
+ dp->dp_scrub_start_time = lbolt64;
+ dp->dp_scrub_isresilver = (dp->dp_scrub_min_txg != 0);
+ dp->dp_spa->spa_scrub_active = B_TRUE;
+
+ if (dp->dp_scrub_bookmark.zb_objset == 0) {
+ /* First do the MOS & ORIGIN */
+ scrub_visit_rootbp(dp, NULL, &dp->dp_meta_rootbp);
+ if (dp->dp_scrub_pausing)
+ goto out;
+
+ if (spa_version(dp->dp_spa) < SPA_VERSION_DSL_SCRUB) {
+ VERIFY(0 == dmu_objset_find_spa(dp->dp_spa,
+ NULL, enqueue_cb, tx, DS_FIND_CHILDREN));
+ } else {
+ scrub_visitds(dp, dp->dp_origin_snap->ds_object, tx);
+ }
+ ASSERT(!dp->dp_scrub_pausing);
+ } else if (dp->dp_scrub_bookmark.zb_objset != -1ULL) {
+ /*
+ * If we were paused, continue from here. Note if the
+ * ds we were paused on was deleted, the zb_objset will
+ * be -1, so we will skip this and find a new objset
+ * below.
+ */
+ scrub_visitds(dp, dp->dp_scrub_bookmark.zb_objset, tx);
+ if (dp->dp_scrub_pausing)
+ goto out;
+ }
+
+ /*
+ * In case we were paused right at the end of the ds, zero the
+ * bookmark so we don't think that we're still trying to resume.
+ */
+ bzero(&dp->dp_scrub_bookmark, sizeof (zbookmark_t));
+
+ /* keep pulling things out of the zap-object-as-queue */
+ while (zap_cursor_init(&zc, dp->dp_meta_objset, dp->dp_scrub_queue_obj),
+ zap_cursor_retrieve(&zc, &za) == 0) {
+ VERIFY(0 == zap_remove(dp->dp_meta_objset,
+ dp->dp_scrub_queue_obj, za.za_name, tx));
+ scrub_visitds(dp, za.za_first_integer, tx);
+ if (dp->dp_scrub_pausing)
+ break;
+ zap_cursor_fini(&zc);
+ }
+ zap_cursor_fini(&zc);
+ if (dp->dp_scrub_pausing)
+ goto out;
+
+ /* done. */
+
+ dsl_pool_scrub_cancel_sync(dp, &complete, kcred, tx);
+ return;
+out:
+ VERIFY(0 == zap_update(dp->dp_meta_objset,
+ DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_SCRUB_BOOKMARK, sizeof (uint64_t), 4,
+ &dp->dp_scrub_bookmark, tx));
+ VERIFY(0 == zap_update(dp->dp_meta_objset,
+ DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_SCRUB_ERRORS, sizeof (uint64_t), 1,
+ &dp->dp_spa->spa_scrub_errors, tx));
+
+ /* XXX this is scrub-clean specific */
+ mutex_enter(&dp->dp_spa->spa_scrub_lock);
+ while (dp->dp_spa->spa_scrub_inflight > 0) {
+ cv_wait(&dp->dp_spa->spa_scrub_io_cv,
+ &dp->dp_spa->spa_scrub_lock);
+ }
+ mutex_exit(&dp->dp_spa->spa_scrub_lock);
+}
+
+void
+dsl_pool_scrub_restart(dsl_pool_t *dp)
+{
+ mutex_enter(&dp->dp_scrub_cancel_lock);
+ dp->dp_scrub_restart = B_TRUE;
+ mutex_exit(&dp->dp_scrub_cancel_lock);
+}
+
+/*
+ * scrub consumers
+ */
+
+static void
+dsl_pool_scrub_clean_done(zio_t *zio)
+{
+ spa_t *spa = zio->io_spa;
+
+ zio_data_buf_free(zio->io_data, zio->io_size);
+
+ mutex_enter(&spa->spa_scrub_lock);
+ spa->spa_scrub_inflight--;
+ cv_broadcast(&spa->spa_scrub_io_cv);
+
+ if (zio->io_error && (zio->io_error != ECKSUM ||
+ !(zio->io_flags & ZIO_FLAG_SPECULATIVE)))
+ spa->spa_scrub_errors++;
+ mutex_exit(&spa->spa_scrub_lock);
+}
+
+static int
+dsl_pool_scrub_clean_cb(dsl_pool_t *dp,
+ const blkptr_t *bp, const zbookmark_t *zb)
+{
+ size_t size = BP_GET_LSIZE(bp);
+ int d;
+ spa_t *spa = dp->dp_spa;
+ boolean_t needs_io;
+ int zio_flags = ZIO_FLAG_SCRUB_THREAD | ZIO_FLAG_CANFAIL;
+ int zio_priority;
+
+ if (dp->dp_scrub_isresilver == 0) {
+ /* It's a scrub */
+ zio_flags |= ZIO_FLAG_SCRUB;
+ zio_priority = ZIO_PRIORITY_SCRUB;
+ needs_io = B_TRUE;
+ } else {
+ /* It's a resilver */
+ zio_flags |= ZIO_FLAG_RESILVER;
+ zio_priority = ZIO_PRIORITY_RESILVER;
+ needs_io = B_FALSE;
+ }
+
+ /* If it's an intent log block, failure is expected. */
+ if (zb->zb_level == -1 && BP_GET_TYPE(bp) != DMU_OT_OBJSET)
+ zio_flags |= ZIO_FLAG_SPECULATIVE;
+
+ for (d = 0; d < BP_GET_NDVAS(bp); d++) {
+ vdev_t *vd = vdev_lookup_top(spa,
+ DVA_GET_VDEV(&bp->blk_dva[d]));
+
+ /*
+ * Keep track of how much data we've examined so that
+ * zpool(1M) status can make useful progress reports.
+ */
+ mutex_enter(&vd->vdev_stat_lock);
+ vd->vdev_stat.vs_scrub_examined +=
+ DVA_GET_ASIZE(&bp->blk_dva[d]);
+ mutex_exit(&vd->vdev_stat_lock);
+
+ /* if it's a resilver, this may not be in the target range */
+ if (!needs_io) {
+ if (DVA_GET_GANG(&bp->blk_dva[d])) {
+ /*
+ * Gang members may be spread across multiple
+ * vdevs, so the best we can do is look at the
+ * pool-wide DTL.
+ * XXX -- it would be better to change our
+ * allocation policy to ensure that this can't
+ * happen.
+ */
+ vd = spa->spa_root_vdev;
+ }
+ needs_io = vdev_dtl_contains(&vd->vdev_dtl_map,
+ bp->blk_birth, 1);
+ }
+ }
+
+ if (needs_io && !zfs_no_scrub_io) {
+ void *data = zio_data_buf_alloc(size);
+
+ mutex_enter(&spa->spa_scrub_lock);
+ while (spa->spa_scrub_inflight >= spa->spa_scrub_maxinflight)
+ cv_wait(&spa->spa_scrub_io_cv, &spa->spa_scrub_lock);
+ spa->spa_scrub_inflight++;
+ mutex_exit(&spa->spa_scrub_lock);
+
+ zio_nowait(zio_read(NULL, spa, bp, data, size,
+ dsl_pool_scrub_clean_done, NULL, zio_priority,
+ zio_flags, zb));
+ }
+
+ /* do not relocate this block */
+ return (0);
+}
+
+int
+dsl_pool_scrub_clean(dsl_pool_t *dp)
+{
+ /*
+ * Purge all vdev caches. We do this here rather than in sync
+ * context because this requires a writer lock on the spa_config
+ * lock, which we can't do from sync context. The
+ * spa_scrub_reopen flag indicates that vdev_open() should not
+ * attempt to start another scrub.
+ */
+ spa_config_enter(dp->dp_spa, SCL_ALL, FTAG, RW_WRITER);
+ dp->dp_spa->spa_scrub_reopen = B_TRUE;
+ vdev_reopen(dp->dp_spa->spa_root_vdev);
+ dp->dp_spa->spa_scrub_reopen = B_FALSE;
+ spa_config_exit(dp->dp_spa, SCL_ALL, FTAG);
+
+ return (dsl_pool_scrub_setup(dp, SCRUB_FUNC_CLEAN));
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_synctask.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_synctask.c
index 17deb56..2110022 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_synctask.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_synctask.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -30,6 +30,7 @@
#include <sys/dsl_pool.h>
#include <sys/dsl_dir.h>
#include <sys/dsl_synctask.h>
+#include <sys/cred.h>
#define DST_AVG_BLKSHIFT 14
@@ -49,6 +50,7 @@ dsl_sync_task_group_create(dsl_pool_t *dp)
list_create(&dstg->dstg_tasks, sizeof (dsl_sync_task_t),
offsetof(dsl_sync_task_t, dst_node));
dstg->dstg_pool = dp;
+ dstg->dstg_cr = CRED();
return (dstg);
}
@@ -123,6 +125,16 @@ top:
}
void
+dsl_sync_task_group_nowait(dsl_sync_task_group_t *dstg, dmu_tx_t *tx)
+{
+ uint64_t txg;
+
+ dstg->dstg_nowaiter = B_TRUE;
+ txg = dmu_tx_get_txg(tx);
+ VERIFY(0 == txg_list_add(&dstg->dstg_pool->dp_sync_tasks, dstg, txg));
+}
+
+void
dsl_sync_task_group_destroy(dsl_sync_task_group_t *dstg)
{
dsl_sync_task_t *dst;
@@ -146,7 +158,7 @@ dsl_sync_task_group_sync(dsl_sync_task_group_t *dstg, dmu_tx_t *tx)
* Check for sufficient space.
*/
dstg->dstg_err = dsl_dir_tempreserve_space(dstg->dstg_pool->dp_mos_dir,
- dstg->dstg_space, dstg->dstg_space * 3, 0, &tr_cookie, tx);
+ dstg->dstg_space, dstg->dstg_space * 3, 0, 0, &tr_cookie, tx);
/* don't bother trying again */
if (dstg->dstg_err == ERESTART)
dstg->dstg_err = EAGAIN;
@@ -171,12 +183,16 @@ dsl_sync_task_group_sync(dsl_sync_task_group_t *dstg, dmu_tx_t *tx)
*/
for (dst = list_head(&dstg->dstg_tasks); dst;
dst = list_next(&dstg->dstg_tasks, dst)) {
- dst->dst_syncfunc(dst->dst_arg1, dst->dst_arg2, tx);
+ dst->dst_syncfunc(dst->dst_arg1, dst->dst_arg2,
+ dstg->dstg_cr, tx);
}
}
rw_exit(&dstg->dstg_pool->dp_config_rwlock);
dsl_dir_tempreserve_clear(tr_cookie, tx);
+
+ if (dstg->dstg_nowaiter)
+ dsl_sync_task_group_destroy(dstg);
}
int
@@ -194,3 +210,16 @@ dsl_sync_task_do(dsl_pool_t *dp,
dsl_sync_task_group_destroy(dstg);
return (err);
}
+
+void
+dsl_sync_task_do_nowait(dsl_pool_t *dp,
+ dsl_checkfunc_t *checkfunc, dsl_syncfunc_t *syncfunc,
+ void *arg1, void *arg2, int blocks_modified, dmu_tx_t *tx)
+{
+ dsl_sync_task_group_t *dstg;
+
+ dstg = dsl_sync_task_group_create(dp);
+ dsl_sync_task_create(dstg, checkfunc, syncfunc,
+ arg1, arg2, blocks_modified);
+ dsl_sync_task_group_nowait(dstg, tx);
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/metaslab.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/metaslab.c
index 0dba134..22b56d6 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/metaslab.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/metaslab.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/zfs_context.h>
#include <sys/spa_impl.h>
#include <sys/dmu.h>
@@ -35,6 +33,7 @@
#include <sys/zio.h>
uint64_t metaslab_aliquot = 512ULL << 10;
+uint64_t metaslab_gang_bang = SPA_MAXBLOCKSIZE + 1; /* force gang blocks */
/*
* ==========================================================================
@@ -341,7 +340,7 @@ metaslab_fini(metaslab_t *msp)
int t;
vdev_space_update(mg->mg_vd, -msp->ms_map.sm_size,
- -msp->ms_smo.smo_alloc);
+ -msp->ms_smo.smo_alloc, B_TRUE);
metaslab_group_remove(mg, msp);
@@ -534,8 +533,8 @@ metaslab_sync(metaslab_t *msp, uint64_t txg)
VERIFY(0 == dmu_bonus_hold(mos, smo->smo_object, FTAG, &db));
dmu_buf_will_dirty(db, tx);
- ASSERT3U(db->db_size, ==, sizeof (*smo));
- bcopy(smo, db->db_data, db->db_size);
+ ASSERT3U(db->db_size, >=, sizeof (*smo));
+ bcopy(smo, db->db_data, sizeof (*smo));
dmu_buf_rele(db, FTAG);
dmu_tx_commit(tx);
@@ -569,10 +568,10 @@ metaslab_sync_done(metaslab_t *msp, uint64_t txg)
space_map_create(&msp->ms_freemap[t], sm->sm_start,
sm->sm_size, sm->sm_shift, sm->sm_lock);
}
- vdev_space_update(vd, sm->sm_size, 0);
+ vdev_space_update(vd, sm->sm_size, 0, B_TRUE);
}
- vdev_space_update(vd, 0, smosync->smo_alloc - smo->smo_alloc);
+ vdev_space_update(vd, 0, smosync->smo_alloc - smo->smo_alloc, B_TRUE);
ASSERT(msp->ms_allocmap[txg & TXG_MASK].sm_space == 0);
ASSERT(msp->ms_freemap[txg & TXG_MASK].sm_space == 0);
@@ -714,11 +713,10 @@ metaslab_group_alloc(metaslab_group_t *mg, uint64_t size, uint64_t txg,
* Allocate a block for the specified i/o.
*/
static int
-metaslab_alloc_dva(spa_t *spa, uint64_t psize, dva_t *dva, int d,
- dva_t *hintdva, uint64_t txg, boolean_t hintdva_avoid)
+metaslab_alloc_dva(spa_t *spa, metaslab_class_t *mc, uint64_t psize,
+ dva_t *dva, int d, dva_t *hintdva, uint64_t txg, int flags)
{
metaslab_group_t *mg, *rotor;
- metaslab_class_t *mc;
vdev_t *vd;
int dshift = 3;
int all_zero;
@@ -728,7 +726,11 @@ metaslab_alloc_dva(spa_t *spa, uint64_t psize, dva_t *dva, int d,
ASSERT(!DVA_IS_VALID(&dva[d]));
- mc = spa_metaslab_class_select(spa);
+ /*
+ * For testing, make some blocks above a certain size be gang blocks.
+ */
+ if (psize >= metaslab_gang_bang && (LBOLT & 3) == 0)
+ return (ENOSPC);
/*
* Start at the rotor and loop through all mgs until we find something.
@@ -754,7 +756,7 @@ metaslab_alloc_dva(spa_t *spa, uint64_t psize, dva_t *dva, int d,
*/
if (hintdva) {
vd = vdev_lookup_top(spa, DVA_GET_VDEV(&hintdva[d]));
- if (hintdva_avoid)
+ if (flags & METASLAB_HINTBP_AVOID)
mg = vd->vdev_mg->mg_next;
else
mg = vd->vdev_mg;
@@ -764,12 +766,34 @@ metaslab_alloc_dva(spa_t *spa, uint64_t psize, dva_t *dva, int d,
} else {
mg = mc->mc_rotor;
}
- rotor = mg;
+ /*
+ * If the hint put us into the wrong class, just follow the rotor.
+ */
+ if (mg->mg_class != mc)
+ mg = mc->mc_rotor;
+
+ rotor = mg;
top:
all_zero = B_TRUE;
do {
vd = mg->mg_vd;
+ /*
+ * Don't allocate from faulted devices.
+ */
+ if (!vdev_writeable(vd))
+ goto next;
+ /*
+ * Avoid writing single-copy data to a failing vdev
+ */
+ if ((vd->vdev_stat.vs_write_errors > 0 ||
+ vd->vdev_state < VDEV_STATE_HEALTHY) &&
+ d == 0 && dshift == 3) {
+ all_zero = B_FALSE;
+ goto next;
+ }
+
+ ASSERT(mg->mg_class == mc);
distance = vd->vdev_asize >> dshift;
if (distance <= (1ULL << vd->vdev_ms_shift))
@@ -818,11 +842,12 @@ top:
DVA_SET_VDEV(&dva[d], vd->vdev_id);
DVA_SET_OFFSET(&dva[d], offset);
- DVA_SET_GANG(&dva[d], 0);
+ DVA_SET_GANG(&dva[d], !!(flags & METASLAB_GANG_HEADER));
DVA_SET_ASIZE(&dva[d], asize);
return (0);
}
+next:
mc->mc_rotor = mg->mg_next;
mc->mc_allocated = 0;
} while ((mg = mg->mg_next) != rotor);
@@ -879,38 +904,6 @@ metaslab_free_dva(spa_t *spa, const dva_t *dva, uint64_t txg, boolean_t now)
if (msp->ms_freemap[txg & TXG_MASK].sm_space == 0)
vdev_dirty(vd, VDD_METASLAB, msp, txg);
space_map_add(&msp->ms_freemap[txg & TXG_MASK], offset, size);
-
- /*
- * verify that this region is actually allocated in
- * either a ms_allocmap or the ms_map
- */
- if (msp->ms_map.sm_loaded) {
- boolean_t allocd = B_FALSE;
- int i;
-
- if (!space_map_contains(&msp->ms_map, offset, size)) {
- allocd = B_TRUE;
- } else {
- for (i = 0; i < TXG_CONCURRENT_STATES; i++) {
- space_map_t *sm = &msp->ms_allocmap
- [(txg - i) & TXG_MASK];
- if (space_map_contains(sm,
- offset, size)) {
- allocd = B_TRUE;
- break;
- }
- }
- }
-
- if (!allocd) {
- zfs_panic_recover("freeing free segment "
- "(vdev=%llu offset=%llx size=%llx)",
- (longlong_t)vdev, (longlong_t)offset,
- (longlong_t)size);
- }
- }
-
-
}
mutex_exit(&msp->ms_lock);
@@ -946,16 +939,18 @@ metaslab_claim_dva(spa_t *spa, const dva_t *dva, uint64_t txg)
mutex_enter(&msp->ms_lock);
error = metaslab_activate(msp, METASLAB_WEIGHT_SECONDARY);
- if (error) {
+ if (error || txg == 0) { /* txg == 0 indicates dry run */
mutex_exit(&msp->ms_lock);
return (error);
}
- if (msp->ms_allocmap[txg & TXG_MASK].sm_space == 0)
- vdev_dirty(vd, VDD_METASLAB, msp, txg);
-
space_map_claim(&msp->ms_map, offset, size);
- space_map_add(&msp->ms_allocmap[txg & TXG_MASK], offset, size);
+
+ if (spa_mode & FWRITE) { /* don't dirty if we're zdb(1M) */
+ if (msp->ms_allocmap[txg & TXG_MASK].sm_space == 0)
+ vdev_dirty(vd, VDD_METASLAB, msp, txg);
+ space_map_add(&msp->ms_allocmap[txg & TXG_MASK], offset, size);
+ }
mutex_exit(&msp->ms_lock);
@@ -963,32 +958,45 @@ metaslab_claim_dva(spa_t *spa, const dva_t *dva, uint64_t txg)
}
int
-metaslab_alloc(spa_t *spa, uint64_t psize, blkptr_t *bp, int ndvas,
- uint64_t txg, blkptr_t *hintbp, boolean_t hintbp_avoid)
+metaslab_alloc(spa_t *spa, metaslab_class_t *mc, uint64_t psize, blkptr_t *bp,
+ int ndvas, uint64_t txg, blkptr_t *hintbp, int flags)
{
dva_t *dva = bp->blk_dva;
dva_t *hintdva = hintbp->blk_dva;
- int d;
int error = 0;
+ ASSERT(bp->blk_birth == 0);
+
+ spa_config_enter(spa, SCL_ALLOC, FTAG, RW_READER);
+
+ if (mc->mc_rotor == NULL) { /* no vdevs in this class */
+ spa_config_exit(spa, SCL_ALLOC, FTAG);
+ return (ENOSPC);
+ }
+
ASSERT(ndvas > 0 && ndvas <= spa_max_replication(spa));
ASSERT(BP_GET_NDVAS(bp) == 0);
ASSERT(hintbp == NULL || ndvas <= BP_GET_NDVAS(hintbp));
- for (d = 0; d < ndvas; d++) {
- error = metaslab_alloc_dva(spa, psize, dva, d, hintdva,
- txg, hintbp_avoid);
+ for (int d = 0; d < ndvas; d++) {
+ error = metaslab_alloc_dva(spa, mc, psize, dva, d, hintdva,
+ txg, flags);
if (error) {
for (d--; d >= 0; d--) {
metaslab_free_dva(spa, &dva[d], txg, B_TRUE);
bzero(&dva[d], sizeof (dva_t));
}
+ spa_config_exit(spa, SCL_ALLOC, FTAG);
return (error);
}
}
ASSERT(error == 0);
ASSERT(BP_GET_NDVAS(bp) == ndvas);
+ spa_config_exit(spa, SCL_ALLOC, FTAG);
+
+ bp->blk_birth = txg;
+
return (0);
}
@@ -997,12 +1005,16 @@ metaslab_free(spa_t *spa, const blkptr_t *bp, uint64_t txg, boolean_t now)
{
const dva_t *dva = bp->blk_dva;
int ndvas = BP_GET_NDVAS(bp);
- int d;
ASSERT(!BP_IS_HOLE(bp));
+ ASSERT(!now || bp->blk_birth >= spa->spa_syncing_txg);
+
+ spa_config_enter(spa, SCL_FREE, FTAG, RW_READER);
- for (d = 0; d < ndvas; d++)
+ for (int d = 0; d < ndvas; d++)
metaslab_free_dva(spa, &dva[d], txg, now);
+
+ spa_config_exit(spa, SCL_FREE, FTAG);
}
int
@@ -1010,14 +1022,28 @@ metaslab_claim(spa_t *spa, const blkptr_t *bp, uint64_t txg)
{
const dva_t *dva = bp->blk_dva;
int ndvas = BP_GET_NDVAS(bp);
- int d, error;
- int last_error = 0;
+ int error = 0;
ASSERT(!BP_IS_HOLE(bp));
- for (d = 0; d < ndvas; d++)
+ if (txg != 0) {
+ /*
+ * First do a dry run to make sure all DVAs are claimable,
+ * so we don't have to unwind from partial failures below.
+ */
+ if ((error = metaslab_claim(spa, bp, 0)) != 0)
+ return (error);
+ }
+
+ spa_config_enter(spa, SCL_ALLOC, FTAG, RW_READER);
+
+ for (int d = 0; d < ndvas; d++)
if ((error = metaslab_claim_dva(spa, &dva[d], txg)) != 0)
- last_error = error;
+ break;
+
+ spa_config_exit(spa, SCL_ALLOC, FTAG);
+
+ ASSERT(error == 0 || txg == 0);
- return (last_error);
+ return (error);
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/refcount.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/refcount.c
index a2f4614..5fe4e63 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/refcount.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/refcount.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -61,11 +60,13 @@ refcount_fini(void)
void
refcount_create(refcount_t *rc)
{
+ mutex_init(&rc->rc_mtx, NULL, MUTEX_DEFAULT, NULL);
list_create(&rc->rc_list, sizeof (reference_t),
offsetof(reference_t, ref_link));
list_create(&rc->rc_removed, sizeof (reference_t),
offsetof(reference_t, ref_link));
- mutex_init(&rc->rc_mtx, NULL, MUTEX_DEFAULT, NULL);
+ rc->rc_count = 0;
+ rc->rc_removed_count = 0;
}
void
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/rrwlock.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/rrwlock.c
new file mode 100644
index 0000000..db3b70f
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/rrwlock.c
@@ -0,0 +1,249 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/refcount.h>
+#include <sys/rrwlock.h>
+
+/*
+ * This file contains the implementation of a re-entrant read
+ * reader/writer lock (aka "rrwlock").
+ *
+ * This is a normal reader/writer lock with the additional feature
+ * of allowing threads who have already obtained a read lock to
+ * re-enter another read lock (re-entrant read) - even if there are
+ * waiting writers.
+ *
+ * Callers who have not obtained a read lock give waiting writers priority.
+ *
+ * The rrwlock_t lock does not allow re-entrant writers, nor does it
+ * allow a re-entrant mix of reads and writes (that is, it does not
+ * allow a caller who has already obtained a read lock to be able to
+ * then grab a write lock without first dropping all read locks, and
+ * vice versa).
+ *
+ * The rrwlock_t uses tsd (thread specific data) to keep a list of
+ * nodes (rrw_node_t), where each node keeps track of which specific
+ * lock (rrw_node_t::rn_rrl) the thread has grabbed. Since re-entering
+ * should be rare, a thread that grabs multiple reads on the same rrwlock_t
+ * will store multiple rrw_node_ts of the same 'rrn_rrl'. Nodes on the
+ * tsd list can represent a different rrwlock_t. This allows a thread
+ * to enter multiple and unique rrwlock_ts for read locks at the same time.
+ *
+ * Since using tsd exposes some overhead, the rrwlock_t only needs to
+ * keep tsd data when writers are waiting. If no writers are waiting, then
+ * a reader just bumps the anonymous read count (rr_anon_rcount) - no tsd
+ * is needed. Once a writer attempts to grab the lock, readers then
+ * keep tsd data and bump the linked readers count (rr_linked_rcount).
+ *
+ * If there are waiting writers and there are anonymous readers, then a
+ * reader doesn't know if it is a re-entrant lock. But since it may be one,
+ * we allow the read to proceed (otherwise it could deadlock). Since once
+ * waiting writers are active, readers no longer bump the anonymous count,
+ * the anonymous readers will eventually flush themselves out. At this point,
+ * readers will be able to tell if they are a re-entrant lock (have a
+ * rrw_node_t entry for the lock) or not. If they are a re-entrant lock, then
+ * we must let the proceed. If they are not, then the reader blocks for the
+ * waiting writers. Hence, we do not starve writers.
+ */
+
+/* global key for TSD */
+uint_t rrw_tsd_key;
+
+typedef struct rrw_node {
+ struct rrw_node *rn_next;
+ rrwlock_t *rn_rrl;
+} rrw_node_t;
+
+static rrw_node_t *
+rrn_find(rrwlock_t *rrl)
+{
+ rrw_node_t *rn;
+
+ if (refcount_count(&rrl->rr_linked_rcount) == 0)
+ return (NULL);
+
+ for (rn = tsd_get(rrw_tsd_key); rn != NULL; rn = rn->rn_next) {
+ if (rn->rn_rrl == rrl)
+ return (rn);
+ }
+ return (NULL);
+}
+
+/*
+ * Add a node to the head of the singly linked list.
+ */
+static void
+rrn_add(rrwlock_t *rrl)
+{
+ rrw_node_t *rn;
+
+ rn = kmem_alloc(sizeof (*rn), KM_SLEEP);
+ rn->rn_rrl = rrl;
+ rn->rn_next = tsd_get(rrw_tsd_key);
+ VERIFY(tsd_set(rrw_tsd_key, rn) == 0);
+}
+
+/*
+ * If a node is found for 'rrl', then remove the node from this
+ * thread's list and return TRUE; otherwise return FALSE.
+ */
+static boolean_t
+rrn_find_and_remove(rrwlock_t *rrl)
+{
+ rrw_node_t *rn;
+ rrw_node_t *prev = NULL;
+
+ if (refcount_count(&rrl->rr_linked_rcount) == 0)
+ return (B_FALSE);
+
+ for (rn = tsd_get(rrw_tsd_key); rn != NULL; rn = rn->rn_next) {
+ if (rn->rn_rrl == rrl) {
+ if (prev)
+ prev->rn_next = rn->rn_next;
+ else
+ VERIFY(tsd_set(rrw_tsd_key, rn->rn_next) == 0);
+ kmem_free(rn, sizeof (*rn));
+ return (B_TRUE);
+ }
+ prev = rn;
+ }
+ return (B_FALSE);
+}
+
+void
+rrw_init(rrwlock_t *rrl)
+{
+ mutex_init(&rrl->rr_lock, NULL, MUTEX_DEFAULT, NULL);
+ cv_init(&rrl->rr_cv, NULL, CV_DEFAULT, NULL);
+ rrl->rr_writer = NULL;
+ refcount_create(&rrl->rr_anon_rcount);
+ refcount_create(&rrl->rr_linked_rcount);
+ rrl->rr_writer_wanted = B_FALSE;
+}
+
+void
+rrw_destroy(rrwlock_t *rrl)
+{
+ mutex_destroy(&rrl->rr_lock);
+ cv_destroy(&rrl->rr_cv);
+ ASSERT(rrl->rr_writer == NULL);
+ refcount_destroy(&rrl->rr_anon_rcount);
+ refcount_destroy(&rrl->rr_linked_rcount);
+}
+
+static void
+rrw_enter_read(rrwlock_t *rrl, void *tag)
+{
+ mutex_enter(&rrl->rr_lock);
+ ASSERT(rrl->rr_writer != curthread);
+ ASSERT(refcount_count(&rrl->rr_anon_rcount) >= 0);
+
+ while (rrl->rr_writer || (rrl->rr_writer_wanted &&
+ refcount_is_zero(&rrl->rr_anon_rcount) &&
+ rrn_find(rrl) == NULL))
+ cv_wait(&rrl->rr_cv, &rrl->rr_lock);
+
+ if (rrl->rr_writer_wanted) {
+ /* may or may not be a re-entrant enter */
+ rrn_add(rrl);
+ (void) refcount_add(&rrl->rr_linked_rcount, tag);
+ } else {
+ (void) refcount_add(&rrl->rr_anon_rcount, tag);
+ }
+ ASSERT(rrl->rr_writer == NULL);
+ mutex_exit(&rrl->rr_lock);
+}
+
+static void
+rrw_enter_write(rrwlock_t *rrl)
+{
+ mutex_enter(&rrl->rr_lock);
+ ASSERT(rrl->rr_writer != curthread);
+
+ while (refcount_count(&rrl->rr_anon_rcount) > 0 ||
+ refcount_count(&rrl->rr_linked_rcount) > 0 ||
+ rrl->rr_writer != NULL) {
+ rrl->rr_writer_wanted = B_TRUE;
+ cv_wait(&rrl->rr_cv, &rrl->rr_lock);
+ }
+ rrl->rr_writer_wanted = B_FALSE;
+ rrl->rr_writer = curthread;
+ mutex_exit(&rrl->rr_lock);
+}
+
+void
+rrw_enter(rrwlock_t *rrl, krw_t rw, void *tag)
+{
+ if (rw == RW_READER)
+ rrw_enter_read(rrl, tag);
+ else
+ rrw_enter_write(rrl);
+}
+
+void
+rrw_exit(rrwlock_t *rrl, void *tag)
+{
+ mutex_enter(&rrl->rr_lock);
+ ASSERT(!refcount_is_zero(&rrl->rr_anon_rcount) ||
+ !refcount_is_zero(&rrl->rr_linked_rcount) ||
+ rrl->rr_writer != NULL);
+
+ if (rrl->rr_writer == NULL) {
+ if (rrn_find_and_remove(rrl)) {
+ if (refcount_remove(&rrl->rr_linked_rcount, tag) == 0)
+ cv_broadcast(&rrl->rr_cv);
+
+ } else {
+ if (refcount_remove(&rrl->rr_anon_rcount, tag) == 0)
+ cv_broadcast(&rrl->rr_cv);
+ }
+ } else {
+ ASSERT(rrl->rr_writer == curthread);
+ ASSERT(refcount_is_zero(&rrl->rr_anon_rcount) &&
+ refcount_is_zero(&rrl->rr_linked_rcount));
+ rrl->rr_writer = NULL;
+ cv_broadcast(&rrl->rr_cv);
+ }
+ mutex_exit(&rrl->rr_lock);
+}
+
+boolean_t
+rrw_held(rrwlock_t *rrl, krw_t rw)
+{
+ boolean_t held;
+
+ mutex_enter(&rrl->rr_lock);
+ if (rw == RW_WRITER) {
+ held = (rrl->rr_writer == curthread);
+ } else {
+ held = (!refcount_is_zero(&rrl->rr_anon_rcount) ||
+ !refcount_is_zero(&rrl->rr_linked_rcount));
+ }
+ mutex_exit(&rrl->rr_lock);
+
+ return (held);
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sha256.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sha256.c
index ce5c261..ca7076c 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sha256.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sha256.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -31,20 +30,20 @@
#include <sys/zio_checksum.h>
/*
- * SHA-256 checksum, as specified in FIPS 180-2, available at:
- * http://csrc.nist.gov/cryptval
+ * SHA-256 checksum, as specified in FIPS 180-3, available at:
+ * http://csrc.nist.gov/publications/PubsFIPS.html
*
* This is a very compact implementation of SHA-256.
* It is designed to be simple and portable, not to be fast.
*/
/*
- * The literal definitions according to FIPS180-2 would be:
+ * The literal definitions of Ch() and Maj() according to FIPS 180-3 are:
*
- * Ch(x, y, z) (((x) & (y)) ^ ((~(x)) & (z)))
- * Maj(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
+ * Ch(x, y, z) (x & y) ^ (~x & z)
+ * Maj(x, y, z) (x & y) ^ (x & z) ^ (y & z)
*
- * We use logical equivalents which require one less op.
+ * We use equivalent logical reductions here that require one less op.
*/
#define Ch(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
#define Maj(x, y, z) (((x) & (y)) ^ ((z) & ((x) ^ (y))))
@@ -105,20 +104,19 @@ zio_checksum_SHA256(const void *buf, uint64_t size, zio_cksum_t *zcp)
uint32_t H[8] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 };
uint8_t pad[128];
- int padsize = size & 63;
- int i;
+ int i, padsize;
- for (i = 0; i < size - padsize; i += 64)
+ for (i = 0; i < (size & ~63ULL); i += 64)
SHA256Transform(H, (uint8_t *)buf + i);
- for (i = 0; i < padsize; i++)
- pad[i] = ((uint8_t *)buf)[i];
+ for (padsize = 0; i < size; i++)
+ pad[padsize++] = *((uint8_t *)buf + i);
for (pad[padsize++] = 0x80; (padsize & 63) != 56; padsize++)
pad[padsize] = 0;
- for (i = 0; i < 8; i++)
- pad[padsize++] = (size << 3) >> (56 - 8 * i);
+ for (i = 56; i >= 0; i -= 8)
+ pad[padsize++] = (size << 3) >> i;
for (i = 0; i < padsize; i += 64)
SHA256Transform(H, pad + i);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c
index 6a7c525..163b215 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c
@@ -20,12 +20,10 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* This file contains all the routines used when modifying on-disk SPA state.
* This includes opening, importing, destroying, exporting a pool, and syncing a
@@ -56,16 +54,388 @@
#include <sys/dsl_prop.h>
#include <sys/dsl_synctask.h>
#include <sys/fs/zfs.h>
+#include <sys/arc.h>
#include <sys/callb.h>
#include <sys/sunddi.h>
+#include <sys/spa_boot.h>
+
+#include "zfs_prop.h"
+#include "zfs_comutil.h"
-int zio_taskq_threads = 0;
-SYSCTL_DECL(_vfs_zfs);
-SYSCTL_NODE(_vfs_zfs, OID_AUTO, zio, CTLFLAG_RW, 0, "ZFS ZIO");
-TUNABLE_INT("vfs.zfs.zio.taskq_threads", &zio_taskq_threads);
-SYSCTL_INT(_vfs_zfs_zio, OID_AUTO, taskq_threads, CTLFLAG_RW,
- &zio_taskq_threads, 0, "Number of ZIO threads per ZIO type");
+int zio_taskq_threads[ZIO_TYPES][ZIO_TASKQ_TYPES] = {
+ /* ISSUE INTR */
+ { 1, 1 }, /* ZIO_TYPE_NULL */
+ { 1, 8 }, /* ZIO_TYPE_READ */
+ { 8, 1 }, /* ZIO_TYPE_WRITE */
+ { 1, 1 }, /* ZIO_TYPE_FREE */
+ { 1, 1 }, /* ZIO_TYPE_CLAIM */
+ { 1, 1 }, /* ZIO_TYPE_IOCTL */
+};
+static void spa_sync_props(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx);
+static boolean_t spa_has_active_shared_spare(spa_t *spa);
+
+/*
+ * ==========================================================================
+ * SPA properties routines
+ * ==========================================================================
+ */
+
+/*
+ * Add a (source=src, propname=propval) list to an nvlist.
+ */
+static void
+spa_prop_add_list(nvlist_t *nvl, zpool_prop_t prop, char *strval,
+ uint64_t intval, zprop_source_t src)
+{
+ const char *propname = zpool_prop_to_name(prop);
+ nvlist_t *propval;
+
+ VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
+ VERIFY(nvlist_add_uint64(propval, ZPROP_SOURCE, src) == 0);
+
+ if (strval != NULL)
+ VERIFY(nvlist_add_string(propval, ZPROP_VALUE, strval) == 0);
+ else
+ VERIFY(nvlist_add_uint64(propval, ZPROP_VALUE, intval) == 0);
+
+ VERIFY(nvlist_add_nvlist(nvl, propname, propval) == 0);
+ nvlist_free(propval);
+}
+
+/*
+ * Get property values from the spa configuration.
+ */
+static void
+spa_prop_get_config(spa_t *spa, nvlist_t **nvp)
+{
+ uint64_t size = spa_get_space(spa);
+ uint64_t used = spa_get_alloc(spa);
+ uint64_t cap, version;
+ zprop_source_t src = ZPROP_SRC_NONE;
+ spa_config_dirent_t *dp;
+
+ ASSERT(MUTEX_HELD(&spa->spa_props_lock));
+
+ /*
+ * readonly properties
+ */
+ spa_prop_add_list(*nvp, ZPOOL_PROP_NAME, spa_name(spa), 0, src);
+ spa_prop_add_list(*nvp, ZPOOL_PROP_SIZE, NULL, size, src);
+ spa_prop_add_list(*nvp, ZPOOL_PROP_USED, NULL, used, src);
+ spa_prop_add_list(*nvp, ZPOOL_PROP_AVAILABLE, NULL, size - used, src);
+
+ cap = (size == 0) ? 0 : (used * 100 / size);
+ spa_prop_add_list(*nvp, ZPOOL_PROP_CAPACITY, NULL, cap, src);
+
+ spa_prop_add_list(*nvp, ZPOOL_PROP_GUID, NULL, spa_guid(spa), src);
+ spa_prop_add_list(*nvp, ZPOOL_PROP_HEALTH, NULL,
+ spa->spa_root_vdev->vdev_state, src);
+
+ /*
+ * settable properties that are not stored in the pool property object.
+ */
+ version = spa_version(spa);
+ if (version == zpool_prop_default_numeric(ZPOOL_PROP_VERSION))
+ src = ZPROP_SRC_DEFAULT;
+ else
+ src = ZPROP_SRC_LOCAL;
+ spa_prop_add_list(*nvp, ZPOOL_PROP_VERSION, NULL, version, src);
+
+ if (spa->spa_root != NULL)
+ spa_prop_add_list(*nvp, ZPOOL_PROP_ALTROOT, spa->spa_root,
+ 0, ZPROP_SRC_LOCAL);
+
+ if ((dp = list_head(&spa->spa_config_list)) != NULL) {
+ if (dp->scd_path == NULL) {
+ spa_prop_add_list(*nvp, ZPOOL_PROP_CACHEFILE,
+ "none", 0, ZPROP_SRC_LOCAL);
+ } else if (strcmp(dp->scd_path, spa_config_path) != 0) {
+ spa_prop_add_list(*nvp, ZPOOL_PROP_CACHEFILE,
+ dp->scd_path, 0, ZPROP_SRC_LOCAL);
+ }
+ }
+}
+
+/*
+ * Get zpool property values.
+ */
+int
+spa_prop_get(spa_t *spa, nvlist_t **nvp)
+{
+ zap_cursor_t zc;
+ zap_attribute_t za;
+ objset_t *mos = spa->spa_meta_objset;
+ int err;
+
+ VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
+
+ mutex_enter(&spa->spa_props_lock);
+
+ /*
+ * Get properties from the spa config.
+ */
+ spa_prop_get_config(spa, nvp);
+
+ /* If no pool property object, no more prop to get. */
+ if (spa->spa_pool_props_object == 0) {
+ mutex_exit(&spa->spa_props_lock);
+ return (0);
+ }
+
+ /*
+ * Get properties from the MOS pool property object.
+ */
+ for (zap_cursor_init(&zc, mos, spa->spa_pool_props_object);
+ (err = zap_cursor_retrieve(&zc, &za)) == 0;
+ zap_cursor_advance(&zc)) {
+ uint64_t intval = 0;
+ char *strval = NULL;
+ zprop_source_t src = ZPROP_SRC_DEFAULT;
+ zpool_prop_t prop;
+
+ if ((prop = zpool_name_to_prop(za.za_name)) == ZPROP_INVAL)
+ continue;
+
+ switch (za.za_integer_length) {
+ case 8:
+ /* integer property */
+ if (za.za_first_integer !=
+ zpool_prop_default_numeric(prop))
+ src = ZPROP_SRC_LOCAL;
+
+ if (prop == ZPOOL_PROP_BOOTFS) {
+ dsl_pool_t *dp;
+ dsl_dataset_t *ds = NULL;
+
+ dp = spa_get_dsl(spa);
+ rw_enter(&dp->dp_config_rwlock, RW_READER);
+ if (err = dsl_dataset_hold_obj(dp,
+ za.za_first_integer, FTAG, &ds)) {
+ rw_exit(&dp->dp_config_rwlock);
+ break;
+ }
+
+ strval = kmem_alloc(
+ MAXNAMELEN + strlen(MOS_DIR_NAME) + 1,
+ KM_SLEEP);
+ dsl_dataset_name(ds, strval);
+ dsl_dataset_rele(ds, FTAG);
+ rw_exit(&dp->dp_config_rwlock);
+ } else {
+ strval = NULL;
+ intval = za.za_first_integer;
+ }
+
+ spa_prop_add_list(*nvp, prop, strval, intval, src);
+
+ if (strval != NULL)
+ kmem_free(strval,
+ MAXNAMELEN + strlen(MOS_DIR_NAME) + 1);
+
+ break;
+
+ case 1:
+ /* string property */
+ strval = kmem_alloc(za.za_num_integers, KM_SLEEP);
+ err = zap_lookup(mos, spa->spa_pool_props_object,
+ za.za_name, 1, za.za_num_integers, strval);
+ if (err) {
+ kmem_free(strval, za.za_num_integers);
+ break;
+ }
+ spa_prop_add_list(*nvp, prop, strval, 0, src);
+ kmem_free(strval, za.za_num_integers);
+ break;
+
+ default:
+ break;
+ }
+ }
+ zap_cursor_fini(&zc);
+ mutex_exit(&spa->spa_props_lock);
+out:
+ if (err && err != ENOENT) {
+ nvlist_free(*nvp);
+ *nvp = NULL;
+ return (err);
+ }
+
+ return (0);
+}
+
+/*
+ * Validate the given pool properties nvlist and modify the list
+ * for the property values to be set.
+ */
+static int
+spa_prop_validate(spa_t *spa, nvlist_t *props)
+{
+ nvpair_t *elem;
+ int error = 0, reset_bootfs = 0;
+ uint64_t objnum;
+
+ elem = NULL;
+ while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
+ zpool_prop_t prop;
+ char *propname, *strval;
+ uint64_t intval;
+ objset_t *os;
+ char *slash;
+
+ propname = nvpair_name(elem);
+
+ if ((prop = zpool_name_to_prop(propname)) == ZPROP_INVAL)
+ return (EINVAL);
+
+ switch (prop) {
+ case ZPOOL_PROP_VERSION:
+ error = nvpair_value_uint64(elem, &intval);
+ if (!error &&
+ (intval < spa_version(spa) || intval > SPA_VERSION))
+ error = EINVAL;
+ break;
+
+ case ZPOOL_PROP_DELEGATION:
+ case ZPOOL_PROP_AUTOREPLACE:
+ case ZPOOL_PROP_LISTSNAPS:
+ error = nvpair_value_uint64(elem, &intval);
+ if (!error && intval > 1)
+ error = EINVAL;
+ break;
+
+ case ZPOOL_PROP_BOOTFS:
+ if (spa_version(spa) < SPA_VERSION_BOOTFS) {
+ error = ENOTSUP;
+ break;
+ }
+
+ /*
+ * Make sure the vdev config is bootable
+ */
+ if (!vdev_is_bootable(spa->spa_root_vdev)) {
+ error = ENOTSUP;
+ break;
+ }
+
+ reset_bootfs = 1;
+
+ error = nvpair_value_string(elem, &strval);
+
+ if (!error) {
+ uint64_t compress;
+
+ if (strval == NULL || strval[0] == '\0') {
+ objnum = zpool_prop_default_numeric(
+ ZPOOL_PROP_BOOTFS);
+ break;
+ }
+
+ if (error = dmu_objset_open(strval, DMU_OST_ZFS,
+ DS_MODE_USER | DS_MODE_READONLY, &os))
+ break;
+
+ /* We don't support gzip bootable datasets */
+ if ((error = dsl_prop_get_integer(strval,
+ zfs_prop_to_name(ZFS_PROP_COMPRESSION),
+ &compress, NULL)) == 0 &&
+ !BOOTFS_COMPRESS_VALID(compress)) {
+ error = ENOTSUP;
+ } else {
+ objnum = dmu_objset_id(os);
+ }
+ dmu_objset_close(os);
+ }
+ break;
+
+ case ZPOOL_PROP_FAILUREMODE:
+ error = nvpair_value_uint64(elem, &intval);
+ if (!error && (intval < ZIO_FAILURE_MODE_WAIT ||
+ intval > ZIO_FAILURE_MODE_PANIC))
+ error = EINVAL;
+
+ /*
+ * This is a special case which only occurs when
+ * the pool has completely failed. This allows
+ * the user to change the in-core failmode property
+ * without syncing it out to disk (I/Os might
+ * currently be blocked). We do this by returning
+ * EIO to the caller (spa_prop_set) to trick it
+ * into thinking we encountered a property validation
+ * error.
+ */
+ if (!error && spa_suspended(spa)) {
+ spa->spa_failmode = intval;
+ error = EIO;
+ }
+ break;
+
+ case ZPOOL_PROP_CACHEFILE:
+ if ((error = nvpair_value_string(elem, &strval)) != 0)
+ break;
+
+ if (strval[0] == '\0')
+ break;
+
+ if (strcmp(strval, "none") == 0)
+ break;
+
+ if (strval[0] != '/') {
+ error = EINVAL;
+ break;
+ }
+
+ slash = strrchr(strval, '/');
+ ASSERT(slash != NULL);
+
+ if (slash[1] == '\0' || strcmp(slash, "/.") == 0 ||
+ strcmp(slash, "/..") == 0)
+ error = EINVAL;
+ break;
+ }
+
+ if (error)
+ break;
+ }
+
+ if (!error && reset_bootfs) {
+ error = nvlist_remove(props,
+ zpool_prop_to_name(ZPOOL_PROP_BOOTFS), DATA_TYPE_STRING);
+
+ if (!error) {
+ error = nvlist_add_uint64(props,
+ zpool_prop_to_name(ZPOOL_PROP_BOOTFS), objnum);
+ }
+ }
+
+ return (error);
+}
+
+int
+spa_prop_set(spa_t *spa, nvlist_t *nvp)
+{
+ int error;
+
+ if ((error = spa_prop_validate(spa, nvp)) != 0)
+ return (error);
+
+ return (dsl_sync_task_do(spa_get_dsl(spa), NULL, spa_sync_props,
+ spa, nvp, 3));
+}
+
+/*
+ * If the bootfs property value is dsobj, clear it.
+ */
+void
+spa_prop_clear_bootfs(spa_t *spa, uint64_t dsobj, dmu_tx_t *tx)
+{
+ if (spa->spa_bootfs == dsobj && spa->spa_pool_props_object != 0) {
+ VERIFY(zap_remove(spa->spa_meta_objset,
+ spa->spa_pool_props_object,
+ zpool_prop_to_name(ZPOOL_PROP_BOOTFS), tx) == 0);
+ spa->spa_bootfs = 0;
+ }
+}
/*
* ==========================================================================
@@ -117,40 +487,26 @@ spa_get_errlists(spa_t *spa, avl_tree_t *last, avl_tree_t *scrub)
static void
spa_activate(spa_t *spa)
{
- int t;
- int nthreads = zio_taskq_threads;
- char name[32];
ASSERT(spa->spa_state == POOL_STATE_UNINITIALIZED);
spa->spa_state = POOL_STATE_ACTIVE;
spa->spa_normal_class = metaslab_class_create();
+ spa->spa_log_class = metaslab_class_create();
- if (nthreads == 0)
- nthreads = max_ncpus;
- for (t = 0; t < ZIO_TYPES; t++) {
- snprintf(name, sizeof(name), "spa_zio_issue %d", t);
- spa->spa_zio_issue_taskq[t] = taskq_create(name, nthreads,
- maxclsyspri, 50, INT_MAX, TASKQ_PREPOPULATE);
- snprintf(name, sizeof(name), "spa_zio_intr %d", t);
- spa->spa_zio_intr_taskq[t] = taskq_create(name, nthreads,
- maxclsyspri, 50, INT_MAX, TASKQ_PREPOPULATE);
+ for (int t = 0; t < ZIO_TYPES; t++) {
+ for (int q = 0; q < ZIO_TASKQ_TYPES; q++) {
+ spa->spa_zio_taskq[t][q] = taskq_create("spa_zio",
+ zio_taskq_threads[t][q], maxclsyspri, 50,
+ INT_MAX, TASKQ_PREPOPULATE);
+ }
}
- rw_init(&spa->spa_traverse_lock, NULL, RW_DEFAULT, NULL);
-
- mutex_init(&spa->spa_uberblock_lock, NULL, MUTEX_DEFAULT, NULL);
- mutex_init(&spa->spa_errlog_lock, NULL, MUTEX_DEFAULT, NULL);
- mutex_init(&spa->spa_errlist_lock, NULL, MUTEX_DEFAULT, NULL);
- mutex_init(&spa->spa_config_lock.scl_lock, NULL, MUTEX_DEFAULT, NULL);
- cv_init(&spa->spa_config_lock.scl_cv, NULL, CV_DEFAULT, NULL);
- mutex_init(&spa->spa_sync_bplist.bpl_lock, NULL, MUTEX_DEFAULT, NULL);
- mutex_init(&spa->spa_history_lock, NULL, MUTEX_DEFAULT, NULL);
- mutex_init(&spa->spa_props_lock, NULL, MUTEX_DEFAULT, NULL);
-
- list_create(&spa->spa_dirty_list, sizeof (vdev_t),
- offsetof(vdev_t, vdev_dirty_node));
+ list_create(&spa->spa_config_dirty_list, sizeof (vdev_t),
+ offsetof(vdev_t, vdev_config_dirty_node));
+ list_create(&spa->spa_state_dirty_list, sizeof (vdev_t),
+ offsetof(vdev_t, vdev_state_dirty_node));
txg_list_create(&spa->spa_vdev_txg_list,
offsetof(struct vdev, vdev_txg_node));
@@ -169,8 +525,6 @@ spa_activate(spa_t *spa)
static void
spa_deactivate(spa_t *spa)
{
- int t;
-
ASSERT(spa->spa_sync_on == B_FALSE);
ASSERT(spa->spa_dsl_pool == NULL);
ASSERT(spa->spa_root_vdev == NULL);
@@ -179,18 +533,22 @@ spa_deactivate(spa_t *spa)
txg_list_destroy(&spa->spa_vdev_txg_list);
- list_destroy(&spa->spa_dirty_list);
+ list_destroy(&spa->spa_config_dirty_list);
+ list_destroy(&spa->spa_state_dirty_list);
- for (t = 0; t < ZIO_TYPES; t++) {
- taskq_destroy(spa->spa_zio_issue_taskq[t]);
- taskq_destroy(spa->spa_zio_intr_taskq[t]);
- spa->spa_zio_issue_taskq[t] = NULL;
- spa->spa_zio_intr_taskq[t] = NULL;
+ for (int t = 0; t < ZIO_TYPES; t++) {
+ for (int q = 0; q < ZIO_TASKQ_TYPES; q++) {
+ taskq_destroy(spa->spa_zio_taskq[t][q]);
+ spa->spa_zio_taskq[t][q] = NULL;
+ }
}
metaslab_class_destroy(spa->spa_normal_class);
spa->spa_normal_class = NULL;
+ metaslab_class_destroy(spa->spa_log_class);
+ spa->spa_log_class = NULL;
+
/*
* If this was part of an import or the open otherwise failed, we may
* still have errors left in the queues. Empty them just in case.
@@ -200,16 +558,6 @@ spa_deactivate(spa_t *spa)
avl_destroy(&spa->spa_errlist_scrub);
avl_destroy(&spa->spa_errlist_last);
- rw_destroy(&spa->spa_traverse_lock);
- mutex_destroy(&spa->spa_uberblock_lock);
- mutex_destroy(&spa->spa_errlog_lock);
- mutex_destroy(&spa->spa_errlist_lock);
- mutex_destroy(&spa->spa_config_lock.scl_lock);
- cv_destroy(&spa->spa_config_lock.scl_cv);
- mutex_destroy(&spa->spa_sync_bplist.bpl_lock);
- mutex_destroy(&spa->spa_history_lock);
- mutex_destroy(&spa->spa_props_lock);
-
spa->spa_state = POOL_STATE_UNINITIALIZED;
}
@@ -233,8 +581,13 @@ spa_config_parse(spa_t *spa, vdev_t **vdp, nvlist_t *nv, vdev_t *parent,
if ((*vdp)->vdev_ops->vdev_op_leaf)
return (0);
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
- &child, &children) != 0) {
+ error = nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
+ &child, &children);
+
+ if (error == ENOENT)
+ return (0);
+
+ if (error) {
vdev_free(*vdp);
*vdp = NULL;
return (EINVAL);
@@ -263,6 +616,8 @@ spa_unload(spa_t *spa)
{
int i;
+ ASSERT(MUTEX_HELD(&spa_namespace_lock));
+
/*
* Stop async tasks.
*/
@@ -277,10 +632,17 @@ spa_unload(spa_t *spa)
}
/*
- * Wait for any outstanding prefetch I/O to complete.
+ * Wait for any outstanding async I/O to complete.
+ */
+ mutex_enter(&spa->spa_async_root_lock);
+ while (spa->spa_async_root_count != 0)
+ cv_wait(&spa->spa_async_root_cv, &spa->spa_async_root_lock);
+ mutex_exit(&spa->spa_async_root_lock);
+
+ /*
+ * Drop and purge level 2 cache
*/
- spa_config_enter(spa, RW_WRITER, FTAG);
- spa_config_exit(spa, FTAG);
+ spa_l2cache_drop(spa);
/*
* Close the dsl pool.
@@ -297,16 +659,31 @@ spa_unload(spa_t *spa)
vdev_free(spa->spa_root_vdev);
ASSERT(spa->spa_root_vdev == NULL);
- for (i = 0; i < spa->spa_nspares; i++)
- vdev_free(spa->spa_spares[i]);
- if (spa->spa_spares) {
- kmem_free(spa->spa_spares, spa->spa_nspares * sizeof (void *));
- spa->spa_spares = NULL;
+ for (i = 0; i < spa->spa_spares.sav_count; i++)
+ vdev_free(spa->spa_spares.sav_vdevs[i]);
+ if (spa->spa_spares.sav_vdevs) {
+ kmem_free(spa->spa_spares.sav_vdevs,
+ spa->spa_spares.sav_count * sizeof (void *));
+ spa->spa_spares.sav_vdevs = NULL;
+ }
+ if (spa->spa_spares.sav_config) {
+ nvlist_free(spa->spa_spares.sav_config);
+ spa->spa_spares.sav_config = NULL;
}
- if (spa->spa_sparelist) {
- nvlist_free(spa->spa_sparelist);
- spa->spa_sparelist = NULL;
+ spa->spa_spares.sav_count = 0;
+
+ for (i = 0; i < spa->spa_l2cache.sav_count; i++)
+ vdev_free(spa->spa_l2cache.sav_vdevs[i]);
+ if (spa->spa_l2cache.sav_vdevs) {
+ kmem_free(spa->spa_l2cache.sav_vdevs,
+ spa->spa_l2cache.sav_count * sizeof (void *));
+ spa->spa_l2cache.sav_vdevs = NULL;
}
+ if (spa->spa_l2cache.sav_config) {
+ nvlist_free(spa->spa_l2cache.sav_config);
+ spa->spa_l2cache.sav_config = NULL;
+ }
+ spa->spa_l2cache.sav_count = 0;
spa->spa_async_suspended = 0;
}
@@ -314,8 +691,8 @@ spa_unload(spa_t *spa)
/*
* Load (or re-load) the current list of vdevs describing the active spares for
* this pool. When this is called, we have some form of basic information in
- * 'spa_sparelist'. We parse this into vdevs, try to open them, and then
- * re-generate a more complete list including status information.
+ * 'spa_spares.sav_config'. We parse this into vdevs, try to open them, and
+ * then re-generate a more complete list including status information.
*/
static void
spa_load_spares(spa_t *spa)
@@ -325,31 +702,34 @@ spa_load_spares(spa_t *spa)
int i;
vdev_t *vd, *tvd;
+ ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL);
+
/*
* First, close and free any existing spare vdevs.
*/
- for (i = 0; i < spa->spa_nspares; i++) {
- vd = spa->spa_spares[i];
+ for (i = 0; i < spa->spa_spares.sav_count; i++) {
+ vd = spa->spa_spares.sav_vdevs[i];
/* Undo the call to spa_activate() below */
- if ((tvd = spa_lookup_by_guid(spa, vd->vdev_guid)) != NULL &&
- tvd->vdev_isspare)
+ if ((tvd = spa_lookup_by_guid(spa, vd->vdev_guid,
+ B_FALSE)) != NULL && tvd->vdev_isspare)
spa_spare_remove(tvd);
vdev_close(vd);
vdev_free(vd);
}
- if (spa->spa_spares)
- kmem_free(spa->spa_spares, spa->spa_nspares * sizeof (void *));
+ if (spa->spa_spares.sav_vdevs)
+ kmem_free(spa->spa_spares.sav_vdevs,
+ spa->spa_spares.sav_count * sizeof (void *));
- if (spa->spa_sparelist == NULL)
+ if (spa->spa_spares.sav_config == NULL)
nspares = 0;
else
- VERIFY(nvlist_lookup_nvlist_array(spa->spa_sparelist,
+ VERIFY(nvlist_lookup_nvlist_array(spa->spa_spares.sav_config,
ZPOOL_CONFIG_SPARES, &spares, &nspares) == 0);
- spa->spa_nspares = (int)nspares;
- spa->spa_spares = NULL;
+ spa->spa_spares.sav_count = (int)nspares;
+ spa->spa_spares.sav_vdevs = NULL;
if (nspares == 0)
return;
@@ -363,15 +743,17 @@ spa_load_spares(spa_t *spa)
* validate each vdev on the spare list. If the vdev also exists in the
* active configuration, then we also mark this vdev as an active spare.
*/
- spa->spa_spares = kmem_alloc(nspares * sizeof (void *), KM_SLEEP);
- for (i = 0; i < spa->spa_nspares; i++) {
+ spa->spa_spares.sav_vdevs = kmem_alloc(nspares * sizeof (void *),
+ KM_SLEEP);
+ for (i = 0; i < spa->spa_spares.sav_count; i++) {
VERIFY(spa_config_parse(spa, &vd, spares[i], NULL, 0,
VDEV_ALLOC_SPARE) == 0);
ASSERT(vd != NULL);
- spa->spa_spares[i] = vd;
+ spa->spa_spares.sav_vdevs[i] = vd;
- if ((tvd = spa_lookup_by_guid(spa, vd->vdev_guid)) != NULL) {
+ if ((tvd = spa_lookup_by_guid(spa, vd->vdev_guid,
+ B_FALSE)) != NULL) {
if (!tvd->vdev_isspare)
spa_spare_add(tvd);
@@ -392,29 +774,167 @@ spa_load_spares(spa_t *spa)
spa_spare_activate(tvd);
}
+ vd->vdev_top = vd;
+
if (vdev_open(vd) != 0)
continue;
- vd->vdev_top = vd;
- (void) vdev_validate_spare(vd);
+ if (vdev_validate_aux(vd) == 0)
+ spa_spare_add(vd);
}
/*
* Recompute the stashed list of spares, with status information
* this time.
*/
- VERIFY(nvlist_remove(spa->spa_sparelist, ZPOOL_CONFIG_SPARES,
+ VERIFY(nvlist_remove(spa->spa_spares.sav_config, ZPOOL_CONFIG_SPARES,
DATA_TYPE_NVLIST_ARRAY) == 0);
- spares = kmem_alloc(spa->spa_nspares * sizeof (void *), KM_SLEEP);
- for (i = 0; i < spa->spa_nspares; i++)
- spares[i] = vdev_config_generate(spa, spa->spa_spares[i],
- B_TRUE, B_TRUE);
- VERIFY(nvlist_add_nvlist_array(spa->spa_sparelist, ZPOOL_CONFIG_SPARES,
- spares, spa->spa_nspares) == 0);
- for (i = 0; i < spa->spa_nspares; i++)
+ spares = kmem_alloc(spa->spa_spares.sav_count * sizeof (void *),
+ KM_SLEEP);
+ for (i = 0; i < spa->spa_spares.sav_count; i++)
+ spares[i] = vdev_config_generate(spa,
+ spa->spa_spares.sav_vdevs[i], B_TRUE, B_TRUE, B_FALSE);
+ VERIFY(nvlist_add_nvlist_array(spa->spa_spares.sav_config,
+ ZPOOL_CONFIG_SPARES, spares, spa->spa_spares.sav_count) == 0);
+ for (i = 0; i < spa->spa_spares.sav_count; i++)
nvlist_free(spares[i]);
- kmem_free(spares, spa->spa_nspares * sizeof (void *));
+ kmem_free(spares, spa->spa_spares.sav_count * sizeof (void *));
+}
+
+/*
+ * Load (or re-load) the current list of vdevs describing the active l2cache for
+ * this pool. When this is called, we have some form of basic information in
+ * 'spa_l2cache.sav_config'. We parse this into vdevs, try to open them, and
+ * then re-generate a more complete list including status information.
+ * Devices which are already active have their details maintained, and are
+ * not re-opened.
+ */
+static void
+spa_load_l2cache(spa_t *spa)
+{
+ nvlist_t **l2cache;
+ uint_t nl2cache;
+ int i, j, oldnvdevs;
+ uint64_t guid, size;
+ vdev_t *vd, **oldvdevs, **newvdevs;
+ spa_aux_vdev_t *sav = &spa->spa_l2cache;
+
+ ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL);
+
+ if (sav->sav_config != NULL) {
+ VERIFY(nvlist_lookup_nvlist_array(sav->sav_config,
+ ZPOOL_CONFIG_L2CACHE, &l2cache, &nl2cache) == 0);
+ newvdevs = kmem_alloc(nl2cache * sizeof (void *), KM_SLEEP);
+ } else {
+ nl2cache = 0;
+ }
+
+ oldvdevs = sav->sav_vdevs;
+ oldnvdevs = sav->sav_count;
+ sav->sav_vdevs = NULL;
+ sav->sav_count = 0;
+
+ /*
+ * Process new nvlist of vdevs.
+ */
+ for (i = 0; i < nl2cache; i++) {
+ VERIFY(nvlist_lookup_uint64(l2cache[i], ZPOOL_CONFIG_GUID,
+ &guid) == 0);
+
+ newvdevs[i] = NULL;
+ for (j = 0; j < oldnvdevs; j++) {
+ vd = oldvdevs[j];
+ if (vd != NULL && guid == vd->vdev_guid) {
+ /*
+ * Retain previous vdev for add/remove ops.
+ */
+ newvdevs[i] = vd;
+ oldvdevs[j] = NULL;
+ break;
+ }
+ }
+
+ if (newvdevs[i] == NULL) {
+ /*
+ * Create new vdev
+ */
+ VERIFY(spa_config_parse(spa, &vd, l2cache[i], NULL, 0,
+ VDEV_ALLOC_L2CACHE) == 0);
+ ASSERT(vd != NULL);
+ newvdevs[i] = vd;
+
+ /*
+ * Commit this vdev as an l2cache device,
+ * even if it fails to open.
+ */
+ spa_l2cache_add(vd);
+
+ vd->vdev_top = vd;
+ vd->vdev_aux = sav;
+
+ spa_l2cache_activate(vd);
+
+ if (vdev_open(vd) != 0)
+ continue;
+
+ (void) vdev_validate_aux(vd);
+
+ if (!vdev_is_dead(vd)) {
+ size = vdev_get_rsize(vd);
+ l2arc_add_vdev(spa, vd,
+ VDEV_LABEL_START_SIZE,
+ size - VDEV_LABEL_START_SIZE);
+ }
+ }
+ }
+
+ /*
+ * Purge vdevs that were dropped
+ */
+ for (i = 0; i < oldnvdevs; i++) {
+ uint64_t pool;
+
+ vd = oldvdevs[i];
+ if (vd != NULL) {
+ if ((spa_mode & FWRITE) &&
+ spa_l2cache_exists(vd->vdev_guid, &pool) &&
+ pool != 0ULL &&
+ l2arc_vdev_present(vd)) {
+ l2arc_remove_vdev(vd);
+ }
+ (void) vdev_close(vd);
+ spa_l2cache_remove(vd);
+ }
+ }
+
+ if (oldvdevs)
+ kmem_free(oldvdevs, oldnvdevs * sizeof (void *));
+
+ if (sav->sav_config == NULL)
+ goto out;
+
+ sav->sav_vdevs = newvdevs;
+ sav->sav_count = (int)nl2cache;
+
+ /*
+ * Recompute the stashed list of l2cache devices, with status
+ * information this time.
+ */
+ VERIFY(nvlist_remove(sav->sav_config, ZPOOL_CONFIG_L2CACHE,
+ DATA_TYPE_NVLIST_ARRAY) == 0);
+
+ l2cache = kmem_alloc(sav->sav_count * sizeof (void *), KM_SLEEP);
+ for (i = 0; i < sav->sav_count; i++)
+ l2cache[i] = vdev_config_generate(spa,
+ sav->sav_vdevs[i], B_TRUE, B_FALSE, B_TRUE);
+ VERIFY(nvlist_add_nvlist_array(sav->sav_config,
+ ZPOOL_CONFIG_L2CACHE, l2cache, sav->sav_count) == 0);
+out:
+ for (i = 0; i < sav->sav_count; i++)
+ nvlist_free(l2cache[i]);
+ if (sav->sav_count)
+ kmem_free(l2cache, sav->sav_count * sizeof (void *));
}
static int
@@ -440,6 +960,50 @@ load_nvlist(spa_t *spa, uint64_t obj, nvlist_t **value)
}
/*
+ * Checks to see if the given vdev could not be opened, in which case we post a
+ * sysevent to notify the autoreplace code that the device has been removed.
+ */
+static void
+spa_check_removed(vdev_t *vd)
+{
+ int c;
+
+ for (c = 0; c < vd->vdev_children; c++)
+ spa_check_removed(vd->vdev_child[c]);
+
+ if (vd->vdev_ops->vdev_op_leaf && vdev_is_dead(vd)) {
+ zfs_post_autoreplace(vd->vdev_spa, vd);
+ spa_event_notify(vd->vdev_spa, vd, ESC_ZFS_VDEV_CHECK);
+ }
+}
+
+/*
+ * Check for missing log devices
+ */
+int
+spa_check_logs(spa_t *spa)
+{
+ switch (spa->spa_log_state) {
+ case SPA_LOG_MISSING:
+ /* need to recheck in case slog has been restored */
+ case SPA_LOG_UNKNOWN:
+ if (dmu_objset_find(spa->spa_name, zil_check_log_chain, NULL,
+ DS_FIND_CHILDREN)) {
+ spa->spa_log_state = SPA_LOG_MISSING;
+ return (1);
+ }
+ break;
+
+ case SPA_LOG_CLEAR:
+ (void) dmu_objset_find(spa->spa_name, zil_clear_log_chain, NULL,
+ DS_FIND_CHILDREN);
+ break;
+ }
+ spa->spa_log_state = SPA_LOG_GOOD;
+ return (0);
+}
+
+/*
* Load an existing storage pool, using the pool's builtin spa_config as a
* source of configuration information.
*/
@@ -453,7 +1017,10 @@ spa_load(spa_t *spa, nvlist_t *config, spa_load_state_t state, int mosconfig)
uint64_t config_cache_txg = spa->spa_config_txg;
uint64_t pool_guid;
uint64_t version;
- zio_t *zio;
+ uint64_t autoreplace = 0;
+ char *ereport = FM_EREPORT_ZFS_POOL;
+
+ ASSERT(MUTEX_HELD(&spa_namespace_lock));
spa->spa_load_state = state;
@@ -468,7 +1035,7 @@ spa_load(spa_t *spa, nvlist_t *config, spa_load_state_t state, int mosconfig)
* it's not present treat it as the initial version.
*/
if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION, &version) != 0)
- version = ZFS_VERSION_INITIAL;
+ version = SPA_VERSION_INITIAL;
(void) nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG,
&spa->spa_config_txg);
@@ -486,10 +1053,10 @@ spa_load(spa_t *spa, nvlist_t *config, spa_load_state_t state, int mosconfig)
* value that will be returned by spa_version() since parsing the
* configuration requires knowing the version number.
*/
- spa_config_enter(spa, RW_WRITER, FTAG);
+ spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
spa->spa_ubsync.ub_version = version;
error = spa_config_parse(spa, &rvd, nvroot, NULL, 0, VDEV_ALLOC_LOAD);
- spa_config_exit(spa, FTAG);
+ spa_config_exit(spa, SCL_ALL, FTAG);
if (error != 0)
goto out;
@@ -500,18 +1067,19 @@ spa_load(spa_t *spa, nvlist_t *config, spa_load_state_t state, int mosconfig)
/*
* Try to open all vdevs, loading each label in the process.
*/
+ spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
error = vdev_open(rvd);
+ spa_config_exit(spa, SCL_ALL, FTAG);
if (error != 0)
goto out;
/*
* Validate the labels for all leaf vdevs. We need to grab the config
- * lock because all label I/O is done with the ZIO_FLAG_CONFIG_HELD
- * flag.
+ * lock because all label I/O is done with ZIO_FLAG_CONFIG_WRITER.
*/
- spa_config_enter(spa, RW_READER, FTAG);
+ spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
error = vdev_validate(rvd);
- spa_config_exit(spa, FTAG);
+ spa_config_exit(spa, SCL_ALL, FTAG);
if (error != 0)
goto out;
@@ -524,12 +1092,7 @@ spa_load(spa_t *spa, nvlist_t *config, spa_load_state_t state, int mosconfig)
/*
* Find the best uberblock.
*/
- bzero(ub, sizeof (uberblock_t));
-
- zio = zio_root(spa, NULL, NULL,
- ZIO_FLAG_CANFAIL | ZIO_FLAG_SPECULATIVE);
- vdev_uberblock_load(zio, rvd, ub);
- error = zio_wait(zio);
+ vdev_uberblock_load(NULL, rvd, ub);
/*
* If we weren't able to find a single valid uberblock, return failure.
@@ -544,7 +1107,7 @@ spa_load(spa_t *spa, nvlist_t *config, spa_load_state_t state, int mosconfig)
/*
* If the pool is newer than the code, we can't open it.
*/
- if (ub->ub_version > ZFS_VERSION) {
+ if (ub->ub_version > SPA_VERSION) {
vdev_set_state(rvd, B_TRUE, VDEV_STATE_CANT_OPEN,
VDEV_AUX_VERSION_NEWER);
error = ENOTSUP;
@@ -596,12 +1159,8 @@ spa_load(spa_t *spa, nvlist_t *config, spa_load_state_t state, int mosconfig)
goto out;
}
- /*
- * hostid is set after the root file system is mounted, so
- * ignore the check until it's done.
- */
- if (nvlist_lookup_uint64(newconfig, ZPOOL_CONFIG_HOSTID,
- &hostid) == 0 && root_mounted()) {
+ if (!spa_is_root(spa) && nvlist_lookup_uint64(newconfig,
+ ZPOOL_CONFIG_HOSTID, &hostid) == 0) {
char *hostname;
unsigned long myhostid = 0;
@@ -609,12 +1168,13 @@ spa_load(spa_t *spa, nvlist_t *config, spa_load_state_t state, int mosconfig)
ZPOOL_CONFIG_HOSTNAME, &hostname) == 0);
(void) ddi_strtoul(hw_serial, NULL, 10, &myhostid);
- if ((unsigned long)hostid != myhostid) {
+ if (hostid != 0 && myhostid != 0 &&
+ (unsigned long)hostid != myhostid) {
cmn_err(CE_WARN, "pool '%s' could not be "
"loaded as it was last accessed by "
- "another system (host: %s hostid: 0x%lx). "
+ "another system (host: %s hostid: 0x%lx). "
"See: http://www.sun.com/msg/ZFS-8000-EY",
- spa->spa_name, hostname,
+ spa_name(spa), hostname,
(unsigned long)hostid);
error = EBADF;
goto out;
@@ -695,7 +1255,7 @@ spa_load(spa_t *spa, nvlist_t *config, spa_load_state_t state, int mosconfig)
* Load any hot spares for this pool.
*/
error = zap_lookup(spa->spa_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
- DMU_POOL_SPARES, sizeof (uint64_t), 1, &spa->spa_spares_object);
+ DMU_POOL_SPARES, sizeof (uint64_t), 1, &spa->spa_spares.sav_object);
if (error != 0 && error != ENOENT) {
vdev_set_state(rvd, B_TRUE, VDEV_STATE_CANT_OPEN,
VDEV_AUX_CORRUPT_DATA);
@@ -703,20 +1263,59 @@ spa_load(spa_t *spa, nvlist_t *config, spa_load_state_t state, int mosconfig)
goto out;
}
if (error == 0) {
- ASSERT(spa_version(spa) >= ZFS_VERSION_SPARES);
- if (load_nvlist(spa, spa->spa_spares_object,
- &spa->spa_sparelist) != 0) {
+ ASSERT(spa_version(spa) >= SPA_VERSION_SPARES);
+ if (load_nvlist(spa, spa->spa_spares.sav_object,
+ &spa->spa_spares.sav_config) != 0) {
vdev_set_state(rvd, B_TRUE, VDEV_STATE_CANT_OPEN,
VDEV_AUX_CORRUPT_DATA);
error = EIO;
goto out;
}
- spa_config_enter(spa, RW_WRITER, FTAG);
+ spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
spa_load_spares(spa);
- spa_config_exit(spa, FTAG);
+ spa_config_exit(spa, SCL_ALL, FTAG);
}
+ /*
+ * Load any level 2 ARC devices for this pool.
+ */
+ error = zap_lookup(spa->spa_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
+ DMU_POOL_L2CACHE, sizeof (uint64_t), 1,
+ &spa->spa_l2cache.sav_object);
+ if (error != 0 && error != ENOENT) {
+ vdev_set_state(rvd, B_TRUE, VDEV_STATE_CANT_OPEN,
+ VDEV_AUX_CORRUPT_DATA);
+ error = EIO;
+ goto out;
+ }
+ if (error == 0) {
+ ASSERT(spa_version(spa) >= SPA_VERSION_L2CACHE);
+ if (load_nvlist(spa, spa->spa_l2cache.sav_object,
+ &spa->spa_l2cache.sav_config) != 0) {
+ vdev_set_state(rvd, B_TRUE,
+ VDEV_STATE_CANT_OPEN,
+ VDEV_AUX_CORRUPT_DATA);
+ error = EIO;
+ goto out;
+ }
+
+ spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
+ spa_load_l2cache(spa);
+ spa_config_exit(spa, SCL_ALL, FTAG);
+ }
+
+ if (spa_check_logs(spa)) {
+ vdev_set_state(rvd, B_TRUE, VDEV_STATE_CANT_OPEN,
+ VDEV_AUX_BAD_LOG);
+ error = ENXIO;
+ ereport = FM_EREPORT_ZFS_LOG_REPLAY;
+ goto out;
+ }
+
+
+ spa->spa_delegation = zpool_prop_default_numeric(ZPOOL_PROP_DELEGATION);
+
error = zap_lookup(spa->spa_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
DMU_POOL_PROPS, sizeof (uint64_t), 1, &spa->spa_pool_props_object);
@@ -730,11 +1329,33 @@ spa_load(spa_t *spa, nvlist_t *config, spa_load_state_t state, int mosconfig)
if (error == 0) {
(void) zap_lookup(spa->spa_meta_objset,
spa->spa_pool_props_object,
- zpool_prop_to_name(ZFS_PROP_BOOTFS),
+ zpool_prop_to_name(ZPOOL_PROP_BOOTFS),
sizeof (uint64_t), 1, &spa->spa_bootfs);
+ (void) zap_lookup(spa->spa_meta_objset,
+ spa->spa_pool_props_object,
+ zpool_prop_to_name(ZPOOL_PROP_AUTOREPLACE),
+ sizeof (uint64_t), 1, &autoreplace);
+ (void) zap_lookup(spa->spa_meta_objset,
+ spa->spa_pool_props_object,
+ zpool_prop_to_name(ZPOOL_PROP_DELEGATION),
+ sizeof (uint64_t), 1, &spa->spa_delegation);
+ (void) zap_lookup(spa->spa_meta_objset,
+ spa->spa_pool_props_object,
+ zpool_prop_to_name(ZPOOL_PROP_FAILUREMODE),
+ sizeof (uint64_t), 1, &spa->spa_failmode);
}
/*
+ * If the 'autoreplace' property is set, then post a resource notifying
+ * the ZFS DE that it should not issue any faults for unopenable
+ * devices. We also iterate over the vdevs, and post a sysevent for any
+ * unopenable vdevs so that the normal autoreplace handler can take
+ * over.
+ */
+ if (autoreplace && state != SPA_LOAD_TRYIMPORT)
+ spa_check_removed(spa->spa_root_vdev);
+
+ /*
* Load the vdev state for all toplevel vdevs.
*/
vdev_load(rvd);
@@ -742,9 +1363,9 @@ spa_load(spa_t *spa, nvlist_t *config, spa_load_state_t state, int mosconfig)
/*
* Propagate the leaf DTLs we just loaded all the way up the tree.
*/
- spa_config_enter(spa, RW_WRITER, FTAG);
+ spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
vdev_dtl_reassess(rvd, 0, 0, B_FALSE);
- spa_config_exit(spa, FTAG);
+ spa_config_exit(spa, SCL_ALL, FTAG);
/*
* Check the state of the root vdev. If it can't be opened, it
@@ -766,7 +1387,7 @@ spa_load(spa_t *spa, nvlist_t *config, spa_load_state_t state, int mosconfig)
*/
tx = dmu_tx_create_assigned(spa_get_dsl(spa),
spa_first_txg(spa));
- (void) dmu_objset_find(spa->spa_name,
+ (void) dmu_objset_find(spa_name(spa),
zil_claim, tx, DS_FIND_CHILDREN);
dmu_tx_commit(tx);
@@ -800,8 +1421,9 @@ spa_load(spa_t *spa, nvlist_t *config, spa_load_state_t state, int mosconfig)
error = 0;
out:
+ spa->spa_minref = refcount_count(&spa->spa_refcount);
if (error && error != EBADF)
- zfs_ereport_post(FM_EREPORT_ZFS_POOL, spa, NULL, NULL, 0, 0);
+ zfs_ereport_post(ereport, spa, NULL, NULL, 0, 0);
spa->spa_load_state = SPA_LOAD_NONE;
spa->spa_ena = 0;
@@ -814,7 +1436,7 @@ out:
* The import case is identical to an open except that the configuration is sent
* down from userland, instead of grabbed from the configuration cache. For the
* case of an open, the pool configuration will exist in the
- * POOL_STATE_UNITIALIZED state.
+ * POOL_STATE_UNINITIALIZED state.
*
* The stats information (gen/count/ustats) is used to gather vdev statistics at
* the same time open the pool, without having to keep around the spa_t in some
@@ -825,7 +1447,6 @@ spa_open_common(const char *pool, spa_t **spapp, void *tag, nvlist_t **config)
{
spa_t *spa;
int error;
- int loaded = B_FALSE;
int locked = B_FALSE;
*spapp = NULL;
@@ -860,11 +1481,10 @@ spa_open_common(const char *pool, spa_t **spapp, void *tag, nvlist_t **config)
* this is the case, the config cache is out of sync and
* we should remove the pool from the namespace.
*/
- zfs_post_ok(spa, NULL);
spa_unload(spa);
spa_deactivate(spa);
+ spa_config_sync(spa, B_TRUE, B_TRUE);
spa_remove(spa);
- spa_config_sync();
if (locked)
mutex_exit(&spa_namespace_lock);
return (ENOENT);
@@ -876,12 +1496,9 @@ spa_open_common(const char *pool, spa_t **spapp, void *tag, nvlist_t **config)
* information: the state of each vdev after the
* attempted vdev_open(). Return this to the user.
*/
- if (config != NULL && spa->spa_root_vdev != NULL) {
- spa_config_enter(spa, RW_READER, FTAG);
+ if (config != NULL && spa->spa_root_vdev != NULL)
*config = spa_config_generate(spa, NULL, -1ULL,
B_TRUE);
- spa_config_exit(spa, FTAG);
- }
spa_unload(spa);
spa_deactivate(spa);
spa->spa_last_open_failed = B_TRUE;
@@ -890,30 +1507,19 @@ spa_open_common(const char *pool, spa_t **spapp, void *tag, nvlist_t **config)
*spapp = NULL;
return (error);
} else {
- zfs_post_ok(spa, NULL);
spa->spa_last_open_failed = B_FALSE;
}
-
- loaded = B_TRUE;
}
spa_open_ref(spa, tag);
+
if (locked)
mutex_exit(&spa_namespace_lock);
*spapp = spa;
- if (config != NULL) {
- spa_config_enter(spa, RW_READER, FTAG);
+ if (config != NULL)
*config = spa_config_generate(spa, NULL, -1ULL, B_TRUE);
- spa_config_exit(spa, FTAG);
- }
-
- /*
- * If we just loaded the pool, resilver anything that's out of date.
- */
- if (loaded && (spa_mode & FWRITE))
- VERIFY(spa_scrub(spa, POOL_SCRUB_RESILVER, B_TRUE) == 0);
return (0);
}
@@ -952,6 +1558,9 @@ spa_inject_delref(spa_t *spa)
mutex_exit(&spa_namespace_lock);
}
+/*
+ * Add spares device information to the nvlist.
+ */
static void
spa_add_spares(spa_t *spa, nvlist_t *config)
{
@@ -963,12 +1572,12 @@ spa_add_spares(spa_t *spa, nvlist_t *config)
uint_t vsc;
uint64_t pool;
- if (spa->spa_nspares == 0)
+ if (spa->spa_spares.sav_count == 0)
return;
VERIFY(nvlist_lookup_nvlist(config,
ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
- VERIFY(nvlist_lookup_nvlist_array(spa->spa_sparelist,
+ VERIFY(nvlist_lookup_nvlist_array(spa->spa_spares.sav_config,
ZPOOL_CONFIG_SPARES, &spares, &nspares) == 0);
if (nspares != 0) {
VERIFY(nvlist_add_nvlist_array(nvroot,
@@ -984,7 +1593,8 @@ spa_add_spares(spa_t *spa, nvlist_t *config)
for (i = 0; i < nspares; i++) {
VERIFY(nvlist_lookup_uint64(spares[i],
ZPOOL_CONFIG_GUID, &guid) == 0);
- if (spa_spare_exists(guid, &pool) && pool != 0ULL) {
+ if (spa_spare_exists(guid, &pool, NULL) &&
+ pool != 0ULL) {
VERIFY(nvlist_lookup_uint64_array(
spares[i], ZPOOL_CONFIG_STATS,
(uint64_t **)&vs, &vsc) == 0);
@@ -995,6 +1605,62 @@ spa_add_spares(spa_t *spa, nvlist_t *config)
}
}
+/*
+ * Add l2cache device information to the nvlist, including vdev stats.
+ */
+static void
+spa_add_l2cache(spa_t *spa, nvlist_t *config)
+{
+ nvlist_t **l2cache;
+ uint_t i, j, nl2cache;
+ nvlist_t *nvroot;
+ uint64_t guid;
+ vdev_t *vd;
+ vdev_stat_t *vs;
+ uint_t vsc;
+
+ if (spa->spa_l2cache.sav_count == 0)
+ return;
+
+ spa_config_enter(spa, SCL_CONFIG, FTAG, RW_READER);
+
+ VERIFY(nvlist_lookup_nvlist(config,
+ ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
+ VERIFY(nvlist_lookup_nvlist_array(spa->spa_l2cache.sav_config,
+ ZPOOL_CONFIG_L2CACHE, &l2cache, &nl2cache) == 0);
+ if (nl2cache != 0) {
+ VERIFY(nvlist_add_nvlist_array(nvroot,
+ ZPOOL_CONFIG_L2CACHE, l2cache, nl2cache) == 0);
+ VERIFY(nvlist_lookup_nvlist_array(nvroot,
+ ZPOOL_CONFIG_L2CACHE, &l2cache, &nl2cache) == 0);
+
+ /*
+ * Update level 2 cache device stats.
+ */
+
+ for (i = 0; i < nl2cache; i++) {
+ VERIFY(nvlist_lookup_uint64(l2cache[i],
+ ZPOOL_CONFIG_GUID, &guid) == 0);
+
+ vd = NULL;
+ for (j = 0; j < spa->spa_l2cache.sav_count; j++) {
+ if (guid ==
+ spa->spa_l2cache.sav_vdevs[j]->vdev_guid) {
+ vd = spa->spa_l2cache.sav_vdevs[j];
+ break;
+ }
+ }
+ ASSERT(vd != NULL);
+
+ VERIFY(nvlist_lookup_uint64_array(l2cache[i],
+ ZPOOL_CONFIG_STATS, (uint64_t **)&vs, &vsc) == 0);
+ vdev_get_stats(vd, vs);
+ }
+ }
+
+ spa_config_exit(spa, SCL_CONFIG, FTAG);
+}
+
int
spa_get_stats(const char *name, nvlist_t **config, char *altroot, size_t buflen)
{
@@ -1008,7 +1674,12 @@ spa_get_stats(const char *name, nvlist_t **config, char *altroot, size_t buflen)
VERIFY(nvlist_add_uint64(*config, ZPOOL_CONFIG_ERRCOUNT,
spa_get_errlog_size(spa)) == 0);
+ if (spa_suspended(spa))
+ VERIFY(nvlist_add_uint64(*config,
+ ZPOOL_CONFIG_SUSPENDED, spa->spa_failmode) == 0);
+
spa_add_spares(spa, *config);
+ spa_add_l2cache(spa, *config);
}
/*
@@ -1037,45 +1708,48 @@ spa_get_stats(const char *name, nvlist_t **config, char *altroot, size_t buflen)
}
/*
- * Validate that the 'spares' array is well formed. We must have an array of
- * nvlists, each which describes a valid leaf vdev. If this is an import (mode
- * is VDEV_ALLOC_SPARE), then we allow corrupted spares to be specified, as long
- * as they are well-formed.
+ * Validate that the auxiliary device array is well formed. We must have an
+ * array of nvlists, each which describes a valid leaf vdev. If this is an
+ * import (mode is VDEV_ALLOC_SPARE), then we allow corrupted spares to be
+ * specified, as long as they are well-formed.
*/
static int
-spa_validate_spares(spa_t *spa, nvlist_t *nvroot, uint64_t crtxg, int mode)
+spa_validate_aux_devs(spa_t *spa, nvlist_t *nvroot, uint64_t crtxg, int mode,
+ spa_aux_vdev_t *sav, const char *config, uint64_t version,
+ vdev_labeltype_t label)
{
- nvlist_t **spares;
- uint_t i, nspares;
+ nvlist_t **dev;
+ uint_t i, ndev;
vdev_t *vd;
int error;
+ ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL);
+
/*
- * It's acceptable to have no spares specified.
+ * It's acceptable to have no devs specified.
*/
- if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
- &spares, &nspares) != 0)
+ if (nvlist_lookup_nvlist_array(nvroot, config, &dev, &ndev) != 0)
return (0);
- if (nspares == 0)
+ if (ndev == 0)
return (EINVAL);
/*
- * Make sure the pool is formatted with a version that supports hot
- * spares.
+ * Make sure the pool is formatted with a version that supports this
+ * device type.
*/
- if (spa_version(spa) < ZFS_VERSION_SPARES)
+ if (spa_version(spa) < version)
return (ENOTSUP);
/*
- * Set the pending spare list so we correctly handle device in-use
+ * Set the pending device list so we correctly handle device in-use
* checking.
*/
- spa->spa_pending_spares = spares;
- spa->spa_pending_nspares = nspares;
+ sav->sav_pending = dev;
+ sav->sav_npending = ndev;
- for (i = 0; i < nspares; i++) {
- if ((error = spa_config_parse(spa, &vd, spares[i], NULL, 0,
+ for (i = 0; i < ndev; i++) {
+ if ((error = spa_config_parse(spa, &vd, dev[i], NULL, 0,
mode)) != 0)
goto out;
@@ -1085,43 +1759,149 @@ spa_validate_spares(spa_t *spa, nvlist_t *nvroot, uint64_t crtxg, int mode)
goto out;
}
+ /*
+ * The L2ARC currently only supports disk devices in
+ * kernel context. For user-level testing, we allow it.
+ */
+#ifdef _KERNEL
+ if ((strcmp(config, ZPOOL_CONFIG_L2CACHE) == 0) &&
+ strcmp(vd->vdev_ops->vdev_op_type, VDEV_TYPE_DISK) != 0) {
+ error = ENOTBLK;
+ goto out;
+ }
+#endif
vd->vdev_top = vd;
if ((error = vdev_open(vd)) == 0 &&
- (error = vdev_label_init(vd, crtxg,
- VDEV_LABEL_SPARE)) == 0) {
- VERIFY(nvlist_add_uint64(spares[i], ZPOOL_CONFIG_GUID,
+ (error = vdev_label_init(vd, crtxg, label)) == 0) {
+ VERIFY(nvlist_add_uint64(dev[i], ZPOOL_CONFIG_GUID,
vd->vdev_guid) == 0);
}
vdev_free(vd);
- if (error && mode != VDEV_ALLOC_SPARE)
+ if (error &&
+ (mode != VDEV_ALLOC_SPARE && mode != VDEV_ALLOC_L2CACHE))
goto out;
else
error = 0;
}
out:
- spa->spa_pending_spares = NULL;
- spa->spa_pending_nspares = 0;
+ sav->sav_pending = NULL;
+ sav->sav_npending = 0;
return (error);
}
+static int
+spa_validate_aux(spa_t *spa, nvlist_t *nvroot, uint64_t crtxg, int mode)
+{
+ int error;
+
+ ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL);
+
+ if ((error = spa_validate_aux_devs(spa, nvroot, crtxg, mode,
+ &spa->spa_spares, ZPOOL_CONFIG_SPARES, SPA_VERSION_SPARES,
+ VDEV_LABEL_SPARE)) != 0) {
+ return (error);
+ }
+
+ return (spa_validate_aux_devs(spa, nvroot, crtxg, mode,
+ &spa->spa_l2cache, ZPOOL_CONFIG_L2CACHE, SPA_VERSION_L2CACHE,
+ VDEV_LABEL_L2CACHE));
+}
+
+static void
+spa_set_aux_vdevs(spa_aux_vdev_t *sav, nvlist_t **devs, int ndevs,
+ const char *config)
+{
+ int i;
+
+ if (sav->sav_config != NULL) {
+ nvlist_t **olddevs;
+ uint_t oldndevs;
+ nvlist_t **newdevs;
+
+ /*
+ * Generate new dev list by concatentating with the
+ * current dev list.
+ */
+ VERIFY(nvlist_lookup_nvlist_array(sav->sav_config, config,
+ &olddevs, &oldndevs) == 0);
+
+ newdevs = kmem_alloc(sizeof (void *) *
+ (ndevs + oldndevs), KM_SLEEP);
+ for (i = 0; i < oldndevs; i++)
+ VERIFY(nvlist_dup(olddevs[i], &newdevs[i],
+ KM_SLEEP) == 0);
+ for (i = 0; i < ndevs; i++)
+ VERIFY(nvlist_dup(devs[i], &newdevs[i + oldndevs],
+ KM_SLEEP) == 0);
+
+ VERIFY(nvlist_remove(sav->sav_config, config,
+ DATA_TYPE_NVLIST_ARRAY) == 0);
+
+ VERIFY(nvlist_add_nvlist_array(sav->sav_config,
+ config, newdevs, ndevs + oldndevs) == 0);
+ for (i = 0; i < oldndevs + ndevs; i++)
+ nvlist_free(newdevs[i]);
+ kmem_free(newdevs, (oldndevs + ndevs) * sizeof (void *));
+ } else {
+ /*
+ * Generate a new dev list.
+ */
+ VERIFY(nvlist_alloc(&sav->sav_config, NV_UNIQUE_NAME,
+ KM_SLEEP) == 0);
+ VERIFY(nvlist_add_nvlist_array(sav->sav_config, config,
+ devs, ndevs) == 0);
+ }
+}
+
+/*
+ * Stop and drop level 2 ARC devices
+ */
+void
+spa_l2cache_drop(spa_t *spa)
+{
+ vdev_t *vd;
+ int i;
+ spa_aux_vdev_t *sav = &spa->spa_l2cache;
+
+ for (i = 0; i < sav->sav_count; i++) {
+ uint64_t pool;
+
+ vd = sav->sav_vdevs[i];
+ ASSERT(vd != NULL);
+
+ if ((spa_mode & FWRITE) &&
+ spa_l2cache_exists(vd->vdev_guid, &pool) && pool != 0ULL &&
+ l2arc_vdev_present(vd)) {
+ l2arc_remove_vdev(vd);
+ }
+ if (vd->vdev_isl2cache)
+ spa_l2cache_remove(vd);
+ vdev_clear_stats(vd);
+ (void) vdev_close(vd);
+ }
+}
+
/*
* Pool Creation
*/
int
-spa_create(const char *pool, nvlist_t *nvroot, const char *altroot)
+spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props,
+ const char *history_str, nvlist_t *zplprops)
{
spa_t *spa;
+ char *altroot = NULL;
vdev_t *rvd;
dsl_pool_t *dp;
dmu_tx_t *tx;
int c, error = 0;
uint64_t txg = TXG_INITIAL;
- nvlist_t **spares;
- uint_t nspares;
+ nvlist_t **spares, **l2cache;
+ uint_t nspares, nl2cache;
+ uint64_t version;
/*
* If this pool already exists, return failure.
@@ -1135,36 +1915,51 @@ spa_create(const char *pool, nvlist_t *nvroot, const char *altroot)
/*
* Allocate a new spa_t structure.
*/
+ (void) nvlist_lookup_string(props,
+ zpool_prop_to_name(ZPOOL_PROP_ALTROOT), &altroot);
spa = spa_add(pool, altroot);
spa_activate(spa);
spa->spa_uberblock.ub_txg = txg - 1;
- spa->spa_uberblock.ub_version = ZFS_VERSION;
+
+ if (props && (error = spa_prop_validate(spa, props))) {
+ spa_unload(spa);
+ spa_deactivate(spa);
+ spa_remove(spa);
+ mutex_exit(&spa_namespace_lock);
+ return (error);
+ }
+
+ if (nvlist_lookup_uint64(props, zpool_prop_to_name(ZPOOL_PROP_VERSION),
+ &version) != 0)
+ version = SPA_VERSION;
+ ASSERT(version <= SPA_VERSION);
+ spa->spa_uberblock.ub_version = version;
spa->spa_ubsync = spa->spa_uberblock;
/*
* Create the root vdev.
*/
- spa_config_enter(spa, RW_WRITER, FTAG);
+ spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
error = spa_config_parse(spa, &rvd, nvroot, NULL, 0, VDEV_ALLOC_ADD);
ASSERT(error != 0 || rvd != NULL);
ASSERT(error != 0 || spa->spa_root_vdev == rvd);
- if (error == 0 && rvd->vdev_children == 0)
+ if (error == 0 && !zfs_allocatable_devs(nvroot))
error = EINVAL;
if (error == 0 &&
(error = vdev_create(rvd, txg, B_FALSE)) == 0 &&
- (error = spa_validate_spares(spa, nvroot, txg,
+ (error = spa_validate_aux(spa, nvroot, txg,
VDEV_ALLOC_ADD)) == 0) {
for (c = 0; c < rvd->vdev_children; c++)
vdev_init(rvd->vdev_child[c], txg);
vdev_config_dirty(rvd);
}
- spa_config_exit(spa, FTAG);
+ spa_config_exit(spa, SCL_ALL, FTAG);
if (error != 0) {
spa_unload(spa);
@@ -1179,17 +1974,32 @@ spa_create(const char *pool, nvlist_t *nvroot, const char *altroot)
*/
if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
&spares, &nspares) == 0) {
- VERIFY(nvlist_alloc(&spa->spa_sparelist, NV_UNIQUE_NAME,
+ VERIFY(nvlist_alloc(&spa->spa_spares.sav_config, NV_UNIQUE_NAME,
KM_SLEEP) == 0);
- VERIFY(nvlist_add_nvlist_array(spa->spa_sparelist,
+ VERIFY(nvlist_add_nvlist_array(spa->spa_spares.sav_config,
ZPOOL_CONFIG_SPARES, spares, nspares) == 0);
- spa_config_enter(spa, RW_WRITER, FTAG);
+ spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
spa_load_spares(spa);
- spa_config_exit(spa, FTAG);
- spa->spa_sync_spares = B_TRUE;
+ spa_config_exit(spa, SCL_ALL, FTAG);
+ spa->spa_spares.sav_sync = B_TRUE;
+ }
+
+ /*
+ * Get the list of level 2 cache devices, if specified.
+ */
+ if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE,
+ &l2cache, &nl2cache) == 0) {
+ VERIFY(nvlist_alloc(&spa->spa_l2cache.sav_config,
+ NV_UNIQUE_NAME, KM_SLEEP) == 0);
+ VERIFY(nvlist_add_nvlist_array(spa->spa_l2cache.sav_config,
+ ZPOOL_CONFIG_L2CACHE, l2cache, nl2cache) == 0);
+ spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
+ spa_load_l2cache(spa);
+ spa_config_exit(spa, SCL_ALL, FTAG);
+ spa->spa_l2cache.sav_sync = B_TRUE;
}
- spa->spa_dsl_pool = dp = dsl_pool_create(spa, txg);
+ spa->spa_dsl_pool = dp = dsl_pool_create(spa, zplprops, txg);
spa->spa_meta_objset = dp->dp_meta_objset;
tx = dmu_tx_create_assigned(dp, txg);
@@ -1198,7 +2008,7 @@ spa_create(const char *pool, nvlist_t *nvroot, const char *altroot)
* Create the pool config object.
*/
spa->spa_config_object = dmu_object_alloc(spa->spa_meta_objset,
- DMU_OT_PACKED_NVLIST, 1 << 14,
+ DMU_OT_PACKED_NVLIST, SPA_CONFIG_BLOCKSIZE,
DMU_OT_PACKED_NVLIST_SIZE, sizeof (uint64_t), tx);
if (zap_add(spa->spa_meta_objset,
@@ -1207,12 +2017,14 @@ spa_create(const char *pool, nvlist_t *nvroot, const char *altroot)
cmn_err(CE_PANIC, "failed to add pool config");
}
- /* Newly created pools are always deflated. */
- spa->spa_deflate = TRUE;
- if (zap_add(spa->spa_meta_objset,
- DMU_POOL_DIRECTORY_OBJECT, DMU_POOL_DEFLATE,
- sizeof (uint64_t), 1, &spa->spa_deflate, tx) != 0) {
- cmn_err(CE_PANIC, "failed to add deflate");
+ /* Newly created pools with the right version are always deflated. */
+ if (version >= SPA_VERSION_RAIDZ_DEFLATE) {
+ spa->spa_deflate = TRUE;
+ if (zap_add(spa->spa_meta_objset,
+ DMU_POOL_DIRECTORY_OBJECT, DMU_POOL_DEFLATE,
+ sizeof (uint64_t), 1, &spa->spa_deflate, tx) != 0) {
+ cmn_err(CE_PANIC, "failed to add deflate");
+ }
}
/*
@@ -1234,11 +2046,20 @@ spa_create(const char *pool, nvlist_t *nvroot, const char *altroot)
/*
* Create the pool's history object.
*/
- spa_history_create_obj(spa, tx);
+ if (version >= SPA_VERSION_ZPOOL_HISTORY)
+ spa_history_create_obj(spa, tx);
+
+ /*
+ * Set pool properties.
+ */
+ spa->spa_bootfs = zpool_prop_default_numeric(ZPOOL_PROP_BOOTFS);
+ spa->spa_delegation = zpool_prop_default_numeric(ZPOOL_PROP_DELEGATION);
+ spa->spa_failmode = zpool_prop_default_numeric(ZPOOL_PROP_FAILUREMODE);
+ if (props)
+ spa_sync_props(spa, props, CRED(), tx);
dmu_tx_commit(tx);
- spa->spa_bootfs = zfs_prop_default_numeric(ZFS_PROP_BOOTFS);
spa->spa_sync_on = B_TRUE;
txg_sync_start(spa->spa_dsl_pool);
@@ -1248,10 +2069,15 @@ spa_create(const char *pool, nvlist_t *nvroot, const char *altroot)
*/
txg_wait_synced(spa->spa_dsl_pool, txg);
- spa_config_sync();
+ spa_config_sync(spa, B_FALSE, B_TRUE);
+
+ if (version >= SPA_VERSION_ZPOOL_HISTORY && history_str != NULL)
+ (void) spa_history_log(spa, history_str, LOG_CMD_POOL_CREATE);
mutex_exit(&spa_namespace_lock);
+ spa->spa_minref = refcount_count(&spa->spa_refcount);
+
return (0);
}
@@ -1259,17 +2085,16 @@ spa_create(const char *pool, nvlist_t *nvroot, const char *altroot)
* Import the given pool into the system. We set up the necessary spa_t and
* then call spa_load() to do the dirty work.
*/
-int
-spa_import(const char *pool, nvlist_t *config, const char *altroot)
+static int
+spa_import_common(const char *pool, nvlist_t *config, nvlist_t *props,
+ boolean_t isroot, boolean_t allowfaulted)
{
spa_t *spa;
- int error;
+ char *altroot = NULL;
+ int error, loaderr;
nvlist_t *nvroot;
- nvlist_t **spares;
- uint_t nspares;
-
- if (!(spa_mode & FWRITE))
- return (EROFS);
+ nvlist_t **spares, **l2cache;
+ uint_t nspares, nl2cache;
/*
* If a pool with this name exists, return failure.
@@ -1283,78 +2108,355 @@ spa_import(const char *pool, nvlist_t *config, const char *altroot)
/*
* Create and initialize the spa structure.
*/
+ (void) nvlist_lookup_string(props,
+ zpool_prop_to_name(ZPOOL_PROP_ALTROOT), &altroot);
spa = spa_add(pool, altroot);
spa_activate(spa);
+ if (allowfaulted)
+ spa->spa_import_faulted = B_TRUE;
+ spa->spa_is_root = isroot;
+
/*
* Pass off the heavy lifting to spa_load().
- * Pass TRUE for mosconfig because the user-supplied config
- * is actually the one to trust when doing an import.
+ * Pass TRUE for mosconfig (unless this is a root pool) because
+ * the user-supplied config is actually the one to trust when
+ * doing an import.
*/
- error = spa_load(spa, config, SPA_LOAD_IMPORT, B_TRUE);
+ loaderr = error = spa_load(spa, config, SPA_LOAD_IMPORT, !isroot);
- spa_config_enter(spa, RW_WRITER, FTAG);
+ spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
/*
* Toss any existing sparelist, as it doesn't have any validity anymore,
* and conflicts with spa_has_spare().
*/
- if (spa->spa_sparelist) {
- nvlist_free(spa->spa_sparelist);
- spa->spa_sparelist = NULL;
+ if (!isroot && spa->spa_spares.sav_config) {
+ nvlist_free(spa->spa_spares.sav_config);
+ spa->spa_spares.sav_config = NULL;
spa_load_spares(spa);
}
+ if (!isroot && spa->spa_l2cache.sav_config) {
+ nvlist_free(spa->spa_l2cache.sav_config);
+ spa->spa_l2cache.sav_config = NULL;
+ spa_load_l2cache(spa);
+ }
VERIFY(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
&nvroot) == 0);
if (error == 0)
- error = spa_validate_spares(spa, nvroot, -1ULL,
- VDEV_ALLOC_SPARE);
- spa_config_exit(spa, FTAG);
+ error = spa_validate_aux(spa, nvroot, -1ULL, VDEV_ALLOC_SPARE);
+ if (error == 0)
+ error = spa_validate_aux(spa, nvroot, -1ULL,
+ VDEV_ALLOC_L2CACHE);
+ spa_config_exit(spa, SCL_ALL, FTAG);
- if (error != 0) {
- spa_unload(spa);
- spa_deactivate(spa);
- spa_remove(spa);
+ if (error != 0 || (props && (error = spa_prop_set(spa, props)))) {
+ if (loaderr != 0 && loaderr != EINVAL && allowfaulted) {
+ /*
+ * If we failed to load the pool, but 'allowfaulted' is
+ * set, then manually set the config as if the config
+ * passed in was specified in the cache file.
+ */
+ error = 0;
+ spa->spa_import_faulted = B_FALSE;
+ if (spa->spa_config == NULL)
+ spa->spa_config = spa_config_generate(spa,
+ NULL, -1ULL, B_TRUE);
+ spa_unload(spa);
+ spa_deactivate(spa);
+ spa_config_sync(spa, B_FALSE, B_TRUE);
+ } else {
+ spa_unload(spa);
+ spa_deactivate(spa);
+ spa_remove(spa);
+ }
mutex_exit(&spa_namespace_lock);
return (error);
}
/*
- * Override any spares as specified by the user, as these may have
- * correct device names/devids, etc.
+ * Override any spares and level 2 cache devices as specified by
+ * the user, as these may have correct device names/devids, etc.
*/
if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
&spares, &nspares) == 0) {
- if (spa->spa_sparelist)
- VERIFY(nvlist_remove(spa->spa_sparelist,
+ if (spa->spa_spares.sav_config)
+ VERIFY(nvlist_remove(spa->spa_spares.sav_config,
ZPOOL_CONFIG_SPARES, DATA_TYPE_NVLIST_ARRAY) == 0);
else
- VERIFY(nvlist_alloc(&spa->spa_sparelist,
+ VERIFY(nvlist_alloc(&spa->spa_spares.sav_config,
NV_UNIQUE_NAME, KM_SLEEP) == 0);
- VERIFY(nvlist_add_nvlist_array(spa->spa_sparelist,
+ VERIFY(nvlist_add_nvlist_array(spa->spa_spares.sav_config,
ZPOOL_CONFIG_SPARES, spares, nspares) == 0);
- spa_config_enter(spa, RW_WRITER, FTAG);
+ spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
spa_load_spares(spa);
- spa_config_exit(spa, FTAG);
- spa->spa_sync_spares = B_TRUE;
+ spa_config_exit(spa, SCL_ALL, FTAG);
+ spa->spa_spares.sav_sync = B_TRUE;
+ }
+ if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE,
+ &l2cache, &nl2cache) == 0) {
+ if (spa->spa_l2cache.sav_config)
+ VERIFY(nvlist_remove(spa->spa_l2cache.sav_config,
+ ZPOOL_CONFIG_L2CACHE, DATA_TYPE_NVLIST_ARRAY) == 0);
+ else
+ VERIFY(nvlist_alloc(&spa->spa_l2cache.sav_config,
+ NV_UNIQUE_NAME, KM_SLEEP) == 0);
+ VERIFY(nvlist_add_nvlist_array(spa->spa_l2cache.sav_config,
+ ZPOOL_CONFIG_L2CACHE, l2cache, nl2cache) == 0);
+ spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
+ spa_load_l2cache(spa);
+ spa_config_exit(spa, SCL_ALL, FTAG);
+ spa->spa_l2cache.sav_sync = B_TRUE;
}
+ if (spa_mode & FWRITE) {
+ /*
+ * Update the config cache to include the newly-imported pool.
+ */
+ spa_config_update_common(spa, SPA_CONFIG_UPDATE_POOL, isroot);
+ }
+
+ spa->spa_import_faulted = B_FALSE;
+ mutex_exit(&spa_namespace_lock);
+
+ return (0);
+}
+
+#if defined(sun)
+#ifdef _KERNEL
+/*
+ * Build a "root" vdev for a top level vdev read in from a rootpool
+ * device label.
+ */
+static void
+spa_build_rootpool_config(nvlist_t *config)
+{
+ nvlist_t *nvtop, *nvroot;
+ uint64_t pgid;
+
/*
- * Update the config cache to include the newly-imported pool.
+ * Add this top-level vdev to the child array.
*/
- spa_config_update(spa, SPA_CONFIG_UPDATE_POOL);
+ VERIFY(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nvtop)
+ == 0);
+ VERIFY(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &pgid)
+ == 0);
- mutex_exit(&spa_namespace_lock);
+ /*
+ * Put this pool's top-level vdevs into a root vdev.
+ */
+ VERIFY(nvlist_alloc(&nvroot, NV_UNIQUE_NAME, KM_SLEEP) == 0);
+ VERIFY(nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT)
+ == 0);
+ VERIFY(nvlist_add_uint64(nvroot, ZPOOL_CONFIG_ID, 0ULL) == 0);
+ VERIFY(nvlist_add_uint64(nvroot, ZPOOL_CONFIG_GUID, pgid) == 0);
+ VERIFY(nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
+ &nvtop, 1) == 0);
/*
- * Resilver anything that's out of date.
+ * Replace the existing vdev_tree with the new root vdev in
+ * this pool's configuration (remove the old, add the new).
*/
- if (spa_mode & FWRITE)
- VERIFY(spa_scrub(spa, POOL_SCRUB_RESILVER, B_TRUE) == 0);
+ VERIFY(nvlist_add_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, nvroot) == 0);
+ nvlist_free(nvroot);
+}
+
+/*
+ * Get the root pool information from the root disk, then import the root pool
+ * during the system boot up time.
+ */
+extern int vdev_disk_read_rootlabel(char *, char *, nvlist_t **);
+
+int
+spa_check_rootconf(char *devpath, char *devid, nvlist_t **bestconf,
+ uint64_t *besttxg)
+{
+ nvlist_t *config;
+ uint64_t txg;
+ int error;
+
+ if (error = vdev_disk_read_rootlabel(devpath, devid, &config))
+ return (error);
+
+ VERIFY(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG, &txg) == 0);
+ if (bestconf != NULL)
+ *bestconf = config;
+ else
+ nvlist_free(config);
+ *besttxg = txg;
return (0);
}
+boolean_t
+spa_rootdev_validate(nvlist_t *nv)
+{
+ uint64_t ival;
+
+ if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_OFFLINE, &ival) == 0 ||
+ nvlist_lookup_uint64(nv, ZPOOL_CONFIG_FAULTED, &ival) == 0 ||
+ nvlist_lookup_uint64(nv, ZPOOL_CONFIG_REMOVED, &ival) == 0)
+ return (B_FALSE);
+
+ return (B_TRUE);
+}
+
+
+/*
+ * Given the boot device's physical path or devid, check if the device
+ * is in a valid state. If so, return the configuration from the vdev
+ * label.
+ */
+int
+spa_get_rootconf(char *devpath, char *devid, nvlist_t **bestconf)
+{
+ nvlist_t *conf = NULL;
+ uint64_t txg = 0;
+ nvlist_t *nvtop, **child;
+ char *type;
+ char *bootpath = NULL;
+ uint_t children, c;
+ char *tmp;
+ int error;
+
+ if (devpath && ((tmp = strchr(devpath, ' ')) != NULL))
+ *tmp = '\0';
+ if (error = spa_check_rootconf(devpath, devid, &conf, &txg)) {
+ cmn_err(CE_NOTE, "error reading device label");
+ return (error);
+ }
+ if (txg == 0) {
+ cmn_err(CE_NOTE, "this device is detached");
+ nvlist_free(conf);
+ return (EINVAL);
+ }
+
+ VERIFY(nvlist_lookup_nvlist(conf, ZPOOL_CONFIG_VDEV_TREE,
+ &nvtop) == 0);
+ VERIFY(nvlist_lookup_string(nvtop, ZPOOL_CONFIG_TYPE, &type) == 0);
+
+ if (strcmp(type, VDEV_TYPE_DISK) == 0) {
+ if (spa_rootdev_validate(nvtop)) {
+ goto out;
+ } else {
+ nvlist_free(conf);
+ return (EINVAL);
+ }
+ }
+
+ ASSERT(strcmp(type, VDEV_TYPE_MIRROR) == 0);
+
+ VERIFY(nvlist_lookup_nvlist_array(nvtop, ZPOOL_CONFIG_CHILDREN,
+ &child, &children) == 0);
+
+ /*
+ * Go thru vdevs in the mirror to see if the given device
+ * has the most recent txg. Only the device with the most
+ * recent txg has valid information and should be booted.
+ */
+ for (c = 0; c < children; c++) {
+ char *cdevid, *cpath;
+ uint64_t tmptxg;
+
+ if (nvlist_lookup_string(child[c], ZPOOL_CONFIG_PHYS_PATH,
+ &cpath) != 0)
+ return (EINVAL);
+ if (nvlist_lookup_string(child[c], ZPOOL_CONFIG_DEVID,
+ &cdevid) != 0)
+ return (EINVAL);
+ if ((spa_check_rootconf(cpath, cdevid, NULL,
+ &tmptxg) == 0) && (tmptxg > txg)) {
+ txg = tmptxg;
+ VERIFY(nvlist_lookup_string(child[c],
+ ZPOOL_CONFIG_PATH, &bootpath) == 0);
+ }
+ }
+
+ /* Does the best device match the one we've booted from? */
+ if (bootpath) {
+ cmn_err(CE_NOTE, "try booting from '%s'", bootpath);
+ return (EINVAL);
+ }
+out:
+ *bestconf = conf;
+ return (0);
+}
+
+/*
+ * Import a root pool.
+ *
+ * For x86. devpath_list will consist of devid and/or physpath name of
+ * the vdev (e.g. "id1,sd@SSEAGATE..." or "/pci@1f,0/ide@d/disk@0,0:a").
+ * The GRUB "findroot" command will return the vdev we should boot.
+ *
+ * For Sparc, devpath_list consists the physpath name of the booting device
+ * no matter the rootpool is a single device pool or a mirrored pool.
+ * e.g.
+ * "/pci@1f,0/ide@d/disk@0,0:a"
+ */
+int
+spa_import_rootpool(char *devpath, char *devid)
+{
+ nvlist_t *conf = NULL;
+ char *pname;
+ int error;
+
+ /*
+ * Get the vdev pathname and configuation from the most
+ * recently updated vdev (highest txg).
+ */
+ if (error = spa_get_rootconf(devpath, devid, &conf))
+ goto msg_out;
+
+ /*
+ * Add type "root" vdev to the config.
+ */
+ spa_build_rootpool_config(conf);
+
+ VERIFY(nvlist_lookup_string(conf, ZPOOL_CONFIG_POOL_NAME, &pname) == 0);
+
+ /*
+ * We specify 'allowfaulted' for this to be treated like spa_open()
+ * instead of spa_import(). This prevents us from marking vdevs as
+ * persistently unavailable, and generates FMA ereports as if it were a
+ * pool open, not import.
+ */
+ error = spa_import_common(pname, conf, NULL, B_TRUE, B_TRUE);
+ if (error == EEXIST)
+ error = 0;
+
+ nvlist_free(conf);
+ return (error);
+
+msg_out:
+ cmn_err(CE_NOTE, "\n"
+ " *************************************************** \n"
+ " * This device is not bootable! * \n"
+ " * It is either offlined or detached or faulted. * \n"
+ " * Please try to boot from a different device. * \n"
+ " *************************************************** ");
+
+ return (error);
+}
+#endif
+#endif
+
+/*
+ * Import a non-root pool into the system.
+ */
+int
+spa_import(const char *pool, nvlist_t *config, nvlist_t *props)
+{
+ return (spa_import_common(pool, config, props, B_FALSE, B_FALSE));
+}
+
+int
+spa_import_faulted(const char *pool, nvlist_t *config, nvlist_t *props)
+{
+ return (spa_import_common(pool, config, props, B_FALSE, B_TRUE));
+}
+
+
/*
* This (illegal) pool name is used when temporarily importing a spa_t in order
* to get the vdev stats associated with the imported devices.
@@ -1393,9 +2495,7 @@ spa_tryimport(nvlist_t *tryconfig)
* If 'tryconfig' was at least parsable, return the current config.
*/
if (spa->spa_root_vdev != NULL) {
- spa_config_enter(spa, RW_READER, FTAG);
config = spa_config_generate(spa, NULL, -1ULL, B_TRUE);
- spa_config_exit(spa, FTAG);
VERIFY(nvlist_add_string(config, ZPOOL_CONFIG_POOL_NAME,
poolname) == 0);
VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_POOL_STATE,
@@ -1404,9 +2504,42 @@ spa_tryimport(nvlist_t *tryconfig)
spa->spa_uberblock.ub_timestamp) == 0);
/*
- * Add the list of hot spares.
+ * If the bootfs property exists on this pool then we
+ * copy it out so that external consumers can tell which
+ * pools are bootable.
+ */
+ if (spa->spa_bootfs) {
+ char *tmpname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+
+ /*
+ * We have to play games with the name since the
+ * pool was opened as TRYIMPORT_NAME.
+ */
+ if (dsl_dsobj_to_dsname(spa_name(spa),
+ spa->spa_bootfs, tmpname) == 0) {
+ char *cp;
+ char *dsname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+
+ cp = strchr(tmpname, '/');
+ if (cp == NULL) {
+ (void) strlcpy(dsname, tmpname,
+ MAXPATHLEN);
+ } else {
+ (void) snprintf(dsname, MAXPATHLEN,
+ "%s/%s", poolname, ++cp);
+ }
+ VERIFY(nvlist_add_string(config,
+ ZPOOL_CONFIG_BOOTFS, dsname) == 0);
+ kmem_free(dsname, MAXPATHLEN);
+ }
+ kmem_free(tmpname, MAXPATHLEN);
+ }
+
+ /*
+ * Add the list of hot spares and level 2 cache devices.
*/
spa_add_spares(spa, config);
+ spa_add_l2cache(spa, config);
}
spa_unload(spa);
@@ -1426,7 +2559,8 @@ spa_tryimport(nvlist_t *tryconfig)
* configuration from the cache afterwards.
*/
static int
-spa_export_common(char *pool, int new_state, nvlist_t **oldconfig)
+spa_export_common(char *pool, int new_state, nvlist_t **oldconfig,
+ boolean_t force)
{
spa_t *spa;
@@ -1461,7 +2595,6 @@ spa_export_common(char *pool, int new_state, nvlist_t **oldconfig)
* Objsets may be open only because they're dirty, so we
* have to force it to sync before checking spa_refcnt.
*/
- spa_scrub_suspend(spa);
txg_wait_synced(spa->spa_dsl_pool, 0);
/*
@@ -1472,14 +2605,23 @@ spa_export_common(char *pool, int new_state, nvlist_t **oldconfig)
if (!spa_refcount_zero(spa) ||
(spa->spa_inject_ref != 0 &&
new_state != POOL_STATE_UNINITIALIZED)) {
- spa_scrub_resume(spa);
spa_async_resume(spa);
mutex_exit(&spa_namespace_lock);
return (EBUSY);
}
- spa_scrub_resume(spa);
- VERIFY(spa_scrub(spa, POOL_SCRUB_NONE, B_TRUE) == 0);
+ /*
+ * A pool cannot be exported if it has an active shared spare.
+ * This is to prevent other pools stealing the active spare
+ * from an exported pool. At user's own will, such pool can
+ * be forcedly exported.
+ */
+ if (!force && new_state == POOL_STATE_EXPORTED &&
+ spa_has_active_shared_spare(spa)) {
+ spa_async_resume(spa);
+ mutex_exit(&spa_namespace_lock);
+ return (EXDEV);
+ }
/*
* We want this to be reflected on every label,
@@ -1487,14 +2629,16 @@ spa_export_common(char *pool, int new_state, nvlist_t **oldconfig)
* final sync that pushes these changes out.
*/
if (new_state != POOL_STATE_UNINITIALIZED) {
- spa_config_enter(spa, RW_WRITER, FTAG);
+ spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
spa->spa_state = new_state;
spa->spa_final_txg = spa_last_synced_txg(spa) + 1;
vdev_config_dirty(spa->spa_root_vdev);
- spa_config_exit(spa, FTAG);
+ spa_config_exit(spa, SCL_ALL, FTAG);
}
}
+ spa_event_notify(spa, NULL, ESC_ZFS_POOL_DESTROY);
+
if (spa->spa_state != POOL_STATE_UNINITIALIZED) {
spa_unload(spa);
spa_deactivate(spa);
@@ -1504,8 +2648,8 @@ spa_export_common(char *pool, int new_state, nvlist_t **oldconfig)
VERIFY(nvlist_dup(spa->spa_config, oldconfig, 0) == 0);
if (new_state != POOL_STATE_UNINITIALIZED) {
+ spa_config_sync(spa, B_TRUE, B_TRUE);
spa_remove(spa);
- spa_config_sync();
}
mutex_exit(&spa_namespace_lock);
@@ -1518,16 +2662,16 @@ spa_export_common(char *pool, int new_state, nvlist_t **oldconfig)
int
spa_destroy(char *pool)
{
- return (spa_export_common(pool, POOL_STATE_DESTROYED, NULL));
+ return (spa_export_common(pool, POOL_STATE_DESTROYED, NULL, B_FALSE));
}
/*
* Export a storage pool.
*/
int
-spa_export(char *pool, nvlist_t **oldconfig)
+spa_export(char *pool, nvlist_t **oldconfig, boolean_t force)
{
- return (spa_export_common(pool, POOL_STATE_EXPORTED, oldconfig));
+ return (spa_export_common(pool, POOL_STATE_EXPORTED, oldconfig, force));
}
/*
@@ -1537,10 +2681,10 @@ spa_export(char *pool, nvlist_t **oldconfig)
int
spa_reset(char *pool)
{
- return (spa_export_common(pool, POOL_STATE_UNINITIALIZED, NULL));
+ return (spa_export_common(pool, POOL_STATE_UNINITIALIZED, NULL,
+ B_FALSE));
}
-
/*
* ==========================================================================
* Device manipulation
@@ -1548,7 +2692,7 @@ spa_reset(char *pool)
*/
/*
- * Add capacity to a storage pool.
+ * Add a device to a storage pool.
*/
int
spa_vdev_add(spa_t *spa, nvlist_t *nvroot)
@@ -1557,8 +2701,8 @@ spa_vdev_add(spa_t *spa, nvlist_t *nvroot)
int c, error;
vdev_t *rvd = spa->spa_root_vdev;
vdev_t *vd, *tvd;
- nvlist_t **spares;
- uint_t i, nspares;
+ nvlist_t **spares, **l2cache;
+ uint_t nspares, nl2cache;
txg = spa_vdev_enter(spa);
@@ -1566,35 +2710,29 @@ spa_vdev_add(spa_t *spa, nvlist_t *nvroot)
VDEV_ALLOC_ADD)) != 0)
return (spa_vdev_exit(spa, NULL, txg, error));
- spa->spa_pending_vdev = vd;
+ spa->spa_pending_vdev = vd; /* spa_vdev_exit() will clear this */
- if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
- &spares, &nspares) != 0)
+ if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES, &spares,
+ &nspares) != 0)
nspares = 0;
- if (vd->vdev_children == 0 && nspares == 0) {
- spa->spa_pending_vdev = NULL;
+ if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE, &l2cache,
+ &nl2cache) != 0)
+ nl2cache = 0;
+
+ if (vd->vdev_children == 0 && nspares == 0 && nl2cache == 0)
return (spa_vdev_exit(spa, vd, txg, EINVAL));
- }
- if (vd->vdev_children != 0) {
- if ((error = vdev_create(vd, txg, B_FALSE)) != 0) {
- spa->spa_pending_vdev = NULL;
- return (spa_vdev_exit(spa, vd, txg, error));
- }
- }
+ if (vd->vdev_children != 0 &&
+ (error = vdev_create(vd, txg, B_FALSE)) != 0)
+ return (spa_vdev_exit(spa, vd, txg, error));
/*
- * We must validate the spares after checking the children. Otherwise,
- * vdev_inuse() will blindly overwrite the spare.
+ * We must validate the spares and l2cache devices after checking the
+ * children. Otherwise, vdev_inuse() will blindly overwrite the spare.
*/
- if ((error = spa_validate_spares(spa, nvroot, txg,
- VDEV_ALLOC_ADD)) != 0) {
- spa->spa_pending_vdev = NULL;
+ if ((error = spa_validate_aux(spa, nvroot, txg, VDEV_ALLOC_ADD)) != 0)
return (spa_vdev_exit(spa, vd, txg, error));
- }
-
- spa->spa_pending_vdev = NULL;
/*
* Transfer each new top-level vdev from vd to rvd.
@@ -1608,43 +2746,17 @@ spa_vdev_add(spa_t *spa, nvlist_t *nvroot)
}
if (nspares != 0) {
- if (spa->spa_sparelist != NULL) {
- nvlist_t **oldspares;
- uint_t oldnspares;
- nvlist_t **newspares;
-
- VERIFY(nvlist_lookup_nvlist_array(spa->spa_sparelist,
- ZPOOL_CONFIG_SPARES, &oldspares, &oldnspares) == 0);
-
- newspares = kmem_alloc(sizeof (void *) *
- (nspares + oldnspares), KM_SLEEP);
- for (i = 0; i < oldnspares; i++)
- VERIFY(nvlist_dup(oldspares[i],
- &newspares[i], KM_SLEEP) == 0);
- for (i = 0; i < nspares; i++)
- VERIFY(nvlist_dup(spares[i],
- &newspares[i + oldnspares],
- KM_SLEEP) == 0);
-
- VERIFY(nvlist_remove(spa->spa_sparelist,
- ZPOOL_CONFIG_SPARES, DATA_TYPE_NVLIST_ARRAY) == 0);
-
- VERIFY(nvlist_add_nvlist_array(spa->spa_sparelist,
- ZPOOL_CONFIG_SPARES, newspares,
- nspares + oldnspares) == 0);
- for (i = 0; i < oldnspares + nspares; i++)
- nvlist_free(newspares[i]);
- kmem_free(newspares, (oldnspares + nspares) *
- sizeof (void *));
- } else {
- VERIFY(nvlist_alloc(&spa->spa_sparelist,
- NV_UNIQUE_NAME, KM_SLEEP) == 0);
- VERIFY(nvlist_add_nvlist_array(spa->spa_sparelist,
- ZPOOL_CONFIG_SPARES, spares, nspares) == 0);
- }
-
+ spa_set_aux_vdevs(&spa->spa_spares, spares, nspares,
+ ZPOOL_CONFIG_SPARES);
spa_load_spares(spa);
- spa->spa_sync_spares = B_TRUE;
+ spa->spa_spares.sav_sync = B_TRUE;
+ }
+
+ if (nl2cache != 0) {
+ spa_set_aux_vdevs(&spa->spa_l2cache, l2cache, nl2cache,
+ ZPOOL_CONFIG_L2CACHE);
+ spa_load_l2cache(spa);
+ spa->spa_l2cache.sav_sync = B_TRUE;
}
/*
@@ -1676,7 +2788,7 @@ spa_vdev_add(spa_t *spa, nvlist_t *nvroot)
*
* If 'replacing' is specified, the new device is intended to replace the
* existing device; in this case the two devices are made into their own
- * mirror using the 'replacing' vdev, which is functionally idendical to
+ * mirror using the 'replacing' vdev, which is functionally identical to
* the mirror vdev (it actually reuses all the same ops) but has a few
* extra rules: you can't attach to it after it's been created, and upon
* completion of resilvering, the first disk (the one being replaced)
@@ -1686,14 +2798,17 @@ int
spa_vdev_attach(spa_t *spa, uint64_t guid, nvlist_t *nvroot, int replacing)
{
uint64_t txg, open_txg;
- int error;
vdev_t *rvd = spa->spa_root_vdev;
vdev_t *oldvd, *newvd, *newrootvd, *pvd, *tvd;
vdev_ops_t *pvops;
+ dmu_tx_t *tx;
+ char *oldvdpath, *newvdpath;
+ int newvd_isspare;
+ int error;
txg = spa_vdev_enter(spa);
- oldvd = vdev_lookup_by_guid(rvd, guid);
+ oldvd = spa_lookup_by_guid(spa, guid, B_FALSE);
if (oldvd == NULL)
return (spa_vdev_exit(spa, NULL, txg, ENODEV));
@@ -1704,7 +2819,10 @@ spa_vdev_attach(spa_t *spa, uint64_t guid, nvlist_t *nvroot, int replacing)
pvd = oldvd->vdev_parent;
if ((error = spa_config_parse(spa, &newrootvd, nvroot, NULL, 0,
- VDEV_ALLOC_ADD)) != 0 || newrootvd->vdev_children != 1)
+ VDEV_ALLOC_ADD)) != 0)
+ return (spa_vdev_exit(spa, NULL, txg, EINVAL));
+
+ if (newrootvd->vdev_children != 1)
return (spa_vdev_exit(spa, newrootvd, txg, EINVAL));
newvd = newrootvd->vdev_child[0];
@@ -1715,6 +2833,12 @@ spa_vdev_attach(spa_t *spa, uint64_t guid, nvlist_t *nvroot, int replacing)
if ((error = vdev_create(newrootvd, txg, replacing)) != 0)
return (spa_vdev_exit(spa, newrootvd, txg, error));
+ /*
+ * Spares can't replace logs
+ */
+ if (oldvd->vdev_top->vdev_islog && newvd->vdev_isspare)
+ return (spa_vdev_exit(spa, newrootvd, txg, ENOTSUP));
+
if (!replacing) {
/*
* For attach, the only allowable parent is a mirror or the root
@@ -1828,6 +2952,9 @@ spa_vdev_attach(spa_t *spa, uint64_t guid, nvlist_t *nvroot, int replacing)
if (newvd->vdev_isspare)
spa_spare_activate(newvd);
+ oldvdpath = spa_strdup(oldvd->vdev_path);
+ newvdpath = spa_strdup(newvd->vdev_path);
+ newvd_isspare = newvd->vdev_isspare;
/*
* Mark newvd's DTL dirty in this txg.
@@ -1836,10 +2963,25 @@ spa_vdev_attach(spa_t *spa, uint64_t guid, nvlist_t *nvroot, int replacing)
(void) spa_vdev_exit(spa, newrootvd, open_txg, 0);
+ tx = dmu_tx_create_dd(spa_get_dsl(spa)->dp_mos_dir);
+ if (dmu_tx_assign(tx, TXG_WAIT) == 0) {
+ spa_history_internal_log(LOG_POOL_VDEV_ATTACH, spa, tx,
+ CRED(), "%s vdev=%s %s vdev=%s",
+ replacing && newvd_isspare ? "spare in" :
+ replacing ? "replace" : "attach", newvdpath,
+ replacing ? "for" : "to", oldvdpath);
+ dmu_tx_commit(tx);
+ } else {
+ dmu_tx_abort(tx);
+ }
+
+ spa_strfree(oldvdpath);
+ spa_strfree(newvdpath);
+
/*
* Kick off a resilver to update newvd.
*/
- VERIFY(spa_scrub(spa, POOL_SCRUB_RESILVER, B_TRUE) == 0);
+ VERIFY3U(spa_scrub(spa, POOL_SCRUB_RESILVER), ==, 0);
return (0);
}
@@ -1858,10 +3000,11 @@ spa_vdev_detach(spa_t *spa, uint64_t guid, int replace_done)
vdev_t *vd, *pvd, *cvd, *tvd;
boolean_t unspare = B_FALSE;
uint64_t unspare_guid;
+ size_t len;
txg = spa_vdev_enter(spa);
- vd = vdev_lookup_by_guid(rvd, guid);
+ vd = spa_lookup_by_guid(spa, guid, B_FALSE);
if (vd == NULL)
return (spa_vdev_exit(spa, NULL, txg, ENODEV));
@@ -1886,7 +3029,7 @@ spa_vdev_detach(spa_t *spa, uint64_t guid, int replace_done)
}
ASSERT(pvd->vdev_ops != &vdev_spare_ops ||
- spa_version(spa) >= ZFS_VERSION_SPARES);
+ spa_version(spa) >= SPA_VERSION_SPARES);
/*
* Only mirror, replacing, and spare vdevs support detach.
@@ -1925,13 +3068,26 @@ spa_vdev_detach(spa_t *spa, uint64_t guid, int replace_done)
break;
}
+ if (c == pvd->vdev_children)
+ return (spa_vdev_exit(spa, NULL, txg, EBUSY));
+
/*
- * If we are a replacing or spare vdev, then we can always detach the
- * latter child, as that is how one cancels the operation.
+ * If we are detaching the second disk from a replacing vdev, then
+ * check to see if we changed the original vdev's path to have "/old"
+ * at the end in spa_vdev_attach(). If so, undo that change now.
*/
- if ((pvd->vdev_ops == &vdev_mirror_ops || vd->vdev_id != 1) &&
- c == pvd->vdev_children)
- return (spa_vdev_exit(spa, NULL, txg, EBUSY));
+ if (pvd->vdev_ops == &vdev_replacing_ops && vd->vdev_id == 1 &&
+ pvd->vdev_child[0]->vdev_path != NULL &&
+ pvd->vdev_child[1]->vdev_path != NULL) {
+ ASSERT(pvd->vdev_child[1] == vd);
+ cvd = pvd->vdev_child[0];
+ len = strlen(vd->vdev_path);
+ if (strncmp(cvd->vdev_path, vd->vdev_path, len) == 0 &&
+ strcmp(cvd->vdev_path + len, "/old") == 0) {
+ spa_strfree(cvd->vdev_path);
+ cvd->vdev_path = spa_strdup(vd->vdev_path);
+ }
+ }
/*
* If we are detaching the original disk from a spare, then it implies
@@ -1992,7 +3148,7 @@ spa_vdev_detach(spa_t *spa, uint64_t guid, int replace_done)
/*
* Reevaluate the parent vdev state.
*/
- vdev_propagate_state(cvd->vdev_parent);
+ vdev_propagate_state(cvd);
/*
* If the device we just detached was smaller than the others, it may be
@@ -2015,6 +3171,8 @@ spa_vdev_detach(spa_t *spa, uint64_t guid, int replace_done)
vd->vdev_detached = B_TRUE;
vdev_dirty(tvd, VDD_DTL, vd, txg);
+ spa_event_notify(spa, vd, ESC_ZFS_VDEV_REMOVE);
+
error = spa_vdev_exit(spa, vd, txg, 0);
/*
@@ -2028,8 +3186,11 @@ spa_vdev_detach(spa_t *spa, uint64_t guid, int replace_done)
while ((spa = spa_next(spa)) != NULL) {
if (spa->spa_state != POOL_STATE_ACTIVE)
continue;
-
+ spa_open_ref(spa, FTAG);
+ mutex_exit(&spa_namespace_lock);
(void) spa_vdev_remove(spa, unspare_guid, B_TRUE);
+ mutex_enter(&spa_namespace_lock);
+ spa_close(spa, FTAG);
}
mutex_exit(&spa_namespace_lock);
}
@@ -2037,100 +3198,125 @@ spa_vdev_detach(spa_t *spa, uint64_t guid, int replace_done)
return (error);
}
+static nvlist_t *
+spa_nvlist_lookup_by_guid(nvlist_t **nvpp, int count, uint64_t target_guid)
+{
+ for (int i = 0; i < count; i++) {
+ uint64_t guid;
+
+ VERIFY(nvlist_lookup_uint64(nvpp[i], ZPOOL_CONFIG_GUID,
+ &guid) == 0);
+
+ if (guid == target_guid)
+ return (nvpp[i]);
+ }
+
+ return (NULL);
+}
+
+static void
+spa_vdev_remove_aux(nvlist_t *config, char *name, nvlist_t **dev, int count,
+ nvlist_t *dev_to_remove)
+{
+ nvlist_t **newdev = NULL;
+
+ if (count > 1)
+ newdev = kmem_alloc((count - 1) * sizeof (void *), KM_SLEEP);
+
+ for (int i = 0, j = 0; i < count; i++) {
+ if (dev[i] == dev_to_remove)
+ continue;
+ VERIFY(nvlist_dup(dev[i], &newdev[j++], KM_SLEEP) == 0);
+ }
+
+ VERIFY(nvlist_remove(config, name, DATA_TYPE_NVLIST_ARRAY) == 0);
+ VERIFY(nvlist_add_nvlist_array(config, name, newdev, count - 1) == 0);
+
+ for (int i = 0; i < count - 1; i++)
+ nvlist_free(newdev[i]);
+
+ if (count > 1)
+ kmem_free(newdev, (count - 1) * sizeof (void *));
+}
+
/*
* Remove a device from the pool. Currently, this supports removing only hot
- * spares.
+ * spares and level 2 ARC devices.
*/
int
spa_vdev_remove(spa_t *spa, uint64_t guid, boolean_t unspare)
{
vdev_t *vd;
- nvlist_t **spares, *nv, **newspares;
- uint_t i, j, nspares;
- int ret = 0;
-
- spa_config_enter(spa, RW_WRITER, FTAG);
+ nvlist_t **spares, **l2cache, *nv;
+ uint_t nspares, nl2cache;
+ uint64_t txg;
+ int error = 0;
- vd = spa_lookup_by_guid(spa, guid);
+ txg = spa_vdev_enter(spa);
- nv = NULL;
- if (spa->spa_spares != NULL &&
- nvlist_lookup_nvlist_array(spa->spa_sparelist, ZPOOL_CONFIG_SPARES,
- &spares, &nspares) == 0) {
- for (i = 0; i < nspares; i++) {
- uint64_t theguid;
+ vd = spa_lookup_by_guid(spa, guid, B_FALSE);
- VERIFY(nvlist_lookup_uint64(spares[i],
- ZPOOL_CONFIG_GUID, &theguid) == 0);
- if (theguid == guid) {
- nv = spares[i];
- break;
- }
+ if (spa->spa_spares.sav_vdevs != NULL &&
+ nvlist_lookup_nvlist_array(spa->spa_spares.sav_config,
+ ZPOOL_CONFIG_SPARES, &spares, &nspares) == 0 &&
+ (nv = spa_nvlist_lookup_by_guid(spares, nspares, guid)) != NULL) {
+ /*
+ * Only remove the hot spare if it's not currently in use
+ * in this pool.
+ */
+ if (vd == NULL || unspare) {
+ spa_vdev_remove_aux(spa->spa_spares.sav_config,
+ ZPOOL_CONFIG_SPARES, spares, nspares, nv);
+ spa_load_spares(spa);
+ spa->spa_spares.sav_sync = B_TRUE;
+ } else {
+ error = EBUSY;
}
- }
-
- /*
- * We only support removing a hot spare, and only if it's not currently
- * in use in this pool.
- */
- if (nv == NULL && vd == NULL) {
- ret = ENOENT;
- goto out;
- }
-
- if (nv == NULL && vd != NULL) {
- ret = ENOTSUP;
- goto out;
- }
-
- if (!unspare && nv != NULL && vd != NULL) {
- ret = EBUSY;
- goto out;
- }
-
- if (nspares == 1) {
- newspares = NULL;
+ } else if (spa->spa_l2cache.sav_vdevs != NULL &&
+ nvlist_lookup_nvlist_array(spa->spa_l2cache.sav_config,
+ ZPOOL_CONFIG_L2CACHE, &l2cache, &nl2cache) == 0 &&
+ (nv = spa_nvlist_lookup_by_guid(l2cache, nl2cache, guid)) != NULL) {
+ /*
+ * Cache devices can always be removed.
+ */
+ spa_vdev_remove_aux(spa->spa_l2cache.sav_config,
+ ZPOOL_CONFIG_L2CACHE, l2cache, nl2cache, nv);
+ spa_load_l2cache(spa);
+ spa->spa_l2cache.sav_sync = B_TRUE;
+ } else if (vd != NULL) {
+ /*
+ * Normal vdevs cannot be removed (yet).
+ */
+ error = ENOTSUP;
} else {
- newspares = kmem_alloc((nspares - 1) * sizeof (void *),
- KM_SLEEP);
- for (i = 0, j = 0; i < nspares; i++) {
- if (spares[i] != nv)
- VERIFY(nvlist_dup(spares[i],
- &newspares[j++], KM_SLEEP) == 0);
- }
+ /*
+ * There is no vdev of any kind with the specified guid.
+ */
+ error = ENOENT;
}
- VERIFY(nvlist_remove(spa->spa_sparelist, ZPOOL_CONFIG_SPARES,
- DATA_TYPE_NVLIST_ARRAY) == 0);
- VERIFY(nvlist_add_nvlist_array(spa->spa_sparelist, ZPOOL_CONFIG_SPARES,
- newspares, nspares - 1) == 0);
- for (i = 0; i < nspares - 1; i++)
- nvlist_free(newspares[i]);
- kmem_free(newspares, (nspares - 1) * sizeof (void *));
- spa_load_spares(spa);
- spa->spa_sync_spares = B_TRUE;
-
-out:
- spa_config_exit(spa, FTAG);
-
- return (ret);
+ return (spa_vdev_exit(spa, NULL, txg, error));
}
/*
- * Find any device that's done replacing, so we can detach it.
+ * Find any device that's done replacing, or a vdev marked 'unspare' that's
+ * current spared, so we can detach it.
*/
static vdev_t *
-spa_vdev_replace_done_hunt(vdev_t *vd)
+spa_vdev_resilver_done_hunt(vdev_t *vd)
{
vdev_t *newvd, *oldvd;
int c;
for (c = 0; c < vd->vdev_children; c++) {
- oldvd = spa_vdev_replace_done_hunt(vd->vdev_child[c]);
+ oldvd = spa_vdev_resilver_done_hunt(vd->vdev_child[c]);
if (oldvd != NULL)
return (oldvd);
}
+ /*
+ * Check for a completed replacement.
+ */
if (vd->vdev_ops == &vdev_replacing_ops && vd->vdev_children == 2) {
oldvd = vd->vdev_child[0];
newvd = vd->vdev_child[1];
@@ -2144,20 +3330,38 @@ spa_vdev_replace_done_hunt(vdev_t *vd)
mutex_exit(&newvd->vdev_dtl_lock);
}
+ /*
+ * Check for a completed resilver with the 'unspare' flag set.
+ */
+ if (vd->vdev_ops == &vdev_spare_ops && vd->vdev_children == 2) {
+ newvd = vd->vdev_child[0];
+ oldvd = vd->vdev_child[1];
+
+ mutex_enter(&newvd->vdev_dtl_lock);
+ if (newvd->vdev_unspare &&
+ newvd->vdev_dtl_map.sm_space == 0 &&
+ newvd->vdev_dtl_scrub.sm_space == 0) {
+ newvd->vdev_unspare = 0;
+ mutex_exit(&newvd->vdev_dtl_lock);
+ return (oldvd);
+ }
+ mutex_exit(&newvd->vdev_dtl_lock);
+ }
+
return (NULL);
}
static void
-spa_vdev_replace_done(spa_t *spa)
+spa_vdev_resilver_done(spa_t *spa)
{
vdev_t *vd;
vdev_t *pvd;
uint64_t guid;
uint64_t pguid = 0;
- spa_config_enter(spa, RW_READER, FTAG);
+ spa_config_enter(spa, SCL_CONFIG, FTAG, RW_READER);
- while ((vd = spa_vdev_replace_done_hunt(spa->spa_root_vdev)) != NULL) {
+ while ((vd = spa_vdev_resilver_done_hunt(spa->spa_root_vdev)) != NULL) {
guid = vd->vdev_guid;
/*
* If we have just finished replacing a hot spared device, then
@@ -2171,15 +3375,15 @@ spa_vdev_replace_done(spa_t *spa)
ASSERT(pvd->vdev_parent->vdev_children == 2);
pguid = pvd->vdev_parent->vdev_child[1]->vdev_guid;
}
- spa_config_exit(spa, FTAG);
+ spa_config_exit(spa, SCL_CONFIG, FTAG);
if (spa_vdev_detach(spa, guid, B_TRUE) != 0)
return;
if (pguid != 0 && spa_vdev_detach(spa, pguid, B_TRUE) != 0)
return;
- spa_config_enter(spa, RW_READER, FTAG);
+ spa_config_enter(spa, SCL_CONFIG, FTAG, RW_READER);
}
- spa_config_exit(spa, FTAG);
+ spa_config_exit(spa, SCL_CONFIG, FTAG);
}
/*
@@ -2189,42 +3393,40 @@ spa_vdev_replace_done(spa_t *spa)
int
spa_vdev_setpath(spa_t *spa, uint64_t guid, const char *newpath)
{
- vdev_t *rvd, *vd;
+ vdev_t *vd;
uint64_t txg;
- rvd = spa->spa_root_vdev;
-
txg = spa_vdev_enter(spa);
- if ((vd = vdev_lookup_by_guid(rvd, guid)) == NULL) {
+ if ((vd = spa_lookup_by_guid(spa, guid, B_TRUE)) == NULL) {
/*
- * Determine if this is a reference to a hot spare. In that
- * case, update the path as stored in the spare list.
+ * Determine if this is a reference to a hot spare device. If
+ * it is, update the path manually as there is no associated
+ * vdev_t that can be synced to disk.
*/
nvlist_t **spares;
uint_t i, nspares;
- if (spa->spa_sparelist != NULL) {
- VERIFY(nvlist_lookup_nvlist_array(spa->spa_sparelist,
- ZPOOL_CONFIG_SPARES, &spares, &nspares) == 0);
+
+ if (spa->spa_spares.sav_config != NULL) {
+ VERIFY(nvlist_lookup_nvlist_array(
+ spa->spa_spares.sav_config, ZPOOL_CONFIG_SPARES,
+ &spares, &nspares) == 0);
for (i = 0; i < nspares; i++) {
uint64_t theguid;
VERIFY(nvlist_lookup_uint64(spares[i],
ZPOOL_CONFIG_GUID, &theguid) == 0);
- if (theguid == guid)
- break;
+ if (theguid == guid) {
+ VERIFY(nvlist_add_string(spares[i],
+ ZPOOL_CONFIG_PATH, newpath) == 0);
+ spa_load_spares(spa);
+ spa->spa_spares.sav_sync = B_TRUE;
+ return (spa_vdev_exit(spa, NULL, txg,
+ 0));
+ }
}
-
- if (i == nspares)
- return (spa_vdev_exit(spa, NULL, txg, ENOENT));
-
- VERIFY(nvlist_add_string(spares[i],
- ZPOOL_CONFIG_PATH, newpath) == 0);
- spa_load_spares(spa);
- spa->spa_sync_spares = B_TRUE;
- return (spa_vdev_exit(spa, NULL, txg, 0));
- } else {
- return (spa_vdev_exit(spa, NULL, txg, ENOENT));
}
+
+ return (spa_vdev_exit(spa, NULL, txg, ENOENT));
}
if (!vd->vdev_ops->vdev_op_leaf)
@@ -2244,397 +3446,36 @@ spa_vdev_setpath(spa_t *spa, uint64_t guid, const char *newpath)
* ==========================================================================
*/
-static void
-spa_scrub_io_done(zio_t *zio)
-{
- spa_t *spa = zio->io_spa;
-
- zio_data_buf_free(zio->io_data, zio->io_size);
-
- mutex_enter(&spa->spa_scrub_lock);
- if (zio->io_error && !(zio->io_flags & ZIO_FLAG_SPECULATIVE)) {
- vdev_t *vd = zio->io_vd ? zio->io_vd : spa->spa_root_vdev;
- spa->spa_scrub_errors++;
- mutex_enter(&vd->vdev_stat_lock);
- vd->vdev_stat.vs_scrub_errors++;
- mutex_exit(&vd->vdev_stat_lock);
- }
-
- if (--spa->spa_scrub_inflight < spa->spa_scrub_maxinflight)
- cv_broadcast(&spa->spa_scrub_io_cv);
-
- ASSERT(spa->spa_scrub_inflight >= 0);
-
- mutex_exit(&spa->spa_scrub_lock);
-}
-
-static void
-spa_scrub_io_start(spa_t *spa, blkptr_t *bp, int priority, int flags,
- zbookmark_t *zb)
-{
- size_t size = BP_GET_LSIZE(bp);
- void *data;
-
- mutex_enter(&spa->spa_scrub_lock);
- /*
- * Do not give too much work to vdev(s).
- */
- while (spa->spa_scrub_inflight >= spa->spa_scrub_maxinflight) {
- cv_wait(&spa->spa_scrub_io_cv, &spa->spa_scrub_lock);
- }
- spa->spa_scrub_inflight++;
- mutex_exit(&spa->spa_scrub_lock);
-
- data = zio_data_buf_alloc(size);
-
- if (zb->zb_level == -1 && BP_GET_TYPE(bp) != DMU_OT_OBJSET)
- flags |= ZIO_FLAG_SPECULATIVE; /* intent log block */
-
- flags |= ZIO_FLAG_SCRUB_THREAD | ZIO_FLAG_CANFAIL;
-
- zio_nowait(zio_read(NULL, spa, bp, data, size,
- spa_scrub_io_done, NULL, priority, flags, zb));
-}
-
-/* ARGSUSED */
-static int
-spa_scrub_cb(traverse_blk_cache_t *bc, spa_t *spa, void *a)
-{
- blkptr_t *bp = &bc->bc_blkptr;
- vdev_t *vd = spa->spa_root_vdev;
- dva_t *dva = bp->blk_dva;
- int needs_resilver = B_FALSE;
- int d;
-
- if (bc->bc_errno) {
- /*
- * We can't scrub this block, but we can continue to scrub
- * the rest of the pool. Note the error and move along.
- */
- mutex_enter(&spa->spa_scrub_lock);
- spa->spa_scrub_errors++;
- mutex_exit(&spa->spa_scrub_lock);
-
- mutex_enter(&vd->vdev_stat_lock);
- vd->vdev_stat.vs_scrub_errors++;
- mutex_exit(&vd->vdev_stat_lock);
-
- return (ERESTART);
- }
-
- ASSERT(bp->blk_birth < spa->spa_scrub_maxtxg);
-
- for (d = 0; d < BP_GET_NDVAS(bp); d++) {
- vd = vdev_lookup_top(spa, DVA_GET_VDEV(&dva[d]));
-
- ASSERT(vd != NULL);
-
- /*
- * Keep track of how much data we've examined so that
- * zpool(1M) status can make useful progress reports.
- */
- mutex_enter(&vd->vdev_stat_lock);
- vd->vdev_stat.vs_scrub_examined += DVA_GET_ASIZE(&dva[d]);
- mutex_exit(&vd->vdev_stat_lock);
-
- if (spa->spa_scrub_type == POOL_SCRUB_RESILVER) {
- if (DVA_GET_GANG(&dva[d])) {
- /*
- * Gang members may be spread across multiple
- * vdevs, so the best we can do is look at the
- * pool-wide DTL.
- * XXX -- it would be better to change our
- * allocation policy to ensure that this can't
- * happen.
- */
- vd = spa->spa_root_vdev;
- }
- if (vdev_dtl_contains(&vd->vdev_dtl_map,
- bp->blk_birth, 1))
- needs_resilver = B_TRUE;
- }
- }
-
- if (spa->spa_scrub_type == POOL_SCRUB_EVERYTHING)
- spa_scrub_io_start(spa, bp, ZIO_PRIORITY_SCRUB,
- ZIO_FLAG_SCRUB, &bc->bc_bookmark);
- else if (needs_resilver)
- spa_scrub_io_start(spa, bp, ZIO_PRIORITY_RESILVER,
- ZIO_FLAG_RESILVER, &bc->bc_bookmark);
-
- return (0);
-}
-
-static void
-spa_scrub_thread(void *arg)
-{
- spa_t *spa = arg;
- callb_cpr_t cprinfo;
- traverse_handle_t *th = spa->spa_scrub_th;
- vdev_t *rvd = spa->spa_root_vdev;
- pool_scrub_type_t scrub_type = spa->spa_scrub_type;
- int error = 0;
- boolean_t complete;
-
- CALLB_CPR_INIT(&cprinfo, &spa->spa_scrub_lock, callb_generic_cpr, FTAG);
-
- /*
- * If we're restarting due to a snapshot create/delete,
- * wait for that to complete.
- */
- txg_wait_synced(spa_get_dsl(spa), 0);
-
- dprintf("start %s mintxg=%llu maxtxg=%llu\n",
- scrub_type == POOL_SCRUB_RESILVER ? "resilver" : "scrub",
- spa->spa_scrub_mintxg, spa->spa_scrub_maxtxg);
-
- spa_config_enter(spa, RW_WRITER, FTAG);
- vdev_reopen(rvd); /* purge all vdev caches */
- vdev_config_dirty(rvd); /* rewrite all disk labels */
- vdev_scrub_stat_update(rvd, scrub_type, B_FALSE);
- spa_config_exit(spa, FTAG);
-
- mutex_enter(&spa->spa_scrub_lock);
- spa->spa_scrub_errors = 0;
- spa->spa_scrub_active = 1;
- ASSERT(spa->spa_scrub_inflight == 0);
-
- while (!spa->spa_scrub_stop) {
- CALLB_CPR_SAFE_BEGIN(&cprinfo);
- while (spa->spa_scrub_suspended) {
- spa->spa_scrub_active = 0;
- cv_broadcast(&spa->spa_scrub_cv);
- cv_wait(&spa->spa_scrub_cv, &spa->spa_scrub_lock);
- spa->spa_scrub_active = 1;
- }
- CALLB_CPR_SAFE_END(&cprinfo, &spa->spa_scrub_lock);
-
- if (spa->spa_scrub_restart_txg != 0)
- break;
-
- mutex_exit(&spa->spa_scrub_lock);
- error = traverse_more(th);
- mutex_enter(&spa->spa_scrub_lock);
- if (error != EAGAIN)
- break;
- }
-
- while (spa->spa_scrub_inflight)
- cv_wait(&spa->spa_scrub_io_cv, &spa->spa_scrub_lock);
-
- spa->spa_scrub_active = 0;
- cv_broadcast(&spa->spa_scrub_cv);
-
- mutex_exit(&spa->spa_scrub_lock);
-
- spa_config_enter(spa, RW_WRITER, FTAG);
-
- mutex_enter(&spa->spa_scrub_lock);
-
- /*
- * Note: we check spa_scrub_restart_txg under both spa_scrub_lock
- * AND the spa config lock to synchronize with any config changes
- * that revise the DTLs under spa_vdev_enter() / spa_vdev_exit().
- */
- if (spa->spa_scrub_restart_txg != 0)
- error = ERESTART;
-
- if (spa->spa_scrub_stop)
- error = EINTR;
-
- /*
- * Even if there were uncorrectable errors, we consider the scrub
- * completed. The downside is that if there is a transient error during
- * a resilver, we won't resilver the data properly to the target. But
- * if the damage is permanent (more likely) we will resilver forever,
- * which isn't really acceptable. Since there is enough information for
- * the user to know what has failed and why, this seems like a more
- * tractable approach.
- */
- complete = (error == 0);
-
- dprintf("end %s to maxtxg=%llu %s, traverse=%d, %llu errors, stop=%u\n",
- scrub_type == POOL_SCRUB_RESILVER ? "resilver" : "scrub",
- spa->spa_scrub_maxtxg, complete ? "done" : "FAILED",
- error, spa->spa_scrub_errors, spa->spa_scrub_stop);
-
- mutex_exit(&spa->spa_scrub_lock);
-
- /*
- * If the scrub/resilver completed, update all DTLs to reflect this.
- * Whether it succeeded or not, vacate all temporary scrub DTLs.
- */
- vdev_dtl_reassess(rvd, spa_last_synced_txg(spa) + 1,
- complete ? spa->spa_scrub_maxtxg : 0, B_TRUE);
- vdev_scrub_stat_update(rvd, POOL_SCRUB_NONE, complete);
- spa_errlog_rotate(spa);
-
- spa_config_exit(spa, FTAG);
-
- mutex_enter(&spa->spa_scrub_lock);
-
- /*
- * We may have finished replacing a device.
- * Let the async thread assess this and handle the detach.
- */
- spa_async_request(spa, SPA_ASYNC_REPLACE_DONE);
-
- /*
- * If we were told to restart, our final act is to start a new scrub.
- */
- if (error == ERESTART)
- spa_async_request(spa, scrub_type == POOL_SCRUB_RESILVER ?
- SPA_ASYNC_RESILVER : SPA_ASYNC_SCRUB);
-
- spa->spa_scrub_type = POOL_SCRUB_NONE;
- spa->spa_scrub_active = 0;
- spa->spa_scrub_thread = NULL;
- cv_broadcast(&spa->spa_scrub_cv);
- CALLB_CPR_EXIT(&cprinfo); /* drops &spa->spa_scrub_lock */
- thread_exit();
-}
-
-void
-spa_scrub_suspend(spa_t *spa)
-{
- mutex_enter(&spa->spa_scrub_lock);
- spa->spa_scrub_suspended++;
- while (spa->spa_scrub_active) {
- cv_broadcast(&spa->spa_scrub_cv);
- cv_wait(&spa->spa_scrub_cv, &spa->spa_scrub_lock);
- }
- while (spa->spa_scrub_inflight)
- cv_wait(&spa->spa_scrub_io_cv, &spa->spa_scrub_lock);
- mutex_exit(&spa->spa_scrub_lock);
-}
-
-void
-spa_scrub_resume(spa_t *spa)
-{
- mutex_enter(&spa->spa_scrub_lock);
- ASSERT(spa->spa_scrub_suspended != 0);
- if (--spa->spa_scrub_suspended == 0)
- cv_broadcast(&spa->spa_scrub_cv);
- mutex_exit(&spa->spa_scrub_lock);
-}
-
-void
-spa_scrub_restart(spa_t *spa, uint64_t txg)
-{
- /*
- * Something happened (e.g. snapshot create/delete) that means
- * we must restart any in-progress scrubs. The itinerary will
- * fix this properly.
- */
- mutex_enter(&spa->spa_scrub_lock);
- spa->spa_scrub_restart_txg = txg;
- mutex_exit(&spa->spa_scrub_lock);
-}
-
int
-spa_scrub(spa_t *spa, pool_scrub_type_t type, boolean_t force)
+spa_scrub(spa_t *spa, pool_scrub_type_t type)
{
- space_seg_t *ss;
- uint64_t mintxg, maxtxg;
- vdev_t *rvd = spa->spa_root_vdev;
+ ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == 0);
if ((uint_t)type >= POOL_SCRUB_TYPES)
return (ENOTSUP);
- mutex_enter(&spa->spa_scrub_lock);
-
/*
- * If there's a scrub or resilver already in progress, stop it.
+ * If a resilver was requested, but there is no DTL on a
+ * writeable leaf device, we have nothing to do.
*/
- while (spa->spa_scrub_thread != NULL) {
- /*
- * Don't stop a resilver unless forced.
- */
- if (spa->spa_scrub_type == POOL_SCRUB_RESILVER && !force) {
- mutex_exit(&spa->spa_scrub_lock);
- return (EBUSY);
- }
- spa->spa_scrub_stop = 1;
- cv_broadcast(&spa->spa_scrub_cv);
- cv_wait(&spa->spa_scrub_cv, &spa->spa_scrub_lock);
- }
-
- /*
- * Terminate the previous traverse.
- */
- if (spa->spa_scrub_th != NULL) {
- traverse_fini(spa->spa_scrub_th);
- spa->spa_scrub_th = NULL;
- }
-
- if (rvd == NULL) {
- ASSERT(spa->spa_scrub_stop == 0);
- ASSERT(spa->spa_scrub_type == type);
- ASSERT(spa->spa_scrub_restart_txg == 0);
- mutex_exit(&spa->spa_scrub_lock);
+ if (type == POOL_SCRUB_RESILVER &&
+ !vdev_resilver_needed(spa->spa_root_vdev, NULL, NULL)) {
+ spa_async_request(spa, SPA_ASYNC_RESILVER_DONE);
return (0);
}
- mintxg = TXG_INITIAL - 1;
- maxtxg = spa_last_synced_txg(spa) + 1;
-
- mutex_enter(&rvd->vdev_dtl_lock);
+ if (type == POOL_SCRUB_EVERYTHING &&
+ spa->spa_dsl_pool->dp_scrub_func != SCRUB_FUNC_NONE &&
+ spa->spa_dsl_pool->dp_scrub_isresilver)
+ return (EBUSY);
- if (rvd->vdev_dtl_map.sm_space == 0) {
- /*
- * The pool-wide DTL is empty.
- * If this is a resilver, there's nothing to do except
- * check whether any in-progress replacements have completed.
- */
- if (type == POOL_SCRUB_RESILVER) {
- type = POOL_SCRUB_NONE;
- spa_async_request(spa, SPA_ASYNC_REPLACE_DONE);
- }
+ if (type == POOL_SCRUB_EVERYTHING || type == POOL_SCRUB_RESILVER) {
+ return (dsl_pool_scrub_clean(spa->spa_dsl_pool));
+ } else if (type == POOL_SCRUB_NONE) {
+ return (dsl_pool_scrub_cancel(spa->spa_dsl_pool));
} else {
- /*
- * The pool-wide DTL is non-empty.
- * If this is a normal scrub, upgrade to a resilver instead.
- */
- if (type == POOL_SCRUB_EVERYTHING)
- type = POOL_SCRUB_RESILVER;
- }
-
- if (type == POOL_SCRUB_RESILVER) {
- /*
- * Determine the resilvering boundaries.
- *
- * Note: (mintxg, maxtxg) is an open interval,
- * i.e. mintxg and maxtxg themselves are not included.
- *
- * Note: for maxtxg, we MIN with spa_last_synced_txg(spa) + 1
- * so we don't claim to resilver a txg that's still changing.
- */
- ss = avl_first(&rvd->vdev_dtl_map.sm_root);
- mintxg = ss->ss_start - 1;
- ss = avl_last(&rvd->vdev_dtl_map.sm_root);
- maxtxg = MIN(ss->ss_end, maxtxg);
- }
-
- mutex_exit(&rvd->vdev_dtl_lock);
-
- spa->spa_scrub_stop = 0;
- spa->spa_scrub_type = type;
- spa->spa_scrub_restart_txg = 0;
-
- if (type != POOL_SCRUB_NONE) {
- spa->spa_scrub_mintxg = mintxg;
- spa->spa_scrub_maxtxg = maxtxg;
- spa->spa_scrub_th = traverse_init(spa, spa_scrub_cb, NULL,
- ADVANCE_PRE | ADVANCE_PRUNE | ADVANCE_ZIL,
- ZIO_FLAG_CANFAIL);
- traverse_add_pool(spa->spa_scrub_th, mintxg, maxtxg);
- spa->spa_scrub_thread = thread_create(NULL, 0,
- spa_scrub_thread, spa, 0, &p0, TS_RUN, minclsyspri);
+ return (EINVAL);
}
-
- mutex_exit(&spa->spa_scrub_lock);
-
- return (0);
}
/*
@@ -2644,23 +3485,29 @@ spa_scrub(spa_t *spa, pool_scrub_type_t type, boolean_t force)
*/
static void
-spa_async_reopen(spa_t *spa)
+spa_async_remove(spa_t *spa, vdev_t *vd)
{
- vdev_t *rvd = spa->spa_root_vdev;
- vdev_t *tvd;
- int c;
+ if (vd->vdev_remove_wanted) {
+ vd->vdev_remove_wanted = 0;
+ vdev_set_state(vd, B_FALSE, VDEV_STATE_REMOVED, VDEV_AUX_NONE);
+ vdev_clear(spa, vd);
+ vdev_state_dirty(vd->vdev_top);
+ }
- spa_config_enter(spa, RW_WRITER, FTAG);
+ for (int c = 0; c < vd->vdev_children; c++)
+ spa_async_remove(spa, vd->vdev_child[c]);
+}
- for (c = 0; c < rvd->vdev_children; c++) {
- tvd = rvd->vdev_child[c];
- if (tvd->vdev_reopen_wanted) {
- tvd->vdev_reopen_wanted = 0;
- vdev_reopen(tvd);
- }
+static void
+spa_async_probe(spa_t *spa, vdev_t *vd)
+{
+ if (vd->vdev_probe_wanted) {
+ vd->vdev_probe_wanted = 0;
+ vdev_reopen(vd); /* vdev_open() does the actual probe */
}
- spa_config_exit(spa, FTAG);
+ for (int c = 0; c < vd->vdev_children; c++)
+ spa_async_probe(spa, vd->vdev_child[c]);
}
static void
@@ -2686,28 +3533,38 @@ spa_async_thread(void *arg)
}
/*
- * See if any devices need to be reopened.
+ * See if any devices need to be marked REMOVED.
*/
- if (tasks & SPA_ASYNC_REOPEN)
- spa_async_reopen(spa);
+ if (tasks & SPA_ASYNC_REMOVE) {
+ spa_vdev_state_enter(spa);
+ spa_async_remove(spa, spa->spa_root_vdev);
+ for (int i = 0; i < spa->spa_l2cache.sav_count; i++)
+ spa_async_remove(spa, spa->spa_l2cache.sav_vdevs[i]);
+ for (int i = 0; i < spa->spa_spares.sav_count; i++)
+ spa_async_remove(spa, spa->spa_spares.sav_vdevs[i]);
+ (void) spa_vdev_state_exit(spa, NULL, 0);
+ }
/*
- * If any devices are done replacing, detach them.
+ * See if any devices need to be probed.
*/
- if (tasks & SPA_ASYNC_REPLACE_DONE)
- spa_vdev_replace_done(spa);
+ if (tasks & SPA_ASYNC_PROBE) {
+ spa_vdev_state_enter(spa);
+ spa_async_probe(spa, spa->spa_root_vdev);
+ (void) spa_vdev_state_exit(spa, NULL, 0);
+ }
/*
- * Kick off a scrub.
+ * If any devices are done replacing, detach them.
*/
- if (tasks & SPA_ASYNC_SCRUB)
- VERIFY(spa_scrub(spa, POOL_SCRUB_EVERYTHING, B_TRUE) == 0);
+ if (tasks & SPA_ASYNC_RESILVER_DONE)
+ spa_vdev_resilver_done(spa);
/*
* Kick off a resilver.
*/
if (tasks & SPA_ASYNC_RESILVER)
- VERIFY(spa_scrub(spa, POOL_SCRUB_RESILVER, B_TRUE) == 0);
+ VERIFY(spa_scrub(spa, POOL_SCRUB_RESILVER) == 0);
/*
* Let the world know that we're done.
@@ -2775,10 +3632,13 @@ spa_sync_deferred_frees(spa_t *spa, uint64_t txg)
int error;
uint8_t c = 1;
- zio = zio_root(spa, NULL, NULL, ZIO_FLAG_CONFIG_HELD);
+ zio = zio_root(spa, NULL, NULL, ZIO_FLAG_CANFAIL);
- while (bplist_iterate(bpl, &itor, &blk) == 0)
- zio_nowait(zio_free(zio, spa, txg, &blk, NULL, NULL));
+ while (bplist_iterate(bpl, &itor, &blk) == 0) {
+ ASSERT(blk.blk_birth < txg);
+ zio_nowait(zio_free(zio, spa, txg, &blk, NULL, NULL,
+ ZIO_FLAG_MUSTSUCCEED));
+ }
error = zio_wait(zio);
ASSERT3U(error, ==, 0);
@@ -2798,19 +3658,27 @@ static void
spa_sync_nvlist(spa_t *spa, uint64_t obj, nvlist_t *nv, dmu_tx_t *tx)
{
char *packed = NULL;
+ size_t bufsize;
size_t nvsize = 0;
dmu_buf_t *db;
VERIFY(nvlist_size(nv, &nvsize, NV_ENCODE_XDR) == 0);
- packed = kmem_alloc(nvsize, KM_SLEEP);
+ /*
+ * Write full (SPA_CONFIG_BLOCKSIZE) blocks of configuration
+ * information. This avoids the dbuf_will_dirty() path and
+ * saves us a pre-read to get data we don't actually care about.
+ */
+ bufsize = P2ROUNDUP(nvsize, SPA_CONFIG_BLOCKSIZE);
+ packed = kmem_alloc(bufsize, KM_SLEEP);
VERIFY(nvlist_pack(nv, &packed, &nvsize, NV_ENCODE_XDR,
KM_SLEEP) == 0);
+ bzero(packed + nvsize, bufsize - nvsize);
- dmu_write(spa->spa_meta_objset, obj, 0, nvsize, packed, tx);
+ dmu_write(spa->spa_meta_objset, obj, 0, bufsize, packed, tx);
- kmem_free(packed, nvsize);
+ kmem_free(packed, bufsize);
VERIFY(0 == dmu_bonus_hold(spa->spa_meta_objset, obj, FTAG, &db));
dmu_buf_will_dirty(db, tx);
@@ -2819,50 +3687,49 @@ spa_sync_nvlist(spa_t *spa, uint64_t obj, nvlist_t *nv, dmu_tx_t *tx)
}
static void
-spa_sync_spares(spa_t *spa, dmu_tx_t *tx)
+spa_sync_aux_dev(spa_t *spa, spa_aux_vdev_t *sav, dmu_tx_t *tx,
+ const char *config, const char *entry)
{
nvlist_t *nvroot;
- nvlist_t **spares;
+ nvlist_t **list;
int i;
- if (!spa->spa_sync_spares)
+ if (!sav->sav_sync)
return;
/*
- * Update the MOS nvlist describing the list of available spares.
- * spa_validate_spares() will have already made sure this nvlist is
- * valid and the vdevs are labelled appropriately.
+ * Update the MOS nvlist describing the list of available devices.
+ * spa_validate_aux() will have already made sure this nvlist is
+ * valid and the vdevs are labeled appropriately.
*/
- if (spa->spa_spares_object == 0) {
- spa->spa_spares_object = dmu_object_alloc(spa->spa_meta_objset,
- DMU_OT_PACKED_NVLIST, 1 << 14,
- DMU_OT_PACKED_NVLIST_SIZE, sizeof (uint64_t), tx);
+ if (sav->sav_object == 0) {
+ sav->sav_object = dmu_object_alloc(spa->spa_meta_objset,
+ DMU_OT_PACKED_NVLIST, 1 << 14, DMU_OT_PACKED_NVLIST_SIZE,
+ sizeof (uint64_t), tx);
VERIFY(zap_update(spa->spa_meta_objset,
- DMU_POOL_DIRECTORY_OBJECT, DMU_POOL_SPARES,
- sizeof (uint64_t), 1, &spa->spa_spares_object, tx) == 0);
+ DMU_POOL_DIRECTORY_OBJECT, entry, sizeof (uint64_t), 1,
+ &sav->sav_object, tx) == 0);
}
VERIFY(nvlist_alloc(&nvroot, NV_UNIQUE_NAME, KM_SLEEP) == 0);
- if (spa->spa_nspares == 0) {
- VERIFY(nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
- NULL, 0) == 0);
+ if (sav->sav_count == 0) {
+ VERIFY(nvlist_add_nvlist_array(nvroot, config, NULL, 0) == 0);
} else {
- spares = kmem_alloc(spa->spa_nspares * sizeof (void *),
- KM_SLEEP);
- for (i = 0; i < spa->spa_nspares; i++)
- spares[i] = vdev_config_generate(spa,
- spa->spa_spares[i], B_FALSE, B_TRUE);
- VERIFY(nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
- spares, spa->spa_nspares) == 0);
- for (i = 0; i < spa->spa_nspares; i++)
- nvlist_free(spares[i]);
- kmem_free(spares, spa->spa_nspares * sizeof (void *));
+ list = kmem_alloc(sav->sav_count * sizeof (void *), KM_SLEEP);
+ for (i = 0; i < sav->sav_count; i++)
+ list[i] = vdev_config_generate(spa, sav->sav_vdevs[i],
+ B_FALSE, B_FALSE, B_TRUE);
+ VERIFY(nvlist_add_nvlist_array(nvroot, config, list,
+ sav->sav_count) == 0);
+ for (i = 0; i < sav->sav_count; i++)
+ nvlist_free(list[i]);
+ kmem_free(list, sav->sav_count * sizeof (void *));
}
- spa_sync_nvlist(spa, spa->spa_spares_object, nvroot, tx);
+ spa_sync_nvlist(spa, sav->sav_object, nvroot, tx);
nvlist_free(nvroot);
- spa->spa_sync_spares = B_FALSE;
+ sav->sav_sync = B_FALSE;
}
static void
@@ -2870,10 +3737,15 @@ spa_sync_config_object(spa_t *spa, dmu_tx_t *tx)
{
nvlist_t *config;
- if (list_is_empty(&spa->spa_dirty_list))
+ if (list_is_empty(&spa->spa_config_dirty_list))
return;
- config = spa_config_generate(spa, NULL, dmu_tx_get_txg(tx), B_FALSE);
+ spa_config_enter(spa, SCL_STATE, FTAG, RW_READER);
+
+ config = spa_config_generate(spa, spa->spa_root_vdev,
+ dmu_tx_get_txg(tx), B_FALSE);
+
+ spa_config_exit(spa, SCL_STATE, FTAG);
if (spa->spa_config_syncing)
nvlist_free(spa->spa_config_syncing);
@@ -2882,41 +3754,140 @@ spa_sync_config_object(spa_t *spa, dmu_tx_t *tx)
spa_sync_nvlist(spa, spa->spa_config_object, config, tx);
}
+/*
+ * Set zpool properties.
+ */
static void
-spa_sync_props(void *arg1, void *arg2, dmu_tx_t *tx)
+spa_sync_props(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
spa_t *spa = arg1;
- nvlist_t *nvp = arg2;
- nvpair_t *nvpair;
objset_t *mos = spa->spa_meta_objset;
- uint64_t zapobj;
+ nvlist_t *nvp = arg2;
+ nvpair_t *elem;
+ uint64_t intval;
+ char *strval;
+ zpool_prop_t prop;
+ const char *propname;
+ zprop_type_t proptype;
+ spa_config_dirent_t *dp;
mutex_enter(&spa->spa_props_lock);
- if (spa->spa_pool_props_object == 0) {
- zapobj = zap_create(mos, DMU_OT_POOL_PROPS, DMU_OT_NONE, 0, tx);
- VERIFY(zapobj > 0);
- spa->spa_pool_props_object = zapobj;
+ elem = NULL;
+ while ((elem = nvlist_next_nvpair(nvp, elem))) {
+ switch (prop = zpool_name_to_prop(nvpair_name(elem))) {
+ case ZPOOL_PROP_VERSION:
+ /*
+ * Only set version for non-zpool-creation cases
+ * (set/import). spa_create() needs special care
+ * for version setting.
+ */
+ if (tx->tx_txg != TXG_INITIAL) {
+ VERIFY(nvpair_value_uint64(elem,
+ &intval) == 0);
+ ASSERT(intval <= SPA_VERSION);
+ ASSERT(intval >= spa_version(spa));
+ spa->spa_uberblock.ub_version = intval;
+ vdev_config_dirty(spa->spa_root_vdev);
+ }
+ break;
- VERIFY(zap_update(mos, DMU_POOL_DIRECTORY_OBJECT,
- DMU_POOL_PROPS, 8, 1,
- &spa->spa_pool_props_object, tx) == 0);
- }
- mutex_exit(&spa->spa_props_lock);
+ case ZPOOL_PROP_ALTROOT:
+ /*
+ * 'altroot' is a non-persistent property. It should
+ * have been set temporarily at creation or import time.
+ */
+ ASSERT(spa->spa_root != NULL);
+ break;
- nvpair = NULL;
- while ((nvpair = nvlist_next_nvpair(nvp, nvpair))) {
- switch (zpool_name_to_prop(nvpair_name(nvpair))) {
- case ZFS_PROP_BOOTFS:
- VERIFY(nvlist_lookup_uint64(nvp,
- nvpair_name(nvpair), &spa->spa_bootfs) == 0);
- VERIFY(zap_update(mos,
- spa->spa_pool_props_object,
- zpool_prop_to_name(ZFS_PROP_BOOTFS), 8, 1,
- &spa->spa_bootfs, tx) == 0);
+ case ZPOOL_PROP_CACHEFILE:
+ /*
+ * 'cachefile' is a non-persistent property, but note
+ * an async request that the config cache needs to be
+ * udpated.
+ */
+ VERIFY(nvpair_value_string(elem, &strval) == 0);
+
+ dp = kmem_alloc(sizeof (spa_config_dirent_t), KM_SLEEP);
+
+ if (strval[0] == '\0')
+ dp->scd_path = spa_strdup(spa_config_path);
+ else if (strcmp(strval, "none") == 0)
+ dp->scd_path = NULL;
+ else
+ dp->scd_path = spa_strdup(strval);
+
+ list_insert_head(&spa->spa_config_list, dp);
+ spa_async_request(spa, SPA_ASYNC_CONFIG_UPDATE);
break;
+ default:
+ /*
+ * Set pool property values in the poolprops mos object.
+ */
+ if (spa->spa_pool_props_object == 0) {
+ objset_t *mos = spa->spa_meta_objset;
+
+ VERIFY((spa->spa_pool_props_object =
+ zap_create(mos, DMU_OT_POOL_PROPS,
+ DMU_OT_NONE, 0, tx)) > 0);
+
+ VERIFY(zap_update(mos,
+ DMU_POOL_DIRECTORY_OBJECT, DMU_POOL_PROPS,
+ 8, 1, &spa->spa_pool_props_object, tx)
+ == 0);
+ }
+
+ /* normalize the property name */
+ propname = zpool_prop_to_name(prop);
+ proptype = zpool_prop_get_type(prop);
+
+ if (nvpair_type(elem) == DATA_TYPE_STRING) {
+ ASSERT(proptype == PROP_TYPE_STRING);
+ VERIFY(nvpair_value_string(elem, &strval) == 0);
+ VERIFY(zap_update(mos,
+ spa->spa_pool_props_object, propname,
+ 1, strlen(strval) + 1, strval, tx) == 0);
+
+ } else if (nvpair_type(elem) == DATA_TYPE_UINT64) {
+ VERIFY(nvpair_value_uint64(elem, &intval) == 0);
+
+ if (proptype == PROP_TYPE_INDEX) {
+ const char *unused;
+ VERIFY(zpool_prop_index_to_string(
+ prop, intval, &unused) == 0);
+ }
+ VERIFY(zap_update(mos,
+ spa->spa_pool_props_object, propname,
+ 8, 1, &intval, tx) == 0);
+ } else {
+ ASSERT(0); /* not allowed */
+ }
+
+ switch (prop) {
+ case ZPOOL_PROP_DELEGATION:
+ spa->spa_delegation = intval;
+ break;
+ case ZPOOL_PROP_BOOTFS:
+ spa->spa_bootfs = intval;
+ break;
+ case ZPOOL_PROP_FAILUREMODE:
+ spa->spa_failmode = intval;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* log internal history if this is not a zpool create */
+ if (spa_version(spa) >= SPA_VERSION_ZPOOL_HISTORY &&
+ tx->tx_txg != TXG_INITIAL) {
+ spa_history_internal_log(LOG_POOL_PROPSET,
+ spa, tx, cr, "%s %lld %s",
+ nvpair_name(elem), intval, spa_name(spa));
}
}
+
+ mutex_exit(&spa->spa_props_lock);
}
/*
@@ -2933,25 +3904,37 @@ spa_sync(spa_t *spa, uint64_t txg)
vdev_t *vd;
dmu_tx_t *tx;
int dirty_vdevs;
+ int error;
/*
* Lock out configuration changes.
*/
- spa_config_enter(spa, RW_READER, FTAG);
+ spa_config_enter(spa, SCL_CONFIG, FTAG, RW_READER);
spa->spa_syncing_txg = txg;
spa->spa_sync_pass = 0;
+ /*
+ * If there are any pending vdev state changes, convert them
+ * into config changes that go out with this transaction group.
+ */
+ spa_config_enter(spa, SCL_STATE, FTAG, RW_READER);
+ while ((vd = list_head(&spa->spa_state_dirty_list)) != NULL) {
+ vdev_state_clean(vd);
+ vdev_config_dirty(vd);
+ }
+ spa_config_exit(spa, SCL_STATE, FTAG);
+
VERIFY(0 == bplist_open(bpl, mos, spa->spa_sync_bplist_obj));
tx = dmu_tx_create_assigned(dp, txg);
/*
- * If we are upgrading to ZFS_VERSION_RAIDZ_DEFLATE this txg,
+ * If we are upgrading to SPA_VERSION_RAIDZ_DEFLATE this txg,
* set spa_deflate if we have no raid-z vdevs.
*/
- if (spa->spa_ubsync.ub_version < ZFS_VERSION_RAIDZ_DEFLATE &&
- spa->spa_uberblock.ub_version >= ZFS_VERSION_RAIDZ_DEFLATE) {
+ if (spa->spa_ubsync.ub_version < SPA_VERSION_RAIDZ_DEFLATE &&
+ spa->spa_uberblock.ub_version >= SPA_VERSION_RAIDZ_DEFLATE) {
int i;
for (i = 0; i < rvd->vdev_children; i++) {
@@ -2967,6 +3950,19 @@ spa_sync(spa_t *spa, uint64_t txg)
}
}
+ if (spa->spa_ubsync.ub_version < SPA_VERSION_ORIGIN &&
+ spa->spa_uberblock.ub_version >= SPA_VERSION_ORIGIN) {
+ dsl_pool_create_origin(dp, tx);
+
+ /* Keeping the origin open increases spa_minref */
+ spa->spa_minref += 3;
+ }
+
+ if (spa->spa_ubsync.ub_version < SPA_VERSION_NEXT_CLONES &&
+ spa->spa_uberblock.ub_version >= SPA_VERSION_NEXT_CLONES) {
+ dsl_pool_upgrade_clones(dp, tx);
+ }
+
/*
* If anything has changed in this txg, push the deferred frees
* from the previous txg. If not, leave them alone so that we
@@ -2984,7 +3980,10 @@ spa_sync(spa_t *spa, uint64_t txg)
spa->spa_sync_pass++;
spa_sync_config_object(spa, tx);
- spa_sync_spares(spa, tx);
+ spa_sync_aux_dev(spa, &spa->spa_spares, tx,
+ ZPOOL_CONFIG_SPARES, DMU_POOL_SPARES);
+ spa_sync_aux_dev(spa, &spa->spa_l2cache, tx,
+ ZPOOL_CONFIG_L2CACHE, DMU_POOL_L2CACHE);
spa_errlog_sync(spa, txg);
dsl_pool_sync(dp, txg);
@@ -3005,35 +4004,52 @@ spa_sync(spa_t *spa, uint64_t txg)
* Rewrite the vdev configuration (which includes the uberblock)
* to commit the transaction group.
*
- * If there are any dirty vdevs, sync the uberblock to all vdevs.
- * Otherwise, pick a random top-level vdev that's known to be
- * visible in the config cache (see spa_vdev_add() for details).
- * If the write fails, try the next vdev until we're tried them all.
+ * If there are no dirty vdevs, we sync the uberblock to a few
+ * random top-level vdevs that are known to be visible in the
+ * config cache (see spa_vdev_add() for a complete description).
+ * If there *are* dirty vdevs, sync the uberblock to all vdevs.
*/
- if (!list_is_empty(&spa->spa_dirty_list)) {
- VERIFY(vdev_config_sync(rvd, txg) == 0);
- } else {
- int children = rvd->vdev_children;
- int c0 = spa_get_random(children);
- int c;
-
- for (c = 0; c < children; c++) {
- vd = rvd->vdev_child[(c0 + c) % children];
- if (vd->vdev_ms_array == 0)
- continue;
- if (vdev_config_sync(vd, txg) == 0)
- break;
+ for (;;) {
+ /*
+ * We hold SCL_STATE to prevent vdev open/close/etc.
+ * while we're attempting to write the vdev labels.
+ */
+ spa_config_enter(spa, SCL_STATE, FTAG, RW_READER);
+
+ if (list_is_empty(&spa->spa_config_dirty_list)) {
+ vdev_t *svd[SPA_DVAS_PER_BP];
+ int svdcount = 0;
+ int children = rvd->vdev_children;
+ int c0 = spa_get_random(children);
+ int c;
+
+ for (c = 0; c < children; c++) {
+ vd = rvd->vdev_child[(c0 + c) % children];
+ if (vd->vdev_ms_array == 0 || vd->vdev_islog)
+ continue;
+ svd[svdcount++] = vd;
+ if (svdcount == SPA_DVAS_PER_BP)
+ break;
+ }
+ error = vdev_config_sync(svd, svdcount, txg);
+ } else {
+ error = vdev_config_sync(rvd->vdev_child,
+ rvd->vdev_children, txg);
}
- if (c == children)
- VERIFY(vdev_config_sync(rvd, txg) == 0);
- }
+ spa_config_exit(spa, SCL_STATE, FTAG);
+
+ if (error == 0)
+ break;
+ zio_suspend(spa, NULL);
+ zio_resume_wait(spa);
+ }
dmu_tx_commit(tx);
/*
* Clear the dirty config list.
*/
- while ((vd = list_head(&spa->spa_dirty_list)) != NULL)
+ while ((vd = list_head(&spa->spa_config_dirty_list)) != NULL)
vdev_config_clean(vd);
/*
@@ -3046,21 +4062,12 @@ spa_sync(spa_t *spa, uint64_t txg)
spa->spa_config_syncing = NULL;
}
- /*
- * Make a stable copy of the fully synced uberblock.
- * We use this as the root for pool traversals.
- */
- spa->spa_traverse_wanted = 1; /* tells traverse_more() to stop */
-
- spa_scrub_suspend(spa); /* stop scrubbing and finish I/Os */
-
+ spa->spa_traverse_wanted = B_TRUE;
rw_enter(&spa->spa_traverse_lock, RW_WRITER);
- spa->spa_traverse_wanted = 0;
+ spa->spa_traverse_wanted = B_FALSE;
spa->spa_ubsync = spa->spa_uberblock;
rw_exit(&spa->spa_traverse_lock);
- spa_scrub_resume(spa); /* resume scrub with new ubsync */
-
/*
* Clean up the ZIL records for the synced txg.
*/
@@ -3081,7 +4088,7 @@ spa_sync(spa_t *spa, uint64_t txg)
ASSERT(txg_list_empty(&spa->spa_vdev_txg_list, txg));
ASSERT(bpl->bpl_queue == NULL);
- spa_config_exit(spa, FTAG);
+ spa_config_exit(spa, SCL_CONFIG, FTAG);
/*
* If any async tasks have been requested, kick them off.
@@ -3100,7 +4107,7 @@ spa_sync_allpools(void)
spa_t *spa = NULL;
mutex_enter(&spa_namespace_lock);
while ((spa = spa_next(spa)) != NULL) {
- if (spa_state(spa) != POOL_STATE_ACTIVE)
+ if (spa_state(spa) != POOL_STATE_ACTIVE || spa_suspended(spa))
continue;
spa_open_ref(spa, FTAG);
mutex_exit(&spa_namespace_lock);
@@ -3139,7 +4146,6 @@ spa_evict_all(void)
spa_open_ref(spa, FTAG);
mutex_exit(&spa_namespace_lock);
spa_async_suspend(spa);
- VERIFY(spa_scrub(spa, POOL_SCRUB_NONE, B_TRUE) == 0);
mutex_enter(&spa_namespace_lock);
spa_close(spa, FTAG);
@@ -3153,27 +4159,42 @@ spa_evict_all(void)
}
vdev_t *
-spa_lookup_by_guid(spa_t *spa, uint64_t guid)
+spa_lookup_by_guid(spa_t *spa, uint64_t guid, boolean_t l2cache)
{
- return (vdev_lookup_by_guid(spa->spa_root_vdev, guid));
+ vdev_t *vd;
+ int i;
+
+ if ((vd = vdev_lookup_by_guid(spa->spa_root_vdev, guid)) != NULL)
+ return (vd);
+
+ if (l2cache) {
+ for (i = 0; i < spa->spa_l2cache.sav_count; i++) {
+ vd = spa->spa_l2cache.sav_vdevs[i];
+ if (vd->vdev_guid == guid)
+ return (vd);
+ }
+ }
+
+ return (NULL);
}
void
-spa_upgrade(spa_t *spa)
+spa_upgrade(spa_t *spa, uint64_t version)
{
- spa_config_enter(spa, RW_WRITER, FTAG);
+ spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
/*
* This should only be called for a non-faulted pool, and since a
* future version would result in an unopenable pool, this shouldn't be
* possible.
*/
- ASSERT(spa->spa_uberblock.ub_version <= ZFS_VERSION);
+ ASSERT(spa->spa_uberblock.ub_version <= SPA_VERSION);
+ ASSERT(version >= spa->spa_uberblock.ub_version);
- spa->spa_uberblock.ub_version = ZFS_VERSION;
+ spa->spa_uberblock.ub_version = version;
vdev_config_dirty(spa->spa_root_vdev);
- spa_config_exit(spa, FTAG);
+ spa_config_exit(spa, SCL_ALL, FTAG);
txg_wait_synced(spa_get_dsl(spa), 0);
}
@@ -3183,119 +4204,98 @@ spa_has_spare(spa_t *spa, uint64_t guid)
{
int i;
uint64_t spareguid;
+ spa_aux_vdev_t *sav = &spa->spa_spares;
- for (i = 0; i < spa->spa_nspares; i++)
- if (spa->spa_spares[i]->vdev_guid == guid)
+ for (i = 0; i < sav->sav_count; i++)
+ if (sav->sav_vdevs[i]->vdev_guid == guid)
return (B_TRUE);
- for (i = 0; i < spa->spa_pending_nspares; i++) {
- if (nvlist_lookup_uint64(spa->spa_pending_spares[i],
- ZPOOL_CONFIG_GUID, &spareguid) == 0 &&
- spareguid == guid)
+ for (i = 0; i < sav->sav_npending; i++) {
+ if (nvlist_lookup_uint64(sav->sav_pending[i], ZPOOL_CONFIG_GUID,
+ &spareguid) == 0 && spareguid == guid)
return (B_TRUE);
}
return (B_FALSE);
}
-int
-spa_set_props(spa_t *spa, nvlist_t *nvp)
-{
- return (dsl_sync_task_do(spa_get_dsl(spa), NULL, spa_sync_props,
- spa, nvp, 3));
-}
-
-int
-spa_get_props(spa_t *spa, nvlist_t **nvp)
+/*
+ * Check if a pool has an active shared spare device.
+ * Note: reference count of an active spare is 2, as a spare and as a replace
+ */
+static boolean_t
+spa_has_active_shared_spare(spa_t *spa)
{
- zap_cursor_t zc;
- zap_attribute_t za;
- objset_t *mos = spa->spa_meta_objset;
- zfs_source_t src;
- zfs_prop_t prop;
- nvlist_t *propval;
- uint64_t value;
- int err;
-
- VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
-
- mutex_enter(&spa->spa_props_lock);
- /* If no props object, then just return empty nvlist */
- if (spa->spa_pool_props_object == 0) {
- mutex_exit(&spa->spa_props_lock);
- return (0);
- }
-
- for (zap_cursor_init(&zc, mos, spa->spa_pool_props_object);
- (err = zap_cursor_retrieve(&zc, &za)) == 0;
- zap_cursor_advance(&zc)) {
-
- if ((prop = zpool_name_to_prop(za.za_name)) == ZFS_PROP_INVAL)
- continue;
-
- VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
- switch (za.za_integer_length) {
- case 8:
- if (zfs_prop_default_numeric(prop) ==
- za.za_first_integer)
- src = ZFS_SRC_DEFAULT;
- else
- src = ZFS_SRC_LOCAL;
- value = za.za_first_integer;
-
- if (prop == ZFS_PROP_BOOTFS) {
- dsl_pool_t *dp;
- dsl_dataset_t *ds = NULL;
- char strval[MAXPATHLEN];
-
- dp = spa_get_dsl(spa);
- rw_enter(&dp->dp_config_rwlock, RW_READER);
- if ((err = dsl_dataset_open_obj(dp,
- za.za_first_integer, NULL, DS_MODE_NONE,
- FTAG, &ds)) != 0) {
- rw_exit(&dp->dp_config_rwlock);
- break;
- }
- dsl_dataset_name(ds, strval);
- dsl_dataset_close(ds, DS_MODE_NONE, FTAG);
- rw_exit(&dp->dp_config_rwlock);
+ int i, refcnt;
+ uint64_t pool;
+ spa_aux_vdev_t *sav = &spa->spa_spares;
- VERIFY(nvlist_add_uint64(propval,
- ZFS_PROP_SOURCE, src) == 0);
- VERIFY(nvlist_add_string(propval,
- ZFS_PROP_VALUE, strval) == 0);
- } else {
- VERIFY(nvlist_add_uint64(propval,
- ZFS_PROP_SOURCE, src) == 0);
- VERIFY(nvlist_add_uint64(propval,
- ZFS_PROP_VALUE, value) == 0);
- }
- VERIFY(nvlist_add_nvlist(*nvp, za.za_name,
- propval) == 0);
- break;
- }
- nvlist_free(propval);
- }
- zap_cursor_fini(&zc);
- mutex_exit(&spa->spa_props_lock);
- if (err && err != ENOENT) {
- nvlist_free(*nvp);
- return (err);
+ for (i = 0; i < sav->sav_count; i++) {
+ if (spa_spare_exists(sav->sav_vdevs[i]->vdev_guid, &pool,
+ &refcnt) && pool != 0ULL && pool == spa_guid(spa) &&
+ refcnt > 2)
+ return (B_TRUE);
}
- return (0);
+ return (B_FALSE);
}
/*
- * If the bootfs property value is dsobj, clear it.
+ * Post a sysevent corresponding to the given event. The 'name' must be one of
+ * the event definitions in sys/sysevent/eventdefs.h. The payload will be
+ * filled in from the spa and (optionally) the vdev. This doesn't do anything
+ * in the userland libzpool, as we don't want consumers to misinterpret ztest
+ * or zdb as real changes.
*/
void
-spa_clear_bootfs(spa_t *spa, uint64_t dsobj, dmu_tx_t *tx)
+spa_event_notify(spa_t *spa, vdev_t *vd, const char *name)
{
- if (spa->spa_bootfs == dsobj && spa->spa_pool_props_object != 0) {
- VERIFY(zap_remove(spa->spa_meta_objset,
- spa->spa_pool_props_object,
- zpool_prop_to_name(ZFS_PROP_BOOTFS), tx) == 0);
- spa->spa_bootfs = 0;
+#if 0
+#ifdef _KERNEL
+ sysevent_t *ev;
+ sysevent_attr_list_t *attr = NULL;
+ sysevent_value_t value;
+ sysevent_id_t eid;
+
+ ev = sysevent_alloc(EC_ZFS, (char *)name, SUNW_KERN_PUB "zfs",
+ SE_SLEEP);
+
+ value.value_type = SE_DATA_TYPE_STRING;
+ value.value.sv_string = spa_name(spa);
+ if (sysevent_add_attr(&attr, ZFS_EV_POOL_NAME, &value, SE_SLEEP) != 0)
+ goto done;
+
+ value.value_type = SE_DATA_TYPE_UINT64;
+ value.value.sv_uint64 = spa_guid(spa);
+ if (sysevent_add_attr(&attr, ZFS_EV_POOL_GUID, &value, SE_SLEEP) != 0)
+ goto done;
+
+ if (vd) {
+ value.value_type = SE_DATA_TYPE_UINT64;
+ value.value.sv_uint64 = vd->vdev_guid;
+ if (sysevent_add_attr(&attr, ZFS_EV_VDEV_GUID, &value,
+ SE_SLEEP) != 0)
+ goto done;
+
+ if (vd->vdev_path) {
+ value.value_type = SE_DATA_TYPE_STRING;
+ value.value.sv_string = vd->vdev_path;
+ if (sysevent_add_attr(&attr, ZFS_EV_VDEV_PATH,
+ &value, SE_SLEEP) != 0)
+ goto done;
+ }
}
+
+ if (sysevent_attach_attributes(ev, attr) != 0)
+ goto done;
+ attr = NULL;
+
+ (void) log_sysevent(ev, SE_SLEEP, &eid);
+
+done:
+ if (attr)
+ sysevent_free_attr(attr);
+ sysevent_free(ev);
+#endif
+#endif
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_config.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_config.c
index 9e8bcf3..1ffdb10 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_config.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_config.c
@@ -20,12 +20,10 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/zfs_context.h>
#include <sys/spa.h>
#include <sys/spa_impl.h>
@@ -43,16 +41,18 @@
/*
* Pool configuration repository.
*
- * The configuration for all pools, in addition to being stored on disk, is
- * stored in /etc/zfs/zpool.cache as a packed nvlist. The kernel maintains
- * this list as pools are created, destroyed, or modified.
+ * Pool configuration is stored as a packed nvlist on the filesystem. By
+ * default, all pools are stored in /etc/zfs/zpool.cache and loaded on boot
+ * (when the ZFS module is loaded). Pools can also have the 'cachefile'
+ * property set that allows them to be stored in an alternate location until
+ * the control of external software.
*
- * We have a single nvlist which holds all the configuration information. When
- * the module loads, we read this information from the cache and populate the
- * SPA namespace. This namespace is maintained independently in spa.c.
- * Whenever the namespace is modified, or the configuration of a pool is
- * changed, we call spa_config_sync(), which walks through all the active pools
- * and writes the configuration to disk.
+ * For each cache file, we have a single nvlist which holds all the
+ * configuration information. When the module loads, we read this information
+ * from /etc/zfs/zpool.cache and populate the SPA namespace. This namespace is
+ * maintained independently in spa.c. Whenever the namespace is modified, or
+ * the configuration of a pool is changed, we call spa_config_sync(), which
+ * walks through all the active pools and writes the configuration to disk.
*/
static uint64_t spa_config_generation = 1;
@@ -61,7 +61,7 @@ static uint64_t spa_config_generation = 1;
* This can be overridden in userland to preserve an alternate namespace for
* userland pools when doing testing.
*/
-const char *spa_config_dir = ZPOOL_CACHE_DIR;
+const char *spa_config_path = ZPOOL_CACHE;
/*
* Called when the module is first loaded, this routine loads the configuration
@@ -75,17 +75,21 @@ spa_config_load(void)
nvlist_t *nvlist, *child;
nvpair_t *nvpair;
spa_t *spa;
- char pathname[128];
+ char *pathname;
struct _buf *file;
uint64_t fsize;
/*
* Open the configuration file.
*/
- (void) snprintf(pathname, sizeof (pathname), "%s/%s",
- spa_config_dir, ZPOOL_CACHE_FILE);
+ pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+
+ (void) snprintf(pathname, MAXPATHLEN, "%s", spa_config_path);
file = kobj_open_file(pathname);
+
+ kmem_free(pathname, MAXPATHLEN);
+
if (file == (struct _buf *)-1) {
ZFS_LOG(1, "Cannot open %s.", pathname);
return;
@@ -148,47 +152,32 @@ out:
kobj_close_file(file);
}
-/*
- * Synchronize all pools to disk. This must be called with the namespace lock
- * held.
- */
-void
-spa_config_sync(void)
+static void
+spa_config_write(spa_config_dirent_t *dp, nvlist_t *nvl)
{
- spa_t *spa = NULL;
- nvlist_t *config;
size_t buflen;
char *buf;
vnode_t *vp;
int oflags = FWRITE | FTRUNC | FCREAT | FOFFMAX;
- char pathname[128];
- char pathname2[128];
-
- ASSERT(MUTEX_HELD(&spa_namespace_lock));
-
- VERIFY(nvlist_alloc(&config, NV_UNIQUE_NAME, KM_SLEEP) == 0);
+ char *temp;
/*
- * Add all known pools to the configuration list, ignoring those with
- * alternate root paths.
+ * If the nvlist is empty (NULL), then remove the old cachefile.
*/
- spa = NULL;
- while ((spa = spa_next(spa)) != NULL) {
- mutex_enter(&spa->spa_config_cache_lock);
- if (spa->spa_config && spa->spa_name && spa->spa_root == NULL)
- VERIFY(nvlist_add_nvlist(config, spa->spa_name,
- spa->spa_config) == 0);
- mutex_exit(&spa->spa_config_cache_lock);
+ if (nvl == NULL) {
+ (void) vn_remove(dp->scd_path, UIO_SYSSPACE, RMFILE);
+ return;
}
/*
* Pack the configuration into a buffer.
*/
- VERIFY(nvlist_size(config, &buflen, NV_ENCODE_XDR) == 0);
+ VERIFY(nvlist_size(nvl, &buflen, NV_ENCODE_XDR) == 0);
buf = kmem_alloc(buflen, KM_SLEEP);
+ temp = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
- VERIFY(nvlist_pack(config, &buf, &buflen, NV_ENCODE_XDR,
+ VERIFY(nvlist_pack(nvl, &buf, &buflen, NV_ENCODE_XDR,
KM_SLEEP) == 0);
/*
@@ -196,29 +185,92 @@ spa_config_sync(void)
* 'write to temporary file, sync, move over original' to make sure we
* always have a consistent view of the data.
*/
- (void) snprintf(pathname, sizeof (pathname), "%s/%s", spa_config_dir,
- ZPOOL_CACHE_TMP);
+ (void) snprintf(temp, MAXPATHLEN, "%s.tmp", dp->scd_path);
- if (vn_open(pathname, UIO_SYSSPACE, oflags, 0644, &vp, CRCREAT, 0) != 0)
- goto out;
+ if (vn_open(temp, UIO_SYSSPACE, oflags, 0644, &vp, CRCREAT, 0) == 0) {
+ if (vn_rdwr(UIO_WRITE, vp, buf, buflen, 0, UIO_SYSSPACE,
+ 0, RLIM64_INFINITY, kcred, NULL) == 0 &&
+ VOP_FSYNC(vp, FSYNC, kcred, NULL) == 0) {
+ (void) vn_rename(temp, dp->scd_path, UIO_SYSSPACE);
+ }
+ (void) VOP_CLOSE(vp, oflags, 1, 0, kcred, NULL);
+ VN_RELE(vp);
+ }
+
+ (void) vn_remove(temp, UIO_SYSSPACE, RMFILE);
+
+ kmem_free(buf, buflen);
+ kmem_free(temp, MAXPATHLEN);
+}
+
+/*
+ * Synchronize pool configuration to disk. This must be called with the
+ * namespace lock held.
+ */
+void
+spa_config_sync(spa_t *target, boolean_t removing, boolean_t postsysevent)
+{
+ spa_config_dirent_t *dp, *tdp;
+ nvlist_t *nvl;
+
+ ASSERT(MUTEX_HELD(&spa_namespace_lock));
+
+ /*
+ * Iterate over all cachefiles for the pool, past or present. When the
+ * cachefile is changed, the new one is pushed onto this list, allowing
+ * us to update previous cachefiles that no longer contain this pool.
+ */
+ for (dp = list_head(&target->spa_config_list); dp != NULL;
+ dp = list_next(&target->spa_config_list, dp)) {
+ spa_t *spa = NULL;
+ if (dp->scd_path == NULL)
+ continue;
- if (vn_rdwr(UIO_WRITE, vp, buf, buflen, 0, UIO_SYSSPACE,
- 0, RLIM64_INFINITY, kcred, NULL) == 0 &&
- VOP_FSYNC(vp, FSYNC, kcred) == 0) {
- (void) snprintf(pathname2, sizeof (pathname2), "%s/%s",
- spa_config_dir, ZPOOL_CACHE_FILE);
- (void) vn_rename(pathname, pathname2, UIO_SYSSPACE);
+ /*
+ * Iterate over all pools, adding any matching pools to 'nvl'.
+ */
+ nvl = NULL;
+ while ((spa = spa_next(spa)) != NULL) {
+ if (spa == target && removing)
+ continue;
+
+ mutex_enter(&spa->spa_props_lock);
+ tdp = list_head(&spa->spa_config_list);
+ if (spa->spa_config == NULL ||
+ tdp->scd_path == NULL ||
+ strcmp(tdp->scd_path, dp->scd_path) != 0) {
+ mutex_exit(&spa->spa_props_lock);
+ continue;
+ }
+
+ if (nvl == NULL)
+ VERIFY(nvlist_alloc(&nvl, NV_UNIQUE_NAME,
+ KM_SLEEP) == 0);
+
+ VERIFY(nvlist_add_nvlist(nvl, spa->spa_name,
+ spa->spa_config) == 0);
+ mutex_exit(&spa->spa_props_lock);
+ }
+
+ spa_config_write(dp, nvl);
+ nvlist_free(nvl);
}
- (void) VOP_CLOSE(vp, oflags, 1, 0, kcred);
- VN_RELE(vp);
+ /*
+ * Remove any config entries older than the current one.
+ */
+ dp = list_head(&target->spa_config_list);
+ while ((tdp = list_next(&target->spa_config_list, dp)) != NULL) {
+ list_remove(&target->spa_config_list, tdp);
+ if (tdp->scd_path != NULL)
+ spa_strfree(tdp->scd_path);
+ kmem_free(tdp, sizeof (spa_config_dirent_t));
+ }
-out:
- (void) vn_remove(pathname, UIO_SYSSPACE, RMFILE);
spa_config_generation++;
- kmem_free(buf, buflen);
- nvlist_free(config);
+ if (postsysevent)
+ spa_event_notify(target, NULL, ESC_ZFS_CONFIG_SYNC);
}
/*
@@ -231,27 +283,25 @@ nvlist_t *
spa_all_configs(uint64_t *generation)
{
nvlist_t *pools;
- spa_t *spa;
+ spa_t *spa = NULL;
if (*generation == spa_config_generation)
return (NULL);
VERIFY(nvlist_alloc(&pools, NV_UNIQUE_NAME, KM_SLEEP) == 0);
- spa = NULL;
mutex_enter(&spa_namespace_lock);
while ((spa = spa_next(spa)) != NULL) {
- if (INGLOBALZONE(curproc) ||
+ if (INGLOBALZONE(curthread) ||
zone_dataset_visible(spa_name(spa), NULL)) {
- mutex_enter(&spa->spa_config_cache_lock);
+ mutex_enter(&spa->spa_props_lock);
VERIFY(nvlist_add_nvlist(pools, spa_name(spa),
spa->spa_config) == 0);
- mutex_exit(&spa->spa_config_cache_lock);
+ mutex_exit(&spa->spa_props_lock);
}
}
- mutex_exit(&spa_namespace_lock);
-
*generation = spa_config_generation;
+ mutex_exit(&spa_namespace_lock);
return (pools);
}
@@ -259,11 +309,11 @@ spa_all_configs(uint64_t *generation)
void
spa_config_set(spa_t *spa, nvlist_t *config)
{
- mutex_enter(&spa->spa_config_cache_lock);
+ mutex_enter(&spa->spa_props_lock);
if (spa->spa_config != NULL)
nvlist_free(spa->spa_config);
spa->spa_config = config;
- mutex_exit(&spa->spa_config_cache_lock);
+ mutex_exit(&spa->spa_props_lock);
}
/*
@@ -277,11 +327,16 @@ spa_config_generate(spa_t *spa, vdev_t *vd, uint64_t txg, int getstats)
nvlist_t *config, *nvroot;
vdev_t *rvd = spa->spa_root_vdev;
unsigned long hostid = 0;
+ boolean_t locked = B_FALSE;
- ASSERT(spa_config_held(spa, RW_READER));
-
- if (vd == NULL)
+ if (vd == NULL) {
vd = rvd;
+ locked = B_TRUE;
+ spa_config_enter(spa, SCL_CONFIG | SCL_STATE, FTAG, RW_READER);
+ }
+
+ ASSERT(spa_config_held(spa, SCL_CONFIG | SCL_STATE, RW_READER) ==
+ (SCL_CONFIG | SCL_STATE));
/*
* If txg is -1, report the current value of spa->spa_config_txg.
@@ -302,8 +357,10 @@ spa_config_generate(spa_t *spa, vdev_t *vd, uint64_t txg, int getstats)
VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_POOL_GUID,
spa_guid(spa)) == 0);
(void) ddi_strtoul(hw_serial, NULL, 10, &hostid);
- VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_HOSTID,
- hostid) == 0);
+ if (hostid != 0) {
+ VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_HOSTID,
+ hostid) == 0);
+ }
VERIFY(nvlist_add_string(config, ZPOOL_CONFIG_HOSTNAME,
utsname.nodename) == 0);
@@ -315,30 +372,48 @@ spa_config_generate(spa_t *spa, vdev_t *vd, uint64_t txg, int getstats)
if (vd->vdev_isspare)
VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_IS_SPARE,
1ULL) == 0);
+ if (vd->vdev_islog)
+ VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_IS_LOG,
+ 1ULL) == 0);
vd = vd->vdev_top; /* label contains top config */
}
- nvroot = vdev_config_generate(spa, vd, getstats, B_FALSE);
+ nvroot = vdev_config_generate(spa, vd, getstats, B_FALSE, B_FALSE);
VERIFY(nvlist_add_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, nvroot) == 0);
nvlist_free(nvroot);
+ if (locked)
+ spa_config_exit(spa, SCL_CONFIG | SCL_STATE, FTAG);
+
return (config);
}
/*
- * Update all disk labels, generate a fresh config based on the current
- * in-core state, and sync the global config cache.
+ * For a pool that's not currently a booting rootpool, update all disk labels,
+ * generate a fresh config based on the current in-core state, and sync the
+ * global config cache.
*/
void
spa_config_update(spa_t *spa, int what)
{
+ spa_config_update_common(spa, what, FALSE);
+}
+
+/*
+ * Update all disk labels, generate a fresh config based on the current
+ * in-core state, and sync the global config cache (do not sync the config
+ * cache if this is a booting rootpool).
+ */
+void
+spa_config_update_common(spa_t *spa, int what, boolean_t isroot)
+{
vdev_t *rvd = spa->spa_root_vdev;
uint64_t txg;
int c;
ASSERT(MUTEX_HELD(&spa_namespace_lock));
- spa_config_enter(spa, RW_WRITER, FTAG);
+ spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
txg = spa_last_synced_txg(spa) + 1;
if (what == SPA_CONFIG_UPDATE_POOL) {
vdev_config_dirty(rvd);
@@ -358,7 +433,7 @@ spa_config_update(spa_t *spa, int what)
}
}
}
- spa_config_exit(spa, FTAG);
+ spa_config_exit(spa, SCL_ALL, FTAG);
/*
* Wait for the mosconfig to be regenerated and synced.
@@ -368,8 +443,9 @@ spa_config_update(spa_t *spa, int what)
/*
* Update the global config cache to reflect the new mosconfig.
*/
- spa_config_sync();
+ if (!isroot)
+ spa_config_sync(spa, B_FALSE, what != SPA_CONFIG_UPDATE_POOL);
if (what == SPA_CONFIG_UPDATE_POOL)
- spa_config_update(spa, SPA_CONFIG_UPDATE_VDEVS);
+ spa_config_update_common(spa, SPA_CONFIG_UPDATE_VDEVS, isroot);
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_errlog.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_errlog.c
index c52acaf..e5c395f 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_errlog.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_errlog.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -298,10 +298,7 @@ void
spa_errlog_rotate(spa_t *spa)
{
mutex_enter(&spa->spa_errlist_lock);
-
- ASSERT(!spa->spa_scrub_finished);
spa->spa_scrub_finished = B_TRUE;
-
mutex_exit(&spa->spa_errlist_lock);
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_history.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_history.c
index 6642801..8e20c4d 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_history.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_history.c
@@ -20,15 +20,24 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
+#include <sys/spa.h>
#include <sys/spa_impl.h>
#include <sys/zap.h>
#include <sys/dsl_synctask.h>
+#include <sys/dmu_tx.h>
+#include <sys/dmu_objset.h>
+#include <sys/utsname.h>
+#include <sys/sunddi.h>
+#ifdef _KERNEL
+#include <sys/cmn_err.h>
+#include <sys/zone.h>
+#endif
/*
* Routines to manage the on-disk history log.
@@ -59,16 +68,6 @@
* and permanently lost.
*/
-typedef enum history_log_type {
- LOG_CMD_CREATE,
- LOG_CMD_NO_CREATE
-} history_log_type_t;
-
-typedef struct history_arg {
- const char *ha_history_str;
- history_log_type_t ha_log_type;
-} history_arg_t;
-
/* convert a logical offset to physical */
static uint64_t
spa_history_log_to_phys(uint64_t log_off, spa_history_phys_t *shpp)
@@ -156,8 +155,9 @@ spa_history_write(spa_t *spa, void *buf, uint64_t len, spa_history_phys_t *shpp,
/* see if we need to reset logical BOF */
while (shpp->sh_phys_max_off - shpp->sh_pool_create_len -
(shpp->sh_eof - shpp->sh_bof) <= len) {
- if ((err = spa_history_advance_bof(spa, shpp)) != 0)
+ if ((err = spa_history_advance_bof(spa, shpp)) != 0) {
return (err);
+ }
}
phys_eof = spa_history_log_to_phys(shpp->sh_eof, shpp);
@@ -175,11 +175,22 @@ spa_history_write(spa_t *spa, void *buf, uint64_t len, spa_history_phys_t *shpp,
return (0);
}
+static char *
+spa_history_zone()
+{
+#ifdef _KERNEL
+ /* XXX: pr_host can be changed by default from within a jail! */
+ if (jailed(curthread->td_ucred))
+ return (curthread->td_ucred->cr_prison->pr_host);
+#endif
+ return ("global");
+}
+
/*
* Write out a history event.
*/
-void
-spa_history_log_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+static void
+spa_history_log_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
{
spa_t *spa = arg1;
history_arg_t *hap = arg2;
@@ -193,9 +204,6 @@ spa_history_log_sync(void *arg1, void *arg2, dmu_tx_t *tx)
char *record_packed = NULL;
int ret;
- if (history_str == NULL)
- return;
-
/*
* If we have an older pool that doesn't have a command
* history object, create it now.
@@ -222,16 +230,39 @@ spa_history_log_sync(void *arg1, void *arg2, dmu_tx_t *tx)
}
#endif
- /* construct a nvlist of the current time and cmd string */
VERIFY(nvlist_alloc(&nvrecord, NV_UNIQUE_NAME, KM_SLEEP) == 0);
VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_TIME,
gethrestime_sec()) == 0);
- VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_CMD, history_str) == 0);
+ VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_WHO,
+ (uint64_t)crgetuid(cr)) == 0);
+ if (hap->ha_zone[0] != '\0')
+ VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_ZONE,
+ hap->ha_zone) == 0);
+#ifdef _KERNEL
+ VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_HOST,
+ utsname.nodename) == 0);
+#endif
+ if (hap->ha_log_type == LOG_CMD_POOL_CREATE ||
+ hap->ha_log_type == LOG_CMD_NORMAL) {
+ VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_CMD,
+ history_str) == 0);
+ } else {
+ VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_INT_EVENT,
+ hap->ha_event) == 0);
+ VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_TXG,
+ tx->tx_txg) == 0);
+ VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_INT_STR,
+ history_str) == 0);
+ }
+
+ VERIFY(nvlist_size(nvrecord, &reclen, NV_ENCODE_XDR) == 0);
+ record_packed = kmem_alloc(reclen, KM_SLEEP);
+
VERIFY(nvlist_pack(nvrecord, &record_packed, &reclen,
NV_ENCODE_XDR, KM_SLEEP) == 0);
mutex_enter(&spa->spa_history_lock);
- if (hap->ha_log_type == LOG_CMD_CREATE)
+ if (hap->ha_log_type == LOG_CMD_POOL_CREATE)
VERIFY(shpp->sh_eof == shpp->sh_pool_create_len);
/* write out the packed length as little endian */
@@ -240,7 +271,7 @@ spa_history_log_sync(void *arg1, void *arg2, dmu_tx_t *tx)
if (!ret)
ret = spa_history_write(spa, record_packed, reclen, shpp, tx);
- if (!ret && hap->ha_log_type == LOG_CMD_CREATE) {
+ if (!ret && hap->ha_log_type == LOG_CMD_POOL_CREATE) {
shpp->sh_pool_create_len += sizeof (le_len) + reclen;
shpp->sh_bof = shpp->sh_pool_create_len;
}
@@ -249,18 +280,26 @@ spa_history_log_sync(void *arg1, void *arg2, dmu_tx_t *tx)
nvlist_free(nvrecord);
kmem_free(record_packed, reclen);
dmu_buf_rele(dbp, FTAG);
+
+ if (hap->ha_log_type == LOG_INTERNAL) {
+ kmem_free((void*)hap->ha_history_str, HIS_MAX_RECORD_LEN);
+ kmem_free(hap, sizeof (history_arg_t));
+ }
}
/*
* Write out a history event.
*/
int
-spa_history_log(spa_t *spa, const char *history_str, uint64_t pool_create)
+spa_history_log(spa_t *spa, const char *history_str, history_log_type_t what)
{
history_arg_t ha;
+ ASSERT(what != LOG_INTERNAL);
+
ha.ha_history_str = history_str;
- ha.ha_log_type = pool_create ? LOG_CMD_CREATE : LOG_CMD_NO_CREATE;
+ ha.ha_log_type = what;
+ (void) strlcpy(ha.ha_zone, spa_history_zone(), sizeof (ha.ha_zone));
return (dsl_sync_task_do(spa_get_dsl(spa), NULL, spa_history_log_sync,
spa, &ha, 0));
}
@@ -352,3 +391,39 @@ spa_history_get(spa_t *spa, uint64_t *offp, uint64_t *len, char *buf)
dmu_buf_rele(dbp, FTAG);
return (err);
}
+
+void
+spa_history_internal_log(history_internal_events_t event, spa_t *spa,
+ dmu_tx_t *tx, cred_t *cr, const char *fmt, ...)
+{
+ history_arg_t *hap;
+ char *str;
+ va_list adx;
+
+ /*
+ * If this is part of creating a pool, not everything is
+ * initialized yet, so don't bother logging the internal events.
+ */
+ if (tx->tx_txg == TXG_INITIAL)
+ return;
+
+ hap = kmem_alloc(sizeof (history_arg_t), KM_SLEEP);
+ str = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP);
+
+ va_start(adx, fmt);
+ (void) vsnprintf(str, HIS_MAX_RECORD_LEN, fmt, adx);
+ va_end(adx);
+
+ hap->ha_log_type = LOG_INTERNAL;
+ hap->ha_history_str = str;
+ hap->ha_event = event;
+ hap->ha_zone[0] = '\0';
+
+ if (dmu_tx_is_syncing(tx)) {
+ spa_history_log_sync(spa, hap, cr, tx);
+ } else {
+ dsl_sync_task_do_nowait(spa_get_dsl(spa), NULL,
+ spa_history_log_sync, spa, hap, 0, tx);
+ }
+ /* spa_history_log_sync() will free hap and str */
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_misc.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_misc.c
index 1e1f0ee..7a41d4f 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_misc.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_misc.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/zfs_context.h>
#include <sys/spa_impl.h>
#include <sys/zio.h>
@@ -44,6 +42,10 @@
#include <sys/dsl_dir.h>
#include <sys/dsl_prop.h>
#include <sys/fs/zfs.h>
+#include <sys/metaslab_impl.h>
+#include <sys/sunddi.h>
+#include <sys/arc.h>
+#include "zfs_prop.h"
/*
* SPA locking
@@ -72,25 +74,17 @@
* This reference count keep track of any active users of the spa_t. The
* spa_t cannot be destroyed or freed while this is non-zero. Internally,
* the refcount is never really 'zero' - opening a pool implicitly keeps
- * some references in the DMU. Internally we check against SPA_MINREF, but
+ * some references in the DMU. Internally we check against spa_minref, but
* present the image of a zero/non-zero value to consumers.
*
- * spa_config_lock (per-spa crazy rwlock)
+ * spa_config_lock[] (per-spa array of rwlocks)
*
- * This SPA special is a recursive rwlock, capable of being acquired from
- * asynchronous threads. It has protects the spa_t from config changes,
- * and must be held in the following circumstances:
+ * This protects the spa_t from config changes, and must be held in
+ * the following circumstances:
*
* - RW_READER to perform I/O to the spa
* - RW_WRITER to change the vdev config
*
- * spa_config_cache_lock (per-spa mutex)
- *
- * This mutex prevents the spa_config nvlist from being updated. No
- * other locks are required to obtain this lock, although implicitly you
- * must have the namespace lock or non-zero refcount to have any kind
- * of spa_t pointer at all.
- *
* The locking order is fairly straightforward:
*
* spa_namespace_lock -> spa_refcount
@@ -98,21 +92,20 @@
* The namespace lock must be acquired to increase the refcount from 0
* or to check if it is zero.
*
- * spa_refcount -> spa_config_lock
+ * spa_refcount -> spa_config_lock[]
*
* There must be at least one valid reference on the spa_t to acquire
* the config lock.
*
- * spa_namespace_lock -> spa_config_lock
+ * spa_namespace_lock -> spa_config_lock[]
*
* The namespace lock must always be taken before the config lock.
*
*
- * The spa_namespace_lock and spa_config_cache_lock can be acquired directly and
- * are globally visible.
+ * The spa_namespace_lock can be acquired directly and is globally visible.
*
- * The namespace is manipulated using the following functions, all which require
- * the spa_namespace_lock to be held.
+ * The namespace is manipulated using the following functions, all of which
+ * require the spa_namespace_lock to be held.
*
* spa_lookup() Lookup a spa_t by name.
*
@@ -143,16 +136,70 @@
* zero. Must be called with spa_namespace_lock
* held.
*
- * The spa_config_lock is manipulated using the following functions:
+ * The spa_config_lock[] is an array of rwlocks, ordered as follows:
+ * SCL_CONFIG > SCL_STATE > SCL_ALLOC > SCL_ZIO > SCL_FREE > SCL_VDEV.
+ * spa_config_lock[] is manipulated with spa_config_{enter,exit,held}().
+ *
+ * To read the configuration, it suffices to hold one of these locks as reader.
+ * To modify the configuration, you must hold all locks as writer. To modify
+ * vdev state without altering the vdev tree's topology (e.g. online/offline),
+ * you must hold SCL_STATE and SCL_ZIO as writer.
+ *
+ * We use these distinct config locks to avoid recursive lock entry.
+ * For example, spa_sync() (which holds SCL_CONFIG as reader) induces
+ * block allocations (SCL_ALLOC), which may require reading space maps
+ * from disk (dmu_read() -> zio_read() -> SCL_ZIO).
+ *
+ * The spa config locks cannot be normal rwlocks because we need the
+ * ability to hand off ownership. For example, SCL_ZIO is acquired
+ * by the issuing thread and later released by an interrupt thread.
+ * They do, however, obey the usual write-wanted semantics to prevent
+ * writer (i.e. system administrator) starvation.
+ *
+ * The lock acquisition rules are as follows:
+ *
+ * SCL_CONFIG
+ * Protects changes to the vdev tree topology, such as vdev
+ * add/remove/attach/detach. Protects the dirty config list
+ * (spa_config_dirty_list) and the set of spares and l2arc devices.
+ *
+ * SCL_STATE
+ * Protects changes to pool state and vdev state, such as vdev
+ * online/offline/fault/degrade/clear. Protects the dirty state list
+ * (spa_state_dirty_list) and global pool state (spa_state).
*
- * spa_config_enter() Acquire the config lock as RW_READER or
- * RW_WRITER. At least one reference on the spa_t
- * must exist.
+ * SCL_ALLOC
+ * Protects changes to metaslab groups and classes.
+ * Held as reader by metaslab_alloc() and metaslab_claim().
*
- * spa_config_exit() Release the config lock.
+ * SCL_ZIO
+ * Held by bp-level zios (those which have no io_vd upon entry)
+ * to prevent changes to the vdev tree. The bp-level zio implicitly
+ * protects all of its vdev child zios, which do not hold SCL_ZIO.
*
- * spa_config_held() Returns true if the config lock is currently
- * held in the given state.
+ * SCL_FREE
+ * Protects changes to metaslab groups and classes.
+ * Held as reader by metaslab_free(). SCL_FREE is distinct from
+ * SCL_ALLOC, and lower than SCL_ZIO, so that we can safely free
+ * blocks in zio_done() while another i/o that holds either
+ * SCL_ALLOC or SCL_ZIO is waiting for this i/o to complete.
+ *
+ * SCL_VDEV
+ * Held as reader to prevent changes to the vdev tree during trivial
+ * inquiries such as bp_get_dasize(). SCL_VDEV is distinct from the
+ * other locks, and lower than all of them, to ensure that it's safe
+ * to acquire regardless of caller context.
+ *
+ * In addition, the following rules apply:
+ *
+ * (a) spa_props_lock protects pool properties, spa_config and spa_config_list.
+ * The lock ordering is SCL_CONFIG > spa_props_lock.
+ *
+ * (b) I/O operations on leaf vdevs. For any zio operation that takes
+ * an explicit vdev_t argument -- such as zio_ioctl(), zio_read_phys(),
+ * or zio_write_phys() -- the caller must ensure that the config cannot
+ * cannot change in the interim, and that the vdev cannot be reopened.
+ * SCL_STATE as reader suffices for both.
*
* The vdev configuration is protected by spa_vdev_enter() / spa_vdev_exit().
*
@@ -163,10 +210,12 @@
* to complete, sync the updated configs to the
* cache, and release the namespace lock.
*
- * The spa_name() function also requires either the spa_namespace_lock
- * or the spa_config_lock, as both are needed to do a rename. spa_rename() is
- * also implemented within this file since is requires manipulation of the
- * namespace.
+ * vdev state is protected by spa_vdev_state_enter() / spa_vdev_state_exit().
+ * Like spa_vdev_enter/exit, these are convenience wrappers -- the actual
+ * locking is, always, based on spa_namespace_lock and spa_config_lock[].
+ *
+ * spa_rename() is also implemented within this file since is requires
+ * manipulation of the namespace.
*/
static avl_tree_t spa_namespace_avl;
@@ -177,12 +226,15 @@ int spa_max_replication_override = SPA_DVAS_PER_BP;
static kmutex_t spa_spare_lock;
static avl_tree_t spa_spare_avl;
+static kmutex_t spa_l2cache_lock;
+static avl_tree_t spa_l2cache_avl;
kmem_cache_t *spa_buffer_pool;
int spa_mode;
#ifdef ZFS_DEBUG
-int zfs_flags = ~0;
+/* Everything except dprintf is on by default in debug builds */
+int zfs_flags = ~ZFS_DEBUG_DPRINTF;
#else
int zfs_flags = 0;
#endif
@@ -198,7 +250,128 @@ TUNABLE_INT("vfs.zfs.recover", &zfs_recover);
SYSCTL_INT(_vfs_zfs, OID_AUTO, recover, CTLFLAG_RDTUN, &zfs_recover, 0,
"Try to recover from otherwise-fatal errors.");
-#define SPA_MINREF 5 /* spa_refcnt for an open-but-idle pool */
+
+/*
+ * ==========================================================================
+ * SPA config locking
+ * ==========================================================================
+ */
+static void
+spa_config_lock_init(spa_t *spa)
+{
+ for (int i = 0; i < SCL_LOCKS; i++) {
+ spa_config_lock_t *scl = &spa->spa_config_lock[i];
+ mutex_init(&scl->scl_lock, NULL, MUTEX_DEFAULT, NULL);
+ cv_init(&scl->scl_cv, NULL, CV_DEFAULT, NULL);
+ refcount_create(&scl->scl_count);
+ scl->scl_writer = NULL;
+ scl->scl_write_wanted = 0;
+ }
+}
+
+static void
+spa_config_lock_destroy(spa_t *spa)
+{
+ for (int i = 0; i < SCL_LOCKS; i++) {
+ spa_config_lock_t *scl = &spa->spa_config_lock[i];
+ mutex_destroy(&scl->scl_lock);
+ cv_destroy(&scl->scl_cv);
+ refcount_destroy(&scl->scl_count);
+ ASSERT(scl->scl_writer == NULL);
+ ASSERT(scl->scl_write_wanted == 0);
+ }
+}
+
+int
+spa_config_tryenter(spa_t *spa, int locks, void *tag, krw_t rw)
+{
+ for (int i = 0; i < SCL_LOCKS; i++) {
+ spa_config_lock_t *scl = &spa->spa_config_lock[i];
+ if (!(locks & (1 << i)))
+ continue;
+ mutex_enter(&scl->scl_lock);
+ if (rw == RW_READER) {
+ if (scl->scl_writer || scl->scl_write_wanted) {
+ mutex_exit(&scl->scl_lock);
+ spa_config_exit(spa, locks ^ (1 << i), tag);
+ return (0);
+ }
+ } else {
+ ASSERT(scl->scl_writer != curthread);
+ if (!refcount_is_zero(&scl->scl_count)) {
+ mutex_exit(&scl->scl_lock);
+ spa_config_exit(spa, locks ^ (1 << i), tag);
+ return (0);
+ }
+ scl->scl_writer = curthread;
+ }
+ (void) refcount_add(&scl->scl_count, tag);
+ mutex_exit(&scl->scl_lock);
+ }
+ return (1);
+}
+
+void
+spa_config_enter(spa_t *spa, int locks, void *tag, krw_t rw)
+{
+ for (int i = 0; i < SCL_LOCKS; i++) {
+ spa_config_lock_t *scl = &spa->spa_config_lock[i];
+ if (!(locks & (1 << i)))
+ continue;
+ mutex_enter(&scl->scl_lock);
+ if (rw == RW_READER) {
+ while (scl->scl_writer || scl->scl_write_wanted) {
+ cv_wait(&scl->scl_cv, &scl->scl_lock);
+ }
+ } else {
+ ASSERT(scl->scl_writer != curthread);
+ while (!refcount_is_zero(&scl->scl_count)) {
+ scl->scl_write_wanted++;
+ cv_wait(&scl->scl_cv, &scl->scl_lock);
+ scl->scl_write_wanted--;
+ }
+ scl->scl_writer = curthread;
+ }
+ (void) refcount_add(&scl->scl_count, tag);
+ mutex_exit(&scl->scl_lock);
+ }
+}
+
+void
+spa_config_exit(spa_t *spa, int locks, void *tag)
+{
+ for (int i = SCL_LOCKS - 1; i >= 0; i--) {
+ spa_config_lock_t *scl = &spa->spa_config_lock[i];
+ if (!(locks & (1 << i)))
+ continue;
+ mutex_enter(&scl->scl_lock);
+ ASSERT(!refcount_is_zero(&scl->scl_count));
+ if (refcount_remove(&scl->scl_count, tag) == 0) {
+ ASSERT(scl->scl_writer == NULL ||
+ scl->scl_writer == curthread);
+ scl->scl_writer = NULL; /* OK in either case */
+ cv_broadcast(&scl->scl_cv);
+ }
+ mutex_exit(&scl->scl_lock);
+ }
+}
+
+int
+spa_config_held(spa_t *spa, int locks, krw_t rw)
+{
+ int locks_held = 0;
+
+ for (int i = 0; i < SCL_LOCKS; i++) {
+ spa_config_lock_t *scl = &spa->spa_config_lock[i];
+ if (!(locks & (1 << i)))
+ continue;
+ if ((rw == RW_READER && !refcount_is_zero(&scl->scl_count)) ||
+ (rw == RW_WRITER && scl->scl_writer == curthread))
+ locks_held |= 1 << i;
+ }
+
+ return (locks_held);
+}
/*
* ==========================================================================
@@ -213,14 +386,30 @@ SYSCTL_INT(_vfs_zfs, OID_AUTO, recover, CTLFLAG_RDTUN, &zfs_recover, 0,
spa_t *
spa_lookup(const char *name)
{
- spa_t search, *spa;
+ static spa_t search; /* spa_t is large; don't allocate on stack */
+ spa_t *spa;
avl_index_t where;
+ char c;
+ char *cp;
ASSERT(MUTEX_HELD(&spa_namespace_lock));
- search.spa_name = (char *)name;
+ /*
+ * If it's a full dataset name, figure out the pool name and
+ * just use that.
+ */
+ cp = strpbrk(name, "/@");
+ if (cp) {
+ c = *cp;
+ *cp = '\0';
+ }
+
+ (void) strlcpy(search.spa_name, name, sizeof (search.spa_name));
spa = avl_find(&spa_namespace_avl, &search, &where);
+ if (cp)
+ *cp = c;
+
return (spa);
}
@@ -233,29 +422,40 @@ spa_t *
spa_add(const char *name, const char *altroot)
{
spa_t *spa;
+ spa_config_dirent_t *dp;
ASSERT(MUTEX_HELD(&spa_namespace_lock));
spa = kmem_zalloc(sizeof (spa_t), KM_SLEEP);
- spa->spa_name = spa_strdup(name);
- spa->spa_state = POOL_STATE_UNINITIALIZED;
- spa->spa_freeze_txg = UINT64_MAX;
- spa->spa_final_txg = UINT64_MAX;
+ rw_init(&spa->spa_traverse_lock, NULL, RW_DEFAULT, NULL);
- mutex_init(&spa->spa_config_cache_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&spa->spa_async_lock, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&spa->spa_async_root_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&spa->spa_scrub_lock, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&spa->spa_errlog_lock, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&spa->spa_errlist_lock, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&spa->spa_sync_bplist.bpl_lock, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&spa->spa_history_lock, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&spa->spa_props_lock, NULL, MUTEX_DEFAULT, NULL);
- cv_init(&spa->spa_scrub_cv, NULL, CV_DEFAULT, NULL);
- cv_init(&spa->spa_scrub_io_cv, NULL, CV_DEFAULT, NULL);
cv_init(&spa->spa_async_cv, NULL, CV_DEFAULT, NULL);
+ cv_init(&spa->spa_async_root_cv, NULL, CV_DEFAULT, NULL);
+ cv_init(&spa->spa_scrub_io_cv, NULL, CV_DEFAULT, NULL);
+ cv_init(&spa->spa_suspend_cv, NULL, CV_DEFAULT, NULL);
+
+ (void) strlcpy(spa->spa_name, name, sizeof (spa->spa_name));
+ spa->spa_state = POOL_STATE_UNINITIALIZED;
+ spa->spa_freeze_txg = UINT64_MAX;
+ spa->spa_final_txg = UINT64_MAX;
refcount_create(&spa->spa_refcount);
- refcount_create(&spa->spa_config_lock.scl_count);
+ spa_config_lock_init(spa);
avl_add(&spa_namespace_avl, spa);
+ mutex_init(&spa->spa_suspend_lock, NULL, MUTEX_DEFAULT, NULL);
+
/*
* Set the alternate root, if there is one.
*/
@@ -264,6 +464,16 @@ spa_add(const char *name, const char *altroot)
spa_active_count++;
}
+ /*
+ * Every pool starts with the default cachefile
+ */
+ list_create(&spa->spa_config_list, sizeof (spa_config_dirent_t),
+ offsetof(spa_config_dirent_t, scd_link));
+
+ dp = kmem_zalloc(sizeof (spa_config_dirent_t), KM_SLEEP);
+ dp->scd_path = spa_strdup(spa_config_path);
+ list_insert_head(&spa->spa_config_list, dp);
+
return (spa);
}
@@ -275,9 +485,10 @@ spa_add(const char *name, const char *altroot)
void
spa_remove(spa_t *spa)
{
+ spa_config_dirent_t *dp;
+
ASSERT(MUTEX_HELD(&spa_namespace_lock));
ASSERT(spa->spa_state == POOL_STATE_UNINITIALIZED);
- ASSERT(spa->spa_scrub_thread == NULL);
avl_remove(&spa_namespace_avl, spa);
cv_broadcast(&spa_namespace_cv);
@@ -287,21 +498,37 @@ spa_remove(spa_t *spa)
spa_active_count--;
}
- if (spa->spa_name)
- spa_strfree(spa->spa_name);
+ while ((dp = list_head(&spa->spa_config_list)) != NULL) {
+ list_remove(&spa->spa_config_list, dp);
+ if (dp->scd_path != NULL)
+ spa_strfree(dp->scd_path);
+ kmem_free(dp, sizeof (spa_config_dirent_t));
+ }
+
+ list_destroy(&spa->spa_config_list);
spa_config_set(spa, NULL);
refcount_destroy(&spa->spa_refcount);
- refcount_destroy(&spa->spa_config_lock.scl_count);
+
+ spa_config_lock_destroy(spa);
+
+ rw_destroy(&spa->spa_traverse_lock);
cv_destroy(&spa->spa_async_cv);
+ cv_destroy(&spa->spa_async_root_cv);
cv_destroy(&spa->spa_scrub_io_cv);
- cv_destroy(&spa->spa_scrub_cv);
+ cv_destroy(&spa->spa_suspend_cv);
- mutex_destroy(&spa->spa_scrub_lock);
mutex_destroy(&spa->spa_async_lock);
- mutex_destroy(&spa->spa_config_cache_lock);
+ mutex_destroy(&spa->spa_async_root_lock);
+ mutex_destroy(&spa->spa_scrub_lock);
+ mutex_destroy(&spa->spa_errlog_lock);
+ mutex_destroy(&spa->spa_errlist_lock);
+ mutex_destroy(&spa->spa_sync_bplist.bpl_lock);
+ mutex_destroy(&spa->spa_history_lock);
+ mutex_destroy(&spa->spa_props_lock);
+ mutex_destroy(&spa->spa_suspend_lock);
kmem_free(spa, sizeof (spa_t));
}
@@ -334,9 +561,8 @@ spa_next(spa_t *prev)
void
spa_open_ref(spa_t *spa, void *tag)
{
- ASSERT(refcount_count(&spa->spa_refcount) > SPA_MINREF ||
+ ASSERT(refcount_count(&spa->spa_refcount) >= spa->spa_minref ||
MUTEX_HELD(&spa_namespace_lock));
-
(void) refcount_add(&spa->spa_refcount, tag);
}
@@ -347,15 +573,14 @@ spa_open_ref(spa_t *spa, void *tag)
void
spa_close(spa_t *spa, void *tag)
{
- ASSERT(refcount_count(&spa->spa_refcount) > SPA_MINREF ||
+ ASSERT(refcount_count(&spa->spa_refcount) > spa->spa_minref ||
MUTEX_HELD(&spa_namespace_lock));
-
(void) refcount_remove(&spa->spa_refcount, tag);
}
/*
* Check to see if the spa refcount is zero. Must be called with
- * spa_namespace_lock held. We really compare against SPA_MINREF, which is the
+ * spa_namespace_lock held. We really compare against spa_minref, which is the
* number of references acquired when opening a pool
*/
boolean_t
@@ -363,16 +588,119 @@ spa_refcount_zero(spa_t *spa)
{
ASSERT(MUTEX_HELD(&spa_namespace_lock));
- return (refcount_count(&spa->spa_refcount) == SPA_MINREF);
+ return (refcount_count(&spa->spa_refcount) == spa->spa_minref);
}
/*
* ==========================================================================
- * SPA spare tracking
+ * SPA spare and l2cache tracking
* ==========================================================================
*/
/*
+ * Hot spares and cache devices are tracked using the same code below,
+ * for 'auxiliary' devices.
+ */
+
+typedef struct spa_aux {
+ uint64_t aux_guid;
+ uint64_t aux_pool;
+ avl_node_t aux_avl;
+ int aux_count;
+} spa_aux_t;
+
+static int
+spa_aux_compare(const void *a, const void *b)
+{
+ const spa_aux_t *sa = a;
+ const spa_aux_t *sb = b;
+
+ if (sa->aux_guid < sb->aux_guid)
+ return (-1);
+ else if (sa->aux_guid > sb->aux_guid)
+ return (1);
+ else
+ return (0);
+}
+
+void
+spa_aux_add(vdev_t *vd, avl_tree_t *avl)
+{
+ avl_index_t where;
+ spa_aux_t search;
+ spa_aux_t *aux;
+
+ search.aux_guid = vd->vdev_guid;
+ if ((aux = avl_find(avl, &search, &where)) != NULL) {
+ aux->aux_count++;
+ } else {
+ aux = kmem_zalloc(sizeof (spa_aux_t), KM_SLEEP);
+ aux->aux_guid = vd->vdev_guid;
+ aux->aux_count = 1;
+ avl_insert(avl, aux, where);
+ }
+}
+
+void
+spa_aux_remove(vdev_t *vd, avl_tree_t *avl)
+{
+ spa_aux_t search;
+ spa_aux_t *aux;
+ avl_index_t where;
+
+ search.aux_guid = vd->vdev_guid;
+ aux = avl_find(avl, &search, &where);
+
+ ASSERT(aux != NULL);
+
+ if (--aux->aux_count == 0) {
+ avl_remove(avl, aux);
+ kmem_free(aux, sizeof (spa_aux_t));
+ } else if (aux->aux_pool == spa_guid(vd->vdev_spa)) {
+ aux->aux_pool = 0ULL;
+ }
+}
+
+boolean_t
+spa_aux_exists(uint64_t guid, uint64_t *pool, int *refcnt, avl_tree_t *avl)
+{
+ spa_aux_t search, *found;
+
+ search.aux_guid = guid;
+ found = avl_find(avl, &search, NULL);
+
+ if (pool) {
+ if (found)
+ *pool = found->aux_pool;
+ else
+ *pool = 0ULL;
+ }
+
+ if (refcnt) {
+ if (found)
+ *refcnt = found->aux_count;
+ else
+ *refcnt = 0;
+ }
+
+ return (found != NULL);
+}
+
+void
+spa_aux_activate(vdev_t *vd, avl_tree_t *avl)
+{
+ spa_aux_t search, *found;
+ avl_index_t where;
+
+ search.aux_guid = vd->vdev_guid;
+ found = avl_find(avl, &search, &where);
+ ASSERT(found != NULL);
+ ASSERT(found->aux_pool == 0ULL);
+
+ found->aux_pool = spa_guid(vd->vdev_spa);
+}
+
+/*
* Spares are tracked globally due to the following constraints:
*
* - A spare may be part of multiple pools.
@@ -394,196 +722,110 @@ spa_refcount_zero(spa_t *spa)
* be completely consistent with respect to other vdev configuration changes.
*/
-typedef struct spa_spare {
- uint64_t spare_guid;
- uint64_t spare_pool;
- avl_node_t spare_avl;
- int spare_count;
-} spa_spare_t;
-
static int
spa_spare_compare(const void *a, const void *b)
{
- const spa_spare_t *sa = a;
- const spa_spare_t *sb = b;
-
- if (sa->spare_guid < sb->spare_guid)
- return (-1);
- else if (sa->spare_guid > sb->spare_guid)
- return (1);
- else
- return (0);
+ return (spa_aux_compare(a, b));
}
void
spa_spare_add(vdev_t *vd)
{
- avl_index_t where;
- spa_spare_t search;
- spa_spare_t *spare;
-
mutex_enter(&spa_spare_lock);
ASSERT(!vd->vdev_isspare);
-
- search.spare_guid = vd->vdev_guid;
- if ((spare = avl_find(&spa_spare_avl, &search, &where)) != NULL) {
- spare->spare_count++;
- } else {
- spare = kmem_zalloc(sizeof (spa_spare_t), KM_SLEEP);
- spare->spare_guid = vd->vdev_guid;
- spare->spare_count = 1;
- avl_insert(&spa_spare_avl, spare, where);
- }
+ spa_aux_add(vd, &spa_spare_avl);
vd->vdev_isspare = B_TRUE;
-
mutex_exit(&spa_spare_lock);
}
void
spa_spare_remove(vdev_t *vd)
{
- spa_spare_t search;
- spa_spare_t *spare;
- avl_index_t where;
-
mutex_enter(&spa_spare_lock);
-
- search.spare_guid = vd->vdev_guid;
- spare = avl_find(&spa_spare_avl, &search, &where);
-
ASSERT(vd->vdev_isspare);
- ASSERT(spare != NULL);
-
- if (--spare->spare_count == 0) {
- avl_remove(&spa_spare_avl, spare);
- kmem_free(spare, sizeof (spa_spare_t));
- } else if (spare->spare_pool == spa_guid(vd->vdev_spa)) {
- spare->spare_pool = 0ULL;
- }
-
+ spa_aux_remove(vd, &spa_spare_avl);
vd->vdev_isspare = B_FALSE;
mutex_exit(&spa_spare_lock);
}
boolean_t
-spa_spare_exists(uint64_t guid, uint64_t *pool)
+spa_spare_exists(uint64_t guid, uint64_t *pool, int *refcnt)
{
- spa_spare_t search, *found;
- avl_index_t where;
+ boolean_t found;
mutex_enter(&spa_spare_lock);
-
- search.spare_guid = guid;
- found = avl_find(&spa_spare_avl, &search, &where);
-
- if (pool) {
- if (found)
- *pool = found->spare_pool;
- else
- *pool = 0ULL;
- }
-
+ found = spa_aux_exists(guid, pool, refcnt, &spa_spare_avl);
mutex_exit(&spa_spare_lock);
- return (found != NULL);
+ return (found);
}
void
spa_spare_activate(vdev_t *vd)
{
- spa_spare_t search, *found;
- avl_index_t where;
-
mutex_enter(&spa_spare_lock);
ASSERT(vd->vdev_isspare);
-
- search.spare_guid = vd->vdev_guid;
- found = avl_find(&spa_spare_avl, &search, &where);
- ASSERT(found != NULL);
- ASSERT(found->spare_pool == 0ULL);
-
- found->spare_pool = spa_guid(vd->vdev_spa);
+ spa_aux_activate(vd, &spa_spare_avl);
mutex_exit(&spa_spare_lock);
}
/*
- * ==========================================================================
- * SPA config locking
- * ==========================================================================
+ * Level 2 ARC devices are tracked globally for the same reasons as spares.
+ * Cache devices currently only support one pool per cache device, and so
+ * for these devices the aux reference count is currently unused beyond 1.
*/
-/*
- * Acquire the config lock. The config lock is a special rwlock that allows for
- * recursive enters. Because these enters come from the same thread as well as
- * asynchronous threads working on behalf of the owner, we must unilaterally
- * allow all reads access as long at least one reader is held (even if a write
- * is requested). This has the side effect of write starvation, but write locks
- * are extremely rare, and a solution to this problem would be significantly
- * more complex (if even possible).
- *
- * We would like to assert that the namespace lock isn't held, but this is a
- * valid use during create.
- */
-void
-spa_config_enter(spa_t *spa, krw_t rw, void *tag)
+static int
+spa_l2cache_compare(const void *a, const void *b)
{
- spa_config_lock_t *scl = &spa->spa_config_lock;
-
- mutex_enter(&scl->scl_lock);
-
- if (scl->scl_writer != curthread) {
- if (rw == RW_READER) {
- while (scl->scl_writer != NULL)
- cv_wait(&scl->scl_cv, &scl->scl_lock);
- } else {
- while (scl->scl_writer != NULL ||
- !refcount_is_zero(&scl->scl_count))
- cv_wait(&scl->scl_cv, &scl->scl_lock);
- scl->scl_writer = curthread;
- }
- }
-
- (void) refcount_add(&scl->scl_count, tag);
+ return (spa_aux_compare(a, b));
+}
- mutex_exit(&scl->scl_lock);
+void
+spa_l2cache_add(vdev_t *vd)
+{
+ mutex_enter(&spa_l2cache_lock);
+ ASSERT(!vd->vdev_isl2cache);
+ spa_aux_add(vd, &spa_l2cache_avl);
+ vd->vdev_isl2cache = B_TRUE;
+ mutex_exit(&spa_l2cache_lock);
}
-/*
- * Release the spa config lock, notifying any waiters in the process.
- */
void
-spa_config_exit(spa_t *spa, void *tag)
+spa_l2cache_remove(vdev_t *vd)
{
- spa_config_lock_t *scl = &spa->spa_config_lock;
+ mutex_enter(&spa_l2cache_lock);
+ ASSERT(vd->vdev_isl2cache);
+ spa_aux_remove(vd, &spa_l2cache_avl);
+ vd->vdev_isl2cache = B_FALSE;
+ mutex_exit(&spa_l2cache_lock);
+}
- mutex_enter(&scl->scl_lock);
+boolean_t
+spa_l2cache_exists(uint64_t guid, uint64_t *pool)
+{
+ boolean_t found;
- ASSERT(!refcount_is_zero(&scl->scl_count));
- if (refcount_remove(&scl->scl_count, tag) == 0) {
- cv_broadcast(&scl->scl_cv);
- scl->scl_writer = NULL; /* OK in either case */
- }
+ mutex_enter(&spa_l2cache_lock);
+ found = spa_aux_exists(guid, pool, NULL, &spa_l2cache_avl);
+ mutex_exit(&spa_l2cache_lock);
- mutex_exit(&scl->scl_lock);
+ return (found);
}
-/*
- * Returns true if the config lock is held in the given manner.
- */
-boolean_t
-spa_config_held(spa_t *spa, krw_t rw)
+void
+spa_l2cache_activate(vdev_t *vd)
{
- spa_config_lock_t *scl = &spa->spa_config_lock;
- boolean_t held;
-
- mutex_enter(&scl->scl_lock);
- if (rw == RW_WRITER)
- held = (scl->scl_writer == curthread);
- else
- held = !refcount_is_zero(&scl->scl_count);
- mutex_exit(&scl->scl_lock);
+ mutex_enter(&spa_l2cache_lock);
+ ASSERT(vd->vdev_isl2cache);
+ spa_aux_activate(vd, &spa_l2cache_avl);
+ mutex_exit(&spa_l2cache_lock);
+}
- return (held);
+void
+spa_l2cache_space_update(vdev_t *vd, int64_t space, int64_t alloc)
+{
+ vdev_space_update(vd, space, alloc, B_FALSE);
}
/*
@@ -600,14 +842,9 @@ spa_config_held(spa_t *spa, krw_t rw)
uint64_t
spa_vdev_enter(spa_t *spa)
{
- /*
- * Suspend scrub activity while we mess with the config.
- */
- spa_scrub_suspend(spa);
-
mutex_enter(&spa_namespace_lock);
- spa_config_enter(spa, RW_WRITER, spa);
+ spa_config_enter(spa, SCL_ALL, spa, RW_WRITER);
return (spa_last_synced_txg(spa) + 1);
}
@@ -625,6 +862,8 @@ spa_vdev_exit(spa_t *spa, vdev_t *vd, uint64_t txg, int error)
ASSERT(txg > spa_last_synced_txg(spa));
+ spa->spa_pending_vdev = NULL;
+
/*
* Reassess the DTLs.
*/
@@ -633,17 +872,12 @@ spa_vdev_exit(spa_t *spa, vdev_t *vd, uint64_t txg, int error)
/*
* If the config changed, notify the scrub thread that it must restart.
*/
- if (error == 0 && !list_is_empty(&spa->spa_dirty_list)) {
+ if (error == 0 && !list_is_empty(&spa->spa_config_dirty_list)) {
+ dsl_pool_scrub_restart(spa->spa_dsl_pool);
config_changed = B_TRUE;
- spa_scrub_restart(spa, txg);
}
- spa_config_exit(spa, spa);
-
- /*
- * Allow scrubbing to resume.
- */
- spa_scrub_resume(spa);
+ spa_config_exit(spa, SCL_ALL, spa);
/*
* Note: this txg_wait_synced() is important because it ensures
@@ -662,7 +896,7 @@ spa_vdev_exit(spa_t *spa, vdev_t *vd, uint64_t txg, int error)
* If the config changed, update the config cache.
*/
if (config_changed)
- spa_config_sync();
+ spa_config_sync(spa, B_FALSE, B_TRUE);
mutex_exit(&spa_namespace_lock);
@@ -670,6 +904,26 @@ spa_vdev_exit(spa_t *spa, vdev_t *vd, uint64_t txg, int error)
}
/*
+ * Lock the given spa_t for the purpose of changing vdev state.
+ */
+void
+spa_vdev_state_enter(spa_t *spa)
+{
+ spa_config_enter(spa, SCL_STATE_ALL, spa, RW_WRITER);
+}
+
+int
+spa_vdev_state_exit(spa_t *spa, vdev_t *vd, int error)
+{
+ if (vd != NULL)
+ vdev_state_dirty(vd->vdev_top);
+
+ spa_config_exit(spa, SCL_STATE_ALL, spa);
+
+ return (error);
+}
+
+/*
* ==========================================================================
* Miscellaneous functions
* ==========================================================================
@@ -696,11 +950,10 @@ spa_rename(const char *name, const char *newname)
return (err);
}
- spa_config_enter(spa, RW_WRITER, FTAG);
+ spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
avl_remove(&spa_namespace_avl, spa);
- spa_strfree(spa->spa_name);
- spa->spa_name = spa_strdup(newname);
+ (void) strlcpy(spa->spa_name, newname, sizeof (spa->spa_name));
avl_add(&spa_namespace_avl, spa);
/*
@@ -710,14 +963,14 @@ spa_rename(const char *name, const char *newname)
*/
vdev_config_dirty(spa->spa_root_vdev);
- spa_config_exit(spa, FTAG);
+ spa_config_exit(spa, SCL_ALL, FTAG);
txg_wait_synced(spa->spa_dsl_pool, 0);
/*
* Sync the updated config cache.
*/
- spa_config_sync();
+ spa_config_sync(spa, B_FALSE, B_TRUE);
spa_close(spa, FTAG);
@@ -754,7 +1007,7 @@ spa_guid_exists(uint64_t pool_guid, uint64_t device_guid)
break;
/*
- * Check any devices we may in the process of adding.
+ * Check any devices we may be in the process of adding.
*/
if (spa->spa_pending_vdev) {
if (vdev_lookup_by_guid(spa->spa_pending_vdev,
@@ -848,12 +1101,12 @@ spa_freeze(spa_t *spa)
{
uint64_t freeze_txg = 0;
- spa_config_enter(spa, RW_WRITER, FTAG);
+ spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
if (spa->spa_freeze_txg == UINT64_MAX) {
freeze_txg = spa_last_synced_txg(spa) + TXG_SIZE;
spa->spa_freeze_txg = freeze_txg;
}
- spa_config_exit(spa, FTAG);
+ spa_config_exit(spa, SCL_ALL, FTAG);
if (freeze_txg != 0)
txg_wait_synced(spa_get_dsl(spa), freeze_txg);
}
@@ -880,7 +1133,7 @@ spa_traverse_rwlock(spa_t *spa)
return (&spa->spa_traverse_lock);
}
-int
+boolean_t
spa_traverse_wanted(spa_t *spa)
{
return (spa->spa_traverse_wanted);
@@ -922,13 +1175,6 @@ spa_sync_pass(spa_t *spa)
char *
spa_name(spa_t *spa)
{
- /*
- * Accessing the name requires holding either the namespace lock or the
- * config lock, both of which are required to do a rename.
- */
- ASSERT(MUTEX_HELD(&spa_namespace_lock) ||
- spa_config_held(spa, RW_READER) || spa_config_held(spa, RW_WRITER));
-
return (spa->spa_name);
}
@@ -972,16 +1218,6 @@ spa_freeze_txg(spa_t *spa)
}
/*
- * In the future, this may select among different metaslab classes
- * depending on the zdp. For now, there's no such distinction.
- */
-metaslab_class_t *
-spa_metaslab_class_select(spa_t *spa)
-{
- return (spa->spa_normal_class);
-}
-
-/*
* Return how much space is allocated in the pool (ie. sum of all asize)
*/
uint64_t
@@ -1024,6 +1260,22 @@ spa_get_asize(spa_t *spa, uint64_t lsize)
return (lsize * 6);
}
+/*
+ * Return the failure mode that has been set to this pool. The default
+ * behavior will be to block all I/Os when a complete failure occurs.
+ */
+uint8_t
+spa_get_failmode(spa_t *spa)
+{
+ return (spa->spa_failmode);
+}
+
+boolean_t
+spa_suspended(spa_t *spa)
+{
+ return (spa->spa_suspended);
+}
+
uint64_t
spa_version(spa_t *spa)
{
@@ -1034,11 +1286,11 @@ int
spa_max_replication(spa_t *spa)
{
/*
- * As of ZFS_VERSION == ZFS_VERSION_DITTO_BLOCKS, we are able to
+ * As of SPA_VERSION == SPA_VERSION_DITTO_BLOCKS, we are able to
* handle BPs with more than one DVA allocated. Set our max
* replication level accordingly.
*/
- if (spa_version(spa) < ZFS_VERSION_DITTO_BLOCKS)
+ if (spa_version(spa) < SPA_VERSION_DITTO_BLOCKS)
return (1);
return (MIN(SPA_DVAS_PER_BP, spa_max_replication_override));
}
@@ -1051,12 +1303,15 @@ bp_get_dasize(spa_t *spa, const blkptr_t *bp)
if (!spa->spa_deflate)
return (BP_GET_ASIZE(bp));
+ spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
for (i = 0; i < SPA_DVAS_PER_BP; i++) {
vdev_t *vd =
vdev_lookup_top(spa, DVA_GET_VDEV(&bp->blk_dva[i]));
- sz += (DVA_GET_ASIZE(&bp->blk_dva[i]) >> SPA_MINBLOCKSHIFT) *
- vd->vdev_deflate_ratio;
+ if (vd)
+ sz += (DVA_GET_ASIZE(&bp->blk_dva[i]) >>
+ SPA_MINBLOCKSHIFT) * vd->vdev_deflate_ratio;
}
+ spa_config_exit(spa, SCL_VDEV, FTAG);
return (sz);
}
@@ -1088,18 +1343,27 @@ spa_busy(void)
}
void
+spa_boot_init()
+{
+ spa_config_load();
+}
+
+void
spa_init(int mode)
{
mutex_init(&spa_namespace_lock, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&spa_spare_lock, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&spa_l2cache_lock, NULL, MUTEX_DEFAULT, NULL);
cv_init(&spa_namespace_cv, NULL, CV_DEFAULT, NULL);
avl_create(&spa_namespace_avl, spa_name_compare, sizeof (spa_t),
offsetof(spa_t, spa_avl));
- mutex_init(&spa_spare_lock, NULL, MUTEX_DEFAULT, NULL);
+ avl_create(&spa_spare_avl, spa_spare_compare, sizeof (spa_aux_t),
+ offsetof(spa_aux_t, aux_avl));
- avl_create(&spa_spare_avl, spa_spare_compare, sizeof (spa_spare_t),
- offsetof(spa_spare_t, spare_avl));
+ avl_create(&spa_l2cache_avl, spa_l2cache_compare, sizeof (spa_aux_t),
+ offsetof(spa_aux_t, aux_avl));
spa_mode = mode;
@@ -1108,23 +1372,53 @@ spa_init(int mode)
zio_init();
dmu_init();
zil_init();
+ vdev_cache_stat_init();
+ zfs_prop_init();
+ zpool_prop_init();
spa_config_load();
+ l2arc_start();
}
void
spa_fini(void)
{
+ l2arc_stop();
+
spa_evict_all();
+ vdev_cache_stat_fini();
zil_fini();
dmu_fini();
zio_fini();
+ unique_fini();
refcount_fini();
avl_destroy(&spa_namespace_avl);
avl_destroy(&spa_spare_avl);
+ avl_destroy(&spa_l2cache_avl);
cv_destroy(&spa_namespace_cv);
mutex_destroy(&spa_namespace_lock);
mutex_destroy(&spa_spare_lock);
+ mutex_destroy(&spa_l2cache_lock);
+}
+
+/*
+ * Return whether this pool has slogs. No locking needed.
+ * It's not a problem if the wrong answer is returned as it's only for
+ * performance and not correctness
+ */
+boolean_t
+spa_has_slogs(spa_t *spa)
+{
+ return (spa->spa_log_class->mc_rotor != NULL);
+}
+
+/*
+ * Return whether this pool is the root pool.
+ */
+boolean_t
+spa_is_root(spa_t *spa)
+{
+ return (spa->spa_is_root);
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/space_map.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/space_map.c
index 23313a9..8fdfa62 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/space_map.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/space_map.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -300,6 +300,7 @@ space_map_load(space_map_t *sm, space_map_ops_t *ops, uint8_t maptype,
uint64_t *entry, *entry_map, *entry_map_end;
uint64_t bufsize, size, offset, end, space;
uint64_t mapstart = sm->sm_start;
+ int error = 0;
ASSERT(MUTEX_HELD(sm->sm_lock));
@@ -337,9 +338,10 @@ space_map_load(space_map_t *sm, space_map_ops_t *ops, uint8_t maptype,
smo->smo_object, offset, size);
mutex_exit(sm->sm_lock);
- VERIFY3U(dmu_read(os, smo->smo_object, offset, size,
- entry_map), ==, 0);
+ error = dmu_read(os, smo->smo_object, offset, size, entry_map);
mutex_enter(sm->sm_lock);
+ if (error != 0)
+ break;
entry_map_end = entry_map + (size / sizeof (uint64_t));
for (entry = entry_map; entry < entry_map_end; entry++) {
@@ -354,20 +356,25 @@ space_map_load(space_map_t *sm, space_map_ops_t *ops, uint8_t maptype,
SM_RUN_DECODE(e) << sm->sm_shift);
}
}
- VERIFY3U(sm->sm_space, ==, space);
+
+ if (error == 0) {
+ VERIFY3U(sm->sm_space, ==, space);
+
+ sm->sm_loaded = B_TRUE;
+ sm->sm_ops = ops;
+ if (ops != NULL)
+ ops->smop_load(sm);
+ } else {
+ space_map_vacate(sm, NULL, NULL);
+ }
zio_buf_free(entry_map, bufsize);
sm->sm_loading = B_FALSE;
- sm->sm_loaded = B_TRUE;
- sm->sm_ops = ops;
cv_broadcast(&sm->sm_load_cv);
- if (ops != NULL)
- ops->smop_load(sm);
-
- return (0);
+ return (error);
}
void
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/arc.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/arc.h
index f58ffc0..f3e0087 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/arc.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/arc.h
@@ -19,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_ARC_H
#define _SYS_ARC_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/zfs_context.h>
#ifdef __cplusplus
@@ -35,11 +33,12 @@ extern "C" {
#endif
#include <sys/zio.h>
+#include <sys/dmu.h>
+#include <sys/spa.h>
typedef struct arc_buf_hdr arc_buf_hdr_t;
typedef struct arc_buf arc_buf_t;
typedef void arc_done_func_t(zio_t *zio, arc_buf_t *buf, void *private);
-typedef void arc_byteswap_func_t(void *buf, size_t size);
typedef int arc_evict_func_t(void *private);
/* generic arc_done_func_t's which you can use */
@@ -49,15 +48,16 @@ arc_done_func_t arc_getbuf_func;
struct arc_buf {
arc_buf_hdr_t *b_hdr;
arc_buf_t *b_next;
+ krwlock_t b_lock;
void *b_data;
arc_evict_func_t *b_efunc;
void *b_private;
};
typedef enum arc_buf_contents {
- ARC_BUFC_UNDEF, /* buffer contents undefined */
ARC_BUFC_DATA, /* buffer contains data */
- ARC_BUFC_METADATA /* buffer contains metadata */
+ ARC_BUFC_METADATA, /* buffer contains metadata */
+ ARC_BUFC_NUMTYPES
} arc_buf_contents_t;
/*
* These are the flags we pass into calls to the arc
@@ -66,7 +66,12 @@ typedef enum arc_buf_contents {
#define ARC_NOWAIT (1 << 2) /* perform I/O asynchronously */
#define ARC_PREFETCH (1 << 3) /* I/O is a prefetch */
#define ARC_CACHED (1 << 4) /* I/O was already in cache */
+#define ARC_L2CACHE (1 << 5) /* cache in L2ARC */
+void arc_space_consume(uint64_t space);
+void arc_space_return(uint64_t space);
+void *arc_data_buf_alloc(uint64_t space);
+void arc_data_buf_free(void *buf, uint64_t space);
arc_buf_t *arc_buf_alloc(spa_t *spa, int size, void *tag,
arc_buf_contents_t type);
void arc_buf_add_ref(arc_buf_t *buf, void *tag);
@@ -81,13 +86,24 @@ void arc_buf_thaw(arc_buf_t *buf);
int arc_referenced(arc_buf_t *buf);
#endif
-int arc_read(zio_t *pio, spa_t *spa, blkptr_t *bp, arc_byteswap_func_t *swap,
+typedef struct writeprops {
+ dmu_object_type_t wp_type;
+ uint8_t wp_level;
+ uint8_t wp_copies;
+ uint8_t wp_dncompress, wp_oscompress;
+ uint8_t wp_dnchecksum, wp_oschecksum;
+} writeprops_t;
+
+int arc_read(zio_t *pio, spa_t *spa, blkptr_t *bp, arc_buf_t *pbuf,
+ arc_done_func_t *done, void *private, int priority, int zio_flags,
+ uint32_t *arc_flags, const zbookmark_t *zb);
+int arc_read_nolock(zio_t *pio, spa_t *spa, blkptr_t *bp,
arc_done_func_t *done, void *private, int priority, int flags,
- uint32_t *arc_flags, zbookmark_t *zb);
-zio_t *arc_write(zio_t *pio, spa_t *spa, int checksum, int compress,
- int ncopies, uint64_t txg, blkptr_t *bp, arc_buf_t *buf,
+ uint32_t *arc_flags, const zbookmark_t *zb);
+zio_t *arc_write(zio_t *pio, spa_t *spa, const writeprops_t *wp,
+ boolean_t l2arc, uint64_t txg, blkptr_t *bp, arc_buf_t *buf,
arc_done_func_t *ready, arc_done_func_t *done, void *private, int priority,
- int flags, zbookmark_t *zb);
+ int zio_flags, const zbookmark_t *zb);
int arc_free(zio_t *pio, spa_t *spa, uint64_t txg, blkptr_t *bp,
zio_done_func_t *done, void *private, uint32_t arc_flags);
int arc_tryread(spa_t *spa, blkptr_t *bp, void *data);
@@ -95,13 +111,25 @@ int arc_tryread(spa_t *spa, blkptr_t *bp, void *data);
void arc_set_callback(arc_buf_t *buf, arc_evict_func_t *func, void *private);
int arc_buf_evict(arc_buf_t *buf);
-void arc_flush(void);
-void arc_tempreserve_clear(uint64_t tempreserve);
-int arc_tempreserve_space(uint64_t tempreserve);
+void arc_flush(spa_t *spa);
+void arc_tempreserve_clear(uint64_t reserve);
+int arc_tempreserve_space(uint64_t reserve, uint64_t txg);
void arc_init(void);
void arc_fini(void);
+/*
+ * Level 2 ARC
+ */
+
+void l2arc_add_vdev(spa_t *spa, vdev_t *vd, uint64_t start, uint64_t end);
+void l2arc_remove_vdev(vdev_t *vd);
+boolean_t l2arc_vdev_present(vdev_t *vd);
+void l2arc_init(void);
+void l2arc_fini(void);
+void l2arc_start(void);
+void l2arc_stop(void);
+
#ifdef __cplusplus
}
#endif
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/bplist.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/bplist.h
index b4c8376..cdb93a6 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/bplist.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/bplist.h
@@ -19,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_BPLIST_H
#define _SYS_BPLIST_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/dmu.h>
#include <sys/spa.h>
#include <sys/txg.h>
@@ -75,12 +73,14 @@ extern int bplist_open(bplist_t *bpl, objset_t *mos, uint64_t object);
extern void bplist_close(bplist_t *bpl);
extern boolean_t bplist_empty(bplist_t *bpl);
extern int bplist_iterate(bplist_t *bpl, uint64_t *itorp, blkptr_t *bp);
-extern int bplist_enqueue(bplist_t *bpl, blkptr_t *bp, dmu_tx_t *tx);
-extern void bplist_enqueue_deferred(bplist_t *bpl, blkptr_t *bp);
+extern int bplist_enqueue(bplist_t *bpl, const blkptr_t *bp, dmu_tx_t *tx);
+extern void bplist_enqueue_deferred(bplist_t *bpl, const blkptr_t *bp);
extern void bplist_sync(bplist_t *bpl, dmu_tx_t *tx);
extern void bplist_vacate(bplist_t *bpl, dmu_tx_t *tx);
extern int bplist_space(bplist_t *bpl,
uint64_t *usedp, uint64_t *compp, uint64_t *uncompp);
+extern int bplist_space_birthrange(bplist_t *bpl,
+ uint64_t mintxg, uint64_t maxtxg, uint64_t *dasizep);
#ifdef __cplusplus
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dbuf.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dbuf.h
index d33657b..b27d89f 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dbuf.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dbuf.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -239,7 +239,7 @@ typedef struct dbuf_hash_table {
uint64_t dbuf_whichblock(struct dnode *di, uint64_t offset);
dmu_buf_impl_t *dbuf_create_tlib(struct dnode *dn, char *data);
-dmu_buf_impl_t *dbuf_create_bonus(struct dnode *dn);
+void dbuf_create_bonus(struct dnode *dn);
dmu_buf_impl_t *dbuf_hold(struct dnode *dn, uint64_t blkid, void *tag);
dmu_buf_impl_t *dbuf_hold_level(struct dnode *dn, int level, uint64_t blkid,
@@ -271,7 +271,7 @@ void dbuf_setdirty(dmu_buf_impl_t *db, dmu_tx_t *tx);
void dbuf_unoverride(dbuf_dirty_record_t *dr);
void dbuf_sync_list(list_t *list, dmu_tx_t *tx);
-void dbuf_free_range(struct dnode *dn, uint64_t blkid, uint64_t nblks,
+void dbuf_free_range(struct dnode *dn, uint64_t start, uint64_t end,
struct dmu_tx *);
void dbuf_new_size(dmu_buf_impl_t *db, int size, dmu_tx_t *tx);
@@ -279,10 +279,21 @@ void dbuf_new_size(dmu_buf_impl_t *db, int size, dmu_tx_t *tx);
void dbuf_init(void);
void dbuf_fini(void);
-#define DBUF_GET_BUFC_TYPE(db) \
- ((((db)->db_level > 0) || \
- (dmu_ot[(db)->db_dnode->dn_type].ot_metadata)) ? \
- ARC_BUFC_METADATA : ARC_BUFC_DATA);
+#define DBUF_IS_METADATA(db) \
+ ((db)->db_level > 0 || dmu_ot[(db)->db_dnode->dn_type].ot_metadata)
+
+#define DBUF_GET_BUFC_TYPE(db) \
+ (DBUF_IS_METADATA(db) ? ARC_BUFC_METADATA : ARC_BUFC_DATA)
+
+#define DBUF_IS_CACHEABLE(db) \
+ ((db)->db_objset->os_primary_cache == ZFS_CACHE_ALL || \
+ (DBUF_IS_METADATA(db) && \
+ ((db)->db_objset->os_primary_cache == ZFS_CACHE_METADATA)))
+
+#define DBUF_IS_L2CACHEABLE(db) \
+ ((db)->db_objset->os_secondary_cache == ZFS_CACHE_ALL || \
+ (DBUF_IS_METADATA(db) && \
+ ((db)->db_objset->os_secondary_cache == ZFS_CACHE_METADATA)))
#ifdef ZFS_DEBUG
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu.h
index 8c2a1fd..4535c68 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -38,6 +38,7 @@
#include <sys/types.h>
#include <sys/param.h>
+#include <sys/cred.h>
#ifdef __cplusplus
extern "C" {
@@ -91,7 +92,7 @@ typedef enum dmu_object_type {
DMU_OT_DSL_DATASET, /* UINT64 */
/* zpl: */
DMU_OT_ZNODE, /* ZNODE */
- DMU_OT_ACL, /* ACL */
+ DMU_OT_OLDACL, /* Old ACL */
DMU_OT_PLAIN_FILE_CONTENTS, /* UINT8 */
DMU_OT_DIRECTORY_CONTENTS, /* ZAP */
DMU_OT_MASTER_NODE, /* ZAP */
@@ -108,7 +109,13 @@ typedef enum dmu_object_type {
DMU_OT_SPA_HISTORY, /* UINT8 */
DMU_OT_SPA_HISTORY_OFFSETS, /* spa_his_phys_t */
DMU_OT_POOL_PROPS, /* ZAP */
-
+ DMU_OT_DSL_PERMS, /* ZAP */
+ DMU_OT_ACL, /* ACL */
+ DMU_OT_SYSACL, /* SYSACL */
+ DMU_OT_FUID, /* FUID table (Packed NVLIST UINT8) */
+ DMU_OT_FUID_SIZE, /* FUID table size UINT64 */
+ DMU_OT_NEXT_CLONES, /* ZAP */
+ DMU_OT_SCRUB_QUEUE, /* ZAP */
DMU_OT_NUMTYPES
} dmu_object_type_t;
@@ -127,15 +134,15 @@ void byteswap_uint32_array(void *buf, size_t size);
void byteswap_uint16_array(void *buf, size_t size);
void byteswap_uint8_array(void *buf, size_t size);
void zap_byteswap(void *buf, size_t size);
+void zfs_oldacl_byteswap(void *buf, size_t size);
void zfs_acl_byteswap(void *buf, size_t size);
void zfs_znode_byteswap(void *buf, size_t size);
-#define DS_MODE_NONE 0 /* invalid, to aid debugging */
-#define DS_MODE_STANDARD 1 /* normal access, no special needs */
-#define DS_MODE_PRIMARY 2 /* the "main" access, e.g. a mount */
-#define DS_MODE_EXCLUSIVE 3 /* exclusive access, e.g. to destroy */
-#define DS_MODE_LEVELS 4
-#define DS_MODE_LEVEL(x) ((x) & (DS_MODE_LEVELS - 1))
+#define DS_MODE_NOHOLD 0 /* internal use only */
+#define DS_MODE_USER 1 /* simple access, no special needs */
+#define DS_MODE_OWNER 2 /* the "main" access, e.g. a mount */
+#define DS_MODE_TYPE_MASK 0x3
+#define DS_MODE_TYPE(x) ((x) & DS_MODE_TYPE_MASK)
#define DS_MODE_READONLY 0x8
#define DS_MODE_IS_READONLY(x) ((x) & DS_MODE_READONLY)
#define DS_MODE_INCONSISTENT 0x10
@@ -149,20 +156,23 @@ void zfs_znode_byteswap(void *buf, size_t size);
* operation, including metadata.
*/
#define DMU_MAX_ACCESS (10<<20) /* 10MB */
+#define DMU_MAX_DELETEBLKCNT (20480) /* ~5MB of indirect blocks */
/*
* Public routines to create, destroy, open, and close objsets.
*/
int dmu_objset_open(const char *name, dmu_objset_type_t type, int mode,
objset_t **osp);
+int dmu_objset_open_ds(struct dsl_dataset *ds, dmu_objset_type_t type,
+ objset_t **osp);
void dmu_objset_close(objset_t *os);
-int dmu_objset_evict_dbufs(objset_t *os, int try);
+int dmu_objset_evict_dbufs(objset_t *os);
int dmu_objset_create(const char *name, dmu_objset_type_t type,
- objset_t *clone_parent,
- void (*func)(objset_t *os, void *arg, dmu_tx_t *tx), void *arg);
+ objset_t *clone_parent, uint64_t flags,
+ void (*func)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx), void *arg);
int dmu_objset_destroy(const char *name);
int dmu_snapshots_destroy(char *fsname, char *snapname);
-int dmu_objset_rollback(const char *name);
+int dmu_objset_rollback(objset_t *os);
int dmu_objset_snapshot(char *fsname, char *snapname, boolean_t recursive);
int dmu_objset_rename(const char *name, const char *newname,
boolean_t recursive);
@@ -180,11 +190,6 @@ typedef struct dmu_buf {
typedef void dmu_buf_evict_func_t(struct dmu_buf *db, void *user_ptr);
/*
- * Callback function to perform byte swapping on a block.
- */
-typedef void dmu_byteswap_func_t(void *buf, size_t size);
-
-/*
* The names of zap entries in the DIRECTORY_OBJECT of the MOS.
*/
#define DMU_POOL_DIRECTORY_OBJECT 1
@@ -197,6 +202,20 @@ typedef void dmu_byteswap_func_t(void *buf, size_t size);
#define DMU_POOL_DEFLATE "deflate"
#define DMU_POOL_HISTORY "history"
#define DMU_POOL_PROPS "pool_props"
+#define DMU_POOL_L2CACHE "l2cache"
+
+/* 4x8 zbookmark_t */
+#define DMU_POOL_SCRUB_BOOKMARK "scrub_bookmark"
+/* 1x8 zap obj DMU_OT_SCRUB_QUEUE */
+#define DMU_POOL_SCRUB_QUEUE "scrub_queue"
+/* 1x8 txg */
+#define DMU_POOL_SCRUB_MIN_TXG "scrub_min_txg"
+/* 1x8 txg */
+#define DMU_POOL_SCRUB_MAX_TXG "scrub_max_txg"
+/* 1x4 enum scrub_func */
+#define DMU_POOL_SCRUB_FUNC "scrub_func"
+/* 1x8 count */
+#define DMU_POOL_SCRUB_ERRORS "scrub_errors"
/*
* Allocate an object from this objset. The range of object numbers
@@ -298,6 +317,7 @@ int dmu_get_replication_level(struct objset_impl *, struct zbookmark *zb,
*/
int dmu_bonus_hold(objset_t *os, uint64_t object, void *tag, dmu_buf_t **);
int dmu_bonus_max(void);
+int dmu_set_bonus(dmu_buf_t *, int, dmu_tx_t *);
/*
* Obtain the DMU buffer from the specified object which contains the
@@ -417,6 +437,9 @@ void dmu_tx_commit(dmu_tx_t *tx);
*/
int dmu_free_range(objset_t *os, uint64_t object, uint64_t offset,
uint64_t size, dmu_tx_t *tx);
+int dmu_free_long_range(objset_t *os, uint64_t object, uint64_t offset,
+ uint64_t size);
+int dmu_free_object(objset_t *os, uint64_t object);
/*
* Convenience functions.
@@ -458,8 +481,10 @@ typedef struct dmu_object_info {
uint64_t doi_max_block_offset;
} dmu_object_info_t;
+typedef void arc_byteswap_func_t(void *buf, size_t size);
+
typedef struct dmu_object_type_info {
- dmu_byteswap_func_t *ot_byteswap;
+ arc_byteswap_func_t *ot_byteswap;
boolean_t ot_metadata;
char *ot_name;
} dmu_object_type_info_t;
@@ -482,10 +507,11 @@ void dmu_object_size_from_db(dmu_buf_t *db, uint32_t *blksize,
typedef struct dmu_objset_stats {
uint64_t dds_num_clones; /* number of clones of this */
uint64_t dds_creation_txg;
+ uint64_t dds_guid;
dmu_objset_type_t dds_type;
uint8_t dds_is_snapshot;
uint8_t dds_inconsistent;
- char dds_clone_of[MAXNAMELEN];
+ char dds_origin[MAXNAMELEN];
} dmu_objset_stats_t;
/*
@@ -531,9 +557,13 @@ extern void dmu_objset_name(objset_t *os, char *buf);
extern dmu_objset_type_t dmu_objset_type(objset_t *os);
extern uint64_t dmu_objset_id(objset_t *os);
extern int dmu_snapshot_list_next(objset_t *os, int namelen, char *name,
- uint64_t *id, uint64_t *offp);
+ uint64_t *id, uint64_t *offp, boolean_t *case_conflict);
+extern int dmu_snapshot_realname(objset_t *os, char *name, char *real,
+ int maxlen, boolean_t *conflict);
extern int dmu_dir_list_next(objset_t *os, int namelen, char *name,
uint64_t *idp, uint64_t *offp);
+extern void dmu_objset_set_user(objset_t *os, void *user_ptr);
+extern void *dmu_objset_get_user(objset_t *os);
/*
* Return the txg number for the given assigned transaction.
@@ -544,7 +574,7 @@ uint64_t dmu_tx_get_txg(dmu_tx_t *tx);
* Synchronous write.
* If a parent zio is provided this function initiates a write on the
* provided buffer as a child of the parent zio.
- * In the absense of a parent zio, the write is completed synchronously.
+ * In the absence of a parent zio, the write is completed synchronously.
* At write completion, blk is filled with the bp of the written block.
* Note that while the data covered by this function will be on stable
* storage when the write completes this new data does not become a
@@ -572,9 +602,30 @@ typedef void (*dmu_traverse_cb_t)(objset_t *os, void *arg, struct blkptr *bp,
void dmu_traverse_objset(objset_t *os, uint64_t txg_start,
dmu_traverse_cb_t cb, void *arg);
-int dmu_sendbackup(objset_t *tosnap, objset_t *fromsnap, struct file *fp);
-int dmu_recvbackup(char *tosnap, struct drr_begin *drrb, uint64_t *sizep,
- boolean_t force, struct file *fp, uint64_t voffset);
+int dmu_sendbackup(objset_t *tosnap, objset_t *fromsnap, boolean_t fromorigin,
+ struct file *fp, offset_t *off);
+
+typedef struct dmu_recv_cookie {
+ /*
+ * This structure is opaque!
+ *
+ * If logical and real are different, we are recving the stream
+ * into the "real" temporary clone, and then switching it with
+ * the "logical" target.
+ */
+ struct dsl_dataset *drc_logical_ds;
+ struct dsl_dataset *drc_real_ds;
+ struct drr_begin *drc_drrb;
+ char *drc_tosnap;
+ boolean_t drc_newfs;
+ boolean_t drc_force;
+} dmu_recv_cookie_t;
+
+int dmu_recv_begin(char *tofs, char *tosnap, struct drr_begin *,
+ boolean_t force, objset_t *origin, boolean_t online, dmu_recv_cookie_t *);
+int dmu_recv_stream(dmu_recv_cookie_t *drc, struct file *fp, offset_t *voffp);
+int dmu_recv_end(dmu_recv_cookie_t *drc);
+void dmu_recv_abort_cleanup(dmu_recv_cookie_t *drc);
/* CRC64 table */
#define ZFS_CRC64_POLY 0xC96C5795D7870F42ULL /* ECMA-182, reflected form */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_impl.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_impl.h
index 807011e..96ce688 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_impl.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_impl.h
@@ -19,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_DMU_IMPL_H
#define _SYS_DMU_IMPL_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/txg_impl.h>
#include <sys/zio.h>
#include <sys/dnode.h>
@@ -51,7 +49,7 @@ extern "C" {
* XXX try to improve evicting path?
*
* dp_config_rwlock > os_obj_lock > dn_struct_rwlock >
- * dn_dbufs_mtx > hash_mutexes > db_mtx > leafs
+ * dn_dbufs_mtx > hash_mutexes > db_mtx > dd_lock > leafs
*
* dp_config_rwlock
* must be held before: everything
@@ -177,7 +175,10 @@ extern "C" {
* dmu_tx_try_assign: dn_notxholds(cv)
* dmu_tx_unassign: none
*
- * dd_lock (leaf)
+ * dd_lock
+ * must be held before:
+ * ds_lock
+ * ancestors' dd_lock
* protects:
* dd_prop_cbs
* dd_sync_*
@@ -207,13 +208,14 @@ extern "C" {
* dnode_setdirty: none (dn_dirtyblksz, os_*_dnodes)
* dnode_free: none (dn_dirtyblksz, os_*_dnodes)
*
- * ds_lock (leaf)
+ * ds_lock
* protects:
* ds_user_ptr
* ds_user_evice_func
* ds_open_refcount
* ds_snapname
* ds_phys accounting
+ * ds_reserved
* held from:
* dsl_dataset_*
*
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_objset.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_objset.h
index 8293a3b..15df29a 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_objset.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_objset.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -69,12 +69,13 @@ typedef struct objset_impl {
uint8_t os_checksum; /* can change, under dsl_dir's locks */
uint8_t os_compress; /* can change, under dsl_dir's locks */
uint8_t os_copies; /* can change, under dsl_dir's locks */
- uint8_t os_md_checksum;
- uint8_t os_md_compress;
+ uint8_t os_primary_cache; /* can change, under dsl_dir's locks */
+ uint8_t os_secondary_cache; /* can change, under dsl_dir's locks */
/* no lock needed: */
struct dmu_tx *os_synctx; /* XXX sketchy */
blkptr_t *os_rootbp;
+ zil_header_t os_zil_header;
/* Protected by os_obj_lock */
kmutex_t os_obj_lock;
@@ -86,19 +87,27 @@ typedef struct objset_impl {
list_t os_free_dnodes[TXG_SIZE];
list_t os_dnodes;
list_t os_downgraded_dbufs;
+
+ /* stuff we store for the user */
+ kmutex_t os_user_ptr_lock;
+ void *os_user_ptr;
} objset_impl_t;
#define DMU_META_DNODE_OBJECT 0
+#define DMU_OS_IS_L2CACHEABLE(os) \
+ ((os)->os_secondary_cache == ZFS_CACHE_ALL || \
+ (os)->os_secondary_cache == ZFS_CACHE_METADATA)
+
/* called from zpl */
int dmu_objset_open(const char *name, dmu_objset_type_t type, int mode,
objset_t **osp);
void dmu_objset_close(objset_t *os);
int dmu_objset_create(const char *name, dmu_objset_type_t type,
- objset_t *clone_parent,
- void (*func)(objset_t *os, void *arg, dmu_tx_t *tx), void *arg);
+ objset_t *clone_parent, uint64_t flags,
+ void (*func)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx), void *arg);
int dmu_objset_destroy(const char *name);
-int dmu_objset_rollback(const char *name);
+int dmu_objset_rollback(objset_t *os);
int dmu_objset_snapshot(char *fsname, char *snapname, boolean_t recursive);
void dmu_objset_stats(objset_t *os, nvlist_t *nv);
void dmu_objset_fast_stat(objset_t *os, dmu_objset_stats_t *stat);
@@ -107,8 +116,10 @@ void dmu_objset_space(objset_t *os, uint64_t *refdbytesp, uint64_t *availbytesp,
uint64_t dmu_objset_fsid_guid(objset_t *os);
int dmu_objset_find(char *name, int func(char *, void *), void *arg,
int flags);
+int dmu_objset_find_spa(spa_t *spa, const char *name,
+ int func(spa_t *, uint64_t, const char *, void *), void *arg, int flags);
void dmu_objset_byteswap(void *buf, size_t size);
-int dmu_objset_evict_dbufs(objset_t *os, int try);
+int dmu_objset_evict_dbufs(objset_t *os);
/* called from dsl */
void dmu_objset_sync(objset_impl_t *os, zio_t *zio, dmu_tx_t *tx);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_traverse.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_traverse.h
index ea9fa6c..05e5ffd 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_traverse.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_traverse.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -100,6 +100,7 @@ struct traverse_handle {
int traverse_dsl_dataset(struct dsl_dataset *ds, uint64_t txg_start,
int advance, blkptr_cb_t func, void *arg);
+int traverse_zvol(objset_t *os, int advance, blkptr_cb_t func, void *arg);
traverse_handle_t *traverse_init(spa_t *spa, blkptr_cb_t *func, void *arg,
int advance, int zio_flags);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_tx.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_tx.h
index 89f4799..6aaf35d 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_tx.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_tx.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -64,6 +64,7 @@ struct dmu_tx {
uint64_t tx_space_towrite;
uint64_t tx_space_tofree;
uint64_t tx_space_tooverwrite;
+ uint64_t tx_space_tounref;
refcount_t tx_space_written;
refcount_t tx_space_freed;
#endif
@@ -86,6 +87,9 @@ typedef struct dmu_tx_hold {
uint64_t txh_space_towrite;
uint64_t txh_space_tofree;
uint64_t txh_space_tooverwrite;
+ uint64_t txh_space_tounref;
+ uint64_t txh_memory_tohold;
+ uint64_t txh_fudge;
#ifdef ZFS_DEBUG
enum dmu_tx_hold_type txh_type;
uint64_t txh_arg1;
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dnode.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dnode.h
index 327e538..c79ff48 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dnode.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dnode.h
@@ -19,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_DNODE_H
#define _SYS_DNODE_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/zfs_context.h>
#include <sys/avl.h>
#include <sys/spa.h>
@@ -41,12 +39,19 @@ extern "C" {
#endif
/*
- * Flags.
+ * dnode_hold() flags.
*/
#define DNODE_MUST_BE_ALLOCATED 1
#define DNODE_MUST_BE_FREE 2
/*
+ * dnode_next_offset() flags.
+ */
+#define DNODE_FIND_HOLE 1
+#define DNODE_FIND_BACKWARDS 2
+#define DNODE_FIND_HAVELOCK 4
+
+/*
* Fixed constants.
*/
#define DNODE_SHIFT 9 /* 512 bytes */
@@ -64,6 +69,7 @@ extern "C" {
#define DN_MAX_NBLKPTR ((DNODE_SIZE - DNODE_CORE_SIZE) >> SPA_BLKPTRSHIFT)
#define DN_MAX_BONUSLEN (DNODE_SIZE - DNODE_CORE_SIZE - (1 << SPA_BLKPTRSHIFT))
#define DN_MAX_OBJECT (1ULL << DN_MAX_OBJECT_SHIFT)
+#define DN_ZERO_BONUSLEN (DN_MAX_BONUSLEN + 1)
#define DNODES_PER_BLOCK_SHIFT (DNODE_BLOCK_SHIFT - DNODE_SHIFT)
#define DNODES_PER_BLOCK (1ULL << DNODES_PER_BLOCK_SHIFT)
@@ -156,6 +162,7 @@ typedef struct dnode {
uint64_t dn_maxblkid;
uint8_t dn_next_nlevels[TXG_SIZE];
uint8_t dn_next_indblkshift[TXG_SIZE];
+ uint16_t dn_next_bonuslen[TXG_SIZE];
uint32_t dn_next_blksz[TXG_SIZE]; /* next block size in bytes */
/* protected by os_lock: */
@@ -197,11 +204,12 @@ dnode_t *dnode_special_open(struct objset_impl *dd, dnode_phys_t *dnp,
uint64_t object);
void dnode_special_close(dnode_t *dn);
+void dnode_setbonuslen(dnode_t *dn, int newsize, dmu_tx_t *tx);
int dnode_hold(struct objset_impl *dd, uint64_t object,
void *ref, dnode_t **dnp);
int dnode_hold_impl(struct objset_impl *dd, uint64_t object, int flag,
void *ref, dnode_t **dnp);
-void dnode_add_ref(dnode_t *dn, void *ref);
+boolean_t dnode_add_ref(dnode_t *dn, void *ref);
void dnode_rele(dnode_t *dn, void *ref);
void dnode_setdirty(dnode_t *dn, dmu_tx_t *tx);
void dnode_sync(dnode_t *dn, dmu_tx_t *tx);
@@ -220,13 +228,13 @@ void dnode_clear_range(dnode_t *dn, uint64_t blkid,
uint64_t nblks, dmu_tx_t *tx);
void dnode_diduse_space(dnode_t *dn, int64_t space);
void dnode_willuse_space(dnode_t *dn, int64_t space, dmu_tx_t *tx);
-void dnode_new_blkid(dnode_t *dn, uint64_t blkid, dmu_tx_t *tx);
+void dnode_new_blkid(dnode_t *dn, uint64_t blkid, dmu_tx_t *tx, boolean_t);
uint64_t dnode_block_freed(dnode_t *dn, uint64_t blkid);
void dnode_init(void);
void dnode_fini(void);
-int dnode_next_offset(dnode_t *dn, boolean_t hole, uint64_t *off, int minlvl,
- uint64_t blkfill, uint64_t txg);
-int dnode_evict_dbufs(dnode_t *dn, int try);
+int dnode_next_offset(dnode_t *dn, int flags, uint64_t *off,
+ int minlvl, uint64_t blkfill, uint64_t txg);
+void dnode_evict_dbufs(dnode_t *dn);
#ifdef ZFS_DEBUG
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dataset.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dataset.h
index 8cfc1dc..8665aec 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dataset.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dataset.h
@@ -19,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_DSL_DATASET_H
#define _SYS_DSL_DATASET_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/dmu.h>
#include <sys/spa.h>
#include <sys/txg.h>
@@ -47,6 +45,8 @@ struct dsl_pool;
typedef void dsl_dataset_evict_func_t(struct dsl_dataset *, void *);
#define DS_FLAG_INCONSISTENT (1ULL<<0)
+#define DS_IS_INCONSISTENT(ds) \
+ ((ds)->ds_phys->ds_flags & DS_FLAG_INCONSISTENT)
/*
* NB: nopromote can not yet be set, but we want support for it in this
* on-disk version, so that we don't need to upgrade for it later. It
@@ -55,16 +55,29 @@ typedef void dsl_dataset_evict_func_t(struct dsl_dataset *, void *);
*/
#define DS_FLAG_NOPROMOTE (1ULL<<1)
+/*
+ * DS_FLAG_UNIQUE_ACCURATE is set if ds_unique_bytes has been correctly
+ * calculated for head datasets (starting with SPA_VERSION_UNIQUE_ACCURATE,
+ * refquota/refreservations).
+ */
+#define DS_FLAG_UNIQUE_ACCURATE (1ULL<<2)
+
+/*
+ * DS_FLAG_CI_DATASET is set if the dataset contains a file system whose
+ * name lookups should be performed case-insensitively.
+ */
+#define DS_FLAG_CI_DATASET (1ULL<<16)
+
typedef struct dsl_dataset_phys {
- uint64_t ds_dir_obj;
- uint64_t ds_prev_snap_obj;
+ uint64_t ds_dir_obj; /* DMU_OT_DSL_DIR */
+ uint64_t ds_prev_snap_obj; /* DMU_OT_DSL_DATASET */
uint64_t ds_prev_snap_txg;
- uint64_t ds_next_snap_obj;
- uint64_t ds_snapnames_zapobj; /* zap obj of snaps; ==0 for snaps */
+ uint64_t ds_next_snap_obj; /* DMU_OT_DSL_DATASET */
+ uint64_t ds_snapnames_zapobj; /* DMU_OT_DSL_DS_SNAP_MAP 0 for snaps */
uint64_t ds_num_children; /* clone/snap children; ==0 for head */
uint64_t ds_creation_time; /* seconds since 1970 */
uint64_t ds_creation_txg;
- uint64_t ds_deadlist_obj;
+ uint64_t ds_deadlist_obj; /* DMU_OT_BPLIST */
uint64_t ds_used_bytes;
uint64_t ds_compressed_bytes;
uint64_t ds_uncompressed_bytes;
@@ -76,9 +89,11 @@ typedef struct dsl_dataset_phys {
*/
uint64_t ds_fsid_guid;
uint64_t ds_guid;
- uint64_t ds_flags;
+ uint64_t ds_flags; /* DS_FLAG_* */
blkptr_t ds_bp;
- uint64_t ds_pad[8]; /* pad out to 320 bytes for good measure */
+ uint64_t ds_next_clones_obj; /* DMU_OT_DSL_CLONES */
+ uint64_t ds_props_obj; /* DMU_OT_DSL_PROPS for snaps */
+ uint64_t ds_pad[6]; /* pad out to 320 bytes for good measure */
} dsl_dataset_phys_t;
typedef struct dsl_dataset {
@@ -87,9 +102,11 @@ typedef struct dsl_dataset {
dsl_dataset_phys_t *ds_phys;
dmu_buf_t *ds_dbuf;
uint64_t ds_object;
+ uint64_t ds_fsid_guid;
- /* only used in syncing context: */
- struct dsl_dataset *ds_prev; /* only valid for non-snapshots */
+ /* only used in syncing context, only valid for non-snapshots: */
+ struct dsl_dataset *ds_prev;
+ uint64_t ds_origin_txg;
/* has internal locking: */
bplist_t ds_deadlist;
@@ -105,11 +122,23 @@ typedef struct dsl_dataset {
kmutex_t ds_lock;
void *ds_user_ptr;
dsl_dataset_evict_func_t *ds_user_evict_func;
- uint64_t ds_open_refcount;
+
+ /*
+ * ds_owner is protected by the ds_rwlock and the ds_lock
+ */
+ krwlock_t ds_rwlock;
+ kcondvar_t ds_exclusive_cv;
+ void *ds_owner;
/* no locking; only for making guesses */
uint64_t ds_trysnap_txg;
+ /* for objset_open() */
+ kmutex_t ds_opening_lock;
+
+ uint64_t ds_reserved; /* cached refreservation */
+ uint64_t ds_quota; /* cached refquota */
+
/* Protected by ds_lock; keep at end of struct for better locality */
char ds_snapname[MAXNAMELEN];
} dsl_dataset_t;
@@ -117,23 +146,38 @@ typedef struct dsl_dataset {
#define dsl_dataset_is_snapshot(ds) \
((ds)->ds_phys->ds_num_children != 0)
-int dsl_dataset_open_spa(spa_t *spa, const char *name, int mode,
- void *tag, dsl_dataset_t **dsp);
-int dsl_dataset_open(const char *name, int mode, void *tag,
+#define DS_UNIQUE_IS_ACCURATE(ds) \
+ (((ds)->ds_phys->ds_flags & DS_FLAG_UNIQUE_ACCURATE) != 0)
+
+int dsl_dataset_hold(const char *name, void *tag, dsl_dataset_t **dsp);
+int dsl_dataset_hold_obj(struct dsl_pool *dp, uint64_t dsobj,
+ void *tag, dsl_dataset_t **);
+int dsl_dataset_own(const char *name, int flags, void *owner,
dsl_dataset_t **dsp);
-int dsl_dataset_open_obj(struct dsl_pool *dp, uint64_t dsobj,
- const char *tail, int mode, void *tag, dsl_dataset_t **);
+int dsl_dataset_own_obj(struct dsl_pool *dp, uint64_t dsobj,
+ int flags, void *owner, dsl_dataset_t **);
void dsl_dataset_name(dsl_dataset_t *ds, char *name);
-void dsl_dataset_close(dsl_dataset_t *ds, int mode, void *tag);
-uint64_t dsl_dataset_create_sync(dsl_dir_t *pds,
- const char *lastname, dsl_dataset_t *clone_parent, dmu_tx_t *tx);
-int dsl_dataset_destroy(const char *name);
+void dsl_dataset_rele(dsl_dataset_t *ds, void *tag);
+void dsl_dataset_disown(dsl_dataset_t *ds, void *owner);
+void dsl_dataset_drop_ref(dsl_dataset_t *ds, void *tag);
+boolean_t dsl_dataset_tryown(dsl_dataset_t *ds, boolean_t inconsistentok,
+ void *owner);
+void dsl_dataset_make_exclusive(dsl_dataset_t *ds, void *owner);
+uint64_t dsl_dataset_create_sync(dsl_dir_t *pds, const char *lastname,
+ dsl_dataset_t *origin, uint64_t flags, cred_t *, dmu_tx_t *);
+uint64_t dsl_dataset_create_sync_dd(dsl_dir_t *dd, dsl_dataset_t *origin,
+ uint64_t flags, dmu_tx_t *tx);
+int dsl_dataset_destroy(dsl_dataset_t *ds, void *tag);
int dsl_snapshots_destroy(char *fsname, char *snapname);
+dsl_checkfunc_t dsl_dataset_destroy_check;
+dsl_syncfunc_t dsl_dataset_destroy_sync;
dsl_checkfunc_t dsl_dataset_snapshot_check;
dsl_syncfunc_t dsl_dataset_snapshot_sync;
-int dsl_dataset_rollback(dsl_dataset_t *ds);
+int dsl_dataset_rollback(dsl_dataset_t *ds, dmu_objset_type_t ost);
int dsl_dataset_rename(char *name, const char *newname, boolean_t recursive);
int dsl_dataset_promote(const char *name);
+int dsl_dataset_clone_swap(dsl_dataset_t *clone, dsl_dataset_t *origin_head,
+ boolean_t force);
void *dsl_dataset_set_user_ptr(dsl_dataset_t *ds,
void *p, dsl_dataset_evict_func_t func);
@@ -144,10 +188,12 @@ void dsl_dataset_set_blkptr(dsl_dataset_t *ds, blkptr_t *bp, dmu_tx_t *tx);
spa_t *dsl_dataset_get_spa(dsl_dataset_t *ds);
+boolean_t dsl_dataset_modified_since_lastsnap(dsl_dataset_t *ds);
+
void dsl_dataset_sync(dsl_dataset_t *os, zio_t *zio, dmu_tx_t *tx);
void dsl_dataset_block_born(dsl_dataset_t *ds, blkptr_t *bp, dmu_tx_t *tx);
-void dsl_dataset_block_kill(dsl_dataset_t *ds, blkptr_t *bp, zio_t *pio,
+int dsl_dataset_block_kill(dsl_dataset_t *ds, blkptr_t *bp, zio_t *pio,
dmu_tx_t *tx);
int dsl_dataset_block_freeable(dsl_dataset_t *ds, uint64_t blk_birth);
uint64_t dsl_dataset_prev_snap_txg(dsl_dataset_t *ds);
@@ -160,11 +206,19 @@ void dsl_dataset_space(dsl_dataset_t *ds,
uint64_t *usedobjsp, uint64_t *availobjsp);
uint64_t dsl_dataset_fsid_guid(dsl_dataset_t *ds);
-void dsl_dataset_create_root(struct dsl_pool *dp, uint64_t *ddobjp,
- dmu_tx_t *tx);
-
int dsl_dsobj_to_dsname(char *pname, uint64_t obj, char *buf);
+int dsl_dataset_check_quota(dsl_dataset_t *ds, boolean_t check_quota,
+ uint64_t asize, uint64_t inflight, uint64_t *used,
+ uint64_t *ref_rsrv);
+int dsl_dataset_set_quota(const char *dsname, uint64_t quota);
+void dsl_dataset_set_quota_sync(void *arg1, void *arg2, cred_t *cr,
+ dmu_tx_t *tx);
+int dsl_dataset_set_reservation(const char *dsname, uint64_t reservation);
+void dsl_dataset_set_flags(dsl_dataset_t *ds, uint64_t flags);
+int64_t dsl_dataset_new_refreservation(dsl_dataset_t *ds, uint64_t reservation,
+ dmu_tx_t *tx);
+
#ifdef ZFS_DEBUG
#define dprintf_ds(ds, fmt, ...) do { \
if (zfs_flags & ZFS_DEBUG_DPRINTF) { \
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_deleg.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_deleg.h
new file mode 100644
index 0000000..a29e44e
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_deleg.h
@@ -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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_DSL_DELEG_H
+#define _SYS_DSL_DELEG_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/dmu.h>
+#include <sys/dsl_pool.h>
+#include <sys/zfs_context.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZFS_DELEG_PERM_NONE ""
+#define ZFS_DELEG_PERM_CREATE "create"
+#define ZFS_DELEG_PERM_DESTROY "destroy"
+#define ZFS_DELEG_PERM_SNAPSHOT "snapshot"
+#define ZFS_DELEG_PERM_ROLLBACK "rollback"
+#define ZFS_DELEG_PERM_CLONE "clone"
+#define ZFS_DELEG_PERM_PROMOTE "promote"
+#define ZFS_DELEG_PERM_RENAME "rename"
+#define ZFS_DELEG_PERM_MOUNT "mount"
+#define ZFS_DELEG_PERM_SHARE "share"
+#define ZFS_DELEG_PERM_SEND "send"
+#define ZFS_DELEG_PERM_RECEIVE "receive"
+#define ZFS_DELEG_PERM_ALLOW "allow"
+#define ZFS_DELEG_PERM_USERPROP "userprop"
+#define ZFS_DELEG_PERM_VSCAN "vscan"
+
+/*
+ * Note: the names of properties that are marked delegatable are also
+ * valid delegated permissions
+ */
+
+int dsl_deleg_get(const char *ddname, nvlist_t **nvp);
+int dsl_deleg_set(const char *ddname, nvlist_t *nvp, boolean_t unset);
+int dsl_deleg_access(const char *ddname, const char *perm, cred_t *cr);
+void dsl_deleg_set_create_perms(dsl_dir_t *dd, dmu_tx_t *tx, cred_t *cr);
+int dsl_deleg_can_allow(char *ddname, nvlist_t *nvp, cred_t *cr);
+int dsl_deleg_can_unallow(char *ddname, nvlist_t *nvp, cred_t *cr);
+int dsl_deleg_destroy(objset_t *os, uint64_t zapobj, dmu_tx_t *tx);
+boolean_t dsl_delegation_on(objset_t *os);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_DSL_DELEG_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dir.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dir.h
index e0595d3..86b9636 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dir.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dir.h
@@ -19,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_DSL_DIR_H
#define _SYS_DSL_DIR_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/dmu.h>
#include <sys/dsl_pool.h>
#include <sys/dsl_synctask.h>
@@ -40,11 +38,22 @@ extern "C" {
struct dsl_dataset;
+typedef enum dd_used {
+ DD_USED_HEAD,
+ DD_USED_SNAP,
+ DD_USED_CHILD,
+ DD_USED_CHILD_RSRV,
+ DD_USED_REFRSRV,
+ DD_USED_NUM
+} dd_used_t;
+
+#define DD_FLAG_USED_BREAKDOWN (1<<0)
+
typedef struct dsl_dir_phys {
uint64_t dd_creation_time; /* not actually used */
uint64_t dd_head_dataset_obj;
uint64_t dd_parent_obj;
- uint64_t dd_clone_parent_obj;
+ uint64_t dd_origin_obj;
uint64_t dd_child_dir_zapobj;
/*
* how much space our children are accounting for; for leaf
@@ -58,7 +67,10 @@ typedef struct dsl_dir_phys {
/* Administrative reservation setting */
uint64_t dd_reserved;
uint64_t dd_props_zapobj;
- uint64_t dd_pad[21]; /* pad out to 256 bytes for good measure */
+ uint64_t dd_deleg_zapobj; /* dataset delegation permissions */
+ uint64_t dd_flags;
+ uint64_t dd_used_breakdown[DD_USED_NUM];
+ uint64_t dd_pad[14]; /* pad out to 256 bytes for good measure */
} dsl_dir_phys_t;
struct dsl_dir {
@@ -78,9 +90,6 @@ struct dsl_dir {
kmutex_t dd_lock;
list_t dd_prop_cbs; /* list of dsl_prop_cb_record_t's */
- /* Accounting */
- /* reflects any changes to dd_phys->dd_used_bytes made this syncing */
- int64_t dd_used_bytes;
/* gross estimate of space used by in-flight tx's */
uint64_t dd_tempreserved[TXG_SIZE];
/* amount of space we expect to write; == amount of dirty data */
@@ -99,8 +108,8 @@ int dsl_dir_open_obj(dsl_pool_t *dp, uint64_t ddobj,
void dsl_dir_name(dsl_dir_t *dd, char *buf);
int dsl_dir_namelen(dsl_dir_t *dd);
int dsl_dir_is_private(dsl_dir_t *dd);
-uint64_t dsl_dir_create_sync(dsl_dir_t *pds, const char *name, dmu_tx_t *tx);
-void dsl_dir_create_root(objset_t *mos, uint64_t *ddobjp, dmu_tx_t *tx);
+uint64_t dsl_dir_create_sync(dsl_pool_t *dp, dsl_dir_t *pds,
+ const char *name, dmu_tx_t *tx);
dsl_checkfunc_t dsl_dir_destroy_check;
dsl_syncfunc_t dsl_dir_destroy_sync;
void dsl_dir_stats(dsl_dir_t *dd, nvlist_t *nv);
@@ -109,18 +118,26 @@ uint64_t dsl_dir_space_available(dsl_dir_t *dd,
void dsl_dir_dirty(dsl_dir_t *dd, dmu_tx_t *tx);
void dsl_dir_sync(dsl_dir_t *dd, dmu_tx_t *tx);
int dsl_dir_tempreserve_space(dsl_dir_t *dd, uint64_t mem,
- uint64_t asize, uint64_t fsize, void **tr_cookiep, dmu_tx_t *tx);
+ uint64_t asize, uint64_t fsize, uint64_t usize, void **tr_cookiep,
+ dmu_tx_t *tx);
void dsl_dir_tempreserve_clear(void *tr_cookie, dmu_tx_t *tx);
void dsl_dir_willuse_space(dsl_dir_t *dd, int64_t space, dmu_tx_t *tx);
-void dsl_dir_diduse_space(dsl_dir_t *dd,
+void dsl_dir_diduse_space(dsl_dir_t *dd, dd_used_t type,
int64_t used, int64_t compressed, int64_t uncompressed, dmu_tx_t *tx);
+void dsl_dir_transfer_space(dsl_dir_t *dd, int64_t delta,
+ dd_used_t oldtype, dd_used_t newtype, dmu_tx_t *tx);
int dsl_dir_set_quota(const char *ddname, uint64_t quota);
int dsl_dir_set_reservation(const char *ddname, uint64_t reservation);
int dsl_dir_rename(dsl_dir_t *dd, const char *newname);
int dsl_dir_transfer_possible(dsl_dir_t *sdd, dsl_dir_t *tdd, uint64_t space);
+int dsl_dir_set_reservation_check(void *arg1, void *arg2, dmu_tx_t *tx);
+boolean_t dsl_dir_is_clone(dsl_dir_t *dd);
+void dsl_dir_new_refreservation(dsl_dir_t *dd, struct dsl_dataset *ds,
+ uint64_t reservation, cred_t *cr, dmu_tx_t *tx);
/* internal reserved dir name */
#define MOS_DIR_NAME "$MOS"
+#define ORIGIN_DIR_NAME "$ORIGIN"
#ifdef ZFS_DEBUG
#define dprintf_dd(dd, fmt, ...) do { \
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_pool.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_pool.h
index f7ec67a..4dd88fe 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_pool.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_pool.h
@@ -19,19 +19,18 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_DSL_POOL_H
#define _SYS_DSL_POOL_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/spa.h>
#include <sys/txg.h>
#include <sys/txg_impl.h>
#include <sys/zfs_context.h>
+#include <sys/zio.h>
#ifdef __cplusplus
extern "C" {
@@ -39,6 +38,16 @@ extern "C" {
struct objset;
struct dsl_dir;
+struct dsl_dataset;
+struct dsl_pool;
+struct dmu_tx;
+
+enum scrub_func {
+ SCRUB_FUNC_NONE,
+ SCRUB_FUNC_CLEAN,
+ SCRUB_FUNC_NUMFUNCS
+};
+
typedef struct dsl_pool {
/* Immutable */
@@ -46,11 +55,31 @@ typedef struct dsl_pool {
struct objset *dp_meta_objset;
struct dsl_dir *dp_root_dir;
struct dsl_dir *dp_mos_dir;
+ struct dsl_dataset *dp_origin_snap;
uint64_t dp_root_dir_obj;
/* No lock needed - sync context only */
blkptr_t dp_meta_rootbp;
- list_t dp_synced_objsets;
+ list_t dp_synced_datasets;
+ hrtime_t dp_read_overhead;
+ uint64_t dp_throughput;
+ uint64_t dp_write_limit;
+
+ /* Uses dp_lock */
+ kmutex_t dp_lock;
+ uint64_t dp_space_towrite[TXG_SIZE];
+ uint64_t dp_tempreserved[TXG_SIZE];
+
+ enum scrub_func dp_scrub_func;
+ uint64_t dp_scrub_queue_obj;
+ uint64_t dp_scrub_min_txg;
+ uint64_t dp_scrub_max_txg;
+ zbookmark_t dp_scrub_bookmark;
+ boolean_t dp_scrub_pausing;
+ boolean_t dp_scrub_isresilver;
+ uint64_t dp_scrub_start_time;
+ kmutex_t dp_scrub_cancel_lock; /* protects dp_scrub_restart */
+ boolean_t dp_scrub_restart;
/* Has its own locking */
tx_state_t dp_tx;
@@ -69,11 +98,26 @@ typedef struct dsl_pool {
int dsl_pool_open(spa_t *spa, uint64_t txg, dsl_pool_t **dpp);
void dsl_pool_close(dsl_pool_t *dp);
-dsl_pool_t *dsl_pool_create(spa_t *spa, uint64_t txg);
+dsl_pool_t *dsl_pool_create(spa_t *spa, nvlist_t *zplprops, uint64_t txg);
void dsl_pool_sync(dsl_pool_t *dp, uint64_t txg);
void dsl_pool_zil_clean(dsl_pool_t *dp);
int dsl_pool_sync_context(dsl_pool_t *dp);
uint64_t dsl_pool_adjustedsize(dsl_pool_t *dp, boolean_t netfree);
+int dsl_pool_tempreserve_space(dsl_pool_t *dp, uint64_t space, dmu_tx_t *tx);
+void dsl_pool_tempreserve_clear(dsl_pool_t *dp, int64_t space, dmu_tx_t *tx);
+void dsl_pool_memory_pressure(dsl_pool_t *dp);
+void dsl_pool_willuse_space(dsl_pool_t *dp, int64_t space, dmu_tx_t *tx);
+int dsl_free(zio_t *pio, dsl_pool_t *dp, uint64_t txg, const blkptr_t *bpp,
+ zio_done_func_t *done, void *private, uint32_t arc_flags);
+void dsl_pool_ds_destroyed(struct dsl_dataset *ds, struct dmu_tx *tx);
+void dsl_pool_ds_snapshotted(struct dsl_dataset *ds, struct dmu_tx *tx);
+void dsl_pool_create_origin(dsl_pool_t *dp, dmu_tx_t *tx);
+void dsl_pool_upgrade_clones(dsl_pool_t *dp, dmu_tx_t *tx);
+
+int dsl_pool_scrub_cancel(dsl_pool_t *dp);
+int dsl_pool_scrub_clean(dsl_pool_t *dp);
+void dsl_pool_scrub_sync(dsl_pool_t *dp, dmu_tx_t *tx);
+void dsl_pool_scrub_restart(dsl_pool_t *dp);
#ifdef __cplusplus
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_prop.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_prop.h
index d2debff..d66caa8 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_prop.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_prop.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -37,6 +37,7 @@ extern "C" {
#endif
struct dsl_dataset;
+struct dsl_dir;
/* The callback func may not call into the DMU or DSL! */
typedef void (dsl_prop_changed_cb_t)(void *arg, uint64_t newval);
@@ -59,12 +60,16 @@ int dsl_prop_get(const char *ddname, const char *propname,
int intsz, int numints, void *buf, char *setpoint);
int dsl_prop_get_integer(const char *ddname, const char *propname,
uint64_t *valuep, char *setpoint);
-int dsl_prop_get_all(objset_t *os, nvlist_t **nvp);
+int dsl_prop_get_all(objset_t *os, nvlist_t **nvp, boolean_t local);
+int dsl_prop_get_ds(struct dsl_dataset *ds, const char *propname,
+ int intsz, int numints, void *buf, char *setpoint);
+int dsl_prop_get_dd(struct dsl_dir *dd, const char *propname,
+ int intsz, int numints, void *buf, char *setpoint);
int dsl_prop_set(const char *ddname, const char *propname,
int intsz, int numints, const void *buf);
-int dsl_prop_set_dd(dsl_dir_t *dd, const char *propname,
- int intsz, int numints, const void *buf);
+void dsl_prop_set_uint64_sync(dsl_dir_t *dd, const char *name, uint64_t val,
+ cred_t *cr, dmu_tx_t *tx);
void dsl_prop_nvlist_add_uint64(nvlist_t *nv, zfs_prop_t prop, uint64_t value);
void dsl_prop_nvlist_add_string(nvlist_t *nv,
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_synctask.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_synctask.h
index e695b18..4995bfe 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_synctask.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_synctask.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -38,7 +38,7 @@ extern "C" {
struct dsl_pool;
typedef int (dsl_checkfunc_t)(void *, void *, dmu_tx_t *);
-typedef void (dsl_syncfunc_t)(void *, void *, dmu_tx_t *);
+typedef void (dsl_syncfunc_t)(void *, void *, cred_t *, dmu_tx_t *);
typedef struct dsl_sync_task {
list_node_t dst_node;
@@ -53,9 +53,11 @@ typedef struct dsl_sync_task_group {
txg_node_t dstg_node;
list_t dstg_tasks;
struct dsl_pool *dstg_pool;
+ cred_t *dstg_cr;
uint64_t dstg_txg;
int dstg_err;
int dstg_space;
+ boolean_t dstg_nowaiter;
} dsl_sync_task_group_t;
dsl_sync_task_group_t *dsl_sync_task_group_create(struct dsl_pool *dp);
@@ -63,12 +65,16 @@ void dsl_sync_task_create(dsl_sync_task_group_t *dstg,
dsl_checkfunc_t *, dsl_syncfunc_t *,
void *arg1, void *arg2, int blocks_modified);
int dsl_sync_task_group_wait(dsl_sync_task_group_t *dstg);
+void dsl_sync_task_group_nowait(dsl_sync_task_group_t *dstg, dmu_tx_t *tx);
void dsl_sync_task_group_destroy(dsl_sync_task_group_t *dstg);
void dsl_sync_task_group_sync(dsl_sync_task_group_t *dstg, dmu_tx_t *tx);
int dsl_sync_task_do(struct dsl_pool *dp,
dsl_checkfunc_t *checkfunc, dsl_syncfunc_t *syncfunc,
void *arg1, void *arg2, int blocks_modified);
+void dsl_sync_task_do_nowait(struct dsl_pool *dp,
+ dsl_checkfunc_t *checkfunc, dsl_syncfunc_t *syncfunc,
+ void *arg1, void *arg2, int blocks_modified, dmu_tx_t *tx);
#ifdef __cplusplus
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/metaslab.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/metaslab.h
index 095dd3c..1c9d89e 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/metaslab.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/metaslab.h
@@ -19,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_METASLAB_H
#define _SYS_METASLAB_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/spa.h>
#include <sys/space_map.h>
#include <sys/txg.h>
@@ -47,8 +45,12 @@ extern void metaslab_fini(metaslab_t *msp);
extern void metaslab_sync(metaslab_t *msp, uint64_t txg);
extern void metaslab_sync_done(metaslab_t *msp, uint64_t txg);
-extern int metaslab_alloc(spa_t *spa, uint64_t psize, blkptr_t *bp,
- int ncopies, uint64_t txg, blkptr_t *hintbp, boolean_t hintbp_avoid);
+#define METASLAB_HINTBP_FAVOR 0x0
+#define METASLAB_HINTBP_AVOID 0x1
+#define METASLAB_GANG_HEADER 0x2
+
+extern int metaslab_alloc(spa_t *spa, metaslab_class_t *mc, uint64_t psize,
+ blkptr_t *bp, int ncopies, uint64_t txg, blkptr_t *hintbp, int flags);
extern void metaslab_free(spa_t *spa, const blkptr_t *bp, uint64_t txg,
boolean_t now);
extern int metaslab_claim(spa_t *spa, const blkptr_t *bp, uint64_t txg);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/refcount.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/refcount.h
index c64c662..e84b1bf 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/refcount.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/refcount.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -28,6 +28,8 @@
#pragma ident "%Z%%M% %I% %E% SMI"
+#include <sys/cdefs.h>
+#include <sys/types.h>
#include_next <sys/refcount.h>
#include <sys/list.h>
#include <sys/zfs_context.h>
@@ -59,7 +61,7 @@ typedef struct refcount {
int64_t rc_removed_count;
} refcount_t;
-/* Note: refcount_t should be initialized to zero before use. */
+/* Note: refcount_t must be initialized with refcount_create() */
void refcount_create(refcount_t *rc);
void refcount_destroy(refcount_t *rc);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/rrwlock.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/rrwlock.h
new file mode 100644
index 0000000..760fc82
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/rrwlock.h
@@ -0,0 +1,79 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: 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 _SYS_RR_RW_LOCK_H
+#define _SYS_RR_RW_LOCK_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/zfs_context.h>
+#include <sys/refcount.h>
+
+/*
+ * A reader-writer lock implementation that allows re-entrant reads, but
+ * still gives writers priority on "new" reads.
+ *
+ * See rrwlock.c for more details about the implementation.
+ *
+ * Fields of the rrwlock_t structure:
+ * - rr_lock: protects modification and reading of rrwlock_t fields
+ * - rr_cv: cv for waking up readers or waiting writers
+ * - rr_writer: thread id of the current writer
+ * - rr_anon_rount: number of active anonymous readers
+ * - rr_linked_rcount: total number of non-anonymous active readers
+ * - rr_writer_wanted: a writer wants the lock
+ */
+typedef struct rrwlock {
+ kmutex_t rr_lock;
+ kcondvar_t rr_cv;
+ kthread_t *rr_writer;
+ refcount_t rr_anon_rcount;
+ refcount_t rr_linked_rcount;
+ boolean_t rr_writer_wanted;
+} rrwlock_t;
+
+/*
+ * 'tag' is used in reference counting tracking. The
+ * 'tag' must be the same in a rrw_enter() as in its
+ * corresponding rrw_exit().
+ */
+void rrw_init(rrwlock_t *rrl);
+void rrw_destroy(rrwlock_t *rrl);
+void rrw_enter(rrwlock_t *rrl, krw_t rw, void *tag);
+void rrw_exit(rrwlock_t *rrl, void *tag);
+boolean_t rrw_held(rrwlock_t *rrl, krw_t rw);
+
+#define RRW_READ_HELD(x) rrw_held(x, RW_READER)
+#define RRW_WRITE_HELD(x) rrw_held(x, RW_WRITER)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_RR_RW_LOCK_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa.h
index f0eb2e1..99bcb91 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa.h
@@ -19,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_SPA_H
#define _SYS_SPA_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/avl.h>
#include <sys/zfs_context.h>
#include <sys/nvpair.h>
@@ -47,6 +45,7 @@ typedef struct vdev vdev_t;
typedef struct metaslab metaslab_t;
typedef struct zilog zilog_t;
typedef struct traverse_handle traverse_handle_t;
+typedef struct spa_aux_vdev spa_aux_vdev_t;
struct dsl_pool;
/*
@@ -88,6 +87,11 @@ struct dsl_pool;
#define SPA_BLOCKSIZES (SPA_MAXBLOCKSHIFT - SPA_MINBLOCKSHIFT + 1)
/*
+ * Size of block to hold the configuration data (a packed nvlist)
+ */
+#define SPA_CONFIG_BLOCKSIZE (1 << 14)
+
+/*
* The DVA size encodings for LSIZE and PSIZE support blocks up to 32MB.
* The ASIZE encoding should be at least 64 times larger (6 more bits)
* to support up to 4-way RAID-Z mirror mode with worst-case gang block
@@ -258,7 +262,6 @@ typedef struct blkptr {
((zc1).zc_word[2] - (zc2).zc_word[2]) | \
((zc1).zc_word[3] - (zc2).zc_word[3])))
-
#define DVA_IS_VALID(dva) (DVA_GET_ASIZE(dva) != 0)
#define ZIO_SET_CHECKSUM(zcp, w0, w1, w2, w3) \
@@ -291,6 +294,8 @@ typedef struct blkptr {
ZIO_SET_CHECKSUM(&(bp)->blk_cksum, 0, 0, 0, 0); \
}
+#define BLK_FILL_ALREADY_FREED (-1ULL)
+
/*
* Note: the byteorder is either 0 or -1, both of which are palindromes.
* This simplifies the endianness handling a bit.
@@ -318,23 +323,30 @@ typedef struct blkptr {
extern int spa_open(const char *pool, spa_t **, void *tag);
extern int spa_get_stats(const char *pool, nvlist_t **config,
char *altroot, size_t buflen);
-extern int spa_create(const char *pool, nvlist_t *config, const char *altroot);
-extern int spa_import(const char *pool, nvlist_t *config, const char *altroot);
+extern int spa_create(const char *pool, nvlist_t *config, nvlist_t *props,
+ const char *history_str, nvlist_t *zplprops);
+extern int spa_check_rootconf(char *devpath, char *devid,
+ nvlist_t **bestconf, uint64_t *besttxg);
+extern boolean_t spa_rootdev_validate(nvlist_t *nv);
+extern int spa_import_rootpool(char *devpath, char *devid);
+extern int spa_import(const char *pool, nvlist_t *config, nvlist_t *props);
+extern int spa_import_faulted(const char *, nvlist_t *, nvlist_t *);
extern nvlist_t *spa_tryimport(nvlist_t *tryconfig);
extern int spa_destroy(char *pool);
-extern int spa_export(char *pool, nvlist_t **oldconfig);
+extern int spa_export(char *pool, nvlist_t **oldconfig, boolean_t force);
extern int spa_reset(char *pool);
extern void spa_async_request(spa_t *spa, int flag);
+extern void spa_async_unrequest(spa_t *spa, int flag);
extern void spa_async_suspend(spa_t *spa);
extern void spa_async_resume(spa_t *spa);
extern spa_t *spa_inject_addref(char *pool);
extern void spa_inject_delref(spa_t *spa);
-#define SPA_ASYNC_REOPEN 0x01
-#define SPA_ASYNC_REPLACE_DONE 0x02
-#define SPA_ASYNC_SCRUB 0x04
-#define SPA_ASYNC_RESILVER 0x08
-#define SPA_ASYNC_CONFIG_UPDATE 0x10
+#define SPA_ASYNC_CONFIG_UPDATE 0x01
+#define SPA_ASYNC_REMOVE 0x02
+#define SPA_ASYNC_PROBE 0x04
+#define SPA_ASYNC_RESILVER_DONE 0x08
+#define SPA_ASYNC_RESILVER 0x10
/* device manipulation */
extern int spa_vdev_add(spa_t *spa, nvlist_t *nvroot);
@@ -347,19 +359,27 @@ extern int spa_vdev_setpath(spa_t *spa, uint64_t guid, const char *newpath);
/* spare state (which is global across all pools) */
extern void spa_spare_add(vdev_t *vd);
extern void spa_spare_remove(vdev_t *vd);
-extern boolean_t spa_spare_exists(uint64_t guid, uint64_t *pool);
+extern boolean_t spa_spare_exists(uint64_t guid, uint64_t *pool, int *refcnt);
extern void spa_spare_activate(vdev_t *vd);
+/* L2ARC state (which is global across all pools) */
+extern void spa_l2cache_add(vdev_t *vd);
+extern void spa_l2cache_remove(vdev_t *vd);
+extern boolean_t spa_l2cache_exists(uint64_t guid, uint64_t *pool);
+extern void spa_l2cache_activate(vdev_t *vd);
+extern void spa_l2cache_drop(spa_t *spa);
+extern void spa_l2cache_space_update(vdev_t *vd, int64_t space, int64_t alloc);
+
/* scrubbing */
-extern int spa_scrub(spa_t *spa, pool_scrub_type_t type, boolean_t force);
-extern void spa_scrub_suspend(spa_t *spa);
-extern void spa_scrub_resume(spa_t *spa);
-extern void spa_scrub_restart(spa_t *spa, uint64_t txg);
+extern int spa_scrub(spa_t *spa, pool_scrub_type_t type);
/* spa syncing */
extern void spa_sync(spa_t *spa, uint64_t txg); /* only for DMU use */
extern void spa_sync_allpools(void);
+/* spa namespace global mutex */
+extern kmutex_t spa_namespace_lock;
+
/*
* SPA configuration functions in spa_config.c
*/
@@ -367,13 +387,14 @@ extern void spa_sync_allpools(void);
#define SPA_CONFIG_UPDATE_POOL 0
#define SPA_CONFIG_UPDATE_VDEVS 1
-extern void spa_config_sync(void);
+extern void spa_config_sync(spa_t *, boolean_t, boolean_t);
extern void spa_config_load(void);
extern nvlist_t *spa_all_configs(uint64_t *);
extern void spa_config_set(spa_t *spa, nvlist_t *config);
extern nvlist_t *spa_config_generate(spa_t *spa, vdev_t *vd, uint64_t txg,
int getstats);
extern void spa_config_update(spa_t *spa, int what);
+extern void spa_config_update_common(spa_t *spa, int what, boolean_t isroot);
/*
* Miscellaneous SPA routines in spa_misc.c
@@ -390,18 +411,34 @@ extern void spa_open_ref(spa_t *spa, void *tag);
extern void spa_close(spa_t *spa, void *tag);
extern boolean_t spa_refcount_zero(spa_t *spa);
-/* Pool configuration lock */
-extern void spa_config_enter(spa_t *spa, krw_t rw, void *tag);
-extern void spa_config_exit(spa_t *spa, void *tag);
-extern boolean_t spa_config_held(spa_t *spa, krw_t rw);
+#define SCL_CONFIG 0x01
+#define SCL_STATE 0x02
+#define SCL_L2ARC 0x04 /* hack until L2ARC 2.0 */
+#define SCL_ALLOC 0x08
+#define SCL_ZIO 0x10
+#define SCL_FREE 0x20
+#define SCL_VDEV 0x40
+#define SCL_LOCKS 7
+#define SCL_ALL ((1 << SCL_LOCKS) - 1)
+#define SCL_STATE_ALL (SCL_STATE | SCL_L2ARC | SCL_ZIO)
+
+/* Pool configuration locks */
+extern int spa_config_tryenter(spa_t *spa, int locks, void *tag, krw_t rw);
+extern void spa_config_enter(spa_t *spa, int locks, void *tag, krw_t rw);
+extern void spa_config_exit(spa_t *spa, int locks, void *tag);
+extern int spa_config_held(spa_t *spa, int locks, krw_t rw);
/* Pool vdev add/remove lock */
extern uint64_t spa_vdev_enter(spa_t *spa);
extern int spa_vdev_exit(spa_t *spa, vdev_t *vd, uint64_t txg, int error);
+/* Pool vdev state change lock */
+extern void spa_vdev_state_enter(spa_t *spa);
+extern int spa_vdev_state_exit(spa_t *spa, vdev_t *vd, int error);
+
/* Accessor functions */
extern krwlock_t *spa_traverse_rwlock(spa_t *spa);
-extern int spa_traverse_wanted(spa_t *spa);
+extern boolean_t spa_traverse_wanted(spa_t *spa);
extern struct dsl_pool *spa_get_dsl(spa_t *spa);
extern blkptr_t *spa_get_rootblkptr(spa_t *spa);
extern void spa_set_rootblkptr(spa_t *spa, const blkptr_t *bp);
@@ -414,8 +451,6 @@ extern uint64_t spa_first_txg(spa_t *spa);
extern uint64_t spa_version(spa_t *spa);
extern int spa_state(spa_t *spa);
extern uint64_t spa_freeze_txg(spa_t *spa);
-struct metaslab_class;
-extern struct metaslab_class *spa_metaslab_class_select(spa_t *spa);
extern uint64_t spa_get_alloc(spa_t *spa);
extern uint64_t spa_get_space(spa_t *spa);
extern uint64_t spa_get_dspace(spa_t *spa);
@@ -423,6 +458,8 @@ extern uint64_t spa_get_asize(spa_t *spa, uint64_t lsize);
extern uint64_t spa_version(spa_t *spa);
extern int spa_max_replication(spa_t *spa);
extern int spa_busy(void);
+extern uint8_t spa_get_failmode(spa_t *spa);
+extern boolean_t spa_suspended(spa_t *spa);
/* Miscellaneous support routines */
extern int spa_rename(const char *oldname, const char *newname);
@@ -432,18 +469,38 @@ extern void spa_strfree(char *);
extern uint64_t spa_get_random(uint64_t range);
extern void sprintf_blkptr(char *buf, int len, const blkptr_t *bp);
extern void spa_freeze(spa_t *spa);
-extern void spa_upgrade(spa_t *spa);
+extern void spa_upgrade(spa_t *spa, uint64_t version);
extern void spa_evict_all(void);
-extern vdev_t *spa_lookup_by_guid(spa_t *spa, uint64_t guid);
+extern vdev_t *spa_lookup_by_guid(spa_t *spa, uint64_t guid,
+ boolean_t l2cache);
extern boolean_t spa_has_spare(spa_t *, uint64_t guid);
extern uint64_t bp_get_dasize(spa_t *spa, const blkptr_t *bp);
+extern boolean_t spa_has_slogs(spa_t *spa);
+extern boolean_t spa_is_root(spa_t *spa);
/* history logging */
+typedef enum history_log_type {
+ LOG_CMD_POOL_CREATE,
+ LOG_CMD_NORMAL,
+ LOG_INTERNAL
+} history_log_type_t;
+
+typedef struct history_arg {
+ const char *ha_history_str;
+ history_log_type_t ha_log_type;
+ history_internal_events_t ha_event;
+ char ha_zone[MAXPATHLEN];
+} history_arg_t;
+
+extern char *spa_his_ievent_table[];
+
extern void spa_history_create_obj(spa_t *spa, dmu_tx_t *tx);
extern int spa_history_get(spa_t *spa, uint64_t *offset, uint64_t *len_read,
char *his_buf);
extern int spa_history_log(spa_t *spa, const char *his_buf,
- uint64_t pool_create);
+ history_log_type_t what);
+void spa_history_internal_log(history_internal_events_t event, spa_t *spa,
+ dmu_tx_t *tx, cred_t *cr, const char *fmt, ...);
/* error handling */
struct zbookmark;
@@ -451,7 +508,8 @@ struct zio;
extern void spa_log_error(spa_t *spa, struct zio *zio);
extern void zfs_ereport_post(const char *class, spa_t *spa, vdev_t *vd,
struct zio *zio, uint64_t stateoroffset, uint64_t length);
-extern void zfs_post_ok(spa_t *spa, vdev_t *vd);
+extern void zfs_post_remove(spa_t *spa, vdev_t *vd);
+extern void zfs_post_autoreplace(spa_t *spa, vdev_t *vd);
extern uint64_t spa_get_errlog_size(spa_t *spa);
extern int spa_get_errlog(spa_t *spa, void *uaddr, size_t *count);
extern void spa_errlog_rotate(spa_t *spa);
@@ -459,15 +517,22 @@ extern void spa_errlog_drain(spa_t *spa);
extern void spa_errlog_sync(spa_t *spa, uint64_t txg);
extern void spa_get_errlists(spa_t *spa, avl_tree_t *last, avl_tree_t *scrub);
+/* vdev cache */
+extern void vdev_cache_stat_init(void);
+extern void vdev_cache_stat_fini(void);
+
/* Initialization and termination */
extern void spa_init(int flags);
extern void spa_fini(void);
+extern void spa_boot_init();
/* properties */
-extern int spa_set_props(spa_t *spa, nvlist_t *nvp);
-extern int spa_get_props(spa_t *spa, nvlist_t **nvp);
-extern void spa_clear_bootfs(spa_t *spa, uint64_t obj, dmu_tx_t *tx);
-extern boolean_t spa_has_bootfs(spa_t *spa);
+extern int spa_prop_set(spa_t *spa, nvlist_t *nvp);
+extern int spa_prop_get(spa_t *spa, nvlist_t **nvp);
+extern void spa_prop_clear_bootfs(spa_t *spa, uint64_t obj, dmu_tx_t *tx);
+
+/* asynchronous event notification */
+extern void spa_event_notify(spa_t *spa, vdev_t *vdev, const char *name);
#ifdef ZFS_DEBUG
#define dprintf_bp(bp, fmt, ...) do { \
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa_boot.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa_boot.h
new file mode 100644
index 0000000..b56073b
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa_boot.h
@@ -0,0 +1,45 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL 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.
+ */
+
+#ifndef _SYS_SPA_BOOT_H
+#define _SYS_SPA_BOOT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/nvpair.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern char *spa_get_bootprop(char *prop);
+extern void spa_free_bootprop(char *prop);
+extern int spa_get_rootconf(char *devpath, char *devid, nvlist_t **bestconf_p);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_SPA_BOOT_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa_impl.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa_impl.h
index 8c57123..ab41ba6 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa_impl.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa_impl.h
@@ -19,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_SPA_IMPL_H
#define _SYS_SPA_IMPL_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/spa.h>
#include <sys/vdev.h>
#include <sys/metaslab.h>
@@ -43,13 +41,6 @@
extern "C" {
#endif
-typedef struct spa_config_lock {
- kmutex_t scl_lock;
- refcount_t scl_count;
- kthread_t *scl_writer;
- kcondvar_t scl_cv;
-} spa_config_lock_t;
-
typedef struct spa_error_entry {
zbookmark_t se_bookmark;
char *se_name;
@@ -64,31 +55,61 @@ typedef struct spa_history_phys {
uint64_t sh_records_lost; /* num of records overwritten */
} spa_history_phys_t;
-typedef struct spa_props {
- nvlist_t *spa_props_nvp;
- list_node_t spa_list_node;
-} spa_props_t;
+struct spa_aux_vdev {
+ uint64_t sav_object; /* MOS object for device list */
+ nvlist_t *sav_config; /* cached device config */
+ vdev_t **sav_vdevs; /* devices */
+ int sav_count; /* number devices */
+ boolean_t sav_sync; /* sync the device list */
+ nvlist_t **sav_pending; /* pending device additions */
+ uint_t sav_npending; /* # pending devices */
+};
+
+typedef struct spa_config_lock {
+ kmutex_t scl_lock;
+ kthread_t *scl_writer;
+ int scl_write_wanted;
+ kcondvar_t scl_cv;
+ refcount_t scl_count;
+} spa_config_lock_t;
+
+typedef struct spa_config_dirent {
+ list_node_t scd_link;
+ char *scd_path;
+} spa_config_dirent_t;
+
+typedef enum spa_log_state {
+ SPA_LOG_UNKNOWN = 0, /* unknown log state */
+ SPA_LOG_MISSING, /* missing log(s) */
+ SPA_LOG_CLEAR, /* clear the log(s) */
+ SPA_LOG_GOOD, /* log(s) are good */
+} spa_log_state_t;
+
+enum zio_taskq_type {
+ ZIO_TASKQ_ISSUE = 0,
+ ZIO_TASKQ_INTERRUPT,
+ ZIO_TASKQ_TYPES
+};
struct spa {
/*
* Fields protected by spa_namespace_lock.
*/
- char *spa_name; /* pool name */
+ char spa_name[MAXNAMELEN]; /* pool name */
avl_node_t spa_avl; /* node in spa_namespace_avl */
nvlist_t *spa_config; /* last synced config */
nvlist_t *spa_config_syncing; /* currently syncing config */
uint64_t spa_config_txg; /* txg of last config change */
- kmutex_t spa_config_cache_lock; /* for spa_config RW_READER */
int spa_sync_pass; /* iterate-to-convergence */
int spa_state; /* pool state */
int spa_inject_ref; /* injection references */
uint8_t spa_traverse_wanted; /* traverse lock wanted */
uint8_t spa_sync_on; /* sync threads are running */
spa_load_state_t spa_load_state; /* current load operation */
- taskq_t *spa_zio_issue_taskq[ZIO_TYPES];
- taskq_t *spa_zio_intr_taskq[ZIO_TYPES];
+ taskq_t *spa_zio_taskq[ZIO_TYPES][ZIO_TASKQ_TYPES];
dsl_pool_t *spa_dsl_pool;
metaslab_class_t *spa_normal_class; /* normal data class */
+ metaslab_class_t *spa_log_class; /* intent log data class */
uint64_t spa_first_txg; /* first txg after spa_open() */
uint64_t spa_final_txg; /* txg of export/destroy */
uint64_t spa_freeze_txg; /* freeze pool at this txg */
@@ -96,12 +117,10 @@ struct spa {
txg_list_t spa_vdev_txg_list; /* per-txg dirty vdev list */
vdev_t *spa_root_vdev; /* top-level vdev container */
uint64_t spa_load_guid; /* initial guid for spa_load */
- list_t spa_dirty_list; /* vdevs with dirty labels */
- uint64_t spa_spares_object; /* MOS object for spare list */
- nvlist_t *spa_sparelist; /* cached spare config */
- vdev_t **spa_spares; /* available hot spares */
- int spa_nspares; /* number of hot spares */
- boolean_t spa_sync_spares; /* sync the spares list */
+ list_t spa_config_dirty_list; /* vdevs with dirty config */
+ list_t spa_state_dirty_list; /* vdevs with dirty state */
+ spa_aux_vdev_t spa_spares; /* hot spares */
+ spa_aux_vdev_t spa_l2cache; /* L2ARC cache devices */
uint64_t spa_config_object; /* MOS object for pool config */
uint64_t spa_syncing_txg; /* txg currently syncing */
uint64_t spa_sync_bplist_obj; /* object for deferred frees */
@@ -110,28 +129,24 @@ struct spa {
uberblock_t spa_ubsync; /* last synced uberblock */
uberblock_t spa_uberblock; /* current uberblock */
kmutex_t spa_scrub_lock; /* resilver/scrub lock */
- kthread_t *spa_scrub_thread; /* scrub/resilver thread */
- traverse_handle_t *spa_scrub_th; /* scrub traverse handle */
- uint64_t spa_scrub_restart_txg; /* need to restart */
- uint64_t spa_scrub_mintxg; /* min txg we'll scrub */
- uint64_t spa_scrub_maxtxg; /* max txg we'll scrub */
uint64_t spa_scrub_inflight; /* in-flight scrub I/Os */
uint64_t spa_scrub_maxinflight; /* max in-flight scrub I/Os */
uint64_t spa_scrub_errors; /* scrub I/O error count */
- int spa_scrub_suspended; /* tell scrubber to suspend */
- kcondvar_t spa_scrub_cv; /* scrub thread state change */
kcondvar_t spa_scrub_io_cv; /* scrub I/O completion */
- uint8_t spa_scrub_stop; /* tell scrubber to stop */
uint8_t spa_scrub_active; /* active or suspended? */
uint8_t spa_scrub_type; /* type of scrub we're doing */
uint8_t spa_scrub_finished; /* indicator to rotate logs */
+ uint8_t spa_scrub_started; /* started since last boot */
+ uint8_t spa_scrub_reopen; /* scrub doing vdev_reopen */
kmutex_t spa_async_lock; /* protect async state */
kthread_t *spa_async_thread; /* thread doing async task */
int spa_async_suspended; /* async tasks suspended */
kcondvar_t spa_async_cv; /* wait for thread_exit() */
uint16_t spa_async_tasks; /* async task mask */
+ kmutex_t spa_async_root_lock; /* protects async root count */
+ uint64_t spa_async_root_count; /* number of async root zios */
+ kcondvar_t spa_async_root_cv; /* notify when count == 0 */
char *spa_root; /* alternate root directory */
- kmutex_t spa_uberblock_lock; /* vdev_uberblock_load_done() */
uint64_t spa_ena; /* spa-wide ereport ENA */
boolean_t spa_last_open_failed; /* true if last open faled */
kmutex_t spa_errlog_lock; /* error log lock */
@@ -144,22 +159,37 @@ struct spa {
uint64_t spa_history; /* history object */
kmutex_t spa_history_lock; /* history lock */
vdev_t *spa_pending_vdev; /* pending vdev additions */
- nvlist_t **spa_pending_spares; /* pending spare additions */
- uint_t spa_pending_nspares; /* # pending spares */
kmutex_t spa_props_lock; /* property lock */
uint64_t spa_pool_props_object; /* object for properties */
uint64_t spa_bootfs; /* default boot filesystem */
+ uint64_t spa_failmode; /* failure mode for the pool */
+ uint64_t spa_delegation; /* delegation on/off */
+ list_t spa_config_list; /* previous cache file(s) */
+ zio_t *spa_suspend_zio_root; /* root of all suspended I/O */
+ kmutex_t spa_suspend_lock; /* protects suspend_zio_root */
+ kcondvar_t spa_suspend_cv; /* notification of resume */
+ uint8_t spa_suspended; /* pool is suspended */
+ boolean_t spa_import_faulted; /* allow faulted vdevs */
+ boolean_t spa_is_root; /* pool is root */
+ int spa_minref; /* num refs when first opened */
+ spa_log_state_t spa_log_state; /* log state */
/*
- * spa_refcnt must be the last element because it changes size based on
- * compilation options. In order for the MDB module to function
- * correctly, the other fields must remain in the same location.
+ * spa_refcnt & spa_config_lock must be the last elements
+ * because refcount_t changes size based on compilation options.
+ * In order for the MDB module to function correctly, the other
+ * fields must remain in the same location.
*/
- spa_config_lock_t spa_config_lock; /* configuration changes */
+ spa_config_lock_t spa_config_lock[SCL_LOCKS]; /* config changes */
refcount_t spa_refcount; /* number of opens */
};
-extern const char *spa_config_dir;
-extern kmutex_t spa_namespace_lock;
+extern const char *spa_config_path;
+
+#define BOOTFS_COMPRESS_VALID(compress) \
+ ((compress) == ZIO_COMPRESS_LZJB || \
+ ((compress) == ZIO_COMPRESS_ON && \
+ ZIO_COMPRESS_ON_VALUE == ZIO_COMPRESS_LZJB) || \
+ (compress) == ZIO_COMPRESS_OFF)
#ifdef __cplusplus
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/txg.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/txg.h
index dae129c..23bdff2 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/txg.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/txg.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -76,6 +75,14 @@ extern void txg_suspend(struct dsl_pool *dp);
extern void txg_resume(struct dsl_pool *dp);
/*
+ * Delay the caller by the specified number of ticks or until
+ * the txg closes (whichever comes first). This is intended
+ * to be used to throttle writers when the system nears its
+ * capacity.
+ */
+extern void txg_delay(struct dsl_pool *dp, uint64_t txg, int ticks);
+
+/*
* Wait until the given transaction group has finished syncing.
* Try to make this happen as soon as possible (eg. kick off any
* necessary syncs immediately). If txg==0, wait for the currently open
@@ -95,7 +102,10 @@ extern void txg_wait_open(struct dsl_pool *dp, uint64_t txg);
* Returns TRUE if we are "backed up" waiting for the syncing
* transaction to complete; otherwise returns FALSE.
*/
-extern int txg_stalled(struct dsl_pool *dp);
+extern boolean_t txg_stalled(struct dsl_pool *dp);
+
+/* returns TRUE if someone is waiting for the next txg to sync */
+extern boolean_t txg_sync_waiting(struct dsl_pool *dp);
/*
* Per-txg object lists.
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/txg_impl.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/txg_impl.h
index 45a138a..a58be84 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/txg_impl.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/txg_impl.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -59,7 +58,7 @@ typedef struct tx_state {
kcondvar_t tx_sync_done_cv;
kcondvar_t tx_quiesce_more_cv;
kcondvar_t tx_quiesce_done_cv;
- kcondvar_t tx_timeout_exit_cv;
+ kcondvar_t tx_timeout_cv;
kcondvar_t tx_exit_cv; /* wait for all threads to exit */
uint8_t tx_threads; /* number of threads */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/uberblock_impl.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/uberblock_impl.h
index ab0f2dc..55a0dd5 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/uberblock_impl.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/uberblock_impl.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -49,7 +49,7 @@ extern "C" {
struct uberblock {
uint64_t ub_magic; /* UBERBLOCK_MAGIC */
- uint64_t ub_version; /* ZFS_VERSION */
+ uint64_t ub_version; /* SPA_VERSION */
uint64_t ub_txg; /* txg of last sync */
uint64_t ub_guid_sum; /* sum of all vdev guids */
uint64_t ub_timestamp; /* UTC time of last sync */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/unique.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/unique.h
index c8c177e..2ef3093 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/unique.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/unique.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -39,8 +38,12 @@ extern "C" {
#define UNIQUE_BITS 56
void unique_init(void);
+void unique_fini(void);
-/* Return a new unique value. */
+/*
+ * Return a new unique value (which will not be uniquified against until
+ * it is unique_insert()-ed.
+ */
uint64_t unique_create(void);
/* Return a unique value, which equals the one passed in if possible. */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev.h
index 3120811..0133895 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev.h
@@ -19,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_VDEV_H
#define _SYS_VDEV_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/spa.h>
#include <sys/zio.h>
#include <sys/dmu.h>
@@ -40,35 +38,31 @@ extern "C" {
extern boolean_t zfs_nocacheflush;
-/*
- * Fault injection modes.
- */
-#define VDEV_FAULT_NONE 0
-#define VDEV_FAULT_RANDOM 1
-#define VDEV_FAULT_COUNT 2
-
extern int vdev_open(vdev_t *);
extern int vdev_validate(vdev_t *);
extern void vdev_close(vdev_t *);
extern int vdev_create(vdev_t *, uint64_t txg, boolean_t isreplace);
extern void vdev_init(vdev_t *, uint64_t txg);
extern void vdev_reopen(vdev_t *);
-extern int vdev_validate_spare(vdev_t *);
+extern int vdev_validate_aux(vdev_t *vd);
+extern zio_t *vdev_probe(vdev_t *vd, zio_t *pio);
+extern boolean_t vdev_is_bootable(vdev_t *vd);
extern vdev_t *vdev_lookup_top(spa_t *spa, uint64_t vdev);
extern vdev_t *vdev_lookup_by_guid(vdev_t *vd, uint64_t guid);
extern void vdev_dtl_dirty(space_map_t *sm, uint64_t txg, uint64_t size);
extern int vdev_dtl_contains(space_map_t *sm, uint64_t txg, uint64_t size);
extern void vdev_dtl_reassess(vdev_t *vd, uint64_t txg, uint64_t scrub_txg,
int scrub_done);
-
-extern const char *vdev_description(vdev_t *vd);
+extern boolean_t vdev_resilver_needed(vdev_t *vd,
+ uint64_t *minp, uint64_t *maxp);
extern int vdev_metaslab_init(vdev_t *vd, uint64_t txg);
extern void vdev_metaslab_fini(vdev_t *vd);
extern void vdev_get_stats(vdev_t *vd, vdev_stat_t *vs);
-extern void vdev_stat_update(zio_t *zio);
+extern void vdev_clear_stats(vdev_t *vd);
+extern void vdev_stat_update(zio_t *zio, uint64_t psize);
extern void vdev_scrub_stat_update(vdev_t *vd, pool_scrub_type_t type,
boolean_t complete);
extern int vdev_getspec(spa_t *spa, uint64_t vdev, char **vdev_spec);
@@ -77,24 +71,27 @@ extern void vdev_set_state(vdev_t *vd, boolean_t isopen, vdev_state_t state,
vdev_aux_t aux);
extern void vdev_space_update(vdev_t *vd, int64_t space_delta,
- int64_t alloc_delta);
+ int64_t alloc_delta, boolean_t update_root);
extern uint64_t vdev_psize_to_asize(vdev_t *vd, uint64_t psize);
-extern void vdev_io_start(zio_t *zio);
-extern void vdev_io_done(zio_t *zio);
-
-extern int vdev_online(spa_t *spa, uint64_t guid);
-extern int vdev_offline(spa_t *spa, uint64_t guid, int istmp);
+extern int vdev_fault(spa_t *spa, uint64_t guid);
+extern int vdev_degrade(spa_t *spa, uint64_t guid);
+extern int vdev_online(spa_t *spa, uint64_t guid, uint64_t flags,
+ vdev_state_t *);
+extern int vdev_offline(spa_t *spa, uint64_t guid, uint64_t flags);
extern void vdev_clear(spa_t *spa, vdev_t *vd);
-extern int vdev_error_inject(vdev_t *vd, zio_t *zio);
-extern int vdev_is_dead(vdev_t *vd);
+extern boolean_t vdev_is_dead(vdev_t *vd);
+extern boolean_t vdev_readable(vdev_t *vd);
+extern boolean_t vdev_writeable(vdev_t *vd);
+extern boolean_t vdev_accessible(vdev_t *vd, zio_t *zio);
extern void vdev_cache_init(vdev_t *vd);
extern void vdev_cache_fini(vdev_t *vd);
extern int vdev_cache_read(zio_t *zio);
extern void vdev_cache_write(zio_t *zio);
+extern void vdev_cache_purge(vdev_t *vd);
extern void vdev_queue_init(vdev_t *vd);
extern void vdev_queue_fini(vdev_t *vd);
@@ -103,16 +100,20 @@ extern void vdev_queue_io_done(zio_t *zio);
extern void vdev_config_dirty(vdev_t *vd);
extern void vdev_config_clean(vdev_t *vd);
-extern int vdev_config_sync(vdev_t *vd, uint64_t txg);
+extern int vdev_config_sync(vdev_t **svd, int svdcount, uint64_t txg);
+
+extern void vdev_state_dirty(vdev_t *vd);
+extern void vdev_state_clean(vdev_t *vd);
extern nvlist_t *vdev_config_generate(spa_t *spa, vdev_t *vd,
- boolean_t getstats, boolean_t isspare);
+ boolean_t getstats, boolean_t isspare, boolean_t isl2cache);
/*
* Label routines
*/
struct uberblock;
extern uint64_t vdev_label_offset(uint64_t psize, int l, uint64_t offset);
+extern int vdev_label_number(uint64_t psise, uint64_t offset);
extern nvlist_t *vdev_label_read_config(vdev_t *vd);
extern void vdev_uberblock_load(zio_t *zio, vdev_t *vd, struct uberblock *ub);
@@ -120,7 +121,8 @@ typedef enum {
VDEV_LABEL_CREATE, /* create/add a new device */
VDEV_LABEL_REPLACE, /* replace an existing device */
VDEV_LABEL_SPARE, /* add a new hot spare */
- VDEV_LABEL_REMOVE /* remove an existing device */
+ VDEV_LABEL_REMOVE, /* remove an existing device */
+ VDEV_LABEL_L2CACHE /* add an L2ARC cache device */
} vdev_labeltype_t;
extern int vdev_label_init(vdev_t *vd, uint64_t txg, vdev_labeltype_t reason);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev_disk.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev_disk.h
index 95536a7..b748571 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev_disk.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev_disk.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -31,6 +30,8 @@
#include <sys/vdev.h>
#ifdef _KERNEL
+#include <sys/buf.h>
+#include <sys/ddi.h>
#include <sys/sunldi.h>
#include <sys/sunddi.h>
#endif
@@ -45,6 +46,9 @@ typedef struct vdev_disk {
ldi_handle_t vd_lh;
} vdev_disk_t;
+#ifdef _KERNEL
+extern int vdev_disk_physio(ldi_handle_t, caddr_t, size_t, uint64_t, int);
+#endif
#ifdef __cplusplus
}
#endif
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev_impl.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev_impl.h
index aba7567..7e24ede 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev_impl.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev_impl.h
@@ -19,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_VDEV_IMPL_H
#define _SYS_VDEV_IMPL_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/avl.h>
#include <sys/dmu.h>
#include <sys/metaslab.h>
@@ -61,7 +59,7 @@ typedef struct vdev_cache_entry vdev_cache_entry_t;
typedef int vdev_open_func_t(vdev_t *vd, uint64_t *size, uint64_t *ashift);
typedef void vdev_close_func_t(vdev_t *vd);
typedef uint64_t vdev_asize_func_t(vdev_t *vd, uint64_t psize);
-typedef void vdev_io_start_func_t(zio_t *zio);
+typedef int vdev_io_start_func_t(zio_t *zio);
typedef void vdev_io_done_func_t(zio_t *zio);
typedef void vdev_state_change_func_t(vdev_t *vd, int, int);
@@ -140,9 +138,12 @@ struct vdev {
txg_list_t vdev_ms_list; /* per-txg dirty metaslab lists */
txg_list_t vdev_dtl_list; /* per-txg dirty DTL lists */
txg_node_t vdev_txg_node; /* per-txg dirty vdev linkage */
- uint8_t vdev_reopen_wanted; /* async reopen wanted? */
- list_node_t vdev_dirty_node; /* config dirty list */
+ boolean_t vdev_remove_wanted; /* async remove wanted? */
+ boolean_t vdev_probe_wanted; /* async probe wanted? */
+ list_node_t vdev_config_dirty_node; /* config dirty list */
+ list_node_t vdev_state_dirty_node; /* state dirty list */
uint64_t vdev_deflate_ratio; /* deflation ratio (x512) */
+ uint64_t vdev_islog; /* is an intent log device */
/*
* Leaf vdev state.
@@ -151,22 +152,30 @@ struct vdev {
space_map_obj_t vdev_dtl; /* dirty time log on-disk state */
txg_node_t vdev_dtl_node; /* per-txg dirty DTL linkage */
uint64_t vdev_wholedisk; /* true if this is a whole disk */
- uint64_t vdev_offline; /* device taken offline? */
+ uint64_t vdev_offline; /* persistent offline state */
+ uint64_t vdev_faulted; /* persistent faulted state */
+ uint64_t vdev_degraded; /* persistent degraded state */
+ uint64_t vdev_removed; /* persistent removed state */
uint64_t vdev_nparity; /* number of parity devices for raidz */
char *vdev_path; /* vdev path (if any) */
char *vdev_devid; /* vdev devid (if any) */
- uint64_t vdev_fault_arg; /* fault injection paramater */
- int vdev_fault_mask; /* zio types to fault */
- uint8_t vdev_fault_mode; /* fault injection mode */
- uint8_t vdev_cache_active; /* vdev_cache and vdev_queue */
+ char *vdev_physpath; /* vdev device path (if any) */
+ uint64_t vdev_not_present; /* not present during import */
+ uint64_t vdev_unspare; /* unspare when resilvering done */
+ hrtime_t vdev_last_try; /* last reopen time */
+ boolean_t vdev_nowritecache; /* true if flushwritecache failed */
+ boolean_t vdev_checkremove; /* temporary online test */
+ boolean_t vdev_forcefault; /* force online fault */
uint8_t vdev_tmpoffline; /* device taken offline temporarily? */
uint8_t vdev_detached; /* device detached? */
- uint64_t vdev_isspare; /* was a hot spare */
+ uint8_t vdev_cant_read; /* vdev is failing all reads */
+ uint8_t vdev_cant_write; /* vdev is failing all writes */
+ uint64_t vdev_isspare; /* was a hot spare */
+ uint64_t vdev_isl2cache; /* was a l2cache device */
vdev_queue_t vdev_queue; /* I/O deadline schedule queue */
vdev_cache_t vdev_cache; /* physical block cache */
- uint64_t vdev_not_present; /* not present during import */
- hrtime_t vdev_last_try; /* last reopen time */
- boolean_t vdev_nowritecache; /* true if flushwritecache failed */
+ spa_aux_vdev_t *vdev_aux; /* for l2cache vdevs */
+ zio_t *vdev_probe_zio; /* root of current probe */
/*
* For DTrace to work in userland (libzpool) context, these fields must
@@ -177,6 +186,7 @@ struct vdev {
*/
kmutex_t vdev_dtl_lock; /* vdev_dtl_{map,resilver} */
kmutex_t vdev_stat_lock; /* vdev_stat */
+ kmutex_t vdev_probe_lock; /* protects vdev_probe_zio */
};
#define VDEV_SKIP_SIZE (8 << 10)
@@ -239,6 +249,7 @@ typedef struct vdev_label {
#define VDEV_ALLOC_LOAD 0
#define VDEV_ALLOC_ADD 1
#define VDEV_ALLOC_SPARE 2
+#define VDEV_ALLOC_L2CACHE 3
/*
* Allocate or free a vdev
@@ -275,8 +286,8 @@ extern vdev_ops_t vdev_raidz_ops;
extern vdev_ops_t vdev_geom_ops;
#else
extern vdev_ops_t vdev_disk_ops;
-extern vdev_ops_t vdev_file_ops;
#endif
+extern vdev_ops_t vdev_file_ops;
extern vdev_ops_t vdev_missing_ops;
extern vdev_ops_t vdev_spare_ops;
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zap.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zap.h
index f89d938..f88cc06 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zap.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zap.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -31,7 +31,7 @@
/*
* ZAP - ZFS Attribute Processor
*
- * The ZAP is a module which sits on top of the DMU (Data Managemnt
+ * The ZAP is a module which sits on top of the DMU (Data Management
* Unit) and implements a higher-level storage primitive using DMU
* objects. Its primary consumer is the ZPL (ZFS Posix Layer).
*
@@ -91,10 +91,38 @@ extern "C" {
#define ZAP_MAXVALUELEN 1024
/*
+ * The matchtype specifies which entry will be accessed.
+ * MT_EXACT: only find an exact match (non-normalized)
+ * MT_FIRST: find the "first" normalized (case and Unicode
+ * form) match; the designated "first" match will not change as long
+ * as the set of entries with this normalization doesn't change
+ * MT_BEST: if there is an exact match, find that, otherwise find the
+ * first normalized match
+ */
+typedef enum matchtype
+{
+ MT_EXACT,
+ MT_BEST,
+ MT_FIRST
+} matchtype_t;
+
+/*
* Create a new zapobj with no attributes and return its object number.
+ * MT_EXACT will cause the zap object to only support MT_EXACT lookups,
+ * otherwise any matchtype can be used for lookups.
+ *
+ * normflags specifies what normalization will be done. values are:
+ * 0: no normalization (legacy on-disk format, supports MT_EXACT matching
+ * only)
+ * U8_TEXTPREP_TOLOWER: case normalization will be performed.
+ * MT_FIRST/MT_BEST matching will find entries that match without
+ * regard to case (eg. looking for "foo" can find an entry "Foo").
+ * Eventually, other flags will permit unicode normalization as well.
*/
uint64_t zap_create(objset_t *ds, dmu_object_type_t ot,
dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx);
+uint64_t zap_create_norm(objset_t *ds, int normflags, dmu_object_type_t ot,
+ dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx);
/*
* Create a new zapobj with no attributes from the given (unallocated)
@@ -102,6 +130,9 @@ uint64_t zap_create(objset_t *ds, dmu_object_type_t ot,
*/
int zap_create_claim(objset_t *ds, uint64_t obj, dmu_object_type_t ot,
dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx);
+int zap_create_claim_norm(objset_t *ds, uint64_t obj,
+ int normflags, dmu_object_type_t ot,
+ dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx);
/*
* The zapobj passed in must be a valid ZAP object for all of the
@@ -140,9 +171,20 @@ int zap_destroy(objset_t *ds, uint64_t zapobj, dmu_tx_t *tx);
* If the attribute is longer than the buffer, as many integers as will
* fit will be transferred to 'buf'. If the entire attribute was not
* transferred, the call will return EOVERFLOW.
+ *
+ * If rn_len is nonzero, realname will be set to the name of the found
+ * entry (which may be different from the requested name if matchtype is
+ * not MT_EXACT).
+ *
+ * If normalization_conflictp is not NULL, it will be set if there is
+ * another name with the same case/unicode normalized form.
*/
int zap_lookup(objset_t *ds, uint64_t zapobj, const char *name,
uint64_t integer_size, uint64_t num_integers, void *buf);
+int zap_lookup_norm(objset_t *ds, uint64_t zapobj, const char *name,
+ uint64_t integer_size, uint64_t num_integers, void *buf,
+ matchtype_t mt, char *realname, int rn_len,
+ boolean_t *normalization_conflictp);
/*
* Create an attribute with the given name and value.
@@ -182,6 +224,8 @@ int zap_length(objset_t *ds, uint64_t zapobj, const char *name,
* return ENOENT.
*/
int zap_remove(objset_t *ds, uint64_t zapobj, const char *name, dmu_tx_t *tx);
+int zap_remove_norm(objset_t *ds, uint64_t zapobj, const char *name,
+ matchtype_t mt, dmu_tx_t *tx);
/*
* Returns (in *count) the number of attributes in the specified zap
@@ -191,11 +235,28 @@ int zap_count(objset_t *ds, uint64_t zapobj, uint64_t *count);
/*
- * Returns (in name) the name of the entry whose value
+ * Returns (in name) the name of the entry whose (value & mask)
* (za_first_integer) is value, or ENOENT if not found. The string
- * pointed to by name must be at least 256 bytes long.
+ * pointed to by name must be at least 256 bytes long. If mask==0, the
+ * match must be exact (ie, same as mask=-1ULL).
*/
-int zap_value_search(objset_t *os, uint64_t zapobj, uint64_t value, char *name);
+int zap_value_search(objset_t *os, uint64_t zapobj,
+ uint64_t value, uint64_t mask, char *name);
+
+/*
+ * Transfer all the entries from fromobj into intoobj. Only works on
+ * int_size=8 num_integers=1 values. Fails if there are any duplicated
+ * entries.
+ */
+int zap_join(objset_t *os, uint64_t fromobj, uint64_t intoobj, dmu_tx_t *tx);
+
+/*
+ * Manipulate entries where the name + value are the "same" (the name is
+ * a stringified version of the value).
+ */
+int zap_add_int(objset_t *os, uint64_t obj, uint64_t value, dmu_tx_t *tx);
+int zap_remove_int(objset_t *os, uint64_t obj, uint64_t value, dmu_tx_t *tx);
+int zap_lookup_int(objset_t *os, uint64_t obj, uint64_t value);
struct zap;
struct zap_leaf;
@@ -211,6 +272,11 @@ typedef struct zap_cursor {
typedef struct {
int za_integer_length;
+ /*
+ * za_normalization_conflict will be set if there are additional
+ * entries with this normalized form (eg, "foo" and "Foo").
+ */
+ boolean_t za_normalization_conflict;
uint64_t za_num_integers;
uint64_t za_first_integer; /* no sign extension for <8byte ints */
char za_name[MAXNAMELEN];
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zap_impl.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zap_impl.h
index 4e43f4a..0dc02ab 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zap_impl.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zap_impl.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -59,7 +59,8 @@ typedef struct mzap_ent_phys {
typedef struct mzap_phys {
uint64_t mz_block_type; /* ZBT_MICRO */
uint64_t mz_salt;
- uint64_t mz_pad[6];
+ uint64_t mz_normflags;
+ uint64_t mz_pad[5];
mzap_ent_phys_t mz_chunk[1];
/* actually variable size depending on block size */
} mzap_phys_t;
@@ -127,6 +128,7 @@ typedef struct zap_phys {
uint64_t zap_num_leafs; /* number of leafs */
uint64_t zap_num_entries; /* number of entries */
uint64_t zap_salt; /* salt to stir into hash function */
+ uint64_t zap_normflags; /* flags for u8_textprep_str() */
/*
* This structure is followed by padding, and then the embedded
* pointer table. The embedded pointer table takes up second
@@ -142,7 +144,8 @@ typedef struct zap {
uint64_t zap_object;
struct dmu_buf *zap_dbuf;
krwlock_t zap_rwlock;
- int zap_ismicro;
+ boolean_t zap_ismicro;
+ int zap_normflags;
uint64_t zap_salt;
union {
struct {
@@ -165,34 +168,45 @@ typedef struct zap {
} zap_u;
} zap_t;
+typedef struct zap_name {
+ zap_t *zn_zap;
+ const char *zn_name_orij;
+ uint64_t zn_hash;
+ matchtype_t zn_matchtype;
+ const char *zn_name_norm;
+ char zn_normbuf[ZAP_MAXNAMELEN];
+} zap_name_t;
+
#define zap_f zap_u.zap_fat
#define zap_m zap_u.zap_micro
-uint64_t zap_hash(zap_t *zap, const char *name);
+boolean_t zap_match(zap_name_t *zn, const char *matchname);
int zap_lockdir(objset_t *os, uint64_t obj, dmu_tx_t *tx,
- krw_t lti, int fatreader, zap_t **zapp);
+ krw_t lti, boolean_t fatreader, boolean_t adding, zap_t **zapp);
void zap_unlockdir(zap_t *zap);
void zap_evict(dmu_buf_t *db, void *vmzap);
+zap_name_t *zap_name_alloc(zap_t *zap, const char *name, matchtype_t mt);
+void zap_name_free(zap_name_t *zn);
#define ZAP_HASH_IDX(hash, n) (((n) == 0) ? 0 : ((hash) >> (64 - (n))))
void fzap_byteswap(void *buf, size_t size);
int fzap_count(zap_t *zap, uint64_t *count);
-int fzap_lookup(zap_t *zap, const char *name,
- uint64_t integer_size, uint64_t num_integers, void *buf);
-int fzap_add(zap_t *zap, const char *name,
- uint64_t integer_size, uint64_t num_integers,
+int fzap_lookup(zap_name_t *zn,
+ uint64_t integer_size, uint64_t num_integers, void *buf,
+ char *realname, int rn_len, boolean_t *normalization_conflictp);
+int fzap_add(zap_name_t *zn, uint64_t integer_size, uint64_t num_integers,
const void *val, dmu_tx_t *tx);
-int fzap_update(zap_t *zap, const char *name,
+int fzap_update(zap_name_t *zn,
int integer_size, uint64_t num_integers, const void *val, dmu_tx_t *tx);
-int fzap_length(zap_t *zap, const char *name,
+int fzap_length(zap_name_t *zn,
uint64_t *integer_size, uint64_t *num_integers);
-int fzap_remove(zap_t *zap, const char *name, dmu_tx_t *tx);
+int fzap_remove(zap_name_t *zn, dmu_tx_t *tx);
int fzap_cursor_retrieve(zap_t *zap, zap_cursor_t *zc, zap_attribute_t *za);
void fzap_get_stats(zap_t *zap, zap_stats_t *zs);
void zap_put_leaf(struct zap_leaf *l);
-int fzap_add_cd(zap_t *zap, const char *name,
+int fzap_add_cd(zap_name_t *zn,
uint64_t integer_size, uint64_t num_integers,
const void *val, uint32_t cd, dmu_tx_t *tx);
void fzap_upgrade(zap_t *zap, dmu_tx_t *tx);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zap_leaf.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zap_leaf.h
index 147fb72..14144e0 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zap_leaf.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zap_leaf.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -92,6 +92,8 @@ typedef enum zap_chunk_type {
ZAP_CHUNK_TYPE_MAX = 250
} zap_chunk_type_t;
+#define ZLF_ENTRIES_CDSORTED (1<<0)
+
/*
* TAKE NOTE:
* If zap_leaf_phys_t is modified, zap_leaf_byteswap() must be modified.
@@ -109,7 +111,8 @@ typedef struct zap_leaf_phys {
/* above is accessable to zap, below is zap_leaf private */
uint16_t lh_freelist; /* chunk head of free list */
- uint8_t lh_pad2[12];
+ uint8_t lh_flags; /* ZLF_* flags */
+ uint8_t lh_pad2[11];
} l_hdr; /* 2 24-byte chunks */
/*
@@ -148,7 +151,7 @@ typedef union zap_leaf_chunk {
} zap_leaf_chunk_t;
typedef struct zap_leaf {
- krwlock_t l_rwlock; /* only used on head of chain */
+ krwlock_t l_rwlock;
uint64_t l_blkid; /* 1<<ZAP_BLOCK_SHIFT byte block off */
int l_bs; /* block size shift */
dmu_buf_t *l_dbuf;
@@ -174,7 +177,7 @@ typedef struct zap_entry_handle {
* value must equal zap_hash(name).
*/
extern int zap_leaf_lookup(zap_leaf_t *l,
- const char *name, uint64_t h, zap_entry_handle_t *zeh);
+ zap_name_t *zn, zap_entry_handle_t *zeh);
/*
* Return a handle to the entry with this hash+cd, or the entry with the
@@ -219,12 +222,19 @@ extern int zap_entry_create(zap_leaf_t *l,
zap_entry_handle_t *zeh);
/*
+ * Return true if there are additional entries with the same normalized
+ * form.
+ */
+extern boolean_t zap_entry_normalization_conflict(zap_entry_handle_t *zeh,
+ zap_name_t *zn, const char *name, zap_t *zap);
+
+/*
* Other stuff.
*/
-extern void zap_leaf_init(zap_leaf_t *l);
+extern void zap_leaf_init(zap_leaf_t *l, boolean_t sort);
extern void zap_leaf_byteswap(zap_leaf_phys_t *buf, int len);
-extern void zap_leaf_split(zap_leaf_t *l, zap_leaf_t *nl);
+extern void zap_leaf_split(zap_leaf_t *l, zap_leaf_t *nl, boolean_t sort);
extern void zap_leaf_stats(zap_t *zap, zap_leaf_t *l, zap_stats_t *zs);
#ifdef __cplusplus
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_acl.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_acl.h
index 3250b76..fe95318 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_acl.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_acl.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -33,6 +33,7 @@
#endif
#include <sys/acl.h>
#include <sys/dmu.h>
+#include <sys/zfs_fuid.h>
#ifdef __cplusplus
extern "C" {
@@ -40,33 +41,131 @@ extern "C" {
struct znode_phys;
-#define ACCESS_UNDETERMINED -1
-
#define ACE_SLOT_CNT 6
+#define ZFS_ACL_VERSION_INITIAL 0ULL
+#define ZFS_ACL_VERSION_FUID 1ULL
+#define ZFS_ACL_VERSION ZFS_ACL_VERSION_FUID
+
+/*
+ * ZFS ACLs are store in various forms.
+ * Files created with ACL version ZFS_ACL_VERSION_INITIAL
+ * will all be created with fixed length ACEs of type
+ * zfs_oldace_t.
+ *
+ * Files with ACL version ZFS_ACL_VERSION_FUID will be created
+ * with various sized ACEs. The abstraction entries will utilize
+ * zfs_ace_hdr_t, normal user/group entries will use zfs_ace_t
+ * and some specialized CIFS ACEs will use zfs_object_ace_t.
+ */
+
+/*
+ * All ACEs have a common hdr. For
+ * owner@, group@, and everyone@ this is all
+ * thats needed.
+ */
+typedef struct zfs_ace_hdr {
+ uint16_t z_type;
+ uint16_t z_flags;
+ uint32_t z_access_mask;
+} zfs_ace_hdr_t;
+
+typedef zfs_ace_hdr_t zfs_ace_abstract_t;
+
+/*
+ * Standard ACE
+ */
+typedef struct zfs_ace {
+ zfs_ace_hdr_t z_hdr;
+ uint64_t z_fuid;
+} zfs_ace_t;
+
+/*
+ * The following type only applies to ACE_ACCESS_ALLOWED|DENIED_OBJECT_ACE_TYPE
+ * and will only be set/retrieved in a CIFS context.
+ */
-typedef struct zfs_znode_acl {
+typedef struct zfs_object_ace {
+ zfs_ace_t z_ace;
+ uint8_t z_object_type[16]; /* object type */
+ uint8_t z_inherit_type[16]; /* inherited object type */
+} zfs_object_ace_t;
+
+typedef struct zfs_oldace {
+ uint32_t z_fuid; /* "who" */
+ uint32_t z_access_mask; /* access mask */
+ uint16_t z_flags; /* flags, i.e inheritance */
+ uint16_t z_type; /* type of entry allow/deny */
+} zfs_oldace_t;
+
+typedef struct zfs_acl_phys_v0 {
+ uint64_t z_acl_extern_obj; /* ext acl pieces */
+ uint32_t z_acl_count; /* Number of ACEs */
+ uint16_t z_acl_version; /* acl version */
+ uint16_t z_acl_pad; /* pad */
+ zfs_oldace_t z_ace_data[ACE_SLOT_CNT]; /* 6 standard ACEs */
+} zfs_acl_phys_v0_t;
+
+#define ZFS_ACE_SPACE (sizeof (zfs_oldace_t) * ACE_SLOT_CNT)
+
+typedef struct zfs_acl_phys {
uint64_t z_acl_extern_obj; /* ext acl pieces */
- uint32_t z_acl_count; /* Number of ACEs */
+ uint32_t z_acl_size; /* Number of bytes in ACL */
uint16_t z_acl_version; /* acl version */
- uint16_t z_acl_pad; /* pad */
- ace_t z_ace_data[ACE_SLOT_CNT]; /* 6 standard ACEs */
-} zfs_znode_acl_t;
-
-#define ACL_DATA_ALLOCED 0x1
+ uint16_t z_acl_count; /* ace count */
+ uint8_t z_ace_data[ZFS_ACE_SPACE]; /* space for embedded ACEs */
+} zfs_acl_phys_t;
+
+
+
+typedef struct acl_ops {
+ uint32_t (*ace_mask_get) (void *acep); /* get access mask */
+ void (*ace_mask_set) (void *acep,
+ uint32_t mask); /* set access mask */
+ uint16_t (*ace_flags_get) (void *acep); /* get flags */
+ void (*ace_flags_set) (void *acep,
+ uint16_t flags); /* set flags */
+ uint16_t (*ace_type_get)(void *acep); /* get type */
+ void (*ace_type_set)(void *acep,
+ uint16_t type); /* set type */
+ uint64_t (*ace_who_get)(void *acep); /* get who/fuid */
+ void (*ace_who_set)(void *acep,
+ uint64_t who); /* set who/fuid */
+ size_t (*ace_size)(void *acep); /* how big is this ace */
+ size_t (*ace_abstract_size)(void); /* sizeof abstract entry */
+ int (*ace_mask_off)(void); /* off of access mask in ace */
+ int (*ace_data)(void *acep, void **datap);
+ /* ptr to data if any */
+} acl_ops_t;
/*
- * Max ACL size is prepended deny for all entries + the
- * canonical six tacked on * the end.
+ * A zfs_acl_t structure is composed of a list of zfs_acl_node_t's.
+ * Each node will have one or more ACEs associated with it. You will
+ * only have multiple nodes during a chmod operation. Normally only
+ * one node is required.
*/
-#define MAX_ACL_SIZE (MAX_ACL_ENTRIES * 2 + 6)
+typedef struct zfs_acl_node {
+ list_node_t z_next; /* Next chunk of ACEs */
+ void *z_acldata; /* pointer into actual ACE(s) */
+ void *z_allocdata; /* pointer to kmem allocated memory */
+ size_t z_allocsize; /* Size of blob in bytes */
+ size_t z_size; /* length of ACL data */
+ int z_ace_count; /* number of ACEs in this acl node */
+ int z_ace_idx; /* ace iterator positioned on */
+} zfs_acl_node_t;
typedef struct zfs_acl {
- int z_slots; /* number of allocated slots for ACEs */
- int z_acl_count;
- uint_t z_state;
- ace_t *z_acl;
+ int z_acl_count; /* Number of ACEs */
+ size_t z_acl_bytes; /* Number of bytes in ACL */
+ uint_t z_version; /* version of ACL */
+ void *z_next_ace; /* pointer to next ACE */
+ int z_hints; /* ACL hints (ZFS_INHERIT_ACE ...) */
+ zfs_acl_node_t *z_curr_node; /* current node iterator is handling */
+ list_t z_acl; /* chunks of ACE data */
+ acl_ops_t z_ops; /* ACL operations */
+ boolean_t z_has_fuids; /* FUIDs present in ACL? */
} zfs_acl_t;
+#define ACL_DATA_ALLOCED 0x1
#define ZFS_ACL_SIZE(aclcnt) (sizeof (ace_t) * (aclcnt))
/*
@@ -80,31 +179,34 @@ typedef struct zfs_acl {
#define ZFS_ACL_NOALLOW 1
#define ZFS_ACL_GROUPMASK 2
#define ZFS_ACL_PASSTHROUGH 3
-#define ZFS_ACL_SECURE 4
+#define ZFS_ACL_RESTRICTED 4
struct znode;
+struct zfsvfs;
+struct zfs_fuid_info;
#ifdef _KERNEL
void zfs_perm_init(struct znode *, struct znode *, int, vattr_t *,
- dmu_tx_t *, cred_t *);
-#ifdef TODO
-int zfs_getacl(struct znode *, vsecattr_t *, cred_t *);
-#endif
-int zfs_mode_update(struct znode *, uint64_t, dmu_tx_t *);
+ dmu_tx_t *, cred_t *, zfs_acl_t *, zfs_fuid_info_t **);
#ifdef TODO
-int zfs_setacl(struct znode *, vsecattr_t *, cred_t *);
+int zfs_getacl(struct znode *, vsecattr_t *, boolean_t, cred_t *);
+int zfs_setacl(struct znode *, vsecattr_t *, boolean_t, cred_t *);
#endif
void zfs_acl_rele(void *);
-void zfs_ace_byteswap(ace_t *, int);
-extern int zfs_zaccess(struct znode *, int, cred_t *);
-extern int zfs_zaccess_rwx(struct znode *, mode_t, cred_t *);
+void zfs_oldace_byteswap(ace_t *, int);
+void zfs_ace_byteswap(void *, size_t, boolean_t);
+extern int zfs_zaccess(struct znode *, int, int, boolean_t, cred_t *);
+extern int zfs_zaccess_rwx(struct znode *, mode_t, int, cred_t *);
+extern int zfs_zaccess_unix(struct znode *, mode_t, cred_t *);
extern int zfs_acl_access(struct znode *, int, cred_t *);
-int zfs_acl_chmod_setattr(struct znode *, uint64_t, dmu_tx_t *);
+int zfs_acl_chmod_setattr(struct znode *, zfs_acl_t **, uint64_t);
int zfs_zaccess_delete(struct znode *, struct znode *, cred_t *);
int zfs_zaccess_rename(struct znode *, struct znode *,
struct znode *, struct znode *, cred_t *cr);
-int zfs_zaccess_v4_perm(struct znode *, int, cred_t *);
void zfs_acl_free(zfs_acl_t *);
+int zfs_vsec_2_aclp(struct zfsvfs *, vtype_t, vsecattr_t *, zfs_acl_t **);
+int zfs_aclset_common(struct znode *, zfs_acl_t *, cred_t *,
+ struct zfs_fuid_info **, dmu_tx_t *);
#endif
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_context.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_context.h
index 4deeb3c..76fdc0d 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_context.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_context.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -45,6 +44,7 @@ extern "C" {
#include <sys/cmn_err.h>
#include <sys/kmem.h>
#include <sys/taskq.h>
+#include <sys/taskqueue.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/mutex.h>
@@ -73,11 +73,19 @@ extern "C" {
#include <sys/ktr.h>
#include <sys/stack.h>
#include <sys/lockf.h>
+#include <sys/pathname.h>
#include <sys/policy.h>
+#include <sys/refstr.h>
#include <sys/zone.h>
#include <sys/eventhandler.h>
+#include <sys/extattr.h>
#include <sys/misc.h>
+#include <sys/sig.h>
+#include <sys/osd.h>
#include <sys/zfs_debug.h>
+#include <sys/sysevent/eventdefs.h>
+#include <sys/u8_textprep.h>
+#include <sys/fm/util.h>
#include <machine/stdarg.h>
@@ -99,6 +107,14 @@ extern "C" {
#define CPU_SEQID (curcpu)
+#define tsd_create(keyp, destructor) do { \
+ *(keyp) = osd_thread_register((destructor)); \
+ KASSERT(*(keyp) > 0, ("cannot register OSD")); \
+} while (0)
+#define tsd_destroy(keyp) osd_thread_deregister(*(keyp))
+#define tsd_get(key) osd_thread_get(curthread, (key))
+#define tsd_set(key, value) osd_thread_set(curthread, (key), (value))
+
#ifdef __cplusplus
}
#endif
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ctldir.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ctldir.h
index a676533..905e8dd 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ctldir.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ctldir.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -57,7 +56,8 @@ int zfsctl_destroy_snapshot(const char *snapname, int force);
int zfsctl_umount_snapshots(vfs_t *, int, cred_t *);
int zfsctl_root_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp,
- int flags, vnode_t *rdir, cred_t *cr);
+ int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
+ int *direntflags, pathname_t *realpnp);
int zfsctl_lookup_objset(vfs_t *vfsp, uint64_t objsetid, zfsvfs_t **zfsvfsp);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_dir.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_dir.h
index f60d614..ebb66e8 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_dir.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_dir.h
@@ -28,6 +28,7 @@
#pragma ident "%Z%%M% %I% %E% SMI"
+#include <sys/pathname.h>
#include <sys/dmu.h>
#include <sys/zfs_znode.h>
@@ -41,6 +42,8 @@ extern "C" {
#define ZSHARED 0x0004 /* shared access (zfs_dirlook()) */
#define ZXATTR 0x0008 /* we want the xattr dir */
#define ZRENAMING 0x0010 /* znode is being renamed */
+#define ZCILOOK 0x0020 /* case-insensitive lookup requested */
+#define ZCIEXACT 0x0040 /* c-i requires c-s match (rename) */
/* mknode flags */
#define IS_ROOT_NODE 0x01 /* create a root node */
@@ -48,15 +51,17 @@ extern "C" {
#define IS_REPLAY 0x04 /* we are replaying intent log */
extern int zfs_dirent_lock(zfs_dirlock_t **, znode_t *, char *, znode_t **,
- int);
+ int, int *, pathname_t *);
extern void zfs_dirent_unlock(zfs_dirlock_t *);
extern int zfs_link_create(zfs_dirlock_t *, znode_t *, dmu_tx_t *, int);
extern int zfs_link_destroy(zfs_dirlock_t *, znode_t *, dmu_tx_t *, int,
boolean_t *);
-extern int zfs_dirlook(znode_t *, char *, vnode_t **);
-extern void zfs_mknode(znode_t *, vattr_t *, uint64_t *,
- dmu_tx_t *, cred_t *, uint_t, znode_t **, int);
+extern int zfs_dirlook(znode_t *, char *, vnode_t **, int, int *,
+ pathname_t *);
+extern void zfs_mknode(znode_t *, vattr_t *, dmu_tx_t *, cred_t *,
+ uint_t, znode_t **, int, zfs_acl_t *, zfs_fuid_info_t **);
extern void zfs_rmnode(znode_t *);
+extern void zfs_dl_name_switch(zfs_dirlock_t *dl, char *new, char **old);
extern boolean_t zfs_dirempty(znode_t *);
extern void zfs_unlinked_add(znode_t *, dmu_tx_t *);
extern void zfs_unlinked_drain(zfsvfs_t *zfsvfs);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_fuid.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_fuid.h
new file mode 100644
index 0000000..8d73b41
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_fuid.h
@@ -0,0 +1,125 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL 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.
+ */
+
+#ifndef _SYS_FS_ZFS_FUID_H
+#define _SYS_FS_ZFS_FUID_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#ifdef _KERNEL
+#include <sys/kidmap.h>
+#include <sys/dmu.h>
+#include <sys/zfs_vfsops.h>
+#endif
+#include <sys/avl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ ZFS_OWNER,
+ ZFS_GROUP,
+ ZFS_ACE_USER,
+ ZFS_ACE_GROUP
+} zfs_fuid_type_t;
+
+/*
+ * Estimate space needed for one more fuid table entry.
+ * for now assume its current size + 1K
+ */
+#define FUID_SIZE_ESTIMATE(z) (z->z_fuid_size + (SPA_MINBLOCKSIZE << 1))
+
+#define FUID_INDEX(x) (x >> 32)
+#define FUID_RID(x) (x & 0xffffffff)
+#define FUID_ENCODE(idx, rid) ((idx << 32) | rid)
+/*
+ * FUIDs cause problems for the intent log
+ * we need to replay the creation of the FUID,
+ * but we can't count on the idmapper to be around
+ * and during replay the FUID index may be different than
+ * before. Also, if an ACL has 100 ACEs and 12 different
+ * domains we don't want to log 100 domain strings, but rather
+ * just the unique 12.
+ */
+
+/*
+ * The FUIDs in the log will index into
+ * domain string table and the bottom half will be the rid.
+ * Used for mapping ephemeral uid/gid during ACL setting to FUIDs
+ */
+typedef struct zfs_fuid {
+ list_node_t z_next;
+ uint64_t z_id; /* uid/gid being converted to fuid */
+ uint64_t z_domidx; /* index in AVL domain table */
+ uint64_t z_logfuid; /* index for domain in log */
+} zfs_fuid_t;
+
+/* list of unique domains */
+typedef struct zfs_fuid_domain {
+ list_node_t z_next;
+ uint64_t z_domidx; /* AVL tree idx */
+ const char *z_domain; /* domain string */
+} zfs_fuid_domain_t;
+
+/*
+ * FUID information necessary for logging create, setattr, and setacl.
+ */
+typedef struct zfs_fuid_info {
+ list_t z_fuids;
+ list_t z_domains;
+ uint64_t z_fuid_owner;
+ uint64_t z_fuid_group;
+ char **z_domain_table; /* Used during replay */
+ uint32_t z_fuid_cnt; /* How many fuids in z_fuids */
+ uint32_t z_domain_cnt; /* How many domains */
+ size_t z_domain_str_sz; /* len of domain strings z_domain list */
+} zfs_fuid_info_t;
+
+#ifdef _KERNEL
+struct znode;
+extern uid_t zfs_fuid_map_id(zfsvfs_t *, uint64_t, cred_t *, zfs_fuid_type_t);
+extern void zfs_fuid_destroy(zfsvfs_t *);
+extern uint64_t zfs_fuid_create_cred(zfsvfs_t *, zfs_fuid_type_t,
+ dmu_tx_t *, cred_t *, zfs_fuid_info_t **);
+extern uint64_t zfs_fuid_create(zfsvfs_t *, uint64_t, cred_t *, zfs_fuid_type_t,
+ dmu_tx_t *, zfs_fuid_info_t **);
+extern void zfs_fuid_map_ids(struct znode *zp, cred_t *cr, uid_t *uid,
+ uid_t *gid);
+extern zfs_fuid_info_t *zfs_fuid_info_alloc(void);
+extern void zfs_fuid_info_free();
+extern boolean_t zfs_groupmember(zfsvfs_t *, uint64_t, cred_t *);
+#endif
+
+char *zfs_fuid_idx_domain(avl_tree_t *, uint32_t);
+uint64_t zfs_fuid_table_load(objset_t *, uint64_t, avl_tree_t *, avl_tree_t *);
+void zfs_fuid_table_destroy(avl_tree_t *, avl_tree_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_FS_ZFS_FUID_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h
index 61a0a9e..05a21c8 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -31,6 +31,11 @@
#include <sys/cred.h>
#include <sys/dmu.h>
#include <sys/zio.h>
+#include <sys/dsl_deleg.h>
+
+#ifdef _KERNEL
+#include <sys/nvpair.h>
+#endif /* _KERNEL */
#ifdef __cplusplus
extern "C" {
@@ -42,9 +47,13 @@ extern "C" {
#define ZFS_SNAPDIR_HIDDEN 0
#define ZFS_SNAPDIR_VISIBLE 1
-#define DMU_BACKUP_VERSION (1ULL)
+#define DMU_BACKUP_STREAM_VERSION (1ULL)
+#define DMU_BACKUP_HEADER_VERSION (2ULL)
#define DMU_BACKUP_MAGIC 0x2F5bacbacULL
+#define DRR_FLAG_CLONE (1<<0)
+#define DRR_FLAG_CI_DATA (1<<1)
+
/*
* zfs ioctl command structure
*/
@@ -53,14 +62,14 @@ typedef struct dmu_replay_record {
DRR_BEGIN, DRR_OBJECT, DRR_FREEOBJECTS,
DRR_WRITE, DRR_FREE, DRR_END,
} drr_type;
- uint32_t drr_pad;
+ uint32_t drr_payloadlen;
union {
struct drr_begin {
uint64_t drr_magic;
uint64_t drr_version;
uint64_t drr_creation_time;
dmu_objset_type_t drr_type;
- uint32_t drr_pad;
+ uint32_t drr_flags;
uint64_t drr_toguid;
uint64_t drr_fromguid;
char drr_toname[MAXNAMELEN];
@@ -109,48 +118,71 @@ typedef struct zinject_record {
uint32_t zi_error;
uint64_t zi_type;
uint32_t zi_freq;
+ uint32_t zi_pad; /* pad out to 64 bit alignment */
} zinject_record_t;
#define ZINJECT_NULL 0x1
#define ZINJECT_FLUSH_ARC 0x2
#define ZINJECT_UNLOAD_SPA 0x4
+typedef struct zfs_share {
+ uint64_t z_exportdata;
+ uint64_t z_sharedata;
+ uint64_t z_sharetype; /* 0 = share, 1 = unshare */
+ uint64_t z_sharemax; /* max length of share string */
+} zfs_share_t;
+
+/*
+ * ZFS file systems may behave the usual, POSIX-compliant way, where
+ * name lookups are case-sensitive. They may also be set up so that
+ * all the name lookups are case-insensitive, or so that only some
+ * lookups, the ones that set an FIGNORECASE flag, are case-insensitive.
+ */
+typedef enum zfs_case {
+ ZFS_CASE_SENSITIVE,
+ ZFS_CASE_INSENSITIVE,
+ ZFS_CASE_MIXED
+} zfs_case_t;
+
typedef struct zfs_cmd {
char zc_name[MAXPATHLEN];
- char zc_value[MAXPATHLEN * 2];
+ char zc_value[MAXPATHLEN];
+ char zc_string[MAXNAMELEN];
uint64_t zc_guid;
- uint64_t zc_nvlist_src; /* really (char *) */
+ uint64_t zc_nvlist_conf; /* really (char *) */
+ uint64_t zc_nvlist_conf_size;
+ uint64_t zc_nvlist_src; /* really (char *) */
uint64_t zc_nvlist_src_size;
- uint64_t zc_nvlist_dst; /* really (char *) */
+ uint64_t zc_nvlist_dst; /* really (char *) */
uint64_t zc_nvlist_dst_size;
uint64_t zc_cookie;
- uint64_t zc_cred;
- uint64_t zc_dev;
uint64_t zc_objset_type;
- uint64_t zc_history; /* really (char *) */
- uint64_t zc_history_len;
+ uint64_t zc_perm_action;
+ uint64_t zc_history; /* really (char *) */
+ uint64_t zc_history_len;
uint64_t zc_history_offset;
uint64_t zc_obj;
+ zfs_share_t zc_share;
uint64_t zc_jailid;
dmu_objset_stats_t zc_objset_stats;
struct drr_begin zc_begin_record;
zinject_record_t zc_inject_record;
} zfs_cmd_t;
-#ifdef _KERNEL
-typedef struct zfs_create_data {
- cred_t *zc_cred;
- dev_t zc_dev;
- nvlist_t *zc_props;
-} zfs_create_data_t;
-#endif
-
#define ZVOL_MAX_MINOR (1 << 16)
#define ZFS_MIN_MINOR (ZVOL_MAX_MINOR + 1)
#ifdef _KERNEL
-extern int zfs_secpolicy_write(const char *dataset, cred_t *cr);
+typedef struct zfs_creat {
+ nvlist_t *zct_zplprops;
+ nvlist_t *zct_props;
+} zfs_creat_t;
+
+extern int zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr);
+extern int zfs_secpolicy_rename_perms(const char *from,
+ const char *to, cred_t *cr);
+extern int zfs_secpolicy_destroy_perms(const char *name, cred_t *cr);
extern int zfs_busy(void);
extern int zfs_unmount_snap(char *, void *);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_vfsops.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_vfsops.h
index aa82cc1..8d53c02 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_vfsops.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_vfsops.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -31,6 +31,8 @@
#include <sys/list.h>
#include <sys/vfs.h>
#include <sys/zil.h>
+#include <sys/rrwlock.h>
+#include <sys/zfs_ioctl.h>
#ifdef __cplusplus
extern "C" {
@@ -46,35 +48,50 @@ struct zfsvfs {
uint64_t z_unlinkedobj; /* id of unlinked zapobj */
uint64_t z_max_blksz; /* maximum block size for files */
uint64_t z_assign; /* TXG_NOWAIT or set by zil_replay() */
+ uint64_t z_fuid_obj; /* fuid table object number */
+ uint64_t z_fuid_size; /* fuid table size */
+ avl_tree_t z_fuid_idx; /* fuid tree keyed by index */
+ avl_tree_t z_fuid_domain; /* fuid tree keyed by domain */
+ krwlock_t z_fuid_lock; /* fuid lock */
+ boolean_t z_fuid_loaded; /* fuid tables are loaded */
+ struct zfs_fuid_info *z_fuid_replay; /* fuid info for replay */
zilog_t *z_log; /* intent log pointer */
uint_t z_acl_mode; /* acl chmod/mode behavior */
uint_t z_acl_inherit; /* acl inheritance behavior */
+ zfs_case_t z_case; /* case-sense */
+ boolean_t z_utf8; /* utf8-only */
+ int z_norm; /* normalization flags */
boolean_t z_atime; /* enable atimes mount option */
- boolean_t z_unmounted1; /* unmounted phase 1 */
- boolean_t z_unmounted2; /* unmounted phase 2 */
- uint32_t z_op_cnt; /* vnode/vfs operations ref count */
- krwlock_t z_um_lock; /* rw lock for umount phase 2 */
+ boolean_t z_unmounted; /* unmounted */
+ rrwlock_t z_teardown_lock;
+ krwlock_t z_teardown_inactive_lock;
list_t z_all_znodes; /* all vnodes in the fs */
kmutex_t z_znodes_lock; /* lock for z_all_znodes */
vnode_t *z_ctldir; /* .zfs directory pointer */
boolean_t z_show_ctldir; /* expose .zfs in the root dir */
boolean_t z_issnap; /* true if this is a snapshot */
+ boolean_t z_vscan; /* virus scan on/off */
+ boolean_t z_use_fuids; /* version allows fuids */
+ kmutex_t z_online_recv_lock; /* recv in prog grabs as WRITER */
+ uint64_t z_version; /* ZPL version */
#define ZFS_OBJ_MTX_SZ 64
kmutex_t z_hold_mtx[ZFS_OBJ_MTX_SZ]; /* znode hold locks */
};
/*
- * The total file ID size is limited to 12 bytes (including the length
- * field) in the NFSv2 protocol. For historical reasons, this same limit
- * is currently being imposed by the Solaris NFSv3 implementation...
- * although the protocol actually permits a maximum of 64 bytes. It will
- * not be possible to expand beyond 12 bytes without abandoning support
- * of NFSv2 and making some changes to the Solaris NFSv3 implementation.
+ * Normal filesystems (those not under .zfs/snapshot) have a total
+ * file ID size limited to 12 bytes (including the length field) due to
+ * NFSv2 protocol's limitation of 32 bytes for a filehandle. For historical
+ * reasons, this same limit is being imposed by the Solaris NFSv3 implementation
+ * (although the NFSv3 protocol actually permits a maximum of 64 bytes). It
+ * is not possible to expand beyond 12 bytes without abandoning support
+ * of NFSv2.
*
- * For the time being, we will partition up the available space as follows:
+ * For normal filesystems, we partition up the available space as follows:
* 2 bytes fid length (required)
* 6 bytes object number (48 bits)
* 4 bytes generation number (32 bits)
+ *
* We reserve only 48 bits for the object number, as this is the limit
* currently defined and imposed by the DMU.
*/
@@ -84,6 +101,22 @@ typedef struct zfid_short {
uint8_t zf_gen[4]; /* gen[i] = gen >> (8 * i) */
} zfid_short_t;
+/*
+ * Filesystems under .zfs/snapshot have a total file ID size of 22 bytes
+ * (including the length field). This makes files under .zfs/snapshot
+ * accessible by NFSv3 and NFSv4, but not NFSv2.
+ *
+ * For files under .zfs/snapshot, we partition up the available space
+ * as follows:
+ * 2 bytes fid length (required)
+ * 6 bytes object number (48 bits)
+ * 4 bytes generation number (32 bits)
+ * 6 bytes objset id (48 bits)
+ * 4 bytes currently just zero (32 bits)
+ *
+ * We reserve only 48 bits for the object number and objset id, as these are
+ * the limits currently defined and imposed by the DMU.
+ */
typedef struct zfid_long {
zfid_short_t z_fid;
uint8_t zf_setid[6]; /* obj[i] = obj >> (8 * i) */
@@ -93,6 +126,12 @@ typedef struct zfid_long {
#define SHORT_FID_LEN (sizeof (zfid_short_t) - sizeof (uint16_t))
#define LONG_FID_LEN (sizeof (zfid_long_t) - sizeof (uint16_t))
+extern uint_t zfs_fsyncer_key;
+extern int zfs_super_owner;
+
+extern int zfs_suspend_fs(zfsvfs_t *zfsvfs, char *osname, int *mode);
+extern int zfs_resume_fs(zfsvfs_t *zfsvfs, const char *osname, int mode);
+
#ifdef __cplusplus
}
#endif
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_znode.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_znode.h
index 6b29232..a0cf440 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_znode.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_znode.h
@@ -19,19 +19,18 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_FS_ZFS_ZNODE_H
#define _SYS_FS_ZFS_ZNODE_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef _KERNEL
#include <sys/list.h>
#include <sys/dmu.h>
#include <sys/zfs_vfsops.h>
+#include <sys/rrwlock.h>
#endif
#include <sys/zfs_acl.h>
#include <sys/zil.h>
@@ -41,34 +40,62 @@ extern "C" {
#endif
/*
- * Define special zfs pflags
+ * Additional file level attributes, that are stored
+ * in the upper half of zp_flags
*/
-#define ZFS_XATTR 0x1 /* is an extended attribute */
-#define ZFS_INHERIT_ACE 0x2 /* ace has inheritable ACEs */
-#define ZFS_ACL_TRIVIAL 0x4 /* files ACL is trivial */
+#define ZFS_READONLY 0x0000000100000000
+#define ZFS_HIDDEN 0x0000000200000000
+#define ZFS_SYSTEM 0x0000000400000000
+#define ZFS_ARCHIVE 0x0000000800000000
+#define ZFS_IMMUTABLE 0x0000001000000000
+#define ZFS_NOUNLINK 0x0000002000000000
+#define ZFS_APPENDONLY 0x0000004000000000
+#define ZFS_NODUMP 0x0000008000000000
+#define ZFS_OPAQUE 0x0000010000000000
+#define ZFS_AV_QUARANTINED 0x0000020000000000
+#define ZFS_AV_MODIFIED 0x0000040000000000
+
+#define ZFS_ATTR_SET(zp, attr, value) \
+{ \
+ if (value) \
+ zp->z_phys->zp_flags |= attr; \
+ else \
+ zp->z_phys->zp_flags &= ~attr; \
+}
-#define MASTER_NODE_OBJ 1
+/*
+ * Define special zfs pflags
+ */
+#define ZFS_XATTR 0x1 /* is an extended attribute */
+#define ZFS_INHERIT_ACE 0x2 /* ace has inheritable ACEs */
+#define ZFS_ACL_TRIVIAL 0x4 /* files ACL is trivial */
+#define ZFS_ACL_OBJ_ACE 0x8 /* ACL has CMPLX Object ACE */
+#define ZFS_ACL_PROTECTED 0x10 /* ACL protected */
+#define ZFS_ACL_DEFAULTED 0x20 /* ACL should be defaulted */
+#define ZFS_ACL_AUTO_INHERIT 0x40 /* ACL should be inherited */
+#define ZFS_BONUS_SCANSTAMP 0x80 /* Scanstamp in bonus area */
/*
- * special attributes for master node.
+ * Is ID ephemeral?
*/
+#define IS_EPHEMERAL(x) (x > MAXUID)
-#define ZFS_FSID "FSID"
-#define ZFS_UNLINKED_SET "DELETE_QUEUE"
-#define ZFS_ROOT_OBJ "ROOT"
-#define ZPL_VERSION_OBJ "VERSION"
-#define ZFS_PROP_BLOCKPERPAGE "BLOCKPERPAGE"
-#define ZFS_PROP_NOGROWBLOCKS "NOGROWBLOCKS"
+/*
+ * Should we use FUIDs?
+ */
+#define USE_FUIDS(version, os) (version >= ZPL_VERSION_FUID &&\
+ spa_version(dmu_objset_spa(os)) >= SPA_VERSION_FUID)
-#define ZFS_FLAG_BLOCKPERPAGE 0x1
-#define ZFS_FLAG_NOGROWBLOCKS 0x2
+#define MASTER_NODE_OBJ 1
/*
- * ZPL version - rev'd whenever an incompatible on-disk format change
- * occurs. Independent of SPA/DMU/ZAP versioning.
+ * Special attributes for master node.
*/
-
-#define ZPL_VERSION 1ULL
+#define ZFS_FSID "FSID"
+#define ZFS_UNLINKED_SET "DELETE_QUEUE"
+#define ZFS_ROOT_OBJ "ROOT"
+#define ZPL_VERSION_STR "VERSION"
+#define ZFS_FUID_TABLES "FUID"
#define ZFS_MAX_BLOCKSIZE (SPA_MAXBLOCKSIZE)
@@ -83,14 +110,20 @@ extern "C" {
#define ZFS_MAXNAMELEN (MAXNAMELEN - 1)
/*
+ * Convert mode bits (zp_mode) to BSD-style DT_* values for storing in
+ * the directory entries.
+ */
+#ifndef IFTODT
+#define IFTODT(mode) (((mode) & S_IFMT) >> 12)
+#endif
+
+/*
* The directory entry has the type (currently unused on Solaris) in the
* top 4 bits, and the object number in the low 48 bits. The "middle"
* 12 bits are unused.
*/
#define ZFS_DIRENT_TYPE(de) BF64_GET(de, 60, 4)
#define ZFS_DIRENT_OBJ(de) BF64_GET(de, 0, 48)
-#define ZFS_DIRENT_MAKE(type, obj) (((uint64_t)type << 60) | obj)
-
/*
* This is the persistent portion of the znode. It is stored
@@ -112,8 +145,9 @@ typedef struct znode_phys {
uint64_t zp_flags; /* 120 - persistent flags */
uint64_t zp_uid; /* 128 - file owner */
uint64_t zp_gid; /* 136 - owning group */
- uint64_t zp_pad[4]; /* 144 - future */
- zfs_znode_acl_t zp_acl; /* 176 - 263 ACL */
+ uint64_t zp_zap; /* 144 - extra attributes */
+ uint64_t zp_pad[3]; /* 152 - future */
+ zfs_acl_phys_t zp_acl; /* 176 - 263 ACL */
/*
* Data may pad out any remaining bytes in the znode buffer, eg:
*
@@ -121,7 +155,9 @@ typedef struct znode_phys {
* |<-- dnode (192) --->|<----------- "bonus" buffer (320) ---------->|
* |<---- znode (264) ---->|<---- data (56) ---->|
*
- * At present, we only use this space to store symbolic links.
+ * At present, we use this space for the following:
+ * - symbolic links
+ * - 32-byte anti-virus scanstamp (regular files only)
*/
} znode_phys_t;
@@ -153,12 +189,12 @@ typedef struct znode {
avl_tree_t z_range_avl; /* avl tree of file range locks */
uint8_t z_unlinked; /* file has been unlinked */
uint8_t z_atime_dirty; /* atime needs to be synced */
- uint8_t z_dbuf_held; /* Is z_dbuf already held? */
uint8_t z_zn_prefetch; /* Prefetch znodes? */
uint_t z_blksz; /* block size in bytes */
uint_t z_seq; /* modification sequence number */
uint64_t z_mapcnt; /* number of pages mapped to file */
uint64_t z_last_itx; /* last ZIL itx on this znode */
+ uint64_t z_gen; /* generation (same as zp_gen) */
uint32_t z_sync_cnt; /* synchronous open count */
kmutex_t z_acl_lock; /* acl data lock */
list_node_t z_link_node; /* all znodes in fs link */
@@ -167,6 +203,8 @@ typedef struct znode {
*/
znode_phys_t *z_phys; /* pointer to persistent znode */
dmu_buf_t *z_dbuf; /* buffer containing the z_phys */
+ /* FreeBSD-specific field. */
+ struct task z_task;
} znode_t;
@@ -195,42 +233,51 @@ typedef struct znode {
/*
* ZFS_ENTER() is called on entry to each ZFS vnode and vfs operation.
* ZFS_EXIT() must be called before exitting the vop.
+ * ZFS_VERIFY_ZP() verifies the znode is valid.
*/
#define ZFS_ENTER(zfsvfs) \
{ \
- atomic_add_32(&(zfsvfs)->z_op_cnt, 1); \
- if ((zfsvfs)->z_unmounted1) { \
+ rrw_enter(&(zfsvfs)->z_teardown_lock, RW_READER, FTAG); \
+ if ((zfsvfs)->z_unmounted) { \
ZFS_EXIT(zfsvfs); \
return (EIO); \
} \
}
-#define ZFS_EXIT(zfsvfs) atomic_add_32(&(zfsvfs)->z_op_cnt, -1)
+
+#define ZFS_EXIT(zfsvfs) rrw_exit(&(zfsvfs)->z_teardown_lock, FTAG)
+
+#define ZFS_VERIFY_ZP(zp) \
+ if ((zp)->z_dbuf == NULL) { \
+ ZFS_EXIT((zp)->z_zfsvfs); \
+ return (EIO); \
+ } \
/*
* Macros for dealing with dmu_buf_hold
*/
-#define ZFS_OBJ_HASH(obj_num) (obj_num & (ZFS_OBJ_MTX_SZ - 1))
-#define ZFS_OBJ_MUTEX(zp) \
- (&zp->z_zfsvfs->z_hold_mtx[ZFS_OBJ_HASH(zp->z_id)])
+#define ZFS_OBJ_HASH(obj_num) ((obj_num) & (ZFS_OBJ_MTX_SZ - 1))
+#define ZFS_OBJ_MUTEX(zfsvfs, obj_num) \
+ (&(zfsvfs)->z_hold_mtx[ZFS_OBJ_HASH(obj_num)])
#define ZFS_OBJ_HOLD_ENTER(zfsvfs, obj_num) \
- mutex_enter(&zfsvfs->z_hold_mtx[ZFS_OBJ_HASH(obj_num)]);
-
+ mutex_enter(ZFS_OBJ_MUTEX((zfsvfs), (obj_num)))
+#define ZFS_OBJ_HOLD_TRYENTER(zfsvfs, obj_num) \
+ mutex_tryenter(ZFS_OBJ_MUTEX((zfsvfs), (obj_num)))
#define ZFS_OBJ_HOLD_EXIT(zfsvfs, obj_num) \
- mutex_exit(&zfsvfs->z_hold_mtx[ZFS_OBJ_HASH(obj_num)])
+ mutex_exit(ZFS_OBJ_MUTEX((zfsvfs), (obj_num)))
/*
* Macros to encode/decode ZFS stored time values from/to struct timespec
*/
#define ZFS_TIME_ENCODE(tp, stmp) \
{ \
- stmp[0] = (uint64_t)(tp)->tv_sec; \
- stmp[1] = (uint64_t)(tp)->tv_nsec; \
+ (stmp)[0] = (uint64_t)(tp)->tv_sec; \
+ (stmp)[1] = (uint64_t)(tp)->tv_nsec; \
}
#define ZFS_TIME_DECODE(tp, stmp) \
{ \
- (tp)->tv_sec = (time_t)stmp[0]; \
- (tp)->tv_nsec = (long)stmp[1]; \
+ (tp)->tv_sec = (time_t)(stmp)[0]; \
+ (tp)->tv_nsec = (long)(stmp)[1]; \
}
/*
@@ -244,9 +291,10 @@ typedef struct znode {
if ((zfsvfs)->z_atime && !((zfsvfs)->z_vfs->vfs_flag & VFS_RDONLY)) \
zfs_time_stamper(zp, ACCESSED, NULL)
-extern int zfs_init_fs(zfsvfs_t *, znode_t **, cred_t *);
+extern int zfs_init_fs(zfsvfs_t *, znode_t **);
extern void zfs_set_dataprop(objset_t *);
-extern void zfs_create_fs(objset_t *os, cred_t *cr, dmu_tx_t *tx);
+extern void zfs_create_fs(objset_t *os, cred_t *cr, nvlist_t *,
+ dmu_tx_t *tx);
extern void zfs_time_stamper(znode_t *, uint_t, dmu_tx_t *);
extern void zfs_time_stamper_locked(znode_t *, uint_t, dmu_tx_t *);
extern void zfs_grow_blocksize(znode_t *, uint64_t, dmu_tx_t *);
@@ -254,33 +302,43 @@ extern int zfs_freesp(znode_t *, uint64_t, uint64_t, int, boolean_t);
extern void zfs_znode_init(void);
extern void zfs_znode_fini(void);
extern int zfs_zget(zfsvfs_t *, uint64_t, znode_t **);
+extern int zfs_rezget(znode_t *);
extern void zfs_zinactive(znode_t *);
extern void zfs_znode_delete(znode_t *, dmu_tx_t *);
extern void zfs_znode_free(znode_t *);
extern void zfs_remove_op_tables();
extern int zfs_create_op_tables();
extern dev_t zfs_cmpldev(uint64_t);
-
-extern void zfs_log_create(zilog_t *zilog, dmu_tx_t *tx, int txtype,
- znode_t *dzp, znode_t *zp, char *name);
-extern void zfs_log_remove(zilog_t *zilog, dmu_tx_t *tx, int txtype,
+extern int zfs_get_zplprop(objset_t *os, zfs_prop_t prop, uint64_t *value);
+extern int zfs_set_version(const char *name, uint64_t newvers);
+extern int zfs_get_stats(objset_t *os, nvlist_t *nv);
+extern void zfs_znode_dmu_fini(znode_t *);
+
+extern void zfs_log_create(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype,
+ znode_t *dzp, znode_t *zp, char *name, vsecattr_t *, zfs_fuid_info_t *,
+ vattr_t *vap);
+extern int zfs_log_create_txtype(zil_create_t, vsecattr_t *vsecp,
+ vattr_t *vap);
+extern void zfs_log_remove(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype,
znode_t *dzp, char *name);
-extern void zfs_log_link(zilog_t *zilog, dmu_tx_t *tx, int txtype,
+extern void zfs_log_link(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype,
znode_t *dzp, znode_t *zp, char *name);
-extern void zfs_log_symlink(zilog_t *zilog, dmu_tx_t *tx, int txtype,
+extern void zfs_log_symlink(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype,
znode_t *dzp, znode_t *zp, char *name, char *link);
-extern void zfs_log_rename(zilog_t *zilog, dmu_tx_t *tx, int txtype,
+extern void zfs_log_rename(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype,
znode_t *sdzp, char *sname, znode_t *tdzp, char *dname, znode_t *szp);
extern void zfs_log_write(zilog_t *zilog, dmu_tx_t *tx, int txtype,
znode_t *zp, offset_t off, ssize_t len, int ioflag);
extern void zfs_log_truncate(zilog_t *zilog, dmu_tx_t *tx, int txtype,
znode_t *zp, uint64_t off, uint64_t len);
extern void zfs_log_setattr(zilog_t *zilog, dmu_tx_t *tx, int txtype,
- znode_t *zp, vattr_t *vap, uint_t mask_applied);
+ znode_t *zp, vattr_t *vap, uint_t mask_applied, zfs_fuid_info_t *fuidp);
#ifndef ZFS_NO_ACL
-extern void zfs_log_acl(zilog_t *zilog, dmu_tx_t *tx, int txtype,
- znode_t *zp, int aclcnt, ace_t *z_ace);
+extern void zfs_log_acl(zilog_t *zilog, dmu_tx_t *tx, znode_t *zp,
+ vsecattr_t *vsecp, zfs_fuid_info_t *fuidp);
#endif
+extern void zfs_xvattr_set(znode_t *zp, xvattr_t *xvap);
+extern void zfs_upgrade(zfsvfs_t *zfsvfs, dmu_tx_t *tx);
extern zil_get_data_t zfs_get_data;
extern zil_replay_func_t *zfs_replay_vector[TX_MAX_TYPE];
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zil.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zil.h
index 947ba9f..4d02d14 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zil.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zil.h
@@ -19,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_ZIL_H
#define _SYS_ZIL_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#include <sys/spa.h>
#include <sys/zio.h>
@@ -88,22 +86,61 @@ typedef struct zil_trailer {
#define ZIL_ZC_OBJSET 2
#define ZIL_ZC_SEQ 3
+typedef enum zil_create {
+ Z_FILE,
+ Z_DIR,
+ Z_XATTRDIR,
+} zil_create_t;
+
+/*
+ * size of xvattr log section.
+ * its composed of lr_attr_t + xvattr bitmap + 2 64 bit timestamps
+ * for create time and a single 64 bit integer for all of the attributes,
+ * and 4 64 bit integers (32 bytes) for the scanstamp.
+ *
+ */
+
+#define ZIL_XVAT_SIZE(mapsize) \
+ sizeof (lr_attr_t) + (sizeof (uint32_t) * (mapsize - 1)) + \
+ (sizeof (uint64_t) * 7)
+
+/*
+ * Size of ACL in log. The ACE data is padded out to properly align
+ * on 8 byte boundary.
+ */
+
+#define ZIL_ACE_LENGTH(x) (roundup(x, sizeof (uint64_t)))
+
/*
* Intent log transaction types and record structures
*/
-#define TX_CREATE 1 /* Create file */
-#define TX_MKDIR 2 /* Make directory */
-#define TX_MKXATTR 3 /* Make XATTR directory */
-#define TX_SYMLINK 4 /* Create symbolic link to a file */
-#define TX_REMOVE 5 /* Remove file */
-#define TX_RMDIR 6 /* Remove directory */
-#define TX_LINK 7 /* Create hard link to a file */
-#define TX_RENAME 8 /* Rename a file */
-#define TX_WRITE 9 /* File write */
-#define TX_TRUNCATE 10 /* Truncate a file */
-#define TX_SETATTR 11 /* Set file attributes */
-#define TX_ACL 12 /* Set acl */
-#define TX_MAX_TYPE 13 /* Max transaction type */
+#define TX_CREATE 1 /* Create file */
+#define TX_MKDIR 2 /* Make directory */
+#define TX_MKXATTR 3 /* Make XATTR directory */
+#define TX_SYMLINK 4 /* Create symbolic link to a file */
+#define TX_REMOVE 5 /* Remove file */
+#define TX_RMDIR 6 /* Remove directory */
+#define TX_LINK 7 /* Create hard link to a file */
+#define TX_RENAME 8 /* Rename a file */
+#define TX_WRITE 9 /* File write */
+#define TX_TRUNCATE 10 /* Truncate a file */
+#define TX_SETATTR 11 /* Set file attributes */
+#define TX_ACL_V0 12 /* Set old formatted ACL */
+#define TX_ACL 13 /* Set ACL */
+#define TX_CREATE_ACL 14 /* create with ACL */
+#define TX_CREATE_ATTR 15 /* create + attrs */
+#define TX_CREATE_ACL_ATTR 16 /* create with ACL + attrs */
+#define TX_MKDIR_ACL 17 /* mkdir with ACL */
+#define TX_MKDIR_ATTR 18 /* mkdir with attr */
+#define TX_MKDIR_ACL_ATTR 19 /* mkdir with ACL + attrs */
+#define TX_MAX_TYPE 20 /* Max transaction type */
+
+/*
+ * The transactions for mkdir, symlink, remove, rmdir, link, and rename
+ * may have the following bit set, indicating the original request
+ * specified case-insensitive handling of names.
+ */
+#define TX_CI ((uint64_t)0x1 << 63) /* case-insensitive behavior requested */
/*
* Format of log records.
@@ -124,6 +161,23 @@ typedef struct { /* common log record header */
uint64_t lrc_seq; /* see comment above */
} lr_t;
+/*
+ * Handle option extended vattr attributes.
+ *
+ * Whenever new attributes are added the version number
+ * will need to be updated as will code in
+ * zfs_log.c and zfs_replay.c
+ */
+typedef struct {
+ uint32_t lr_attr_masksize; /* number of elements in array */
+ uint32_t lr_attr_bitmap; /* First entry of array */
+ /* remainder of array and any additional fields */
+} lr_attr_t;
+
+/*
+ * log record for creates without optional ACL.
+ * This log record does support optional xvattr_t attributes.
+ */
typedef struct {
lr_t lr_common; /* common portion of log record */
uint64_t lr_doid; /* object id of directory */
@@ -136,8 +190,42 @@ typedef struct {
uint64_t lr_rdev; /* rdev of object to create */
/* name of object to create follows this */
/* for symlinks, link content follows name */
+ /* for creates with xvattr data, the name follows the xvattr info */
} lr_create_t;
+/*
+ * FUID ACL record will be an array of ACEs from the original ACL.
+ * If this array includes ephemeral IDs, the record will also include
+ * an array of log-specific FUIDs to replace the ephemeral IDs.
+ * Only one copy of each unique domain will be present, so the log-specific
+ * FUIDs will use an index into a compressed domain table. On replay this
+ * information will be used to construct real FUIDs (and bypass idmap,
+ * since it may not be available).
+ */
+
+/*
+ * Log record for creates with optional ACL
+ * This log record is also used for recording any FUID
+ * information needed for replaying the create. If the
+ * file doesn't have any actual ACEs then the lr_aclcnt
+ * would be zero.
+ */
+typedef struct {
+ lr_create_t lr_create; /* common create portion */
+ uint64_t lr_aclcnt; /* number of ACEs in ACL */
+ uint64_t lr_domcnt; /* number of unique domains */
+ uint64_t lr_fuidcnt; /* number of real fuids */
+ uint64_t lr_acl_bytes; /* number of bytes in ACL */
+ uint64_t lr_acl_flags; /* ACL flags */
+ /* lr_acl_bytes number of variable sized ace's follows */
+ /* if create is also setting xvattr's, then acl data follows xvattr */
+ /* if ACE FUIDs are needed then they will follow the xvattr_t */
+ /* Following the FUIDs will be the domain table information. */
+ /* The FUIDs for the owner and group will be in the lr_create */
+ /* portion of the record. */
+ /* name follows ACL data */
+} lr_acl_create_t;
+
typedef struct {
lr_t lr_common; /* common portion of log record */
uint64_t lr_doid; /* obj id of directory */
@@ -185,6 +273,7 @@ typedef struct {
uint64_t lr_size; /* size to set */
uint64_t lr_atime[2]; /* access time */
uint64_t lr_mtime[2]; /* modification time */
+ /* optional attribute lr_attr_t may be here */
} lr_setattr_t;
typedef struct {
@@ -192,6 +281,17 @@ typedef struct {
uint64_t lr_foid; /* obj id of file */
uint64_t lr_aclcnt; /* number of acl entries */
/* lr_aclcnt number of ace_t entries follow this */
+} lr_acl_v0_t;
+
+typedef struct {
+ lr_t lr_common; /* common portion of log record */
+ uint64_t lr_foid; /* obj id of file */
+ uint64_t lr_aclcnt; /* number of ACEs in ACL */
+ uint64_t lr_domcnt; /* number of unique domains */
+ uint64_t lr_fuidcnt; /* number of real fuids */
+ uint64_t lr_acl_bytes; /* number of bytes in ACL */
+ uint64_t lr_acl_flags; /* ACL flags */
+ /* lr_acl_bytes number of variable sized ace's follows */
} lr_acl_t;
/*
@@ -213,6 +313,7 @@ typedef struct itx {
void *itx_private; /* type-specific opaque data */
itx_wr_state_t itx_wr_state; /* write state */
uint8_t itx_sync; /* synchronous transaction */
+ uint64_t itx_sod; /* record size on disk */
lr_t itx_lr; /* common part of log record */
/* followed by type-specific part of lr_xx_t and its immediate data */
} itx_t;
@@ -234,6 +335,7 @@ typedef void zil_parse_blk_func_t(zilog_t *zilog, blkptr_t *bp, void *arg,
typedef void zil_parse_lr_func_t(zilog_t *zilog, lr_t *lr, void *arg,
uint64_t txg);
typedef int zil_replay_func_t();
+typedef void zil_replay_cleaner_t();
typedef int zil_get_data_t(void *arg, lr_write_t *lr, char *dbuf, zio_t *zio);
extern uint64_t zil_parse(zilog_t *zilog, zil_parse_blk_func_t *parse_blk_func,
@@ -249,15 +351,19 @@ extern zilog_t *zil_open(objset_t *os, zil_get_data_t *get_data);
extern void zil_close(zilog_t *zilog);
extern void zil_replay(objset_t *os, void *arg, uint64_t *txgp,
- zil_replay_func_t *replay_func[TX_MAX_TYPE]);
+ zil_replay_func_t *replay_func[TX_MAX_TYPE],
+ zil_replay_cleaner_t *replay_cleaner);
extern void zil_destroy(zilog_t *zilog, boolean_t keep_first);
+extern void zil_rollback_destroy(zilog_t *zilog, dmu_tx_t *tx);
-extern itx_t *zil_itx_create(int txtype, size_t lrsize);
+extern itx_t *zil_itx_create(uint64_t txtype, size_t lrsize);
extern uint64_t zil_itx_assign(zilog_t *zilog, itx_t *itx, dmu_tx_t *tx);
extern void zil_commit(zilog_t *zilog, uint64_t seq, uint64_t oid);
extern int zil_claim(char *osname, void *txarg);
+extern int zil_check_log_chain(char *osname, void *txarg);
+extern int zil_clear_log_chain(char *osname, void *txarg);
extern void zil_sync(zilog_t *zilog, dmu_tx_t *tx);
extern void zil_clean(zilog_t *zilog);
extern int zil_is_committed(zilog_t *zilog);
@@ -265,7 +371,7 @@ extern int zil_is_committed(zilog_t *zilog);
extern int zil_suspend(zilog_t *zilog);
extern void zil_resume(zilog_t *zilog);
-extern void zil_add_vdev(zilog_t *zilog, uint64_t vdev);
+extern void zil_add_block(zilog_t *zilog, blkptr_t *bp);
extern int zil_disable;
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zil_impl.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zil_impl.h
index 3ecf4e4..0fc800b 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zil_impl.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zil_impl.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -51,15 +51,13 @@ typedef struct lwb {
} lwb_t;
/*
- * Vdev flushing: We use a bit map of size ZIL_VDEV_BMAP bytes.
- * Any vdev numbers beyond that use a linked list of zil_vdev_t structures.
+ * Vdev flushing: during a zil_commit(), we build up an AVL tree of the vdevs
+ * we've touched so we know which ones need a write cache flush at the end.
*/
-
-#define ZIL_VDEV_BMSZ 16 /* 16 * 8 = 128 vdevs */
-typedef struct zil_vdev {
- uint64_t vdev; /* device written */
- list_node_t vdev_seq_node; /* zilog->zl_vdev_list linkage */
-} zil_vdev_t;
+typedef struct zil_vdev_node {
+ uint64_t zv_vdev; /* vdev to be flushed */
+ avl_node_t zv_node; /* AVL tree linkage */
+} zil_vdev_node_t;
/*
* Stable storage intent log management structure. One per dataset.
@@ -91,8 +89,8 @@ struct zilog {
uint64_t zl_cur_used; /* current commit log size used */
uint64_t zl_prev_used; /* previous commit log size used */
list_t zl_lwb_list; /* in-flight log write list */
- list_t zl_vdev_list; /* list of [vdev, seq] pairs */
- uint8_t zl_vdev_bmap[ZIL_VDEV_BMSZ]; /* bitmap of vdevs */
+ kmutex_t zl_vdev_lock; /* protects zl_vdev_tree */
+ avl_tree_t zl_vdev_tree; /* vdevs to flush in zil_commit() */
taskq_t *zl_clean_taskq; /* runs lwb and itx clean tasks */
avl_tree_t zl_dva_tree; /* track DVAs during log parse */
clock_t zl_replay_time; /* lbolt of when replay started */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio.h
index b026ae6..6331567 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio.h
@@ -20,20 +20,17 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _ZIO_H
#define _ZIO_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/zfs_context.h>
#include <sys/spa.h>
#include <sys/txg.h>
#include <sys/avl.h>
-#include <sys/dkio.h>
#include <sys/fs/zfs.h>
#include <sys/zio_impl.h>
@@ -60,10 +57,6 @@ typedef struct zio_block_tail {
(SPA_GBH_NBLKPTRS * sizeof (blkptr_t))) /\
sizeof (uint64_t))
-#define ZIO_GET_IOSIZE(zio) \
- (BP_IS_GANG((zio)->io_bp) ? \
- SPA_GANGBLOCKSIZE : BP_GET_PSIZE((zio)->io_bp))
-
typedef struct zio_gbh {
blkptr_t zg_blkptr[SPA_GBH_NBLKPTRS];
uint64_t zg_filler[SPA_GBH_FILLER];
@@ -107,6 +100,10 @@ enum zio_compress {
#define ZIO_COMPRESS_ON_VALUE ZIO_COMPRESS_LZJB
#define ZIO_COMPRESS_DEFAULT ZIO_COMPRESS_OFF
+#define ZIO_FAILURE_MODE_WAIT 0
+#define ZIO_FAILURE_MODE_CONTINUE 1
+#define ZIO_FAILURE_MODE_PANIC 2
+
#define ZIO_PRIORITY_NOW (zio_priority_table[0])
#define ZIO_PRIORITY_SYNC_READ (zio_priority_table[1])
#define ZIO_PRIORITY_SYNC_WRITE (zio_priority_table[2])
@@ -121,51 +118,70 @@ enum zio_compress {
#define ZIO_FLAG_MUSTSUCCEED 0x00000
#define ZIO_FLAG_CANFAIL 0x00001
-#define ZIO_FLAG_FAILFAST 0x00002
-#define ZIO_FLAG_CONFIG_HELD 0x00004
-#define ZIO_FLAG_CONFIG_GRABBED 0x00008
+#define ZIO_FLAG_SPECULATIVE 0x00002
+#define ZIO_FLAG_CONFIG_WRITER 0x00004
+#define ZIO_FLAG_DONT_RETRY 0x00008
#define ZIO_FLAG_DONT_CACHE 0x00010
#define ZIO_FLAG_DONT_QUEUE 0x00020
-#define ZIO_FLAG_DONT_PROPAGATE 0x00040
-#define ZIO_FLAG_DONT_RETRY 0x00080
-
-#define ZIO_FLAG_PHYSICAL 0x00100
-#define ZIO_FLAG_IO_BYPASS 0x00200
-#define ZIO_FLAG_IO_REPAIR 0x00400
-#define ZIO_FLAG_SPECULATIVE 0x00800
+#define ZIO_FLAG_DONT_AGGREGATE 0x00040
+#define ZIO_FLAG_DONT_PROPAGATE 0x00080
-#define ZIO_FLAG_RESILVER 0x01000
-#define ZIO_FLAG_SCRUB 0x02000
-#define ZIO_FLAG_SCRUB_THREAD 0x04000
-#define ZIO_FLAG_SUBBLOCK 0x08000
+#define ZIO_FLAG_IO_BYPASS 0x00100
+#define ZIO_FLAG_IO_REPAIR 0x00200
+#define ZIO_FLAG_IO_RETRY 0x00400
+#define ZIO_FLAG_IO_REWRITE 0x00800
-#define ZIO_FLAG_NOBOOKMARK 0x10000
-#define ZIO_FLAG_USER 0x20000
+#define ZIO_FLAG_PROBE 0x01000
+#define ZIO_FLAG_RESILVER 0x02000
+#define ZIO_FLAG_SCRUB 0x04000
+#define ZIO_FLAG_SCRUB_THREAD 0x08000
-#define ZIO_FLAG_METADATA 0x40000
+#define ZIO_FLAG_GANG_CHILD 0x10000
#define ZIO_FLAG_GANG_INHERIT \
(ZIO_FLAG_CANFAIL | \
- ZIO_FLAG_FAILFAST | \
- ZIO_FLAG_CONFIG_HELD | \
- ZIO_FLAG_DONT_RETRY | \
- ZIO_FLAG_IO_REPAIR | \
ZIO_FLAG_SPECULATIVE | \
+ ZIO_FLAG_CONFIG_WRITER | \
+ ZIO_FLAG_DONT_RETRY | \
+ ZIO_FLAG_DONT_CACHE | \
+ ZIO_FLAG_DONT_AGGREGATE | \
ZIO_FLAG_RESILVER | \
ZIO_FLAG_SCRUB | \
ZIO_FLAG_SCRUB_THREAD)
#define ZIO_FLAG_VDEV_INHERIT \
(ZIO_FLAG_GANG_INHERIT | \
- ZIO_FLAG_DONT_CACHE | \
- ZIO_FLAG_PHYSICAL)
+ ZIO_FLAG_IO_REPAIR | \
+ ZIO_FLAG_IO_RETRY | \
+ ZIO_FLAG_PROBE)
+
+#define ZIO_PIPELINE_CONTINUE 0x100
+#define ZIO_PIPELINE_STOP 0x101
+
+#define ZIO_GANG_CHILD_FLAGS(zio) \
+ (((zio)->io_flags & ZIO_FLAG_GANG_INHERIT) | \
+ ZIO_FLAG_GANG_CHILD | ZIO_FLAG_CANFAIL)
+
+enum zio_child {
+ ZIO_CHILD_VDEV = 0,
+ ZIO_CHILD_GANG,
+ ZIO_CHILD_LOGICAL,
+ ZIO_CHILD_TYPES
+};
+
+enum zio_wait_type {
+ ZIO_WAIT_READY = 0,
+ ZIO_WAIT_DONE,
+ ZIO_WAIT_TYPES
+};
/*
- * We'll take the EILSEQ (Illegal byte sequence) errno
- * to indicate checksum errors.
+ * We'll take the EILSEQ and ENOMSG to indicate checksum errors and
+ * fragmentation.
*/
#define ECKSUM EILSEQ
+#define EFRAGS ENOMSG
typedef struct zio zio_t;
typedef void zio_done_func_t(zio_t *zio);
@@ -200,23 +216,64 @@ typedef struct zbookmark {
uint64_t zb_blkid;
} zbookmark_t;
+typedef struct zio_prop {
+ enum zio_checksum zp_checksum;
+ enum zio_compress zp_compress;
+ dmu_object_type_t zp_type;
+ uint8_t zp_level;
+ uint8_t zp_ndvas;
+} zio_prop_t;
+
+typedef struct zio_gang_node {
+ zio_gbh_phys_t *gn_gbh;
+ struct zio_gang_node *gn_child[SPA_GBH_NBLKPTRS];
+} zio_gang_node_t;
+
+typedef zio_t *zio_gang_issue_func_t(zio_t *zio, blkptr_t *bp,
+ zio_gang_node_t *gn, void *data);
+
+typedef void zio_transform_func_t(zio_t *zio, void *data, uint64_t size);
+
+typedef struct zio_transform {
+ void *zt_orig_data;
+ uint64_t zt_orig_size;
+ uint64_t zt_bufsize;
+ zio_transform_func_t *zt_transform;
+ struct zio_transform *zt_next;
+} zio_transform_t;
+
+typedef int zio_pipe_stage_t(zio_t *zio);
+
+/*
+ * The io_reexecute flags are distinct from io_flags because the child must
+ * be able to propagate them to the parent. The normal io_flags are local
+ * to the zio, not protected by any lock, and not modifiable by children;
+ * the reexecute flags are protected by io_lock, modifiable by children,
+ * and always propagated -- even when ZIO_FLAG_DONT_PROPAGATE is set.
+ */
+#define ZIO_REEXECUTE_NOW 0x01
+#define ZIO_REEXECUTE_SUSPEND 0x02
+
struct zio {
/* Core information about this I/O */
- zio_t *io_parent;
- zio_t *io_root;
- spa_t *io_spa;
zbookmark_t io_bookmark;
- enum zio_checksum io_checksum;
- enum zio_compress io_compress;
- int io_ndvas;
+ zio_prop_t io_prop;
+ zio_type_t io_type;
+ enum zio_child io_child_type;
+ int io_cmd;
+ uint8_t io_priority;
+ uint8_t io_reexecute;
+ uint8_t io_async_root;
uint64_t io_txg;
+ spa_t *io_spa;
blkptr_t *io_bp;
blkptr_t io_bp_copy;
+ zio_t *io_parent;
zio_t *io_child;
zio_t *io_sibling_prev;
zio_t *io_sibling_next;
- zio_transform_t *io_transform_stack;
zio_t *io_logical;
+ zio_transform_t *io_transform_stack;
/* Callback info */
zio_done_func_t *io_ready;
@@ -231,9 +288,9 @@ struct zio {
/* Stuff for the vdev stack */
vdev_t *io_vd;
void *io_vsd;
+ zio_done_func_t *io_vsd_free;
uint64_t io_offset;
uint64_t io_deadline;
- uint64_t io_timestamp;
avl_node_t io_offset_node;
avl_node_t io_deadline_node;
avl_tree_t *io_vdev_tree;
@@ -242,19 +299,17 @@ struct zio {
/* Internal pipeline state */
int io_flags;
- enum zio_type io_type;
- enum zio_stage io_stage;
- uint8_t io_stalled;
- uint8_t io_priority;
- struct dk_callback io_dk_callback;
- int io_cmd;
- int io_retries;
- int io_error;
- uint32_t io_numerrors;
+ zio_stage_t io_stage;
uint32_t io_pipeline;
- uint32_t io_async_stages;
- uint64_t io_children_notready;
- uint64_t io_children_notdone;
+ int io_orig_flags;
+ zio_stage_t io_orig_stage;
+ uint32_t io_orig_pipeline;
+ int io_error;
+ int io_child_error[ZIO_CHILD_TYPES];
+ uint64_t io_children[ZIO_CHILD_TYPES][ZIO_WAIT_TYPES];
+ uint64_t *io_stall;
+ zio_gang_node_t *io_gang_tree;
+ void *io_executor;
void *io_waiter;
kmutex_t io_lock;
kcondvar_t io_cv;
@@ -269,76 +324,76 @@ extern zio_t *zio_null(zio_t *pio, spa_t *spa,
extern zio_t *zio_root(spa_t *spa,
zio_done_func_t *done, void *private, int flags);
-extern zio_t *zio_read(zio_t *pio, spa_t *spa, blkptr_t *bp, void *data,
+extern zio_t *zio_read(zio_t *pio, spa_t *spa, const blkptr_t *bp, void *data,
uint64_t size, zio_done_func_t *done, void *private,
- int priority, int flags, zbookmark_t *zb);
+ int priority, int flags, const zbookmark_t *zb);
-extern zio_t *zio_write(zio_t *pio, spa_t *spa, int checksum, int compress,
- int ncopies, uint64_t txg, blkptr_t *bp, void *data, uint64_t size,
- zio_done_func_t *ready, zio_done_func_t *done, void *private, int priority,
- int flags, zbookmark_t *zb);
+extern zio_t *zio_write(zio_t *pio, spa_t *spa, uint64_t txg, blkptr_t *bp,
+ void *data, uint64_t size, zio_prop_t *zp,
+ zio_done_func_t *ready, zio_done_func_t *done, void *private,
+ int priority, int flags, const zbookmark_t *zb);
-extern zio_t *zio_rewrite(zio_t *pio, spa_t *spa, int checksum,
- uint64_t txg, blkptr_t *bp, void *data, uint64_t size,
- zio_done_func_t *done, void *private, int priority, int flags,
- zbookmark_t *zb);
+extern zio_t *zio_rewrite(zio_t *pio, spa_t *spa, uint64_t txg, blkptr_t *bp,
+ void *data, uint64_t size, zio_done_func_t *done, void *private,
+ int priority, int flags, zbookmark_t *zb);
extern zio_t *zio_free(zio_t *pio, spa_t *spa, uint64_t txg, blkptr_t *bp,
- zio_done_func_t *done, void *private);
+ zio_done_func_t *done, void *private, int flags);
extern zio_t *zio_claim(zio_t *pio, spa_t *spa, uint64_t txg, blkptr_t *bp,
- zio_done_func_t *done, void *private);
+ zio_done_func_t *done, void *private, int flags);
extern zio_t *zio_ioctl(zio_t *pio, spa_t *spa, vdev_t *vd, int cmd,
zio_done_func_t *done, void *private, int priority, int flags);
extern zio_t *zio_read_phys(zio_t *pio, vdev_t *vd, uint64_t offset,
uint64_t size, void *data, int checksum,
- zio_done_func_t *done, void *private, int priority, int flags);
+ zio_done_func_t *done, void *private, int priority, int flags,
+ boolean_t labels);
extern zio_t *zio_write_phys(zio_t *pio, vdev_t *vd, uint64_t offset,
uint64_t size, void *data, int checksum,
- zio_done_func_t *done, void *private, int priority, int flags);
+ zio_done_func_t *done, void *private, int priority, int flags,
+ boolean_t labels);
extern int zio_alloc_blk(spa_t *spa, uint64_t size, blkptr_t *new_bp,
blkptr_t *old_bp, uint64_t txg);
extern void zio_free_blk(spa_t *spa, blkptr_t *bp, uint64_t txg);
+extern void zio_flush(zio_t *zio, vdev_t *vd);
extern int zio_wait(zio_t *zio);
extern void zio_nowait(zio_t *zio);
+extern void zio_execute(zio_t *zio);
+extern void zio_interrupt(zio_t *zio);
extern void *zio_buf_alloc(size_t size);
extern void zio_buf_free(void *buf, size_t size);
extern void *zio_data_buf_alloc(size_t size);
extern void zio_data_buf_free(void *buf, size_t size);
-/*
- * Move an I/O to the next stage of the pipeline and execute that stage.
- * There's no locking on io_stage because there's no legitimate way for
- * multiple threads to be attempting to process the same I/O.
- */
-extern void zio_next_stage(zio_t *zio);
-extern void zio_next_stage_async(zio_t *zio);
-extern void zio_wait_children_done(zio_t *zio);
+extern void zio_resubmit_stage_async(void *);
-/*
- * Delegate I/O to a child vdev.
- */
extern zio_t *zio_vdev_child_io(zio_t *zio, blkptr_t *bp, vdev_t *vd,
uint64_t offset, void *data, uint64_t size, int type, int priority,
int flags, zio_done_func_t *done, void *private);
+extern zio_t *zio_vdev_delegated_io(vdev_t *vd, uint64_t offset,
+ void *data, uint64_t size, int type, int priority,
+ int flags, zio_done_func_t *done, void *private);
+
extern void zio_vdev_io_bypass(zio_t *zio);
extern void zio_vdev_io_reissue(zio_t *zio);
extern void zio_vdev_io_redone(zio_t *zio);
extern void zio_checksum_verified(zio_t *zio);
-extern void zio_set_gang_verifier(zio_t *zio, zio_cksum_t *zcp);
+extern int zio_worst_error(int e1, int e2);
extern uint8_t zio_checksum_select(uint8_t child, uint8_t parent);
extern uint8_t zio_compress_select(uint8_t child, uint8_t parent);
-boolean_t zio_should_retry(zio_t *zio);
+extern void zio_suspend(spa_t *spa, zio_t *zio);
+extern void zio_resume(spa_t *spa);
+extern void zio_resume_wait(spa_t *spa);
/*
* Initial setup and teardown.
@@ -358,6 +413,7 @@ extern int zio_inject_list_next(int *id, char *name, size_t buflen,
extern int zio_clear_fault(int id);
extern int zio_handle_fault_injection(zio_t *zio, int error);
extern int zio_handle_device_injection(vdev_t *vd, int error);
+extern int zio_handle_label_injection(zio_t *zio, int error);
#ifdef __cplusplus
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio_checksum.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio_checksum.h
index bb7bd41..da40739 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio_checksum.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio_checksum.h
@@ -19,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_ZIO_CHECKSUM_H
#define _SYS_ZIO_CHECKSUM_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/zio.h>
#ifdef __cplusplus
@@ -64,7 +62,7 @@ extern zio_checksum_t fletcher_4_incremental_byteswap;
extern zio_checksum_t zio_checksum_SHA256;
-extern void zio_checksum(uint_t checksum, zio_cksum_t *zcp,
+extern void zio_checksum_compute(zio_t *zio, enum zio_checksum checksum,
void *data, uint64_t size);
extern int zio_checksum_error(zio_t *zio);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio_impl.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio_impl.h
index d2ddbc3..e7503b7 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio_impl.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio_impl.h
@@ -19,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _ZIO_IMPL_H
#define _ZIO_IMPL_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/zfs_context.h>
#include <sys/zio.h>
@@ -38,162 +36,102 @@ extern "C" {
/*
* I/O Groups: pipeline stage definitions.
*/
-
typedef enum zio_stage {
ZIO_STAGE_OPEN = 0, /* RWFCI */
- ZIO_STAGE_WAIT_CHILDREN_READY, /* RWFCI */
- ZIO_STAGE_WRITE_COMPRESS, /* -W--- */
- ZIO_STAGE_CHECKSUM_GENERATE, /* -W--- */
+ ZIO_STAGE_ISSUE_ASYNC, /* -W--- */
- ZIO_STAGE_GANG_PIPELINE, /* -WFC- */
+ ZIO_STAGE_READ_BP_INIT, /* R---- */
+ ZIO_STAGE_WRITE_BP_INIT, /* -W--- */
+
+ ZIO_STAGE_CHECKSUM_GENERATE, /* -W--- */
- ZIO_STAGE_GET_GANG_HEADER, /* -WFC- */
- ZIO_STAGE_REWRITE_GANG_MEMBERS, /* -W--- */
- ZIO_STAGE_FREE_GANG_MEMBERS, /* --F-- */
- ZIO_STAGE_CLAIM_GANG_MEMBERS, /* ---C- */
+ ZIO_STAGE_GANG_ASSEMBLE, /* RWFC- */
+ ZIO_STAGE_GANG_ISSUE, /* RWFC- */
ZIO_STAGE_DVA_ALLOCATE, /* -W--- */
ZIO_STAGE_DVA_FREE, /* --F-- */
ZIO_STAGE_DVA_CLAIM, /* ---C- */
- ZIO_STAGE_GANG_CHECKSUM_GENERATE, /* -W--- */
-
ZIO_STAGE_READY, /* RWFCI */
ZIO_STAGE_VDEV_IO_START, /* RW--I */
ZIO_STAGE_VDEV_IO_DONE, /* RW--I */
ZIO_STAGE_VDEV_IO_ASSESS, /* RW--I */
- ZIO_STAGE_WAIT_CHILDREN_DONE, /* RWFCI */
-
ZIO_STAGE_CHECKSUM_VERIFY, /* R---- */
- ZIO_STAGE_READ_GANG_MEMBERS, /* R---- */
- ZIO_STAGE_READ_DECOMPRESS, /* R---- */
- ZIO_STAGE_DONE /* RWFCI */
+ ZIO_STAGE_DONE, /* RWFCI */
+ ZIO_STAGES
} zio_stage_t;
-/*
- * The stages for which there's some performance value in going async.
- * When compression is enabled, ZIO_STAGE_WRITE_COMPRESS is ORed in as well.
- */
-#define ZIO_ASYNC_PIPELINE_STAGES \
- ((1U << ZIO_STAGE_CHECKSUM_GENERATE) | \
- (1U << ZIO_STAGE_VDEV_IO_DONE) | \
- (1U << ZIO_STAGE_CHECKSUM_VERIFY) | \
- (1U << ZIO_STAGE_READ_DECOMPRESS))
+#define ZIO_INTERLOCK_STAGES \
+ ((1U << ZIO_STAGE_READY) | \
+ (1U << ZIO_STAGE_DONE))
-#define ZIO_VDEV_IO_PIPELINE \
+#define ZIO_INTERLOCK_PIPELINE \
+ ZIO_INTERLOCK_STAGES
+
+#define ZIO_VDEV_IO_STAGES \
((1U << ZIO_STAGE_VDEV_IO_START) | \
(1U << ZIO_STAGE_VDEV_IO_DONE) | \
(1U << ZIO_STAGE_VDEV_IO_ASSESS))
-#define ZIO_READ_PHYS_PIPELINE \
- ((1U << ZIO_STAGE_OPEN) | \
- (1U << ZIO_STAGE_WAIT_CHILDREN_READY) | \
- (1U << ZIO_STAGE_READY) | \
- ZIO_VDEV_IO_PIPELINE | \
- (1U << ZIO_STAGE_WAIT_CHILDREN_DONE) | \
- (1U << ZIO_STAGE_CHECKSUM_VERIFY) | \
+#define ZIO_VDEV_CHILD_PIPELINE \
+ (ZIO_VDEV_IO_STAGES | \
(1U << ZIO_STAGE_DONE))
-#define ZIO_READ_PIPELINE \
- ZIO_READ_PHYS_PIPELINE
+#define ZIO_READ_COMMON_STAGES \
+ (ZIO_INTERLOCK_STAGES | \
+ ZIO_VDEV_IO_STAGES | \
+ (1U << ZIO_STAGE_CHECKSUM_VERIFY))
-#define ZIO_WRITE_PHYS_PIPELINE \
- ((1U << ZIO_STAGE_OPEN) | \
- (1U << ZIO_STAGE_WAIT_CHILDREN_READY) | \
- (1U << ZIO_STAGE_CHECKSUM_GENERATE) | \
- (1U << ZIO_STAGE_READY) | \
- ZIO_VDEV_IO_PIPELINE | \
- (1U << ZIO_STAGE_WAIT_CHILDREN_DONE) | \
- (1U << ZIO_STAGE_DONE))
+#define ZIO_READ_PHYS_PIPELINE \
+ ZIO_READ_COMMON_STAGES
-#define ZIO_WRITE_COMMON_PIPELINE \
- ZIO_WRITE_PHYS_PIPELINE
+#define ZIO_READ_PIPELINE \
+ (ZIO_READ_COMMON_STAGES | \
+ (1U << ZIO_STAGE_READ_BP_INIT))
-#define ZIO_WRITE_PIPELINE \
- ((1U << ZIO_STAGE_WRITE_COMPRESS) | \
- ZIO_WRITE_COMMON_PIPELINE)
+#define ZIO_WRITE_COMMON_STAGES \
+ (ZIO_INTERLOCK_STAGES | \
+ ZIO_VDEV_IO_STAGES | \
+ (1U << ZIO_STAGE_ISSUE_ASYNC) | \
+ (1U << ZIO_STAGE_CHECKSUM_GENERATE))
-#define ZIO_GANG_STAGES \
- ((1U << ZIO_STAGE_GET_GANG_HEADER) | \
- (1U << ZIO_STAGE_REWRITE_GANG_MEMBERS) | \
- (1U << ZIO_STAGE_FREE_GANG_MEMBERS) | \
- (1U << ZIO_STAGE_CLAIM_GANG_MEMBERS) | \
- (1U << ZIO_STAGE_GANG_CHECKSUM_GENERATE) | \
- (1U << ZIO_STAGE_READ_GANG_MEMBERS))
+#define ZIO_WRITE_PHYS_PIPELINE \
+ ZIO_WRITE_COMMON_STAGES
#define ZIO_REWRITE_PIPELINE \
- ((1U << ZIO_STAGE_GANG_PIPELINE) | \
- (1U << ZIO_STAGE_GET_GANG_HEADER) | \
- (1U << ZIO_STAGE_REWRITE_GANG_MEMBERS) | \
- (1U << ZIO_STAGE_GANG_CHECKSUM_GENERATE) | \
- ZIO_WRITE_COMMON_PIPELINE)
+ (ZIO_WRITE_COMMON_STAGES | \
+ (1U << ZIO_STAGE_WRITE_BP_INIT))
-#define ZIO_WRITE_ALLOCATE_PIPELINE \
- ((1U << ZIO_STAGE_DVA_ALLOCATE) | \
- ZIO_WRITE_COMMON_PIPELINE)
+#define ZIO_WRITE_PIPELINE \
+ (ZIO_WRITE_COMMON_STAGES | \
+ (1U << ZIO_STAGE_WRITE_BP_INIT) | \
+ (1U << ZIO_STAGE_DVA_ALLOCATE))
-#define ZIO_GANG_FREE_STAGES \
- ((1U << ZIO_STAGE_GET_GANG_HEADER) | \
- (1U << ZIO_STAGE_FREE_GANG_MEMBERS))
+#define ZIO_GANG_STAGES \
+ ((1U << ZIO_STAGE_GANG_ASSEMBLE) | \
+ (1U << ZIO_STAGE_GANG_ISSUE))
#define ZIO_FREE_PIPELINE \
- ((1U << ZIO_STAGE_OPEN) | \
- (1U << ZIO_STAGE_WAIT_CHILDREN_READY) | \
- (1U << ZIO_STAGE_GANG_PIPELINE) | \
- (1U << ZIO_STAGE_GET_GANG_HEADER) | \
- (1U << ZIO_STAGE_FREE_GANG_MEMBERS) | \
- (1U << ZIO_STAGE_DVA_FREE) | \
- (1U << ZIO_STAGE_READY) | \
- (1U << ZIO_STAGE_WAIT_CHILDREN_DONE) | \
- (1U << ZIO_STAGE_DONE))
+ (ZIO_INTERLOCK_STAGES | \
+ (1U << ZIO_STAGE_DVA_FREE))
#define ZIO_CLAIM_PIPELINE \
- ((1U << ZIO_STAGE_OPEN) | \
- (1U << ZIO_STAGE_WAIT_CHILDREN_READY) | \
- (1U << ZIO_STAGE_GANG_PIPELINE) | \
- (1U << ZIO_STAGE_GET_GANG_HEADER) | \
- (1U << ZIO_STAGE_CLAIM_GANG_MEMBERS) | \
- (1U << ZIO_STAGE_DVA_CLAIM) | \
- (1U << ZIO_STAGE_READY) | \
- (1U << ZIO_STAGE_WAIT_CHILDREN_DONE) | \
- (1U << ZIO_STAGE_DONE))
+ (ZIO_INTERLOCK_STAGES | \
+ (1U << ZIO_STAGE_DVA_CLAIM))
#define ZIO_IOCTL_PIPELINE \
- ((1U << ZIO_STAGE_OPEN) | \
- (1U << ZIO_STAGE_WAIT_CHILDREN_READY) | \
- (1U << ZIO_STAGE_READY) | \
- ZIO_VDEV_IO_PIPELINE | \
- (1U << ZIO_STAGE_WAIT_CHILDREN_DONE) | \
- (1U << ZIO_STAGE_DONE))
-
-#define ZIO_WAIT_FOR_CHILDREN_PIPELINE \
- ((1U << ZIO_STAGE_WAIT_CHILDREN_READY) | \
- (1U << ZIO_STAGE_READY) | \
- (1U << ZIO_STAGE_WAIT_CHILDREN_DONE) | \
- (1U << ZIO_STAGE_DONE))
-
-#define ZIO_WAIT_FOR_CHILDREN_DONE_PIPELINE \
- ((1U << ZIO_STAGE_WAIT_CHILDREN_DONE) | \
- (1U << ZIO_STAGE_DONE))
+ (ZIO_INTERLOCK_STAGES | \
+ (1U << ZIO_STAGE_VDEV_IO_START) | \
+ (1U << ZIO_STAGE_VDEV_IO_ASSESS))
-#define ZIO_VDEV_CHILD_PIPELINE \
- (ZIO_WAIT_FOR_CHILDREN_DONE_PIPELINE | \
- ZIO_VDEV_IO_PIPELINE)
-
-#define ZIO_ERROR_PIPELINE_MASK \
- ZIO_WAIT_FOR_CHILDREN_PIPELINE
-
-typedef struct zio_transform zio_transform_t;
-struct zio_transform {
- void *zt_data;
- uint64_t zt_size;
- uint64_t zt_bufsize;
- zio_transform_t *zt_next;
-};
+#define ZIO_CONFIG_LOCK_BLOCKING_STAGES \
+ ((1U << ZIO_STAGE_VDEV_IO_START) | \
+ (1U << ZIO_STAGE_DVA_ALLOCATE) | \
+ (1U << ZIO_STAGE_DVA_CLAIM))
extern void zio_inject_init(void);
extern void zio_inject_fini(void);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h
index df85824..2a6452a 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -35,17 +35,21 @@
extern "C" {
#endif
+#define ZVOL_OBJ 1ULL
+#define ZVOL_ZAP_OBJ 2ULL
+
#ifdef _KERNEL
extern int zvol_check_volsize(uint64_t volsize, uint64_t blocksize);
extern int zvol_check_volblocksize(uint64_t volblocksize);
extern int zvol_get_stats(objset_t *os, nvlist_t *nv);
-extern void zvol_create_cb(objset_t *os, void *arg, dmu_tx_t *tx);
-extern int zvol_create_minor(const char *, dev_t);
+extern void zvol_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx);
+extern int zvol_create_minor(const char *, major_t);
extern int zvol_remove_minor(const char *);
-extern int zvol_set_volsize(const char *, dev_t, uint64_t);
+extern int zvol_set_volsize(const char *, major_t, uint64_t);
extern int zvol_set_volblocksize(const char *, uint64_t);
extern int zvol_open(dev_t *devp, int flag, int otyp, cred_t *cr);
+extern int zvol_dump(dev_t dev, caddr_t addr, daddr_t offset, int nblocks);
extern int zvol_close(dev_t dev, int flag, int otyp, cred_t *cr);
#ifndef __FreeBSD__
extern int zvol_strategy(buf_t *bp);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/txg.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/txg.c
index 844beb6..040e4d7 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/txg.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/txg.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/zfs_context.h>
#include <sys/txg_impl.h>
#include <sys/dmu_impl.h>
@@ -37,9 +35,18 @@
static void txg_sync_thread(void *arg);
static void txg_quiesce_thread(void *arg);
-static void txg_timelimit_thread(void *arg);
-int txg_time = 5; /* max 5 seconds worth of delta per txg */
+int zfs_txg_timeout = 30; /* max seconds worth of delta per txg */
+extern int zfs_txg_synctime;
+
+SYSCTL_DECL(_vfs_zfs);
+SYSCTL_NODE(_vfs_zfs, OID_AUTO, txg, CTLFLAG_RW, 0, "ZFS TXG");
+TUNABLE_INT("vfs.zfs.txg.timeout", &zfs_txg_timeout);
+SYSCTL_INT(_vfs_zfs_txg, OID_AUTO, timeout, CTLFLAG_RDTUN, &zfs_txg_timeout, 0,
+ "Maximum seconds worth of delta per txg");
+TUNABLE_INT("vfs.zfs.txg.synctime", &zfs_txg_synctime);
+SYSCTL_INT(_vfs_zfs_txg, OID_AUTO, synctime, CTLFLAG_RDTUN, &zfs_txg_synctime,
+ 0, "Target seconds to sync a txg");
/*
* Prepare the txg subsystem.
@@ -48,14 +55,19 @@ void
txg_init(dsl_pool_t *dp, uint64_t txg)
{
tx_state_t *tx = &dp->dp_tx;
- int c, i;
+ int c;
bzero(tx, sizeof (tx_state_t));
tx->tx_cpu = kmem_zalloc(max_ncpus * sizeof (tx_cpu_t), KM_SLEEP);
+
for (c = 0; c < max_ncpus; c++) {
+ int i;
+
mutex_init(&tx->tx_cpu[c].tc_lock, NULL, MUTEX_DEFAULT, NULL);
- for (i = 0; i < TXG_SIZE; i++)
- cv_init(&tx->tx_cpu[c].tc_cv[i], NULL, CV_DEFAULT, NULL);
+ for (i = 0; i < TXG_SIZE; i++) {
+ cv_init(&tx->tx_cpu[c].tc_cv[i], NULL, CV_DEFAULT,
+ NULL);
+ }
}
rw_init(&tx->tx_suspend, NULL, RW_DEFAULT, NULL);
@@ -64,7 +76,6 @@ txg_init(dsl_pool_t *dp, uint64_t txg)
cv_init(&tx->tx_sync_done_cv, NULL, CV_DEFAULT, NULL);
cv_init(&tx->tx_quiesce_more_cv, NULL, CV_DEFAULT, NULL);
cv_init(&tx->tx_quiesce_done_cv, NULL, CV_DEFAULT, NULL);
- cv_init(&tx->tx_timeout_exit_cv, NULL, CV_DEFAULT, NULL);
cv_init(&tx->tx_exit_cv, NULL, CV_DEFAULT, NULL);
tx->tx_open_txg = txg;
@@ -77,12 +88,11 @@ void
txg_fini(dsl_pool_t *dp)
{
tx_state_t *tx = &dp->dp_tx;
- int c, i;
+ int c;
ASSERT(tx->tx_threads == 0);
cv_destroy(&tx->tx_exit_cv);
- cv_destroy(&tx->tx_timeout_exit_cv);
cv_destroy(&tx->tx_quiesce_done_cv);
cv_destroy(&tx->tx_quiesce_more_cv);
cv_destroy(&tx->tx_sync_done_cv);
@@ -91,9 +101,11 @@ txg_fini(dsl_pool_t *dp)
mutex_destroy(&tx->tx_sync_lock);
for (c = 0; c < max_ncpus; c++) {
+ int i;
+
+ mutex_destroy(&tx->tx_cpu[c].tc_lock);
for (i = 0; i < TXG_SIZE; i++)
cv_destroy(&tx->tx_cpu[c].tc_cv[i]);
- mutex_destroy(&tx->tx_cpu[c].tc_lock);
}
kmem_free(tx->tx_cpu, max_ncpus * sizeof (tx_cpu_t));
@@ -115,15 +127,17 @@ txg_sync_start(dsl_pool_t *dp)
ASSERT(tx->tx_threads == 0);
- tx->tx_threads = 3;
+ tx->tx_threads = 2;
tx->tx_quiesce_thread = thread_create(NULL, 0, txg_quiesce_thread,
dp, 0, &p0, TS_RUN, minclsyspri);
- tx->tx_sync_thread = thread_create(NULL, 0, txg_sync_thread,
- dp, 0, &p0, TS_RUN, minclsyspri);
-
- tx->tx_timelimit_thread = thread_create(NULL, 0, txg_timelimit_thread,
+ /*
+ * The sync thread can need a larger-than-default stack size on
+ * 32-bit x86. This is due in part to nested pools and
+ * scrub_visitbp() recursion.
+ */
+ tx->tx_sync_thread = thread_create(NULL, 12<<10, txg_sync_thread,
dp, 0, &p0, TS_RUN, minclsyspri);
mutex_exit(&tx->tx_sync_lock);
@@ -148,12 +162,12 @@ txg_thread_exit(tx_state_t *tx, callb_cpr_t *cpr, kthread_t **tpp)
}
static void
-txg_thread_wait(tx_state_t *tx, callb_cpr_t *cpr, kcondvar_t *cv, int secmax)
+txg_thread_wait(tx_state_t *tx, callb_cpr_t *cpr, kcondvar_t *cv, uint64_t time)
{
CALLB_CPR_SAFE_BEGIN(cpr);
- if (secmax)
- (void) cv_timedwait(cv, &tx->tx_sync_lock, secmax * hz);
+ if (time)
+ (void) cv_timedwait(cv, &tx->tx_sync_lock, time);
else
cv_wait(cv, &tx->tx_sync_lock);
@@ -172,22 +186,21 @@ txg_sync_stop(dsl_pool_t *dp)
/*
* Finish off any work in progress.
*/
- ASSERT(tx->tx_threads == 3);
+ ASSERT(tx->tx_threads == 2);
txg_wait_synced(dp, 0);
/*
- * Wake all 3 sync threads (one per state) and wait for them to die.
+ * Wake all sync threads and wait for them to die.
*/
mutex_enter(&tx->tx_sync_lock);
- ASSERT(tx->tx_threads == 3);
+ ASSERT(tx->tx_threads == 2);
tx->tx_exiting = 1;
cv_broadcast(&tx->tx_quiesce_more_cv);
cv_broadcast(&tx->tx_quiesce_done_cv);
cv_broadcast(&tx->tx_sync_more_cv);
- cv_broadcast(&tx->tx_timeout_exit_cv);
while (tx->tx_threads != 0)
cv_wait(&tx->tx_exit_cv, &tx->tx_sync_lock);
@@ -279,22 +292,29 @@ txg_sync_thread(void *arg)
dsl_pool_t *dp = arg;
tx_state_t *tx = &dp->dp_tx;
callb_cpr_t cpr;
+ uint64_t start, delta;
txg_thread_enter(tx, &cpr);
+ start = delta = 0;
for (;;) {
+ uint64_t timer, timeout = zfs_txg_timeout * hz;
uint64_t txg;
/*
* We sync when there's someone waiting on us, or the
- * quiesce thread has handed off a txg to us.
+ * quiesce thread has handed off a txg to us, or we have
+ * reached our timeout.
*/
- while (!tx->tx_exiting &&
+ timer = (delta >= timeout ? 0 : timeout - delta);
+ while (!tx->tx_exiting && timer > 0 &&
tx->tx_synced_txg >= tx->tx_sync_txg_waiting &&
tx->tx_quiesced_txg == 0) {
dprintf("waiting; tx_synced=%llu waiting=%llu dp=%p\n",
tx->tx_synced_txg, tx->tx_sync_txg_waiting, dp);
- txg_thread_wait(tx, &cpr, &tx->tx_sync_more_cv, 0);
+ txg_thread_wait(tx, &cpr, &tx->tx_sync_more_cv, timer);
+ delta = LBOLT - start;
+ timer = (delta > timeout ? 0 : timeout - delta);
}
/*
@@ -325,10 +345,13 @@ txg_sync_thread(void *arg)
rw_exit(&tx->tx_suspend);
dprintf("txg=%llu quiesce_txg=%llu sync_txg=%llu\n",
- txg, tx->tx_quiesce_txg_waiting,
- tx->tx_sync_txg_waiting);
+ txg, tx->tx_quiesce_txg_waiting, tx->tx_sync_txg_waiting);
mutex_exit(&tx->tx_sync_lock);
+
+ start = LBOLT;
spa_sync(dp->dp_spa, txg);
+ delta = LBOLT - start;
+
mutex_enter(&tx->tx_sync_lock);
rw_enter(&tx->tx_suspend, RW_WRITER);
tx->tx_synced_txg = txg;
@@ -383,13 +406,43 @@ txg_quiesce_thread(void *arg)
}
}
+/*
+ * Delay this thread by 'ticks' if we are still in the open transaction
+ * group and there is already a waiting txg quiesing or quiesced. Abort
+ * the delay if this txg stalls or enters the quiesing state.
+ */
+void
+txg_delay(dsl_pool_t *dp, uint64_t txg, int ticks)
+{
+ tx_state_t *tx = &dp->dp_tx;
+ int timeout = LBOLT + ticks;
+
+ /* don't delay if this txg could transition to quiesing immediately */
+ if (tx->tx_open_txg > txg ||
+ tx->tx_syncing_txg == txg-1 || tx->tx_synced_txg == txg-1)
+ return;
+
+ mutex_enter(&tx->tx_sync_lock);
+ if (tx->tx_open_txg > txg || tx->tx_synced_txg == txg-1) {
+ mutex_exit(&tx->tx_sync_lock);
+ return;
+ }
+
+ while (LBOLT < timeout &&
+ tx->tx_syncing_txg < txg-1 && !txg_stalled(dp))
+ (void) cv_timedwait(&tx->tx_quiesce_more_cv, &tx->tx_sync_lock,
+ timeout - LBOLT);
+
+ mutex_exit(&tx->tx_sync_lock);
+}
+
void
txg_wait_synced(dsl_pool_t *dp, uint64_t txg)
{
tx_state_t *tx = &dp->dp_tx;
mutex_enter(&tx->tx_sync_lock);
- ASSERT(tx->tx_threads == 3);
+ ASSERT(tx->tx_threads == 2);
if (txg == 0)
txg = tx->tx_open_txg;
if (tx->tx_sync_txg_waiting < txg)
@@ -412,7 +465,7 @@ txg_wait_open(dsl_pool_t *dp, uint64_t txg)
tx_state_t *tx = &dp->dp_tx;
mutex_enter(&tx->tx_sync_lock);
- ASSERT(tx->tx_threads == 3);
+ ASSERT(tx->tx_threads == 2);
if (txg == 0)
txg = tx->tx_open_txg + 1;
if (tx->tx_quiesce_txg_waiting < txg)
@@ -426,37 +479,20 @@ txg_wait_open(dsl_pool_t *dp, uint64_t txg)
mutex_exit(&tx->tx_sync_lock);
}
-static void
-txg_timelimit_thread(void *arg)
+boolean_t
+txg_stalled(dsl_pool_t *dp)
{
- dsl_pool_t *dp = arg;
tx_state_t *tx = &dp->dp_tx;
- callb_cpr_t cpr;
-
- txg_thread_enter(tx, &cpr);
-
- while (!tx->tx_exiting) {
- uint64_t txg = tx->tx_open_txg + 1;
-
- txg_thread_wait(tx, &cpr, &tx->tx_timeout_exit_cv, txg_time);
-
- if (tx->tx_quiesce_txg_waiting < txg)
- tx->tx_quiesce_txg_waiting = txg;
-
- while (!tx->tx_exiting && tx->tx_open_txg < txg) {
- dprintf("pushing out %llu\n", txg);
- cv_broadcast(&tx->tx_quiesce_more_cv);
- txg_thread_wait(tx, &cpr, &tx->tx_quiesce_done_cv, 0);
- }
- }
- txg_thread_exit(tx, &cpr, &tx->tx_timelimit_thread);
+ return (tx->tx_quiesce_txg_waiting > tx->tx_open_txg);
}
-int
-txg_stalled(dsl_pool_t *dp)
+boolean_t
+txg_sync_waiting(dsl_pool_t *dp)
{
tx_state_t *tx = &dp->dp_tx;
- return (tx->tx_quiesce_txg_waiting > tx->tx_open_txg);
+
+ return (tx->tx_syncing_txg <= tx->tx_sync_txg_waiting ||
+ tx->tx_quiesced_txg != 0);
}
void
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/unique.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/unique.c
index b52e729..fbe7b61 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/unique.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/unique.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -30,8 +30,7 @@
#include <sys/unique.h>
static avl_tree_t unique_avl;
-static kmutex_t unique_mtx; /* Lock never initialized. */
-SX_SYSINIT(unique, &unique_mtx, "unique lock");
+static kmutex_t unique_mtx;
typedef struct unique {
avl_node_t un_link;
@@ -58,12 +57,22 @@ unique_init(void)
{
avl_create(&unique_avl, unique_compare,
sizeof (unique_t), offsetof(unique_t, un_link));
+ mutex_init(&unique_mtx, NULL, MUTEX_DEFAULT, NULL);
+}
+
+void
+unique_fini(void)
+{
+ avl_destroy(&unique_avl);
+ mutex_destroy(&unique_mtx);
}
uint64_t
unique_create(void)
{
- return (unique_insert(0));
+ uint64_t value = unique_insert(0);
+ unique_remove(value);
+ return (value);
}
uint64_t
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev.c
index b966099..7d0602c 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev.c
@@ -20,12 +20,10 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/zfs_context.h>
#include <sys/fm/fs/zfs.h>
#include <sys/spa.h>
@@ -40,6 +38,7 @@
#include <sys/zio.h>
#include <sys/zap.h>
#include <sys/fs/zfs.h>
+#include <sys/arc.h>
SYSCTL_DECL(_vfs_zfs);
SYSCTL_NODE(_vfs_zfs, OID_AUTO, vdev, CTLFLAG_RW, 0, "ZFS VDEV");
@@ -58,14 +57,18 @@ static vdev_ops_t *vdev_ops_table[] = {
&vdev_geom_ops,
#else
&vdev_disk_ops,
- &vdev_file_ops,
#endif
+ &vdev_file_ops,
&vdev_missing_ops,
NULL
};
-/* maximum scrub/resilver I/O queue */
-int zfs_scrub_limit = 70;
+/* maximum scrub/resilver I/O queue per leaf vdev */
+int zfs_scrub_limit = 10;
+
+TUNABLE_INT("vfs.zfs.scrub_limit", &zfs_scrub_limit);
+SYSCTL_INT(_vfs_zfs, OID_AUTO, scrub_limit, CTLFLAG_RDTUN, &zfs_scrub_limit, 0,
+ "Maximum scrub/resilver I/O queue");
/*
* Given a vdev type, return the appropriate ops vector.
@@ -143,8 +146,12 @@ vdev_lookup_top(spa_t *spa, uint64_t vdev)
{
vdev_t *rvd = spa->spa_root_vdev;
- if (vdev < rvd->vdev_children)
+ ASSERT(spa_config_held(spa, SCL_ALL, RW_READER) != 0);
+
+ if (vdev < rvd->vdev_children) {
+ ASSERT(rvd->vdev_child[vdev] != NULL);
return (rvd->vdev_child[vdev]);
+ }
return (NULL);
}
@@ -173,7 +180,7 @@ vdev_add_child(vdev_t *pvd, vdev_t *cvd)
uint64_t id = cvd->vdev_id;
vdev_t **newchild;
- ASSERT(spa_config_held(cvd->vdev_spa, RW_WRITER));
+ ASSERT(spa_config_held(cvd->vdev_spa, SCL_ALL, RW_WRITER) == SCL_ALL);
ASSERT(cvd->vdev_parent == NULL);
cvd->vdev_parent = pvd;
@@ -256,7 +263,7 @@ vdev_compact_children(vdev_t *pvd)
int oldc = pvd->vdev_children;
int newc, c;
- ASSERT(spa_config_held(pvd->vdev_spa, RW_WRITER));
+ ASSERT(spa_config_held(pvd->vdev_spa, SCL_ALL, RW_WRITER) == SCL_ALL);
for (c = newc = 0; c < oldc; c++)
if (pvd->vdev_child[c])
@@ -319,6 +326,7 @@ vdev_alloc_common(spa_t *spa, uint_t id, uint64_t guid, vdev_ops_t *ops)
mutex_init(&vd->vdev_dtl_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&vd->vdev_stat_lock, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&vd->vdev_probe_lock, NULL, MUTEX_DEFAULT, NULL);
space_map_create(&vd->vdev_dtl_map, 0, -1ULL, 0, &vd->vdev_dtl_lock);
space_map_create(&vd->vdev_dtl_scrub, 0, -1ULL, 0, &vd->vdev_dtl_lock);
txg_list_create(&vd->vdev_ms_list,
@@ -326,44 +334,13 @@ vdev_alloc_common(spa_t *spa, uint_t id, uint64_t guid, vdev_ops_t *ops)
txg_list_create(&vd->vdev_dtl_list,
offsetof(struct vdev, vdev_dtl_node));
vd->vdev_stat.vs_timestamp = gethrtime();
+ vdev_queue_init(vd);
+ vdev_cache_init(vd);
return (vd);
}
/*
- * Free a vdev_t that has been removed from service.
- */
-static void
-vdev_free_common(vdev_t *vd)
-{
- spa_t *spa = vd->vdev_spa;
-
- if (vd->vdev_path)
- spa_strfree(vd->vdev_path);
- if (vd->vdev_devid)
- spa_strfree(vd->vdev_devid);
-
- if (vd->vdev_isspare)
- spa_spare_remove(vd);
-
- txg_list_destroy(&vd->vdev_ms_list);
- txg_list_destroy(&vd->vdev_dtl_list);
- mutex_enter(&vd->vdev_dtl_lock);
- space_map_unload(&vd->vdev_dtl_map);
- space_map_destroy(&vd->vdev_dtl_map);
- space_map_vacate(&vd->vdev_dtl_scrub, NULL, NULL);
- space_map_destroy(&vd->vdev_dtl_scrub);
- mutex_exit(&vd->vdev_dtl_lock);
- mutex_destroy(&vd->vdev_dtl_lock);
- mutex_destroy(&vd->vdev_stat_lock);
-
- if (vd == spa->spa_root_vdev)
- spa->spa_root_vdev = NULL;
-
- kmem_free(vd, sizeof (vdev_t));
-}
-
-/*
* Allocate a new vdev. The 'alloctype' is used to control whether we are
* creating a new vdev or loading an existing one - the behavior is slightly
* different for each case.
@@ -374,10 +351,10 @@ vdev_alloc(spa_t *spa, vdev_t **vdp, nvlist_t *nv, vdev_t *parent, uint_t id,
{
vdev_ops_t *ops;
char *type;
- uint64_t guid = 0;
+ uint64_t guid = 0, islog, nparity;
vdev_t *vd;
- ASSERT(spa_config_held(spa, RW_WRITER));
+ ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL);
if (nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) != 0)
return (EINVAL);
@@ -401,6 +378,9 @@ vdev_alloc(spa_t *spa, vdev_t **vdp, nvlist_t *nv, vdev_t *parent, uint_t id,
} else if (alloctype == VDEV_ALLOC_SPARE) {
if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) != 0)
return (EINVAL);
+ } else if (alloctype == VDEV_ALLOC_L2CACHE) {
+ if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) != 0)
+ return (EINVAL);
}
/*
@@ -409,47 +389,61 @@ vdev_alloc(spa_t *spa, vdev_t **vdp, nvlist_t *nv, vdev_t *parent, uint_t id,
if (ops != &vdev_root_ops && spa->spa_root_vdev == NULL)
return (EINVAL);
- vd = vdev_alloc_common(spa, id, guid, ops);
-
- if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &vd->vdev_path) == 0)
- vd->vdev_path = spa_strdup(vd->vdev_path);
- if (nvlist_lookup_string(nv, ZPOOL_CONFIG_DEVID, &vd->vdev_devid) == 0)
- vd->vdev_devid = spa_strdup(vd->vdev_devid);
+ /*
+ * Determine whether we're a log vdev.
+ */
+ islog = 0;
+ (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_IS_LOG, &islog);
+ if (islog && spa_version(spa) < SPA_VERSION_SLOGS)
+ return (ENOTSUP);
/*
- * Set the nparity propery for RAID-Z vdevs.
+ * Set the nparity property for RAID-Z vdevs.
*/
+ nparity = -1ULL;
if (ops == &vdev_raidz_ops) {
if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NPARITY,
- &vd->vdev_nparity) == 0) {
+ &nparity) == 0) {
/*
* Currently, we can only support 2 parity devices.
*/
- if (vd->vdev_nparity > 2)
+ if (nparity == 0 || nparity > 2)
return (EINVAL);
/*
* Older versions can only support 1 parity device.
*/
- if (vd->vdev_nparity == 2 &&
- spa_version(spa) < ZFS_VERSION_RAID6)
+ if (nparity == 2 &&
+ spa_version(spa) < SPA_VERSION_RAID6)
return (ENOTSUP);
-
} else {
/*
* We require the parity to be specified for SPAs that
* support multiple parity levels.
*/
- if (spa_version(spa) >= ZFS_VERSION_RAID6)
+ if (spa_version(spa) >= SPA_VERSION_RAID6)
return (EINVAL);
-
/*
* Otherwise, we default to 1 parity device for RAID-Z.
*/
- vd->vdev_nparity = 1;
+ nparity = 1;
}
} else {
- vd->vdev_nparity = 0;
+ nparity = 0;
}
+ ASSERT(nparity != -1ULL);
+
+ vd = vdev_alloc_common(spa, id, guid, ops);
+
+ vd->vdev_islog = islog;
+ vd->vdev_nparity = nparity;
+
+ if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &vd->vdev_path) == 0)
+ vd->vdev_path = spa_strdup(vd->vdev_path);
+ if (nvlist_lookup_string(nv, ZPOOL_CONFIG_DEVID, &vd->vdev_devid) == 0)
+ vd->vdev_devid = spa_strdup(vd->vdev_devid);
+ if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PHYS_PATH,
+ &vd->vdev_physpath) == 0)
+ vd->vdev_physpath = spa_strdup(vd->vdev_physpath);
/*
* Set the whole_disk property. If it's not specified, leave the value
@@ -463,8 +457,9 @@ vdev_alloc(spa_t *spa, vdev_t **vdp, nvlist_t *nv, vdev_t *parent, uint_t id,
* Look for the 'not present' flag. This will only be set if the device
* was not present at the time of import.
*/
- (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT,
- &vd->vdev_not_present);
+ if (!spa->spa_import_faulted)
+ (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT,
+ &vd->vdev_not_present);
/*
* Get the alignment requirement.
@@ -484,13 +479,32 @@ vdev_alloc(spa_t *spa, vdev_t **vdp, nvlist_t *nv, vdev_t *parent, uint_t id,
}
/*
- * If we're a leaf vdev, try to load the DTL object and offline state.
+ * If we're a leaf vdev, try to load the DTL object and other state.
*/
- if (vd->vdev_ops->vdev_op_leaf && alloctype == VDEV_ALLOC_LOAD) {
- (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_DTL,
- &vd->vdev_dtl.smo_object);
+ if (vd->vdev_ops->vdev_op_leaf &&
+ (alloctype == VDEV_ALLOC_LOAD || alloctype == VDEV_ALLOC_L2CACHE)) {
+ if (alloctype == VDEV_ALLOC_LOAD) {
+ (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_DTL,
+ &vd->vdev_dtl.smo_object);
+ (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_UNSPARE,
+ &vd->vdev_unspare);
+ }
(void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_OFFLINE,
&vd->vdev_offline);
+
+ /*
+ * When importing a pool, we want to ignore the persistent fault
+ * state, as the diagnosis made on another system may not be
+ * valid in the current context.
+ */
+ if (spa->spa_load_state == SPA_LOAD_OPEN) {
+ (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_FAULTED,
+ &vd->vdev_faulted);
+ (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_DEGRADED,
+ &vd->vdev_degraded);
+ (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_REMOVED,
+ &vd->vdev_removed);
+ }
}
/*
@@ -507,6 +521,7 @@ void
vdev_free(vdev_t *vd)
{
int c;
+ spa_t *spa = vd->vdev_spa;
/*
* vdev_free() implies closing the vdev first. This is simpler than
@@ -514,7 +529,7 @@ vdev_free(vdev_t *vd)
*/
vdev_close(vd);
- ASSERT(!list_link_active(&vd->vdev_dirty_node));
+ ASSERT(!list_link_active(&vd->vdev_config_dirty_node));
/*
* Free all children.
@@ -542,7 +557,40 @@ vdev_free(vdev_t *vd)
ASSERT(vd->vdev_parent == NULL);
- vdev_free_common(vd);
+ /*
+ * Clean up vdev structure.
+ */
+ vdev_queue_fini(vd);
+ vdev_cache_fini(vd);
+
+ if (vd->vdev_path)
+ spa_strfree(vd->vdev_path);
+ if (vd->vdev_devid)
+ spa_strfree(vd->vdev_devid);
+ if (vd->vdev_physpath)
+ spa_strfree(vd->vdev_physpath);
+
+ if (vd->vdev_isspare)
+ spa_spare_remove(vd);
+ if (vd->vdev_isl2cache)
+ spa_l2cache_remove(vd);
+
+ txg_list_destroy(&vd->vdev_ms_list);
+ txg_list_destroy(&vd->vdev_dtl_list);
+ mutex_enter(&vd->vdev_dtl_lock);
+ space_map_unload(&vd->vdev_dtl_map);
+ space_map_destroy(&vd->vdev_dtl_map);
+ space_map_vacate(&vd->vdev_dtl_scrub, NULL, NULL);
+ space_map_destroy(&vd->vdev_dtl_scrub);
+ mutex_exit(&vd->vdev_dtl_lock);
+ mutex_destroy(&vd->vdev_dtl_lock);
+ mutex_destroy(&vd->vdev_stat_lock);
+ mutex_destroy(&vd->vdev_probe_lock);
+
+ if (vd == spa->spa_root_vdev)
+ spa->spa_root_vdev = NULL;
+
+ kmem_free(vd, sizeof (vdev_t));
}
/*
@@ -592,16 +640,21 @@ vdev_top_transfer(vdev_t *svd, vdev_t *tvd)
(void) txg_list_add(&spa->spa_vdev_txg_list, tvd, t);
}
- if (list_link_active(&svd->vdev_dirty_node)) {
+ if (list_link_active(&svd->vdev_config_dirty_node)) {
vdev_config_clean(svd);
vdev_config_dirty(tvd);
}
- tvd->vdev_reopen_wanted = svd->vdev_reopen_wanted;
- svd->vdev_reopen_wanted = 0;
+ if (list_link_active(&svd->vdev_state_dirty_node)) {
+ vdev_state_clean(svd);
+ vdev_state_dirty(tvd);
+ }
tvd->vdev_deflate_ratio = svd->vdev_deflate_ratio;
svd->vdev_deflate_ratio = 0;
+
+ tvd->vdev_islog = svd->vdev_islog;
+ svd->vdev_islog = 0;
}
static void
@@ -628,7 +681,7 @@ vdev_add_parent(vdev_t *cvd, vdev_ops_t *ops)
vdev_t *pvd = cvd->vdev_parent;
vdev_t *mvd;
- ASSERT(spa_config_held(spa, RW_WRITER));
+ ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL);
mvd = vdev_alloc_common(spa, cvd->vdev_id, 0, ops);
@@ -657,7 +710,7 @@ vdev_remove_parent(vdev_t *cvd)
vdev_t *mvd = cvd->vdev_parent;
vdev_t *pvd = mvd->vdev_parent;
- ASSERT(spa_config_held(cvd->vdev_spa, RW_WRITER));
+ ASSERT(spa_config_held(cvd->vdev_spa, SCL_ALL, RW_WRITER) == SCL_ALL);
ASSERT(mvd->vdev_children == 1);
ASSERT(mvd->vdev_ops == &vdev_mirror_ops ||
@@ -667,22 +720,16 @@ vdev_remove_parent(vdev_t *cvd)
vdev_remove_child(mvd, cvd);
vdev_remove_child(pvd, mvd);
- cvd->vdev_id = mvd->vdev_id;
- vdev_add_child(pvd, cvd);
/*
- * If we created a new toplevel vdev, then we need to change the child's
- * vdev GUID to match the old toplevel vdev. Otherwise, we could have
- * detached an offline device, and when we go to import the pool we'll
- * think we have two toplevel vdevs, instead of a different version of
- * the same toplevel vdev.
+ * If cvd will replace mvd as a top-level vdev, preserve mvd's guid.
+ * Otherwise, we could have detached an offline device, and when we
+ * go to import the pool we'll think we have two top-level vdevs,
+ * instead of a different version of the same top-level vdev.
*/
- if (cvd->vdev_top == cvd) {
- pvd->vdev_guid_sum -= cvd->vdev_guid;
- cvd->vdev_guid_sum -= cvd->vdev_guid;
- cvd->vdev_guid = mvd->vdev_guid;
- cvd->vdev_guid_sum += mvd->vdev_guid;
- pvd->vdev_guid_sum += cvd->vdev_guid;
- }
+ if (mvd->vdev_top == mvd)
+ cvd->vdev_guid = cvd->vdev_guid_sum = mvd->vdev_guid;
+ cvd->vdev_id = mvd->vdev_id;
+ vdev_add_child(pvd, cvd);
vdev_top_update(cvd->vdev_top, cvd->vdev_top);
if (cvd == cvd->vdev_top)
@@ -697,7 +744,7 @@ vdev_metaslab_init(vdev_t *vd, uint64_t txg)
{
spa_t *spa = vd->vdev_spa;
objset_t *mos = spa->spa_meta_objset;
- metaslab_class_t *mc = spa_metaslab_class_select(spa);
+ metaslab_class_t *mc;
uint64_t m;
uint64_t oldc = vd->vdev_ms_count;
uint64_t newc = vd->vdev_asize >> vd->vdev_ms_shift;
@@ -707,10 +754,13 @@ vdev_metaslab_init(vdev_t *vd, uint64_t txg)
if (vd->vdev_ms_shift == 0) /* not being allocated from yet */
return (0);
- dprintf("%s oldc %llu newc %llu\n", vdev_description(vd), oldc, newc);
-
ASSERT(oldc <= newc);
+ if (vd->vdev_islog)
+ mc = spa->spa_log_class;
+ else
+ mc = spa->spa_normal_class;
+
if (vd->vdev_mg == NULL)
vd->vdev_mg = metaslab_group_create(mc, vd);
@@ -737,8 +787,8 @@ vdev_metaslab_init(vdev_t *vd, uint64_t txg)
error = dmu_bonus_hold(mos, object, FTAG, &db);
if (error)
return (error);
- ASSERT3U(db->db_size, ==, sizeof (smo));
- bcopy(db->db_data, &smo, db->db_size);
+ ASSERT3U(db->db_size, >=, sizeof (smo));
+ bcopy(db->db_data, &smo, sizeof (smo));
ASSERT3U(smo.smo_object, ==, object);
dmu_buf_rele(db, FTAG);
}
@@ -765,6 +815,112 @@ vdev_metaslab_fini(vdev_t *vd)
}
}
+typedef struct vdev_probe_stats {
+ boolean_t vps_readable;
+ boolean_t vps_writeable;
+ int vps_flags;
+ zio_t *vps_root;
+ vdev_t *vps_vd;
+} vdev_probe_stats_t;
+
+static void
+vdev_probe_done(zio_t *zio)
+{
+ vdev_probe_stats_t *vps = zio->io_private;
+ vdev_t *vd = vps->vps_vd;
+
+ if (zio->io_type == ZIO_TYPE_READ) {
+ ASSERT(zio->io_vd == vd);
+ if (zio->io_error == 0)
+ vps->vps_readable = 1;
+ if (zio->io_error == 0 && (spa_mode & FWRITE)) {
+ zio_nowait(zio_write_phys(vps->vps_root, vd,
+ zio->io_offset, zio->io_size, zio->io_data,
+ ZIO_CHECKSUM_OFF, vdev_probe_done, vps,
+ ZIO_PRIORITY_SYNC_WRITE, vps->vps_flags, B_TRUE));
+ } else {
+ zio_buf_free(zio->io_data, zio->io_size);
+ }
+ } else if (zio->io_type == ZIO_TYPE_WRITE) {
+ ASSERT(zio->io_vd == vd);
+ if (zio->io_error == 0)
+ vps->vps_writeable = 1;
+ zio_buf_free(zio->io_data, zio->io_size);
+ } else if (zio->io_type == ZIO_TYPE_NULL) {
+ ASSERT(zio->io_vd == NULL);
+ ASSERT(zio == vps->vps_root);
+
+ vd->vdev_cant_read |= !vps->vps_readable;
+ vd->vdev_cant_write |= !vps->vps_writeable;
+
+ if (vdev_readable(vd) &&
+ (vdev_writeable(vd) || !(spa_mode & FWRITE))) {
+ zio->io_error = 0;
+ } else {
+ ASSERT(zio->io_error != 0);
+ zfs_ereport_post(FM_EREPORT_ZFS_PROBE_FAILURE,
+ zio->io_spa, vd, NULL, 0, 0);
+ zio->io_error = ENXIO;
+ }
+ kmem_free(vps, sizeof (*vps));
+ }
+}
+
+/*
+ * Determine whether this device is accessible by reading and writing
+ * to several known locations: the pad regions of each vdev label
+ * but the first (which we leave alone in case it contains a VTOC).
+ */
+zio_t *
+vdev_probe(vdev_t *vd, zio_t *pio)
+{
+ spa_t *spa = vd->vdev_spa;
+ vdev_probe_stats_t *vps;
+ zio_t *zio;
+
+ vps = kmem_zalloc(sizeof (*vps), KM_SLEEP);
+
+ vps->vps_flags = ZIO_FLAG_CANFAIL | ZIO_FLAG_PROBE |
+ ZIO_FLAG_DONT_CACHE | ZIO_FLAG_DONT_AGGREGATE | ZIO_FLAG_DONT_RETRY;
+
+ if (spa_config_held(spa, SCL_ZIO, RW_WRITER)) {
+ /*
+ * vdev_cant_read and vdev_cant_write can only transition
+ * from TRUE to FALSE when we have the SCL_ZIO lock as writer;
+ * otherwise they can only transition from FALSE to TRUE.
+ * This ensures that any zio looking at these values can
+ * assume that failures persist for the life of the I/O.
+ * That's important because when a device has intermittent
+ * connectivity problems, we want to ensure that they're
+ * ascribed to the device (ENXIO) and not the zio (EIO).
+ *
+ * Since we hold SCL_ZIO as writer here, clear both values
+ * so the probe can reevaluate from first principles.
+ */
+ vps->vps_flags |= ZIO_FLAG_CONFIG_WRITER;
+ vd->vdev_cant_read = B_FALSE;
+ vd->vdev_cant_write = B_FALSE;
+ }
+
+ ASSERT(vd->vdev_ops->vdev_op_leaf);
+
+ zio = zio_null(pio, spa, vdev_probe_done, vps, vps->vps_flags);
+
+ vps->vps_root = zio;
+ vps->vps_vd = vd;
+
+ for (int l = 1; l < VDEV_LABELS; l++) {
+ zio_nowait(zio_read_phys(zio, vd,
+ vdev_label_offset(vd->vdev_psize, l,
+ offsetof(vdev_label_t, vl_pad)),
+ VDEV_SKIP_SIZE, zio_buf_alloc(VDEV_SKIP_SIZE),
+ ZIO_CHECKSUM_OFF, vdev_probe_done, vps,
+ ZIO_PRIORITY_SYNC_READ, vps->vps_flags, B_TRUE));
+ }
+
+ return (zio);
+}
+
/*
* Prepare a virtual device for access.
*/
@@ -781,20 +937,14 @@ vdev_open(vdev_t *vd)
vd->vdev_state == VDEV_STATE_CANT_OPEN ||
vd->vdev_state == VDEV_STATE_OFFLINE);
- if (vd->vdev_fault_mode == VDEV_FAULT_COUNT)
- vd->vdev_fault_arg >>= 1;
- else
- vd->vdev_fault_mode = VDEV_FAULT_NONE;
-
vd->vdev_stat.vs_aux = VDEV_AUX_NONE;
- if (vd->vdev_ops->vdev_op_leaf) {
- vdev_cache_init(vd);
- vdev_queue_init(vd);
- vd->vdev_cache_active = B_TRUE;
- }
-
- if (vd->vdev_offline) {
+ if (!vd->vdev_removed && vd->vdev_faulted) {
+ ASSERT(vd->vdev_children == 0);
+ vdev_set_state(vd, B_TRUE, VDEV_STATE_FAULTED,
+ VDEV_AUX_ERR_EXCEEDED);
+ return (ENXIO);
+ } else if (vd->vdev_offline) {
ASSERT(vd->vdev_children == 0);
vdev_set_state(vd, B_TRUE, VDEV_STATE_OFFLINE, VDEV_AUX_NONE);
return (ENXIO);
@@ -805,16 +955,25 @@ vdev_open(vdev_t *vd)
if (zio_injection_enabled && error == 0)
error = zio_handle_device_injection(vd, ENXIO);
- dprintf("%s = %d, osize %llu, state = %d\n",
- vdev_description(vd), error, osize, vd->vdev_state);
-
if (error) {
+ if (vd->vdev_removed &&
+ vd->vdev_stat.vs_aux != VDEV_AUX_OPEN_FAILED)
+ vd->vdev_removed = B_FALSE;
+
vdev_set_state(vd, B_TRUE, VDEV_STATE_CANT_OPEN,
vd->vdev_stat.vs_aux);
return (error);
}
- vd->vdev_state = VDEV_STATE_HEALTHY;
+ vd->vdev_removed = B_FALSE;
+
+ if (vd->vdev_degraded) {
+ ASSERT(vd->vdev_children == 0);
+ vdev_set_state(vd, B_TRUE, VDEV_STATE_DEGRADED,
+ VDEV_AUX_ERR_EXCEEDED);
+ } else {
+ vd->vdev_state = VDEV_STATE_HEALTHY;
+ }
for (c = 0; c < vd->vdev_children; c++)
if (vd->vdev_child[c]->vdev_state != VDEV_STATE_HEALTHY) {
@@ -883,6 +1042,17 @@ vdev_open(vdev_t *vd)
}
/*
+ * Ensure we can issue some IO before declaring the
+ * vdev open for business.
+ */
+ if (vd->vdev_ops->vdev_op_leaf &&
+ (error = zio_wait(vdev_probe(vd, NULL))) != 0) {
+ vdev_set_state(vd, B_TRUE, VDEV_STATE_CANT_OPEN,
+ VDEV_AUX_IO_FAILURE);
+ return (error);
+ }
+
+ /*
* If this is a top-level vdev, compute the raidz-deflation
* ratio. Note, we hard-code in 128k (1<<17) because it is the
* current "typical" blocksize. Even if SPA_MAXBLOCKSIZE
@@ -895,16 +1065,17 @@ vdev_open(vdev_t *vd)
}
/*
- * This allows the ZFS DE to close cases appropriately. If a device
- * goes away and later returns, we want to close the associated case.
- * But it's not enough to simply post this only when a device goes from
- * CANT_OPEN -> HEALTHY. If we reboot the system and the device is
- * back, we also need to close the case (otherwise we will try to replay
- * it). So we have to post this notifier every time. Since this only
- * occurs during pool open or error recovery, this should not be an
- * issue.
+ * If a leaf vdev has a DTL, and seems healthy, then kick off a
+ * resilver. But don't do this if we are doing a reopen for a
+ * scrub, since this would just restart the scrub we are already
+ * doing.
*/
- zfs_post_ok(vd->vdev_spa, vd);
+ if (vd->vdev_children == 0 && !vd->vdev_spa->spa_scrub_reopen) {
+ mutex_enter(&vd->vdev_dtl_lock);
+ if (vd->vdev_dtl_map.sm_space != 0 && vdev_writeable(vd))
+ spa_async_request(vd->vdev_spa, SPA_ASYNC_RESILVER);
+ mutex_exit(&vd->vdev_dtl_lock);
+ }
return (0);
}
@@ -912,8 +1083,7 @@ vdev_open(vdev_t *vd)
/*
* Called once the vdevs are all opened, this routine validates the label
* contents. This needs to be done before vdev_load() so that we don't
- * inadvertently do repair I/Os to the wrong device, and so that vdev_reopen()
- * won't succeed if the device has been changed underneath.
+ * inadvertently do repair I/Os to the wrong device.
*
* This function will only return failure if one of the vdevs indicates that it
* has since been destroyed or exported. This is only possible if
@@ -926,7 +1096,7 @@ vdev_validate(vdev_t *vd)
spa_t *spa = vd->vdev_spa;
int c;
nvlist_t *label;
- uint64_t guid;
+ uint64_t guid, top_guid;
uint64_t state;
for (c = 0; c < vd->vdev_children; c++)
@@ -938,7 +1108,7 @@ vdev_validate(vdev_t *vd)
* any further validation. Otherwise, label I/O will fail and we will
* overwrite the previous state.
*/
- if (vd->vdev_ops->vdev_op_leaf && !vdev_is_dead(vd)) {
+ if (vd->vdev_ops->vdev_op_leaf && vdev_readable(vd)) {
if ((label = vdev_label_read_config(vd)) == NULL) {
vdev_set_state(vd, B_TRUE, VDEV_STATE_CANT_OPEN,
@@ -954,8 +1124,20 @@ vdev_validate(vdev_t *vd)
return (0);
}
+ /*
+ * If this vdev just became a top-level vdev because its
+ * sibling was detached, it will have adopted the parent's
+ * vdev guid -- but the label may or may not be on disk yet.
+ * Fortunately, either version of the label will have the
+ * same top guid, so if we're a top-level vdev, we can
+ * safely compare to that instead.
+ */
if (nvlist_lookup_uint64(label, ZPOOL_CONFIG_GUID,
- &guid) != 0 || guid != vd->vdev_guid) {
+ &guid) != 0 ||
+ nvlist_lookup_uint64(label, ZPOOL_CONFIG_TOP_GUID,
+ &top_guid) != 0 ||
+ (vd->vdev_guid != guid &&
+ (vd->vdev_guid != top_guid || vd != vd->vdev_top))) {
vdev_set_state(vd, B_FALSE, VDEV_STATE_CANT_OPEN,
VDEV_AUX_CORRUPT_DATA);
nvlist_free(label);
@@ -975,14 +1157,15 @@ vdev_validate(vdev_t *vd)
if (spa->spa_load_state == SPA_LOAD_OPEN &&
state != POOL_STATE_ACTIVE)
return (EBADF);
- }
- /*
- * If we were able to open and validate a vdev that was previously
- * marked permanently unavailable, clear that state now.
- */
- if (vd->vdev_not_present)
- vd->vdev_not_present = 0;
+ /*
+ * If we were able to open and validate a vdev that was
+ * previously marked permanently unavailable, clear that state
+ * now.
+ */
+ if (vd->vdev_not_present)
+ vd->vdev_not_present = 0;
+ }
return (0);
}
@@ -995,11 +1178,7 @@ vdev_close(vdev_t *vd)
{
vd->vdev_ops->vdev_op_close(vd);
- if (vd->vdev_cache_active) {
- vdev_cache_fini(vd);
- vdev_queue_fini(vd);
- vd->vdev_cache_active = B_FALSE;
- }
+ vdev_cache_purge(vd);
/*
* We record the previous state before we close it, so that if we are
@@ -1020,7 +1199,7 @@ vdev_reopen(vdev_t *vd)
{
spa_t *spa = vd->vdev_spa;
- ASSERT(spa_config_held(spa, RW_WRITER));
+ ASSERT(spa_config_held(spa, SCL_STATE_ALL, RW_WRITER) == SCL_STATE_ALL);
vdev_close(vd);
(void) vdev_open(vd);
@@ -1029,22 +1208,24 @@ vdev_reopen(vdev_t *vd)
* Call vdev_validate() here to make sure we have the same device.
* Otherwise, a device with an invalid label could be successfully
* opened in response to vdev_reopen().
- *
- * The downside to this is that if the user is simply experimenting by
- * overwriting an entire disk, we'll fault the device rather than
- * demonstrate self-healing capabilities. On the other hand, with
- * proper FMA integration, the series of errors we'd see from the device
- * would result in a faulted device anyway. Given that this doesn't
- * model any real-world corruption, it's better to catch this here and
- * correctly identify that the device has either changed beneath us, or
- * is corrupted beyond recognition.
*/
- (void) vdev_validate(vd);
+ if (vd->vdev_aux) {
+ (void) vdev_validate_aux(vd);
+ if (vdev_readable(vd) && vdev_writeable(vd) &&
+ !l2arc_vdev_present(vd)) {
+ uint64_t size = vdev_get_rsize(vd);
+ l2arc_add_vdev(spa, vd,
+ VDEV_LABEL_START_SIZE,
+ size - VDEV_LABEL_START_SIZE);
+ }
+ } else {
+ (void) vdev_validate(vd);
+ }
/*
- * Reassess root vdev's health.
+ * Reassess parent vdev's health.
*/
- vdev_propagate_state(spa->spa_root_vdev);
+ vdev_propagate_state(vd);
}
int
@@ -1150,22 +1331,27 @@ vdev_dtl_reassess(vdev_t *vd, uint64_t txg, uint64_t scrub_txg, int scrub_done)
spa_t *spa = vd->vdev_spa;
int c;
- ASSERT(spa_config_held(spa, RW_WRITER));
+ ASSERT(spa_config_held(spa, SCL_CONFIG, RW_READER));
if (vd->vdev_children == 0) {
mutex_enter(&vd->vdev_dtl_lock);
- /*
- * We're successfully scrubbed everything up to scrub_txg.
- * Therefore, excise all old DTLs up to that point, then
- * fold in the DTLs for everything we couldn't scrub.
- */
- if (scrub_txg != 0) {
+ if (scrub_txg != 0 &&
+ (spa->spa_scrub_started || spa->spa_scrub_errors == 0)) {
+ /* XXX should check scrub_done? */
+ /*
+ * We completed a scrub up to scrub_txg. If we
+ * did it without rebooting, then the scrub dtl
+ * will be valid, so excise the old region and
+ * fold in the scrub dtl. Otherwise, leave the
+ * dtl as-is if there was an error.
+ */
space_map_excise(&vd->vdev_dtl_map, 0, scrub_txg);
space_map_union(&vd->vdev_dtl_map, &vd->vdev_dtl_scrub);
}
if (scrub_done)
space_map_vacate(&vd->vdev_dtl_scrub, NULL, NULL);
mutex_exit(&vd->vdev_dtl_lock);
+
if (txg != 0)
vdev_dirty(vd->vdev_top, VDD_DTL, vd, txg);
return;
@@ -1212,8 +1398,8 @@ vdev_dtl_load(vdev_t *vd)
if ((error = dmu_bonus_hold(mos, smo->smo_object, FTAG, &db)) != 0)
return (error);
- ASSERT3U(db->db_size, ==, sizeof (*smo));
- bcopy(db->db_data, smo, db->db_size);
+ ASSERT3U(db->db_size, >=, sizeof (*smo));
+ bcopy(db->db_data, smo, sizeof (*smo));
dmu_buf_rele(db, FTAG);
mutex_enter(&vd->vdev_dtl_lock);
@@ -1235,9 +1421,6 @@ vdev_dtl_sync(vdev_t *vd, uint64_t txg)
dmu_buf_t *db;
dmu_tx_t *tx;
- dprintf("%s in txg %llu pass %d\n",
- vdev_description(vd), (u_longlong_t)txg, spa_sync_pass(spa));
-
tx = dmu_tx_create_assigned(spa->spa_dsl_pool, txg);
if (vd->vdev_detached) {
@@ -1247,8 +1430,6 @@ vdev_dtl_sync(vdev_t *vd, uint64_t txg)
smo->smo_object = 0;
}
dmu_tx_commit(tx);
- dprintf("detach %s committed in txg %llu\n",
- vdev_description(vd), txg);
return;
}
@@ -1283,13 +1464,56 @@ vdev_dtl_sync(vdev_t *vd, uint64_t txg)
VERIFY(0 == dmu_bonus_hold(mos, smo->smo_object, FTAG, &db));
dmu_buf_will_dirty(db, tx);
- ASSERT3U(db->db_size, ==, sizeof (*smo));
- bcopy(smo, db->db_data, db->db_size);
+ ASSERT3U(db->db_size, >=, sizeof (*smo));
+ bcopy(smo, db->db_data, sizeof (*smo));
dmu_buf_rele(db, FTAG);
dmu_tx_commit(tx);
}
+/*
+ * Determine if resilver is needed, and if so the txg range.
+ */
+boolean_t
+vdev_resilver_needed(vdev_t *vd, uint64_t *minp, uint64_t *maxp)
+{
+ boolean_t needed = B_FALSE;
+ uint64_t thismin = UINT64_MAX;
+ uint64_t thismax = 0;
+
+ if (vd->vdev_children == 0) {
+ mutex_enter(&vd->vdev_dtl_lock);
+ if (vd->vdev_dtl_map.sm_space != 0 && vdev_writeable(vd)) {
+ space_seg_t *ss;
+
+ ss = avl_first(&vd->vdev_dtl_map.sm_root);
+ thismin = ss->ss_start - 1;
+ ss = avl_last(&vd->vdev_dtl_map.sm_root);
+ thismax = ss->ss_end;
+ needed = B_TRUE;
+ }
+ mutex_exit(&vd->vdev_dtl_lock);
+ } else {
+ int c;
+ for (c = 0; c < vd->vdev_children; c++) {
+ vdev_t *cvd = vd->vdev_child[c];
+ uint64_t cmin, cmax;
+
+ if (vdev_resilver_needed(cvd, &cmin, &cmax)) {
+ thismin = MIN(thismin, cmin);
+ thismax = MAX(thismax, cmax);
+ needed = B_TRUE;
+ }
+ }
+ }
+
+ if (needed && minp) {
+ *minp = thismin;
+ *maxp = thismax;
+ }
+ return (needed);
+}
+
void
vdev_load(vdev_t *vd)
{
@@ -1319,19 +1543,22 @@ vdev_load(vdev_t *vd)
}
/*
- * This special case of vdev_spare() is used for hot spares. It's sole purpose
- * it to set the vdev state for the associated vdev. To do this, we make sure
- * that we can open the underlying device, then try to read the label, and make
- * sure that the label is sane and that it hasn't been repurposed to another
- * pool.
+ * The special vdev case is used for hot spares and l2cache devices. Its
+ * sole purpose it to set the vdev state for the associated vdev. To do this,
+ * we make sure that we can open the underlying device, then try to read the
+ * label, and make sure that the label is sane and that it hasn't been
+ * repurposed to another pool.
*/
int
-vdev_validate_spare(vdev_t *vd)
+vdev_validate_aux(vdev_t *vd)
{
nvlist_t *label;
uint64_t guid, version;
uint64_t state;
+ if (!vdev_readable(vd))
+ return (0);
+
if ((label = vdev_label_read_config(vd)) == NULL) {
vdev_set_state(vd, B_TRUE, VDEV_STATE_CANT_OPEN,
VDEV_AUX_CORRUPT_DATA);
@@ -1339,7 +1566,7 @@ vdev_validate_spare(vdev_t *vd)
}
if (nvlist_lookup_uint64(label, ZPOOL_CONFIG_VERSION, &version) != 0 ||
- version > ZFS_VERSION ||
+ version > SPA_VERSION ||
nvlist_lookup_uint64(label, ZPOOL_CONFIG_GUID, &guid) != 0 ||
guid != vd->vdev_guid ||
nvlist_lookup_uint64(label, ZPOOL_CONFIG_POOL_STATE, &state) != 0) {
@@ -1349,8 +1576,6 @@ vdev_validate_spare(vdev_t *vd)
return (-1);
}
- spa_spare_add(vd);
-
/*
* We don't actually check the pool state here. If it's in fact in
* use by another pool, we update this fact on the fly when requested.
@@ -1364,8 +1589,6 @@ vdev_sync_done(vdev_t *vd, uint64_t txg)
{
metaslab_t *msp;
- dprintf("%s txg %llu\n", vdev_description(vd), txg);
-
while (msp = txg_list_remove(&vd->vdev_ms_list, TXG_CLEAN(txg)))
metaslab_sync_done(msp, txg);
}
@@ -1378,9 +1601,6 @@ vdev_sync(vdev_t *vd, uint64_t txg)
metaslab_t *msp;
dmu_tx_t *tx;
- dprintf("%s txg %llu pass %d\n",
- vdev_description(vd), (u_longlong_t)txg, spa_sync_pass(spa));
-
if (vd->vdev_ms_array == 0 && vd->vdev_ms_shift != 0) {
ASSERT(vd == vd->vdev_top);
tx = dmu_tx_create_assigned(spa->spa_dsl_pool, txg);
@@ -1408,81 +1628,139 @@ vdev_psize_to_asize(vdev_t *vd, uint64_t psize)
return (vd->vdev_ops->vdev_op_asize(vd, psize));
}
-void
-vdev_io_start(zio_t *zio)
+/*
+ * Mark the given vdev faulted. A faulted vdev behaves as if the device could
+ * not be opened, and no I/O is attempted.
+ */
+int
+vdev_fault(spa_t *spa, uint64_t guid)
{
- zio->io_vd->vdev_ops->vdev_op_io_start(zio);
-}
+ vdev_t *vd;
-void
-vdev_io_done(zio_t *zio)
-{
- zio->io_vd->vdev_ops->vdev_op_io_done(zio);
+ spa_vdev_state_enter(spa);
+
+ if ((vd = spa_lookup_by_guid(spa, guid, B_TRUE)) == NULL)
+ return (spa_vdev_state_exit(spa, NULL, ENODEV));
+
+ if (!vd->vdev_ops->vdev_op_leaf)
+ return (spa_vdev_state_exit(spa, NULL, ENOTSUP));
+
+ /*
+ * Faulted state takes precedence over degraded.
+ */
+ vd->vdev_faulted = 1ULL;
+ vd->vdev_degraded = 0ULL;
+ vdev_set_state(vd, B_FALSE, VDEV_STATE_FAULTED, VDEV_AUX_ERR_EXCEEDED);
+
+ /*
+ * If marking the vdev as faulted cause the top-level vdev to become
+ * unavailable, then back off and simply mark the vdev as degraded
+ * instead.
+ */
+ if (vdev_is_dead(vd->vdev_top) && vd->vdev_aux == NULL) {
+ vd->vdev_degraded = 1ULL;
+ vd->vdev_faulted = 0ULL;
+
+ /*
+ * If we reopen the device and it's not dead, only then do we
+ * mark it degraded.
+ */
+ vdev_reopen(vd);
+
+ if (vdev_readable(vd)) {
+ vdev_set_state(vd, B_FALSE, VDEV_STATE_DEGRADED,
+ VDEV_AUX_ERR_EXCEEDED);
+ }
+ }
+
+ return (spa_vdev_state_exit(spa, vd, 0));
}
-const char *
-vdev_description(vdev_t *vd)
+/*
+ * Mark the given vdev degraded. A degraded vdev is purely an indication to the
+ * user that something is wrong. The vdev continues to operate as normal as far
+ * as I/O is concerned.
+ */
+int
+vdev_degrade(spa_t *spa, uint64_t guid)
{
- if (vd == NULL || vd->vdev_ops == NULL)
- return ("<unknown>");
+ vdev_t *vd;
+
+ spa_vdev_state_enter(spa);
+
+ if ((vd = spa_lookup_by_guid(spa, guid, B_TRUE)) == NULL)
+ return (spa_vdev_state_exit(spa, NULL, ENODEV));
- if (vd->vdev_path != NULL)
- return (vd->vdev_path);
+ if (!vd->vdev_ops->vdev_op_leaf)
+ return (spa_vdev_state_exit(spa, NULL, ENOTSUP));
+
+ /*
+ * If the vdev is already faulted, then don't do anything.
+ */
+ if (vd->vdev_faulted || vd->vdev_degraded)
+ return (spa_vdev_state_exit(spa, NULL, 0));
- if (vd->vdev_parent == NULL)
- return (spa_name(vd->vdev_spa));
+ vd->vdev_degraded = 1ULL;
+ if (!vdev_is_dead(vd))
+ vdev_set_state(vd, B_FALSE, VDEV_STATE_DEGRADED,
+ VDEV_AUX_ERR_EXCEEDED);
- return (vd->vdev_ops->vdev_op_type);
+ return (spa_vdev_state_exit(spa, vd, 0));
}
+/*
+ * Online the given vdev. If 'unspare' is set, it implies two things. First,
+ * any attached spare device should be detached when the device finishes
+ * resilvering. Second, the online should be treated like a 'test' online case,
+ * so no FMA events are generated if the device fails to open.
+ */
int
-vdev_online(spa_t *spa, uint64_t guid)
+vdev_online(spa_t *spa, uint64_t guid, uint64_t flags, vdev_state_t *newstate)
{
- vdev_t *rvd, *vd;
- uint64_t txg;
-
- txg = spa_vdev_enter(spa);
+ vdev_t *vd;
- rvd = spa->spa_root_vdev;
+ spa_vdev_state_enter(spa);
- if ((vd = vdev_lookup_by_guid(rvd, guid)) == NULL)
- return (spa_vdev_exit(spa, NULL, txg, ENODEV));
+ if ((vd = spa_lookup_by_guid(spa, guid, B_TRUE)) == NULL)
+ return (spa_vdev_state_exit(spa, NULL, ENODEV));
if (!vd->vdev_ops->vdev_op_leaf)
- return (spa_vdev_exit(spa, NULL, txg, ENOTSUP));
-
- dprintf("ONLINE: %s\n", vdev_description(vd));
+ return (spa_vdev_state_exit(spa, NULL, ENOTSUP));
vd->vdev_offline = B_FALSE;
vd->vdev_tmpoffline = B_FALSE;
+ vd->vdev_checkremove = !!(flags & ZFS_ONLINE_CHECKREMOVE);
+ vd->vdev_forcefault = !!(flags & ZFS_ONLINE_FORCEFAULT);
vdev_reopen(vd->vdev_top);
+ vd->vdev_checkremove = vd->vdev_forcefault = B_FALSE;
- vdev_config_dirty(vd->vdev_top);
+ if (newstate)
+ *newstate = vd->vdev_state;
+ if ((flags & ZFS_ONLINE_UNSPARE) &&
+ !vdev_is_dead(vd) && vd->vdev_parent &&
+ vd->vdev_parent->vdev_ops == &vdev_spare_ops &&
+ vd->vdev_parent->vdev_child[0] == vd)
+ vd->vdev_unspare = B_TRUE;
- (void) spa_vdev_exit(spa, NULL, txg, 0);
+ (void) spa_vdev_state_exit(spa, vd, 0);
- VERIFY(spa_scrub(spa, POOL_SCRUB_RESILVER, B_TRUE) == 0);
+ VERIFY3U(spa_scrub(spa, POOL_SCRUB_RESILVER), ==, 0);
return (0);
}
int
-vdev_offline(spa_t *spa, uint64_t guid, int istmp)
+vdev_offline(spa_t *spa, uint64_t guid, uint64_t flags)
{
- vdev_t *rvd, *vd;
- uint64_t txg;
-
- txg = spa_vdev_enter(spa);
+ vdev_t *vd;
- rvd = spa->spa_root_vdev;
+ spa_vdev_state_enter(spa);
- if ((vd = vdev_lookup_by_guid(rvd, guid)) == NULL)
- return (spa_vdev_exit(spa, NULL, txg, ENODEV));
+ if ((vd = spa_lookup_by_guid(spa, guid, B_TRUE)) == NULL)
+ return (spa_vdev_state_exit(spa, NULL, ENODEV));
if (!vd->vdev_ops->vdev_op_leaf)
- return (spa_vdev_exit(spa, NULL, txg, ENOTSUP));
-
- dprintf("OFFLINE: %s\n", vdev_description(vd));
+ return (spa_vdev_state_exit(spa, NULL, ENOTSUP));
/*
* If the device isn't already offline, try to offline it.
@@ -1496,7 +1774,7 @@ vdev_offline(spa_t *spa, uint64_t guid, int istmp)
* as long as the remaining devices don't have any DTL holes.
*/
if (vd->vdev_top->vdev_dtl_map.sm_space != 0)
- return (spa_vdev_exit(spa, NULL, txg, EBUSY));
+ return (spa_vdev_state_exit(spa, NULL, EBUSY));
/*
* Offline this device and reopen its top-level vdev.
@@ -1505,18 +1783,16 @@ vdev_offline(spa_t *spa, uint64_t guid, int istmp)
*/
vd->vdev_offline = B_TRUE;
vdev_reopen(vd->vdev_top);
- if (vdev_is_dead(vd->vdev_top)) {
+ if (vdev_is_dead(vd->vdev_top) && vd->vdev_aux == NULL) {
vd->vdev_offline = B_FALSE;
vdev_reopen(vd->vdev_top);
- return (spa_vdev_exit(spa, NULL, txg, EBUSY));
+ return (spa_vdev_state_exit(spa, NULL, EBUSY));
}
}
- vd->vdev_tmpoffline = istmp;
+ vd->vdev_tmpoffline = !!(flags & ZFS_OFFLINE_TEMPORARY);
- vdev_config_dirty(vd->vdev_top);
-
- return (spa_vdev_exit(spa, NULL, txg, 0));
+ return (spa_vdev_state_exit(spa, vd, 0));
}
/*
@@ -1527,56 +1803,78 @@ vdev_offline(spa_t *spa, uint64_t guid, int istmp)
void
vdev_clear(spa_t *spa, vdev_t *vd)
{
- int c;
+ vdev_t *rvd = spa->spa_root_vdev;
+
+ ASSERT(spa_config_held(spa, SCL_STATE_ALL, RW_WRITER) == SCL_STATE_ALL);
if (vd == NULL)
- vd = spa->spa_root_vdev;
+ vd = rvd;
vd->vdev_stat.vs_read_errors = 0;
vd->vdev_stat.vs_write_errors = 0;
vd->vdev_stat.vs_checksum_errors = 0;
- for (c = 0; c < vd->vdev_children; c++)
+ for (int c = 0; c < vd->vdev_children; c++)
vdev_clear(spa, vd->vdev_child[c]);
+
+ /*
+ * If we're in the FAULTED state or have experienced failed I/O, then
+ * clear the persistent state and attempt to reopen the device. We
+ * also mark the vdev config dirty, so that the new faulted state is
+ * written out to disk.
+ */
+ if (vd->vdev_faulted || vd->vdev_degraded ||
+ !vdev_readable(vd) || !vdev_writeable(vd)) {
+
+ vd->vdev_faulted = vd->vdev_degraded = 0;
+ vd->vdev_cant_read = B_FALSE;
+ vd->vdev_cant_write = B_FALSE;
+
+ vdev_reopen(vd);
+
+ if (vd != rvd)
+ vdev_state_dirty(vd->vdev_top);
+
+ if (vd->vdev_aux == NULL && !vdev_is_dead(vd))
+ spa_async_request(spa, SPA_ASYNC_RESILVER);
+
+ spa_event_notify(spa, vd, ESC_ZFS_VDEV_CLEAR);
+ }
}
-int
+boolean_t
vdev_is_dead(vdev_t *vd)
{
- return (vd->vdev_state <= VDEV_STATE_CANT_OPEN);
+ return (vd->vdev_state < VDEV_STATE_DEGRADED);
}
-int
-vdev_error_inject(vdev_t *vd, zio_t *zio)
+boolean_t
+vdev_readable(vdev_t *vd)
+{
+ return (!vdev_is_dead(vd) && !vd->vdev_cant_read);
+}
+
+boolean_t
+vdev_writeable(vdev_t *vd)
{
- int error = 0;
+ return (!vdev_is_dead(vd) && !vd->vdev_cant_write);
+}
- if (vd->vdev_fault_mode == VDEV_FAULT_NONE)
- return (0);
+boolean_t
+vdev_accessible(vdev_t *vd, zio_t *zio)
+{
+ ASSERT(zio->io_vd == vd);
- if (((1ULL << zio->io_type) & vd->vdev_fault_mask) == 0)
- return (0);
+ if (vdev_is_dead(vd) || vd->vdev_remove_wanted)
+ return (B_FALSE);
- switch (vd->vdev_fault_mode) {
- case VDEV_FAULT_RANDOM:
- if (spa_get_random(vd->vdev_fault_arg) == 0)
- error = EIO;
- break;
-
- case VDEV_FAULT_COUNT:
- if ((int64_t)--vd->vdev_fault_arg <= 0)
- vd->vdev_fault_mode = VDEV_FAULT_NONE;
- error = EIO;
- break;
- }
+ if (zio->io_type == ZIO_TYPE_READ)
+ return (!vd->vdev_cant_read);
- if (error != 0) {
- dprintf("returning %d for type %d on %s state %d offset %llx\n",
- error, zio->io_type, vdev_description(vd),
- vd->vdev_state, zio->io_offset);
- }
+ if (zio->io_type == ZIO_TYPE_WRITE)
+ return (!vd->vdev_cant_write);
- return (error);
+ return (B_TRUE);
}
/*
@@ -1586,10 +1884,10 @@ void
vdev_get_stats(vdev_t *vd, vdev_stat_t *vs)
{
vdev_t *rvd = vd->vdev_spa->spa_root_vdev;
- int c, t;
mutex_enter(&vd->vdev_stat_lock);
bcopy(&vd->vdev_stat, vs, sizeof (*vs));
+ vs->vs_scrub_errors = vd->vdev_spa->spa_scrub_errors;
vs->vs_timestamp = gethrtime() - vs->vs_timestamp;
vs->vs_state = vd->vdev_state;
vs->vs_rsize = vdev_get_rsize(vd);
@@ -1600,49 +1898,80 @@ vdev_get_stats(vdev_t *vd, vdev_stat_t *vs)
* over all top-level vdevs (i.e. the direct children of the root).
*/
if (vd == rvd) {
- for (c = 0; c < rvd->vdev_children; c++) {
+ for (int c = 0; c < rvd->vdev_children; c++) {
vdev_t *cvd = rvd->vdev_child[c];
vdev_stat_t *cvs = &cvd->vdev_stat;
mutex_enter(&vd->vdev_stat_lock);
- for (t = 0; t < ZIO_TYPES; t++) {
+ for (int t = 0; t < ZIO_TYPES; t++) {
vs->vs_ops[t] += cvs->vs_ops[t];
vs->vs_bytes[t] += cvs->vs_bytes[t];
}
- vs->vs_read_errors += cvs->vs_read_errors;
- vs->vs_write_errors += cvs->vs_write_errors;
- vs->vs_checksum_errors += cvs->vs_checksum_errors;
vs->vs_scrub_examined += cvs->vs_scrub_examined;
- vs->vs_scrub_errors += cvs->vs_scrub_errors;
mutex_exit(&vd->vdev_stat_lock);
}
}
}
void
-vdev_stat_update(zio_t *zio)
+vdev_clear_stats(vdev_t *vd)
{
- vdev_t *vd = zio->io_vd;
+ mutex_enter(&vd->vdev_stat_lock);
+ vd->vdev_stat.vs_space = 0;
+ vd->vdev_stat.vs_dspace = 0;
+ vd->vdev_stat.vs_alloc = 0;
+ mutex_exit(&vd->vdev_stat_lock);
+}
+
+void
+vdev_stat_update(zio_t *zio, uint64_t psize)
+{
+ vdev_t *rvd = zio->io_spa->spa_root_vdev;
+ vdev_t *vd = zio->io_vd ? zio->io_vd : rvd;
vdev_t *pvd;
uint64_t txg = zio->io_txg;
vdev_stat_t *vs = &vd->vdev_stat;
zio_type_t type = zio->io_type;
int flags = zio->io_flags;
+ /*
+ * If this i/o is a gang leader, it didn't do any actual work.
+ */
+ if (zio->io_gang_tree)
+ return;
+
if (zio->io_error == 0) {
+ /*
+ * If this is a root i/o, don't count it -- we've already
+ * counted the top-level vdevs, and vdev_get_stats() will
+ * aggregate them when asked. This reduces contention on
+ * the root vdev_stat_lock and implicitly handles blocks
+ * that compress away to holes, for which there is no i/o.
+ * (Holes never create vdev children, so all the counters
+ * remain zero, which is what we want.)
+ *
+ * Note: this only applies to successful i/o (io_error == 0)
+ * because unlike i/o counts, errors are not additive.
+ * When reading a ditto block, for example, failure of
+ * one top-level vdev does not imply a root-level error.
+ */
+ if (vd == rvd)
+ return;
+
+ ASSERT(vd == zio->io_vd);
if (!(flags & ZIO_FLAG_IO_BYPASS)) {
mutex_enter(&vd->vdev_stat_lock);
vs->vs_ops[type]++;
- vs->vs_bytes[type] += zio->io_size;
+ vs->vs_bytes[type] += psize;
mutex_exit(&vd->vdev_stat_lock);
}
- if ((flags & ZIO_FLAG_IO_REPAIR) &&
- zio->io_delegate_list == NULL) {
+ if (flags & ZIO_FLAG_IO_REPAIR) {
+ ASSERT(zio->io_delegate_list == NULL);
mutex_enter(&vd->vdev_stat_lock);
if (flags & ZIO_FLAG_SCRUB_THREAD)
- vs->vs_scrub_repaired += zio->io_size;
+ vs->vs_scrub_repaired += psize;
else
- vs->vs_self_healed += zio->io_size;
+ vs->vs_self_healed += psize;
mutex_exit(&vd->vdev_stat_lock);
}
return;
@@ -1651,22 +1980,18 @@ vdev_stat_update(zio_t *zio)
if (flags & ZIO_FLAG_SPECULATIVE)
return;
- if (!vdev_is_dead(vd)) {
- mutex_enter(&vd->vdev_stat_lock);
- if (type == ZIO_TYPE_READ) {
- if (zio->io_error == ECKSUM)
- vs->vs_checksum_errors++;
- else
- vs->vs_read_errors++;
- }
- if (type == ZIO_TYPE_WRITE)
- vs->vs_write_errors++;
- mutex_exit(&vd->vdev_stat_lock);
+ mutex_enter(&vd->vdev_stat_lock);
+ if (type == ZIO_TYPE_READ) {
+ if (zio->io_error == ECKSUM)
+ vs->vs_checksum_errors++;
+ else
+ vs->vs_read_errors++;
}
+ if (type == ZIO_TYPE_WRITE)
+ vs->vs_write_errors++;
+ mutex_exit(&vd->vdev_stat_lock);
- if (type == ZIO_TYPE_WRITE) {
- if (txg == 0 || vd->vdev_children != 0)
- return;
+ if (type == ZIO_TYPE_WRITE && txg != 0 && vd->vdev_children == 0) {
if (flags & ZIO_FLAG_SCRUB_THREAD) {
ASSERT(flags & ZIO_FLAG_IO_REPAIR);
for (pvd = vd; pvd != NULL; pvd = pvd->vdev_parent)
@@ -1705,7 +2030,6 @@ vdev_scrub_stat_update(vdev_t *vd, pool_scrub_type_t type, boolean_t complete)
vs->vs_scrub_complete = 0;
vs->vs_scrub_examined = 0;
vs->vs_scrub_repaired = 0;
- vs->vs_scrub_errors = 0;
vs->vs_scrub_start = gethrestime_sec();
vs->vs_scrub_end = 0;
}
@@ -1717,33 +2041,48 @@ vdev_scrub_stat_update(vdev_t *vd, pool_scrub_type_t type, boolean_t complete)
* Update the in-core space usage stats for this vdev and the root vdev.
*/
void
-vdev_space_update(vdev_t *vd, int64_t space_delta, int64_t alloc_delta)
+vdev_space_update(vdev_t *vd, int64_t space_delta, int64_t alloc_delta,
+ boolean_t update_root)
{
- ASSERT(vd == vd->vdev_top);
int64_t dspace_delta = space_delta;
+ spa_t *spa = vd->vdev_spa;
+ vdev_t *rvd = spa->spa_root_vdev;
- do {
- if (vd->vdev_ms_count) {
- /*
- * If this is a top-level vdev, apply the
- * inverse of its psize-to-asize (ie. RAID-Z)
- * space-expansion factor. We must calculate
- * this here and not at the root vdev because
- * the root vdev's psize-to-asize is simply the
- * max of its childrens', thus not accurate
- * enough for us.
- */
- ASSERT((dspace_delta & (SPA_MINBLOCKSIZE-1)) == 0);
- dspace_delta = (dspace_delta >> SPA_MINBLOCKSHIFT) *
- vd->vdev_deflate_ratio;
- }
+ ASSERT(vd == vd->vdev_top);
+
+ /*
+ * Apply the inverse of the psize-to-asize (ie. RAID-Z) space-expansion
+ * factor. We must calculate this here and not at the root vdev
+ * because the root vdev's psize-to-asize is simply the max of its
+ * childrens', thus not accurate enough for us.
+ */
+ ASSERT((dspace_delta & (SPA_MINBLOCKSIZE-1)) == 0);
+ dspace_delta = (dspace_delta >> SPA_MINBLOCKSHIFT) *
+ vd->vdev_deflate_ratio;
+
+ mutex_enter(&vd->vdev_stat_lock);
+ vd->vdev_stat.vs_space += space_delta;
+ vd->vdev_stat.vs_alloc += alloc_delta;
+ vd->vdev_stat.vs_dspace += dspace_delta;
+ mutex_exit(&vd->vdev_stat_lock);
+
+ if (update_root) {
+ ASSERT(rvd == vd->vdev_parent);
+ ASSERT(vd->vdev_ms_count != 0);
+
+ /*
+ * Don't count non-normal (e.g. intent log) space as part of
+ * the pool's capacity.
+ */
+ if (vd->vdev_mg->mg_class != spa->spa_normal_class)
+ return;
- mutex_enter(&vd->vdev_stat_lock);
- vd->vdev_stat.vs_space += space_delta;
- vd->vdev_stat.vs_alloc += alloc_delta;
- vd->vdev_stat.vs_dspace += dspace_delta;
- mutex_exit(&vd->vdev_stat_lock);
- } while ((vd = vd->vdev_parent) != NULL);
+ mutex_enter(&rvd->vdev_stat_lock);
+ rvd->vdev_stat.vs_space += space_delta;
+ rvd->vdev_stat.vs_alloc += alloc_delta;
+ rvd->vdev_stat.vs_dspace += dspace_delta;
+ mutex_exit(&rvd->vdev_stat_lock);
+ }
}
/*
@@ -1759,13 +2098,53 @@ vdev_config_dirty(vdev_t *vd)
int c;
/*
- * The dirty list is protected by the config lock. The caller must
- * either hold the config lock as writer, or must be the sync thread
- * (which holds the lock as reader). There's only one sync thread,
+ * If this is an aux vdev (as with l2cache devices), then we update the
+ * vdev config manually and set the sync flag.
+ */
+ if (vd->vdev_aux != NULL) {
+ spa_aux_vdev_t *sav = vd->vdev_aux;
+ nvlist_t **aux;
+ uint_t naux;
+
+ for (c = 0; c < sav->sav_count; c++) {
+ if (sav->sav_vdevs[c] == vd)
+ break;
+ }
+
+ if (c == sav->sav_count) {
+ /*
+ * We're being removed. There's nothing more to do.
+ */
+ ASSERT(sav->sav_sync == B_TRUE);
+ return;
+ }
+
+ sav->sav_sync = B_TRUE;
+
+ VERIFY(nvlist_lookup_nvlist_array(sav->sav_config,
+ ZPOOL_CONFIG_L2CACHE, &aux, &naux) == 0);
+
+ ASSERT(c < naux);
+
+ /*
+ * Setting the nvlist in the middle if the array is a little
+ * sketchy, but it will work.
+ */
+ nvlist_free(aux[c]);
+ aux[c] = vdev_config_generate(spa, vd, B_TRUE, B_FALSE, B_TRUE);
+
+ return;
+ }
+
+ /*
+ * The dirty list is protected by the SCL_CONFIG lock. The caller
+ * must either hold SCL_CONFIG as writer, or must be the sync thread
+ * (which holds SCL_CONFIG as reader). There's only one sync thread,
* so this is sufficient to ensure mutual exclusion.
*/
- ASSERT(spa_config_held(spa, RW_WRITER) ||
- dsl_pool_sync_context(spa_get_dsl(spa)));
+ ASSERT(spa_config_held(spa, SCL_CONFIG, RW_WRITER) ||
+ (dsl_pool_sync_context(spa_get_dsl(spa)) &&
+ spa_config_held(spa, SCL_CONFIG, RW_READER)));
if (vd == rvd) {
for (c = 0; c < rvd->vdev_children; c++)
@@ -1773,8 +2152,8 @@ vdev_config_dirty(vdev_t *vd)
} else {
ASSERT(vd == vd->vdev_top);
- if (!list_link_active(&vd->vdev_dirty_node))
- list_insert_head(&spa->spa_dirty_list, vd);
+ if (!list_link_active(&vd->vdev_config_dirty_node))
+ list_insert_head(&spa->spa_config_dirty_list, vd);
}
}
@@ -1783,14 +2162,58 @@ vdev_config_clean(vdev_t *vd)
{
spa_t *spa = vd->vdev_spa;
- ASSERT(spa_config_held(spa, RW_WRITER) ||
- dsl_pool_sync_context(spa_get_dsl(spa)));
+ ASSERT(spa_config_held(spa, SCL_CONFIG, RW_WRITER) ||
+ (dsl_pool_sync_context(spa_get_dsl(spa)) &&
+ spa_config_held(spa, SCL_CONFIG, RW_READER)));
+
+ ASSERT(list_link_active(&vd->vdev_config_dirty_node));
+ list_remove(&spa->spa_config_dirty_list, vd);
+}
+
+/*
+ * Mark a top-level vdev's state as dirty, so that the next pass of
+ * spa_sync() can convert this into vdev_config_dirty(). We distinguish
+ * the state changes from larger config changes because they require
+ * much less locking, and are often needed for administrative actions.
+ */
+void
+vdev_state_dirty(vdev_t *vd)
+{
+ spa_t *spa = vd->vdev_spa;
+
+ ASSERT(vd == vd->vdev_top);
+
+ /*
+ * The state list is protected by the SCL_STATE lock. The caller
+ * must either hold SCL_STATE as writer, or must be the sync thread
+ * (which holds SCL_STATE as reader). There's only one sync thread,
+ * so this is sufficient to ensure mutual exclusion.
+ */
+ ASSERT(spa_config_held(spa, SCL_STATE, RW_WRITER) ||
+ (dsl_pool_sync_context(spa_get_dsl(spa)) &&
+ spa_config_held(spa, SCL_STATE, RW_READER)));
- ASSERT(list_link_active(&vd->vdev_dirty_node));
- list_remove(&spa->spa_dirty_list, vd);
+ if (!list_link_active(&vd->vdev_state_dirty_node))
+ list_insert_head(&spa->spa_state_dirty_list, vd);
}
void
+vdev_state_clean(vdev_t *vd)
+{
+ spa_t *spa = vd->vdev_spa;
+
+ ASSERT(spa_config_held(spa, SCL_STATE, RW_WRITER) ||
+ (dsl_pool_sync_context(spa_get_dsl(spa)) &&
+ spa_config_held(spa, SCL_STATE, RW_READER)));
+
+ ASSERT(list_link_active(&vd->vdev_state_dirty_node));
+ list_remove(&spa->spa_state_dirty_list, vd);
+}
+
+/*
+ * Propagate vdev state up from children to parent.
+ */
+void
vdev_propagate_state(vdev_t *vd)
{
vdev_t *rvd = vd->vdev_spa->spa_root_vdev;
@@ -1799,28 +2222,45 @@ vdev_propagate_state(vdev_t *vd)
int c;
vdev_t *child;
- for (c = 0; c < vd->vdev_children; c++) {
- child = vd->vdev_child[c];
- if (child->vdev_state <= VDEV_STATE_CANT_OPEN)
- faulted++;
- else if (child->vdev_state == VDEV_STATE_DEGRADED)
- degraded++;
-
- if (child->vdev_stat.vs_aux == VDEV_AUX_CORRUPT_DATA)
- corrupted++;
- }
+ if (vd->vdev_children > 0) {
+ for (c = 0; c < vd->vdev_children; c++) {
+ child = vd->vdev_child[c];
+
+ if (!vdev_readable(child) ||
+ (!vdev_writeable(child) && (spa_mode & FWRITE))) {
+ /*
+ * Root special: if there is a top-level log
+ * device, treat the root vdev as if it were
+ * degraded.
+ */
+ if (child->vdev_islog && vd == rvd)
+ degraded++;
+ else
+ faulted++;
+ } else if (child->vdev_state <= VDEV_STATE_DEGRADED) {
+ degraded++;
+ }
- vd->vdev_ops->vdev_op_state_change(vd, faulted, degraded);
+ if (child->vdev_stat.vs_aux == VDEV_AUX_CORRUPT_DATA)
+ corrupted++;
+ }
- /*
- * Root special: if there is a toplevel vdev that cannot be
- * opened due to corrupted metadata, then propagate the root
- * vdev's aux state as 'corrupt' rather than 'insufficient
- * replicas'.
- */
- if (corrupted && vd == rvd && rvd->vdev_state == VDEV_STATE_CANT_OPEN)
- vdev_set_state(rvd, B_FALSE, VDEV_STATE_CANT_OPEN,
- VDEV_AUX_CORRUPT_DATA);
+ vd->vdev_ops->vdev_op_state_change(vd, faulted, degraded);
+
+ /*
+ * Root special: if there is a top-level vdev that cannot be
+ * opened due to corrupted metadata, then propagate the root
+ * vdev's aux state as 'corrupt' rather than 'insufficient
+ * replicas'.
+ */
+ if (corrupted && vd == rvd &&
+ rvd->vdev_state == VDEV_STATE_CANT_OPEN)
+ vdev_set_state(rvd, B_FALSE, VDEV_STATE_CANT_OPEN,
+ VDEV_AUX_CORRUPT_DATA);
+ }
+
+ if (vd->vdev_parent)
+ vdev_propagate_state(vd->vdev_parent);
}
/*
@@ -1835,6 +2275,7 @@ void
vdev_set_state(vdev_t *vd, boolean_t isopen, vdev_state_t state, vdev_aux_t aux)
{
uint64_t save_state;
+ spa_t *spa = vd->vdev_spa;
if (state == vd->vdev_state) {
vd->vdev_stat.vs_aux = aux;
@@ -1857,14 +2298,36 @@ vdev_set_state(vdev_t *vd, boolean_t isopen, vdev_state_t state, vdev_aux_t aux)
if (vdev_is_dead(vd) && vd->vdev_ops->vdev_op_leaf)
vd->vdev_ops->vdev_op_close(vd);
- if (state == VDEV_STATE_CANT_OPEN) {
+ if (vd->vdev_removed &&
+ state == VDEV_STATE_CANT_OPEN &&
+ (aux == VDEV_AUX_OPEN_FAILED || vd->vdev_checkremove)) {
+ /*
+ * If the previous state is set to VDEV_STATE_REMOVED, then this
+ * device was previously marked removed and someone attempted to
+ * reopen it. If this failed due to a nonexistent device, then
+ * keep the device in the REMOVED state. We also let this be if
+ * it is one of our special test online cases, which is only
+ * attempting to online the device and shouldn't generate an FMA
+ * fault.
+ */
+ vd->vdev_state = VDEV_STATE_REMOVED;
+ vd->vdev_stat.vs_aux = VDEV_AUX_NONE;
+ } else if (state == VDEV_STATE_REMOVED) {
+ /*
+ * Indicate to the ZFS DE that this device has been removed, and
+ * any recent errors should be ignored.
+ */
+ zfs_post_remove(spa, vd);
+ vd->vdev_removed = B_TRUE;
+ } else if (state == VDEV_STATE_CANT_OPEN) {
/*
* If we fail to open a vdev during an import, we mark it as
* "not available", which signifies that it was never there to
* begin with. Failure to open such a device is not considered
* an error.
*/
- if (vd->vdev_spa->spa_load_state == SPA_LOAD_IMPORT &&
+ if (spa->spa_load_state == SPA_LOAD_IMPORT &&
+ !spa->spa_import_faulted &&
vd->vdev_ops->vdev_op_leaf)
vd->vdev_not_present = 1;
@@ -1874,9 +2337,18 @@ vdev_set_state(vdev_t *vd, boolean_t isopen, vdev_state_t state, vdev_aux_t aux)
* that this is part of a vdev_reopen(). In this case, we don't
* want to post the ereport if the device was already in the
* CANT_OPEN state beforehand.
+ *
+ * If the 'checkremove' flag is set, then this is an attempt to
+ * online the device in response to an insertion event. If we
+ * hit this case, then we have detected an insertion event for a
+ * faulted or offline device that wasn't in the removed state.
+ * In this scenario, we don't post an ereport because we are
+ * about to replace the device, or attempt an online with
+ * vdev_forcefault, which will generate the fault for us.
*/
- if (vd->vdev_prevstate != state && !vd->vdev_not_present &&
- vd != vd->vdev_spa->spa_root_vdev) {
+ if ((vd->vdev_prevstate != state || vd->vdev_forcefault) &&
+ !vd->vdev_not_present && !vd->vdev_checkremove &&
+ vd != spa->spa_root_vdev) {
const char *class;
switch (aux) {
@@ -1898,18 +2370,54 @@ vdev_set_state(vdev_t *vd, boolean_t isopen, vdev_state_t state, vdev_aux_t aux)
case VDEV_AUX_BAD_LABEL:
class = FM_EREPORT_ZFS_DEVICE_BAD_LABEL;
break;
+ case VDEV_AUX_IO_FAILURE:
+ class = FM_EREPORT_ZFS_IO_FAILURE;
+ break;
default:
class = FM_EREPORT_ZFS_DEVICE_UNKNOWN;
}
- zfs_ereport_post(class, vd->vdev_spa,
- vd, NULL, save_state, 0);
+ zfs_ereport_post(class, spa, vd, NULL, save_state, 0);
}
+
+ /* Erase any notion of persistent removed state */
+ vd->vdev_removed = B_FALSE;
+ } else {
+ vd->vdev_removed = B_FALSE;
}
- if (isopen)
- return;
+ if (!isopen)
+ vdev_propagate_state(vd);
+}
- if (vd->vdev_parent != NULL)
- vdev_propagate_state(vd->vdev_parent);
+/*
+ * Check the vdev configuration to ensure that it's capable of supporting
+ * a root pool. Currently, we do not support RAID-Z or partial configuration.
+ * In addition, only a single top-level vdev is allowed and none of the leaves
+ * can be wholedisks.
+ */
+boolean_t
+vdev_is_bootable(vdev_t *vd)
+{
+ int c;
+
+ if (!vd->vdev_ops->vdev_op_leaf) {
+ char *vdev_type = vd->vdev_ops->vdev_op_type;
+
+ if (strcmp(vdev_type, VDEV_TYPE_ROOT) == 0 &&
+ vd->vdev_children > 1) {
+ return (B_FALSE);
+ } else if (strcmp(vdev_type, VDEV_TYPE_RAIDZ) == 0 ||
+ strcmp(vdev_type, VDEV_TYPE_MISSING) == 0) {
+ return (B_FALSE);
+ }
+ } else if (vd->vdev_wholedisk == 1) {
+ return (B_FALSE);
+ }
+
+ for (c = 0; c < vd->vdev_children; c++) {
+ if (!vdev_is_bootable(vd->vdev_child[c]))
+ return (B_FALSE);
+ }
+ return (B_TRUE);
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_cache.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_cache.c
index 4e419b6..aa8f6f0 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_cache.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_cache.c
@@ -19,16 +19,15 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/zfs_context.h>
#include <sys/spa.h>
#include <sys/vdev_impl.h>
#include <sys/zio.h>
+#include <sys/kstat.h>
/*
* Virtual device read-ahead caching.
@@ -36,15 +35,16 @@
* This file implements a simple LRU read-ahead cache. When the DMU reads
* a given block, it will often want other, nearby blocks soon thereafter.
* We take advantage of this by reading a larger disk region and caching
- * the result. In the best case, this can turn 256 back-to-back 512-byte
- * reads into a single 128k read followed by 255 cache hits; this reduces
+ * the result. In the best case, this can turn 128 back-to-back 512-byte
+ * reads into a single 64k read followed by 127 cache hits; this reduces
* latency dramatically. In the worst case, it can turn an isolated 512-byte
- * read into a 128k read, which doesn't affect latency all that much but is
+ * read into a 64k read, which doesn't affect latency all that much but is
* terribly wasteful of bandwidth. A more intelligent version of the cache
* could keep track of access patterns and not do read-ahead unless it sees
- * at least two temporally close I/Os to the same region. It could also
- * take advantage of semantic information about the I/O. And it could use
- * something faster than an AVL tree; that was chosen solely for convenience.
+ * at least two temporally close I/Os to the same region. Currently, only
+ * metadata I/O is inflated. A futher enhancement could take advantage of
+ * more semantic information about the I/O. And it could use something
+ * faster than an AVL tree; that was chosen solely for convenience.
*
* There are five cache operations: allocate, fill, read, write, evict.
*
@@ -69,13 +69,15 @@
/*
* All i/os smaller than zfs_vdev_cache_max will be turned into
* 1<<zfs_vdev_cache_bshift byte reads by the vdev_cache (aka software
- * track buffer. At most zfs_vdev_cache_size bytes will be kept in each
+ * track buffer). At most zfs_vdev_cache_size bytes will be kept in each
* vdev's vdev_cache.
*/
-int zfs_vdev_cache_max = 1<<14;
-int zfs_vdev_cache_size = 10ULL << 20;
+int zfs_vdev_cache_max = 1<<14; /* 16KB */
+int zfs_vdev_cache_size = 10ULL << 20; /* 10MB */
int zfs_vdev_cache_bshift = 16;
+#define VCBS (1 << zfs_vdev_cache_bshift) /* 64KB */
+
SYSCTL_DECL(_vfs_zfs_vdev);
SYSCTL_NODE(_vfs_zfs_vdev, OID_AUTO, cache, CTLFLAG_RW, 0, "ZFS VDEV Cache");
TUNABLE_INT("vfs.zfs.vdev.cache.max", &zfs_vdev_cache_max);
@@ -84,8 +86,25 @@ SYSCTL_INT(_vfs_zfs_vdev_cache, OID_AUTO, max, CTLFLAG_RDTUN,
TUNABLE_INT("vfs.zfs.vdev.cache.size", &zfs_vdev_cache_size);
SYSCTL_INT(_vfs_zfs_vdev_cache, OID_AUTO, size, CTLFLAG_RDTUN,
&zfs_vdev_cache_size, 0, "Size of VDEV cache");
+TUNABLE_INT("vfs.zfs.vdev.cache.bshift", &zfs_vdev_cache_bshift);
+SYSCTL_INT(_vfs_zfs_vdev_cache, OID_AUTO, bshift, CTLFLAG_RDTUN,
+ &zfs_vdev_cache_bshift, 0, "Turn too small requests into 1 << this value");
+
+kstat_t *vdc_ksp = NULL;
+
+typedef struct vdc_stats {
+ kstat_named_t vdc_stat_delegations;
+ kstat_named_t vdc_stat_hits;
+ kstat_named_t vdc_stat_misses;
+} vdc_stats_t;
+
+static vdc_stats_t vdc_stats = {
+ { "delegations", KSTAT_DATA_UINT64 },
+ { "hits", KSTAT_DATA_UINT64 },
+ { "misses", KSTAT_DATA_UINT64 }
+};
-#define VCBS (1 << zfs_vdev_cache_bshift)
+#define VDCSTAT_BUMP(stat) atomic_add_64(&vdc_stats.stat.value.ui64, 1);
static int
vdev_cache_offset_compare(const void *a1, const void *a2)
@@ -127,10 +146,6 @@ vdev_cache_evict(vdev_cache_t *vc, vdev_cache_entry_t *ve)
ASSERT(ve->ve_fill_io == NULL);
ASSERT(ve->ve_data != NULL);
- dprintf("evicting %p, off %llx, LRU %llu, age %lu, hits %u, stale %u\n",
- vc, ve->ve_offset, ve->ve_lastused, LBOLT - ve->ve_lastused,
- ve->ve_hits, ve->ve_missed_update);
-
avl_remove(&vc->vc_lastused_tree, ve);
avl_remove(&vc->vc_offset_tree, ve);
zio_buf_free(ve->ve_data, VCBS);
@@ -161,10 +176,8 @@ vdev_cache_allocate(zio_t *zio)
if ((avl_numnodes(&vc->vc_lastused_tree) << zfs_vdev_cache_bshift) >
zfs_vdev_cache_size) {
ve = avl_first(&vc->vc_lastused_tree);
- if (ve->ve_fill_io != NULL) {
- dprintf("can't evict in %p, still filling\n", vc);
+ if (ve->ve_fill_io != NULL)
return (NULL);
- }
ASSERT(ve->ve_hits != 0);
vdev_cache_evict(vc, ve);
}
@@ -239,7 +252,7 @@ vdev_cache_fill(zio_t *zio)
zio->io_delegate_list = dio->io_delegate_next;
dio->io_delegate_next = NULL;
dio->io_error = zio->io_error;
- zio_next_stage(dio);
+ zio_execute(dio);
}
}
@@ -287,6 +300,7 @@ vdev_cache_read(zio_t *zio)
fio->io_delegate_list = zio;
zio_vdev_io_bypass(zio);
mutex_exit(&vc->vc_lock);
+ VDCSTAT_BUMP(vdc_stat_delegations);
return (0);
}
@@ -294,7 +308,8 @@ vdev_cache_read(zio_t *zio)
zio_vdev_io_bypass(zio);
mutex_exit(&vc->vc_lock);
- zio_next_stage(zio);
+ zio_execute(zio);
+ VDCSTAT_BUMP(vdc_stat_hits);
return (0);
}
@@ -305,11 +320,9 @@ vdev_cache_read(zio_t *zio)
return (ENOMEM);
}
- fio = zio_vdev_child_io(zio, NULL, zio->io_vd, cache_offset,
+ fio = zio_vdev_delegated_io(zio->io_vd, cache_offset,
ve->ve_data, VCBS, ZIO_TYPE_READ, ZIO_PRIORITY_CACHE_FILL,
- ZIO_FLAG_DONT_CACHE | ZIO_FLAG_DONT_PROPAGATE |
- ZIO_FLAG_DONT_RETRY | ZIO_FLAG_NOBOOKMARK,
- vdev_cache_fill, ve);
+ ZIO_FLAG_DONT_CACHE, vdev_cache_fill, ve);
ve->ve_fill_io = fio;
fio->io_delegate_list = zio;
@@ -317,6 +330,7 @@ vdev_cache_read(zio_t *zio)
mutex_exit(&vc->vc_lock);
zio_nowait(fio);
+ VDCSTAT_BUMP(vdc_stat_misses);
return (0);
}
@@ -361,6 +375,18 @@ vdev_cache_write(zio_t *zio)
}
void
+vdev_cache_purge(vdev_t *vd)
+{
+ vdev_cache_t *vc = &vd->vdev_cache;
+ vdev_cache_entry_t *ve;
+
+ mutex_enter(&vc->vc_lock);
+ while ((ve = avl_first(&vc->vc_offset_tree)) != NULL)
+ vdev_cache_evict(vc, ve);
+ mutex_exit(&vc->vc_lock);
+}
+
+void
vdev_cache_init(vdev_t *vd)
{
vdev_cache_t *vc = &vd->vdev_cache;
@@ -380,15 +406,32 @@ void
vdev_cache_fini(vdev_t *vd)
{
vdev_cache_t *vc = &vd->vdev_cache;
- vdev_cache_entry_t *ve;
- mutex_enter(&vc->vc_lock);
- while ((ve = avl_first(&vc->vc_offset_tree)) != NULL)
- vdev_cache_evict(vc, ve);
- mutex_exit(&vc->vc_lock);
+ vdev_cache_purge(vd);
avl_destroy(&vc->vc_offset_tree);
avl_destroy(&vc->vc_lastused_tree);
mutex_destroy(&vc->vc_lock);
}
+
+void
+vdev_cache_stat_init(void)
+{
+ vdc_ksp = kstat_create("zfs", 0, "vdev_cache_stats", "misc",
+ KSTAT_TYPE_NAMED, sizeof (vdc_stats) / sizeof (kstat_named_t),
+ KSTAT_FLAG_VIRTUAL);
+ if (vdc_ksp != NULL) {
+ vdc_ksp->ks_data = &vdc_stats;
+ kstat_install(vdc_ksp);
+ }
+}
+
+void
+vdev_cache_stat_fini(void)
+{
+ if (vdc_ksp != NULL) {
+ kstat_delete(vdc_ksp);
+ vdc_ksp = NULL;
+ }
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_disk.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_disk.c
index b965b1c..35d4e2a 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_disk.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_disk.c
@@ -19,19 +19,19 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/zfs_context.h>
#include <sys/spa.h>
+#include <sys/refcount.h>
#include <sys/vdev_disk.h>
#include <sys/vdev_impl.h>
#include <sys/fs/zfs.h>
#include <sys/zio.h>
#include <sys/sunldi.h>
+#include <sys/fm/fs/zfs.h>
/*
* Virtual device vector for disks.
@@ -50,6 +50,8 @@ vdev_disk_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift)
vdev_disk_t *dvd;
struct dk_minfo dkm;
int error;
+ dev_t dev;
+ int otyp;
/*
* We must have a pathname, and it must be absolute.
@@ -77,6 +79,11 @@ vdev_disk_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift)
* 3. Otherwise, the device may have moved. Try opening the device
* by the devid instead.
*
+ * If the vdev is part of the root pool, we avoid opening it by path.
+ * We do this because there is no /dev path available early in boot,
+ * and if we try to open the device by path at a later point, we can
+ * deadlock when devfsadm attempts to open the underlying backing store
+ * file.
*/
if (vd->vdev_devid != NULL) {
if (ddi_devid_str_decode(vd->vdev_devid, &dvd->vd_devid,
@@ -88,7 +95,7 @@ vdev_disk_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift)
error = EINVAL; /* presume failure */
- if (vd->vdev_path != NULL) {
+ if (vd->vdev_path != NULL && !spa_is_root(vd->vdev_spa)) {
ddi_devid_t devid;
if (vd->vdev_wholedisk == -1ULL) {
@@ -141,12 +148,60 @@ vdev_disk_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift)
error = ldi_open_by_devid(dvd->vd_devid, dvd->vd_minor,
spa_mode, kcred, &dvd->vd_lh, zfs_li);
+ /*
+ * If all else fails, then try opening by physical path (if available)
+ * or the logical path (if we failed due to the devid check). While not
+ * as reliable as the devid, this will give us something, and the higher
+ * level vdev validation will prevent us from opening the wrong device.
+ */
+ if (error) {
+ if (vd->vdev_physpath != NULL &&
+ (dev = ddi_pathname_to_dev_t(vd->vdev_physpath)) != ENODEV)
+ error = ldi_open_by_dev(&dev, OTYP_BLK, spa_mode,
+ kcred, &dvd->vd_lh, zfs_li);
+
+ /*
+ * Note that we don't support the legacy auto-wholedisk support
+ * as above. This hasn't been used in a very long time and we
+ * don't need to propagate its oddities to this edge condition.
+ */
+ if (error && vd->vdev_path != NULL &&
+ !spa_is_root(vd->vdev_spa))
+ error = ldi_open_by_name(vd->vdev_path, spa_mode, kcred,
+ &dvd->vd_lh, zfs_li);
+ }
+
if (error) {
vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED;
return (error);
}
/*
+ * Once a device is opened, verify that the physical device path (if
+ * available) is up to date.
+ */
+ if (ldi_get_dev(dvd->vd_lh, &dev) == 0 &&
+ ldi_get_otyp(dvd->vd_lh, &otyp) == 0) {
+ char *physpath, *minorname;
+
+ physpath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+ minorname = NULL;
+ if (ddi_dev_pathname(dev, otyp, physpath) == 0 &&
+ ldi_get_minor_name(dvd->vd_lh, &minorname) == 0 &&
+ (vd->vdev_physpath == NULL ||
+ strcmp(vd->vdev_physpath, physpath) != 0)) {
+ if (vd->vdev_physpath)
+ spa_strfree(vd->vdev_physpath);
+ (void) strlcat(physpath, ":", MAXPATHLEN);
+ (void) strlcat(physpath, minorname, MAXPATHLEN);
+ vd->vdev_physpath = spa_strdup(physpath);
+ }
+ if (minorname)
+ kmem_free(minorname, strlen(minorname) + 1);
+ kmem_free(physpath, MAXPATHLEN);
+ }
+
+ /*
* Determine the actual size of the device.
*/
if (ldi_get_size(dvd->vd_lh, psize) != 0) {
@@ -191,10 +246,6 @@ vdev_disk_close(vdev_t *vd)
if (dvd == NULL)
return;
- dprintf("removing disk %s, devid %s\n",
- vd->vdev_path ? vd->vdev_path : "<none>",
- vd->vdev_devid ? vd->vdev_devid : "<none>");
-
if (dvd->vd_minor != NULL)
ddi_devid_str_free(dvd->vd_minor);
@@ -208,18 +259,59 @@ vdev_disk_close(vdev_t *vd)
vd->vdev_tsd = NULL;
}
+int
+vdev_disk_physio(ldi_handle_t vd_lh, caddr_t data, size_t size,
+ uint64_t offset, int flags)
+{
+ buf_t *bp;
+ int error = 0;
+
+ if (vd_lh == NULL)
+ return (EINVAL);
+
+ ASSERT(flags & B_READ || flags & B_WRITE);
+
+ bp = getrbuf(KM_SLEEP);
+ bp->b_flags = flags | B_BUSY | B_NOCACHE | B_FAILFAST;
+ bp->b_bcount = size;
+ bp->b_un.b_addr = (void *)data;
+ bp->b_lblkno = lbtodb(offset);
+ bp->b_bufsize = size;
+
+ error = ldi_strategy(vd_lh, bp);
+ ASSERT(error == 0);
+ if ((error = biowait(bp)) == 0 && bp->b_resid != 0)
+ error = EIO;
+ freerbuf(bp);
+
+ return (error);
+}
+
static void
vdev_disk_io_intr(buf_t *bp)
{
vdev_disk_buf_t *vdb = (vdev_disk_buf_t *)bp;
zio_t *zio = vdb->vdb_io;
- if ((zio->io_error = geterror(bp)) == 0 && bp->b_resid != 0)
+ /*
+ * The rest of the zio stack only deals with EIO, ECKSUM, and ENXIO.
+ * Rather than teach the rest of the stack about other error
+ * possibilities (EFAULT, etc), we normalize the error value here.
+ */
+ zio->io_error = (geterror(bp) != 0 ? EIO : 0);
+
+ if (zio->io_error == 0 && bp->b_resid != 0)
zio->io_error = EIO;
kmem_free(vdb, sizeof (vdev_disk_buf_t));
- zio_next_stage_async(zio);
+ zio_interrupt(zio);
+}
+
+static void
+vdev_disk_ioctl_free(zio_t *zio)
+{
+ kmem_free(zio->io_vsd, sizeof (struct dk_callback));
}
static void
@@ -229,26 +321,24 @@ vdev_disk_ioctl_done(void *zio_arg, int error)
zio->io_error = error;
- zio_next_stage_async(zio);
+ zio_interrupt(zio);
}
-static void
+static int
vdev_disk_io_start(zio_t *zio)
{
vdev_t *vd = zio->io_vd;
vdev_disk_t *dvd = vd->vdev_tsd;
vdev_disk_buf_t *vdb;
+ struct dk_callback *dkc;
buf_t *bp;
- int flags, error;
+ int error;
if (zio->io_type == ZIO_TYPE_IOCTL) {
- zio_vdev_io_bypass(zio);
-
/* XXPOLICY */
- if (vdev_is_dead(vd)) {
+ if (!vdev_readable(vd)) {
zio->io_error = ENXIO;
- zio_next_stage_async(zio);
- return;
+ return (ZIO_PIPELINE_CONTINUE);
}
switch (zio->io_cmd) {
@@ -263,12 +353,15 @@ vdev_disk_io_start(zio_t *zio)
break;
}
- zio->io_dk_callback.dkc_callback = vdev_disk_ioctl_done;
- zio->io_dk_callback.dkc_cookie = zio;
+ zio->io_vsd = dkc = kmem_alloc(sizeof (*dkc), KM_SLEEP);
+ zio->io_vsd_free = vdev_disk_ioctl_free;
+
+ dkc->dkc_callback = vdev_disk_ioctl_done;
+ dkc->dkc_flag = FLUSH_VOLATILE;
+ dkc->dkc_cookie = zio;
error = ldi_ioctl(dvd->vd_lh, zio->io_cmd,
- (uintptr_t)&zio->io_dk_callback,
- FKIOCTL, kcred, NULL);
+ (uintptr_t)dkc, FKIOCTL, kcred, NULL);
if (error == 0) {
/*
@@ -276,13 +369,16 @@ vdev_disk_io_start(zio_t *zio)
* and will call vdev_disk_ioctl_done()
* upon completion.
*/
- return;
- } else if (error == ENOTSUP) {
+ return (ZIO_PIPELINE_STOP);
+ }
+
+ if (error == ENOTSUP || error == ENOTTY) {
/*
- * If we get ENOTSUP, we know that no future
- * attempts will ever succeed. In this case we
- * set a persistent bit so that we don't bother
- * with the ioctl in the future.
+ * If we get ENOTSUP or ENOTTY, we know that
+ * no future attempts will ever succeed.
+ * In this case we set a persistent bit so
+ * that we don't bother with the ioctl in the
+ * future.
*/
vd->vdev_nowritecache = B_TRUE;
}
@@ -294,61 +390,51 @@ vdev_disk_io_start(zio_t *zio)
zio->io_error = ENOTSUP;
}
- zio_next_stage_async(zio);
- return;
+ return (ZIO_PIPELINE_CONTINUE);
}
- if (zio->io_type == ZIO_TYPE_READ && vdev_cache_read(zio) == 0)
- return;
-
- if ((zio = vdev_queue_io(zio)) == NULL)
- return;
-
- flags = (zio->io_type == ZIO_TYPE_READ ? B_READ : B_WRITE);
- flags |= B_BUSY | B_NOCACHE;
- if (zio->io_flags & ZIO_FLAG_FAILFAST)
- flags |= B_FAILFAST;
-
vdb = kmem_alloc(sizeof (vdev_disk_buf_t), KM_SLEEP);
vdb->vdb_io = zio;
bp = &vdb->vdb_buf;
bioinit(bp);
- bp->b_flags = flags;
+ bp->b_flags = B_BUSY | B_NOCACHE |
+ (zio->io_type == ZIO_TYPE_READ ? B_READ : B_WRITE) |
+ ((zio->io_flags & ZIO_FLAG_IO_RETRY) ? 0 : B_FAILFAST);
bp->b_bcount = zio->io_size;
bp->b_un.b_addr = zio->io_data;
bp->b_lblkno = lbtodb(zio->io_offset);
bp->b_bufsize = zio->io_size;
bp->b_iodone = (int (*)())vdev_disk_io_intr;
- /* XXPOLICY */
- error = vdev_is_dead(vd) ? ENXIO : vdev_error_inject(vd, zio);
- if (error) {
- zio->io_error = error;
- bioerror(bp, error);
- bp->b_resid = bp->b_bcount;
- bp->b_iodone(bp);
- return;
- }
-
- error = ldi_strategy(dvd->vd_lh, bp);
/* ldi_strategy() will return non-zero only on programming errors */
- ASSERT(error == 0);
+ VERIFY(ldi_strategy(dvd->vd_lh, bp) == 0);
+
+ return (ZIO_PIPELINE_STOP);
}
static void
vdev_disk_io_done(zio_t *zio)
{
- vdev_queue_io_done(zio);
-
- if (zio->io_type == ZIO_TYPE_WRITE)
- vdev_cache_write(zio);
-
- if (zio_injection_enabled && zio->io_error == 0)
- zio->io_error = zio_handle_device_injection(zio->io_vd, EIO);
+ vdev_t *vd = zio->io_vd;
- zio_next_stage(zio);
+ /*
+ * If the device returned EIO, then attempt a DKIOCSTATE ioctl to see if
+ * the device has been removed. If this is the case, then we trigger an
+ * asynchronous removal of the device. Otherwise, probe the device and
+ * make sure it's still accessible.
+ */
+ if (zio->io_error == EIO) {
+ vdev_disk_t *dvd = vd->vdev_tsd;
+ int state = DKIO_NONE;
+
+ if (ldi_ioctl(dvd->vd_lh, DKIOCSTATE, (intptr_t)&state,
+ FKIOCTL, kcred, NULL) == 0 && state != DKIO_INSERTED) {
+ vd->vdev_remove_wanted = B_TRUE;
+ spa_async_request(zio->io_spa, SPA_ASYNC_REMOVE);
+ }
+ }
}
vdev_ops_t vdev_disk_ops = {
@@ -361,3 +447,80 @@ vdev_ops_t vdev_disk_ops = {
VDEV_TYPE_DISK, /* name of this vdev type */
B_TRUE /* leaf vdev */
};
+
+/*
+ * Given the root disk device devid or pathname, read the label from
+ * the device, and construct a configuration nvlist.
+ */
+int
+vdev_disk_read_rootlabel(char *devpath, char *devid, nvlist_t **config)
+{
+ ldi_handle_t vd_lh;
+ vdev_label_t *label;
+ uint64_t s, size;
+ int l;
+ ddi_devid_t tmpdevid;
+ int error = -1;
+ char *minor_name;
+
+ /*
+ * Read the device label and build the nvlist.
+ */
+ if (devid != NULL && ddi_devid_str_decode(devid, &tmpdevid,
+ &minor_name) == 0) {
+ error = ldi_open_by_devid(tmpdevid, minor_name,
+ spa_mode, kcred, &vd_lh, zfs_li);
+ ddi_devid_free(tmpdevid);
+ ddi_devid_str_free(minor_name);
+ }
+
+ if (error && (error = ldi_open_by_name(devpath, FREAD, kcred, &vd_lh,
+ zfs_li)))
+ return (error);
+
+ if (ldi_get_size(vd_lh, &s)) {
+ (void) ldi_close(vd_lh, FREAD, kcred);
+ return (EIO);
+ }
+
+ size = P2ALIGN_TYPED(s, sizeof (vdev_label_t), uint64_t);
+ label = kmem_alloc(sizeof (vdev_label_t), KM_SLEEP);
+
+ for (l = 0; l < VDEV_LABELS; l++) {
+ uint64_t offset, state, txg = 0;
+
+ /* read vdev label */
+ offset = vdev_label_offset(size, l, 0);
+ if (vdev_disk_physio(vd_lh, (caddr_t)label,
+ VDEV_SKIP_SIZE + VDEV_BOOT_HEADER_SIZE +
+ VDEV_PHYS_SIZE, offset, B_READ) != 0)
+ continue;
+
+ if (nvlist_unpack(label->vl_vdev_phys.vp_nvlist,
+ sizeof (label->vl_vdev_phys.vp_nvlist), config, 0) != 0) {
+ *config = NULL;
+ continue;
+ }
+
+ if (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_STATE,
+ &state) != 0 || state >= POOL_STATE_DESTROYED) {
+ nvlist_free(*config);
+ *config = NULL;
+ continue;
+ }
+
+ if (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_TXG,
+ &txg) != 0 || txg == 0) {
+ nvlist_free(*config);
+ *config = NULL;
+ continue;
+ }
+
+ break;
+ }
+
+ kmem_free(label, sizeof (vdev_label_t));
+ (void) ldi_close(vd_lh, FREAD, kcred);
+
+ return (error);
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_file.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_file.c
index ab2d34c..673b633 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_file.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_file.c
@@ -19,18 +19,17 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/zfs_context.h>
#include <sys/spa.h>
#include <sys/vdev_file.h>
#include <sys/vdev_impl.h>
#include <sys/zio.h>
#include <sys/fs/zfs.h>
+#include <sys/fm/fs/zfs.h>
/*
* Virtual device vector for files.
@@ -61,8 +60,8 @@ vdev_file_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift)
* to local zone users, so the underlying devices should be as well.
*/
ASSERT(vd->vdev_path != NULL && vd->vdev_path[0] == '/');
- error = vn_openat(vd->vdev_path + 1, UIO_SYSSPACE, spa_mode | FOFFMAX,
- 0, &vp, 0, 0, rootdir);
+ error = vn_openat(vd->vdev_path + 1, UIO_SYSSPACE,
+ spa_mode | FOFFMAX, 0, &vp, 0, 0, rootdir, -1);
if (error) {
vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED;
@@ -80,12 +79,13 @@ vdev_file_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift)
return (ENODEV);
}
#endif
-
/*
* Determine the physical size of the file.
*/
vattr.va_mask = AT_SIZE;
- error = VOP_GETATTR(vp, &vattr, 0);
+ vn_lock(vp, LK_SHARED | LK_RETRY);
+ error = VOP_GETATTR(vp, &vattr, kcred);
+ VOP_UNLOCK(vp, 0);
if (error) {
vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED;
return (error);
@@ -101,71 +101,46 @@ static void
vdev_file_close(vdev_t *vd)
{
vdev_file_t *vf = vd->vdev_tsd;
+ int vfslocked;
if (vf == NULL)
return;
if (vf->vf_vnode != NULL) {
- (void) VOP_PUTPAGE(vf->vf_vnode, 0, 0, B_INVAL, kcred);
- (void) VOP_CLOSE(vf->vf_vnode, spa_mode, 1, 0, kcred);
+ vfslocked = VFS_LOCK_GIANT(vf->vf_vnode->v_mount);
+ (void)vn_close(vf->vf_vnode, spa_mode, kcred, curthread);
VN_RELE(vf->vf_vnode);
+ VFS_UNLOCK_GIANT(vfslocked);
}
kmem_free(vf, sizeof (vdev_file_t));
vd->vdev_tsd = NULL;
}
-static void
+static int
vdev_file_io_start(zio_t *zio)
{
vdev_t *vd = zio->io_vd;
vdev_file_t *vf = vd->vdev_tsd;
ssize_t resid;
- int error;
if (zio->io_type == ZIO_TYPE_IOCTL) {
- zio_vdev_io_bypass(zio);
-
/* XXPOLICY */
- if (vdev_is_dead(vd)) {
+ if (!vdev_readable(vd)) {
zio->io_error = ENXIO;
- zio_next_stage_async(zio);
- return;
+ return (ZIO_PIPELINE_CONTINUE);
}
switch (zio->io_cmd) {
case DKIOCFLUSHWRITECACHE:
zio->io_error = VOP_FSYNC(vf->vf_vnode, FSYNC | FDSYNC,
- kcred);
- dprintf("fsync(%s) = %d\n", vdev_description(vd),
- zio->io_error);
+ kcred, NULL);
break;
default:
zio->io_error = ENOTSUP;
}
- zio_next_stage_async(zio);
- return;
- }
-
- /*
- * In the kernel, don't bother double-caching, but in userland,
- * we want to test the vdev_cache code.
- */
-#ifndef _KERNEL
- if (zio->io_type == ZIO_TYPE_READ && vdev_cache_read(zio) == 0)
- return;
-#endif
-
- if ((zio = vdev_queue_io(zio)) == NULL)
- return;
-
- /* XXPOLICY */
- error = vdev_is_dead(vd) ? ENXIO : vdev_error_inject(vd, zio);
- if (error) {
- zio->io_error = error;
- zio_next_stage_async(zio);
- return;
+ return (ZIO_PIPELINE_CONTINUE);
}
zio->io_error = vn_rdwr(zio->io_type == ZIO_TYPE_READ ?
@@ -176,23 +151,15 @@ vdev_file_io_start(zio_t *zio)
if (resid != 0 && zio->io_error == 0)
zio->io_error = ENOSPC;
- zio_next_stage_async(zio);
+ zio_interrupt(zio);
+
+ return (ZIO_PIPELINE_STOP);
}
+/* ARGSUSED */
static void
vdev_file_io_done(zio_t *zio)
{
- vdev_queue_io_done(zio);
-
-#ifndef _KERNEL
- if (zio->io_type == ZIO_TYPE_WRITE)
- vdev_cache_write(zio);
-#endif
-
- if (zio_injection_enabled && zio->io_error == 0)
- zio->io_error = zio_handle_device_injection(zio->io_vd, EIO);
-
- zio_next_stage(zio);
}
vdev_ops_t vdev_file_ops = {
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c
index eebc911..f151f83 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c
@@ -96,13 +96,9 @@ vdev_geom_orphan(struct g_consumer *cp)
g_wither_geom(gp, error);
}
vdev_geom_release(vd);
- /* Both methods below work, but in a bit different way. */
-#if 0
- vd->vdev_reopen_wanted = 1;
-#else
- vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED;
- vdev_set_state(vd, B_TRUE, VDEV_STATE_CANT_OPEN, vd->vdev_stat.vs_aux);
-#endif
+
+ vd->vdev_remove_wanted = B_TRUE;
+ spa_async_request(vd->vdev_spa, SPA_ASYNC_REMOVE);
}
static struct g_consumer *
@@ -229,7 +225,7 @@ vdev_geom_worker(void *arg)
vd->vdev_nowritecache = B_TRUE;
}
g_destroy_bio(bp);
- zio_next_stage_async(zio);
+ zio_interrupt(zio);
}
}
@@ -249,6 +245,194 @@ vdev_geom_get_id(struct g_consumer *cp)
return (id);
}
+static uint64_t
+nvlist_get_guid(nvlist_t *list)
+{
+ nvpair_t *elem = NULL;
+ uint64_t value;
+
+ while ((elem = nvlist_next_nvpair(list, elem)) != NULL) {
+ if (nvpair_type(elem) == DATA_TYPE_UINT64 &&
+ strcmp(nvpair_name(elem), "guid") == 0) {
+ VERIFY(nvpair_value_uint64(elem, &value) == 0);
+ return (value);
+ }
+ }
+ return (0);
+}
+
+static char *
+nvlist_get_devid(nvlist_t *list, uint64_t guid)
+{
+ nvpair_t *elem = NULL;
+ int progress;
+ char *id;
+
+ progress = 0;
+ id = NULL;
+
+ while ((elem = nvlist_next_nvpair(list, elem)) != NULL) {
+ switch (nvpair_type(elem)) {
+ case DATA_TYPE_STRING:
+ {
+ char *value;
+
+ VERIFY(nvpair_value_string(elem, &value) == 0);
+ if (strcmp(nvpair_name(elem), "type") == 0 &&
+ strcmp(value, "disk") == 0) {
+ progress |= 0x01;
+ } else if (strcmp(nvpair_name(elem), "devid") == 0) {
+ progress |= 0x02;
+ id = value;
+ }
+ break;
+ }
+ case DATA_TYPE_UINT64:
+ {
+ uint64_t value;
+
+ VERIFY(nvpair_value_uint64(elem, &value) == 0);
+ if (strcmp(nvpair_name(elem), "guid") == 0 &&
+ value == guid) {
+ progress |= 0x04;
+ }
+ break;
+ }
+ case DATA_TYPE_NVLIST:
+ {
+ nvlist_t *value;
+ char *lid;
+
+ VERIFY(nvpair_value_nvlist(elem, &value) == 0);
+ lid = nvlist_get_devid(value, guid);
+ if (lid != NULL)
+ return (lid);
+ break;
+ }
+ case DATA_TYPE_NVLIST_ARRAY:
+ {
+ nvlist_t **value;
+ u_int c, count;
+ char *lid;
+
+ VERIFY(nvpair_value_nvlist_array(elem, &value,
+ &count) == 0);
+
+ for (c = 0; c < count; c++) {
+ lid = nvlist_get_devid(value[c], guid);
+ if (lid != NULL)
+ return (lid);
+ }
+ break;
+ }
+ }
+ if (progress == 0x07)
+ break;
+ }
+ if (progress != 0x07)
+ id = NULL;
+ return (id);
+}
+
+static int
+vdev_geom_io(struct g_consumer *cp, int cmd, void *data, off_t offset, off_t size)
+{
+ struct bio *bp;
+ u_char *p;
+ off_t off;
+ int error;
+
+ ASSERT((offset % cp->provider->sectorsize) == 0);
+ ASSERT((size % cp->provider->sectorsize) == 0);
+
+ bp = g_alloc_bio();
+ off = offset;
+ offset += size;
+ p = data;
+ error = 0;
+
+ for (; off < offset; off += MAXPHYS, p += MAXPHYS, size -= MAXPHYS) {
+ bzero(bp, sizeof(*bp));
+ bp->bio_cmd = cmd;
+ bp->bio_done = NULL;
+ bp->bio_offset = off;
+ bp->bio_length = MIN(size, MAXPHYS);
+ bp->bio_data = p;
+ g_io_request(bp, cp);
+ error = biowait(bp, "vdev_geom_io");
+ if (error != 0)
+ break;
+ }
+
+ g_destroy_bio(bp);
+ return (error);
+}
+
+static char *
+vdev_geom_read_id(struct g_consumer *cp)
+{
+ struct g_provider *pp;
+ vdev_label_t *label;
+ char *p, *buf;
+ size_t buflen;
+ uint64_t psize;
+ off_t offset, size;
+ char *id;
+ int error, l, len;
+
+ g_topology_assert_not();
+
+ pp = cp->provider;
+
+ psize = pp->mediasize;
+ psize = P2ALIGN(psize, (uint64_t)sizeof(vdev_label_t));
+
+ size = sizeof(*label) + pp->sectorsize -
+ ((sizeof(*label) - 1) % pp->sectorsize) - 1;
+
+ id = NULL;
+ label = kmem_alloc(size, KM_SLEEP);
+ buflen = sizeof(label->vl_vdev_phys.vp_nvlist);
+
+ for (l = 0; l < VDEV_LABELS && id == NULL; l++) {
+ nvlist_t *config = NULL;
+ uint64_t guid;
+
+ offset = vdev_label_offset(psize, l, 0);
+ if ((offset % pp->sectorsize) != 0)
+ continue;
+
+ error = vdev_geom_io(cp, BIO_READ, label, offset, size);
+ if (error != 0)
+ continue;
+ buf = label->vl_vdev_phys.vp_nvlist;
+
+ if (nvlist_unpack(buf, buflen, &config, 0) != 0)
+ continue;
+
+ guid = nvlist_get_guid(config);
+ if (guid == 0) {
+ nvlist_free(config);
+ continue;
+ }
+ id = nvlist_get_devid(config, guid);
+ if (id != NULL) {
+ char *tmp;
+
+ tmp = kmem_zalloc(DISK_IDENT_SIZE, KM_SLEEP);
+ strlcpy(tmp, id, DISK_IDENT_SIZE);
+ id = tmp;
+ }
+
+ nvlist_free(config);
+ }
+
+ kmem_free(label, size);
+ if (id != NULL)
+ ZFS_LOG(1, "ID of %s: %s", pp->name, id);
+ return (id);
+}
+
static void
vdev_geom_free_id(char *id)
{
@@ -290,6 +474,7 @@ vdev_geom_attach_by_id_event(void *arg, int flags __unused)
zgp->orphan = vdev_geom_taste_orphan;
zcp = g_new_consumer(zgp);
+ /* First round tries to get provider's ID without reading metadata. */
LIST_FOREACH(mp, &g_classes, class) {
if (mp == &zfs_vdev_class)
continue;
@@ -324,6 +509,41 @@ vdev_geom_attach_by_id_event(void *arg, int flags __unused)
}
}
}
+ /* Second round looks for ID by reading ZFS metadata. */
+ LIST_FOREACH(mp, &g_classes, class) {
+ if (mp == &zfs_vdev_class)
+ continue;
+ LIST_FOREACH(gp, &mp->geom, geom) {
+ if (gp->flags & G_GEOM_WITHER)
+ continue;
+ LIST_FOREACH(pp, &gp->provider, provider) {
+ if (pp->flags & G_PF_WITHER)
+ continue;
+ g_attach(zcp, pp);
+ if (g_access(zcp, 1, 0, 0) != 0) {
+ g_detach(zcp);
+ continue;
+ }
+ g_topology_unlock();
+ id = vdev_geom_read_id(zcp);
+ g_topology_lock();
+ g_access(zcp, -1, 0, 0);
+ g_detach(zcp);
+ if (id == NULL || strcmp(id, ap->id) != 0) {
+ vdev_geom_free_id(id);
+ continue;
+ }
+ vdev_geom_free_id(id);
+ ap->cp = vdev_geom_attach(pp, ap->write);
+ if (ap->cp == NULL) {
+ printf("ZFS WARNING: Cannot open %s "
+ "for writting.\n", pp->name);
+ continue;
+ }
+ goto end;
+ }
+ }
+ }
ap->cp = NULL;
end:
g_destroy_consumer(zcp);
@@ -345,25 +565,13 @@ vdev_geom_attach_by_id(const char *id, int write)
return (cp);
}
-static int
-vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift)
+static struct g_consumer *
+vdev_geom_open_by_path_and_devid(vdev_t *vd)
{
- vdev_geom_ctx_t *ctx;
struct g_provider *pp;
struct g_consumer *cp;
- char *id = NULL;
- int owned;
-
- /*
- * We must have a pathname, and it must be absolute.
- */
- if (vd->vdev_path == NULL || vd->vdev_path[0] != '/') {
- vd->vdev_stat.vs_aux = VDEV_AUX_BAD_LABEL;
- return (EINVAL);
- }
+ char *id;
- if ((owned = mtx_owned(&Giant)))
- mtx_unlock(&Giant);
cp = NULL;
g_topology_lock();
pp = g_provider_by_name(vd->vdev_path + sizeof("/dev/") - 1);
@@ -380,40 +588,101 @@ vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift)
ZFS_LOG(1, "ID mismatch for provider %s: "
"[%s]!=[%s].", vd->vdev_path,
vd->vdev_devid, id);
- goto next;
- }
- ZFS_LOG(1, "ID match for provider %s.", vd->vdev_path);
+ } else
+ ZFS_LOG(1, "ID match for provider %s.",
+ vd->vdev_path);
+ vdev_geom_free_id(id);
}
}
-next:
g_topology_unlock();
- vdev_geom_free_id(id);
- if (cp == NULL && vd->vdev_devid != NULL) {
- ZFS_LOG(1, "Searching by ID [%s].", vd->vdev_devid);
- cp = vdev_geom_attach_by_id(vd->vdev_devid,
- !!(spa_mode & FWRITE));
- if (cp != NULL) {
- size_t len = strlen(cp->provider->name) + 6; /* 6 == strlen("/dev/") + 1 */
- char *buf = kmem_alloc(len, KM_SLEEP);
-
- snprintf(buf, len, "/dev/%s", cp->provider->name);
- spa_strfree(vd->vdev_path);
- vd->vdev_path = buf;
-
- ZFS_LOG(1, "Attach by ID [%s] succeeded, provider %s.",
- vd->vdev_devid, vd->vdev_path);
+
+ return (cp);
+}
+
+static struct g_consumer *
+vdev_geom_open_by_devid(vdev_t *vd)
+{
+ struct g_consumer *cp;
+ char *buf;
+ size_t len;
+
+ /*
+ * We can't search by devid if it's missing.
+ */
+ if (vd->vdev_devid == NULL)
+ return (NULL);
+
+ ZFS_LOG(1, "Searching by ID [%s].", vd->vdev_devid);
+ cp = vdev_geom_attach_by_id(vd->vdev_devid, !!(spa_mode & FWRITE));
+ if (cp != NULL) {
+ len = strlen(cp->provider->name) + strlen("/dev/") + 1;
+ buf = kmem_alloc(len, KM_SLEEP);
+
+ snprintf(buf, len, "/dev/%s", cp->provider->name);
+ spa_strfree(vd->vdev_path);
+ vd->vdev_path = buf;
+
+ ZFS_LOG(1, "Attach by ID [%s] succeeded, provider %s.",
+ vd->vdev_devid, vd->vdev_path);
+ } else
+ ZFS_LOG(1, "Search by ID [%s] failed.", vd->vdev_devid);
+
+ return (cp);
+}
+
+static int
+vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift)
+{
+ vdev_geom_ctx_t *ctx;
+ struct g_provider *pp;
+ struct g_consumer *cp;
+ int owned;
+
+ /*
+ * We must have a pathname, and it must be absolute.
+ */
+ if (vd->vdev_path == NULL || vd->vdev_path[0] != '/') {
+ vd->vdev_stat.vs_aux = VDEV_AUX_BAD_LABEL;
+ return (EINVAL);
+ }
+
+ vd->vdev_tsd = NULL;
+
+ if ((owned = mtx_owned(&Giant)))
+ mtx_unlock(&Giant);
+ cp = vdev_geom_open_by_path_and_devid(vd);
+ if (cp == NULL) {
+ /*
+ * The device at vd->vdev_path doesn't have the right devid.
+ * The disks might have merely moved around so try all other
+ * geom providers to find one with the right devid.
+ */
+ cp = vdev_geom_open_by_devid(vd);
+ if (cp == NULL) {
+ ZFS_LOG(1, "Provider %s not found.", vd->vdev_path);
+ vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED;
+ if (owned)
+ mtx_lock(&Giant);
+ return (EACCES);
}
}
if (owned)
mtx_lock(&Giant);
- if (cp == NULL) {
- ZFS_LOG(1, "Provider %s (id=[%s]) not found.", vd->vdev_path,
- vd->vdev_devid);
- vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED;
- return (EACCES);
- }
+
+ cp->private = vd;
+
+ ctx = kmem_zalloc(sizeof(*ctx), KM_SLEEP);
+ bioq_init(&ctx->gc_queue);
+ mtx_init(&ctx->gc_queue_mtx, "zfs:vdev:geom:queue", NULL, MTX_DEF);
+ ctx->gc_consumer = cp;
+ ctx->gc_state = 0;
+
+ vd->vdev_tsd = ctx;
pp = cp->provider;
+ kproc_create(vdev_geom_worker, ctx, NULL, 0, 0, "vdev:worker %s",
+ pp->name);
+
/*
* Determine the actual size of the device.
*/
@@ -430,19 +699,6 @@ next:
*/
vd->vdev_nowritecache = B_FALSE;
- cp->private = vd;
-
- ctx = kmem_zalloc(sizeof(*ctx), KM_SLEEP);
- bioq_init(&ctx->gc_queue);
- mtx_init(&ctx->gc_queue_mtx, "zfs:vdev:geom:queue", NULL, MTX_DEF);
- ctx->gc_consumer = cp;
- ctx->gc_state = 0;
-
- vd->vdev_tsd = ctx;
-
- kproc_create(vdev_geom_worker, ctx, NULL, 0, 0, "vdev:worker %s",
- pp->name);
-
return (0);
}
@@ -469,13 +725,16 @@ vdev_geom_io_intr(struct bio *bp)
zio = bp->bio_caller1;
ctx = zio->io_vd->vdev_tsd;
+ if ((zio->io_error = bp->bio_error) == 0 && bp->bio_resid != 0)
+ zio->io_error = EIO;
+
mtx_lock(&ctx->gc_queue_mtx);
bioq_insert_tail(&ctx->gc_queue, bp);
wakeup_one(&ctx->gc_queue);
mtx_unlock(&ctx->gc_queue_mtx);
}
-static void
+static int
vdev_geom_io_start(zio_t *zio)
{
vdev_t *vd;
@@ -492,18 +751,19 @@ vdev_geom_io_start(zio_t *zio)
cp = ctx->gc_consumer;
if (zio->io_type == ZIO_TYPE_IOCTL) {
- zio_vdev_io_bypass(zio);
-
/* XXPOLICY */
- if (vdev_is_dead(vd)) {
+ if (!vdev_readable(vd)) {
zio->io_error = ENXIO;
- zio_next_stage_async(zio);
- return;
+ return (ZIO_PIPELINE_CONTINUE);
}
switch (zio->io_cmd) {
case DKIOCFLUSHWRITECACHE:
+
+ if (zfs_nocacheflush)
+ break;
+
if (vd->vdev_nowritecache) {
zio->io_error = ENOTSUP;
break;
@@ -514,27 +774,13 @@ vdev_geom_io_start(zio_t *zio)
zio->io_error = ENOTSUP;
}
- zio_next_stage_async(zio);
- return;
+ return (ZIO_PIPELINE_CONTINUE);
}
-
- if (zio->io_type == ZIO_TYPE_READ && vdev_cache_read(zio) == 0)
- return;
-
- if ((zio = vdev_queue_io(zio)) == NULL)
- return;
-
sendreq:
-
- error = vdev_is_dead(vd) ? ENXIO : vdev_error_inject(vd, zio);
- if (error == 0 && cp == NULL)
- error = ENXIO;
- if (error) {
- zio->io_error = error;
- zio_next_stage_async(zio);
- return;
+ if (cp == NULL) {
+ zio->io_error = ENXIO;
+ return (ZIO_PIPELINE_CONTINUE);
}
-
bp = g_alloc_bio();
bp->bio_caller1 = zio;
switch (zio->io_type) {
@@ -555,20 +801,33 @@ sendreq:
bp->bio_done = vdev_geom_io_intr;
g_io_request(bp, cp);
+
+ return (ZIO_PIPELINE_STOP);
}
static void
vdev_geom_io_done(zio_t *zio)
{
- vdev_queue_io_done(zio);
-
- if (zio->io_type == ZIO_TYPE_WRITE)
- vdev_cache_write(zio);
- if (zio_injection_enabled && zio->io_error == 0)
- zio->io_error = zio_handle_device_injection(zio->io_vd, EIO);
-
- zio_next_stage(zio);
+ /*
+ * If the device returned ENXIO, then attempt we should verify if GEOM
+ * provider has been removed. If this is the case, then we trigger an
+ * asynchronous removal of the device.
+ */
+ if (zio->io_error == ENXIO) {
+ vdev_t *vd = zio->io_vd;
+ vdev_geom_ctx_t *ctx;
+ struct g_provider *pp = NULL;
+
+ ctx = vd->vdev_tsd;
+ if (ctx != NULL && ctx->gc_consumer != NULL)
+ pp = ctx->gc_consumer->provider;
+
+ if (pp == NULL || (pp->flags & G_PF_ORPHAN)) {
+ vd->vdev_remove_wanted = B_TRUE;
+ spa_async_request(zio->io_spa, SPA_ASYNC_REMOVE);
+ }
+ }
}
vdev_ops_t vdev_geom_ops = {
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_label.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_label.c
index 9d9f555..bf93046 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_label.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_label.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* Virtual Device Labels
* ---------------------
@@ -62,7 +60,7 @@
* or a device was added, we want to update all the labels such that we can deal
* with fatal failure at any point. To this end, each disk has two labels which
* are updated before and after the uberblock is synced. Assuming we have
- * labels and an uberblock with the following transacation groups:
+ * labels and an uberblock with the following transaction groups:
*
* L1 UB L2
* +------+ +------+ +------+
@@ -153,34 +151,56 @@ uint64_t
vdev_label_offset(uint64_t psize, int l, uint64_t offset)
{
ASSERT(offset < sizeof (vdev_label_t));
+ ASSERT(P2PHASE_TYPED(psize, sizeof (vdev_label_t), uint64_t) == 0);
return (offset + l * sizeof (vdev_label_t) + (l < VDEV_LABELS / 2 ?
0 : psize - VDEV_LABELS * sizeof (vdev_label_t)));
}
+/*
+ * Returns back the vdev label associated with the passed in offset.
+ */
+int
+vdev_label_number(uint64_t psize, uint64_t offset)
+{
+ int l;
+
+ if (offset >= psize - VDEV_LABEL_END_SIZE) {
+ offset -= psize - VDEV_LABEL_END_SIZE;
+ offset += (VDEV_LABELS / 2) * sizeof (vdev_label_t);
+ }
+ l = offset / sizeof (vdev_label_t);
+ return (l < VDEV_LABELS ? l : -1);
+}
+
static void
vdev_label_read(zio_t *zio, vdev_t *vd, int l, void *buf, uint64_t offset,
- uint64_t size, zio_done_func_t *done, void *private)
+ uint64_t size, zio_done_func_t *done, void *private, int flags)
{
- ASSERT(vd->vdev_children == 0);
+ ASSERT(spa_config_held(zio->io_spa, SCL_STATE_ALL, RW_WRITER) ==
+ SCL_STATE_ALL);
+ ASSERT(flags & ZIO_FLAG_CONFIG_WRITER);
zio_nowait(zio_read_phys(zio, vd,
vdev_label_offset(vd->vdev_psize, l, offset),
size, buf, ZIO_CHECKSUM_LABEL, done, private,
- ZIO_PRIORITY_SYNC_READ,
- ZIO_FLAG_CONFIG_HELD | ZIO_FLAG_CANFAIL | ZIO_FLAG_SPECULATIVE));
+ ZIO_PRIORITY_SYNC_READ, flags, B_TRUE));
}
static void
vdev_label_write(zio_t *zio, vdev_t *vd, int l, void *buf, uint64_t offset,
- uint64_t size, zio_done_func_t *done, void *private)
+ uint64_t size, zio_done_func_t *done, void *private, int flags)
{
- ASSERT(vd->vdev_children == 0);
+ ASSERT(spa_config_held(zio->io_spa, SCL_ALL, RW_WRITER) == SCL_ALL ||
+ (spa_config_held(zio->io_spa, SCL_CONFIG | SCL_STATE, RW_READER) ==
+ (SCL_CONFIG | SCL_STATE) &&
+ dsl_pool_sync_context(spa_get_dsl(zio->io_spa))));
+ ASSERT(flags & ZIO_FLAG_CONFIG_WRITER);
zio_nowait(zio_write_phys(zio, vd,
vdev_label_offset(vd->vdev_psize, l, offset),
size, buf, ZIO_CHECKSUM_LABEL, done, private,
- ZIO_PRIORITY_SYNC_WRITE, ZIO_FLAG_CONFIG_HELD | ZIO_FLAG_CANFAIL));
+ ZIO_PRIORITY_SYNC_WRITE, flags, B_TRUE));
}
/*
@@ -188,7 +208,7 @@ vdev_label_write(zio_t *zio, vdev_t *vd, int l, void *buf, uint64_t offset,
*/
nvlist_t *
vdev_config_generate(spa_t *spa, vdev_t *vd, boolean_t getstats,
- boolean_t isspare)
+ boolean_t isspare, boolean_t isl2cache)
{
nvlist_t *nv = NULL;
@@ -196,7 +216,7 @@ vdev_config_generate(spa_t *spa, vdev_t *vd, boolean_t getstats,
VERIFY(nvlist_add_string(nv, ZPOOL_CONFIG_TYPE,
vd->vdev_ops->vdev_op_type) == 0);
- if (!isspare)
+ if (!isspare && !isl2cache)
VERIFY(nvlist_add_uint64(nv, ZPOOL_CONFIG_ID, vd->vdev_id)
== 0);
VERIFY(nvlist_add_uint64(nv, ZPOOL_CONFIG_GUID, vd->vdev_guid) == 0);
@@ -209,6 +229,10 @@ vdev_config_generate(spa_t *spa, vdev_t *vd, boolean_t getstats,
VERIFY(nvlist_add_string(nv, ZPOOL_CONFIG_DEVID,
vd->vdev_devid) == 0);
+ if (vd->vdev_physpath != NULL)
+ VERIFY(nvlist_add_string(nv, ZPOOL_CONFIG_PHYS_PATH,
+ vd->vdev_physpath) == 0);
+
if (vd->vdev_nparity != 0) {
ASSERT(strcmp(vd->vdev_ops->vdev_op_type,
VDEV_TYPE_RAIDZ) == 0);
@@ -219,7 +243,7 @@ vdev_config_generate(spa_t *spa, vdev_t *vd, boolean_t getstats,
*/
ASSERT(vd->vdev_nparity == 1 ||
(vd->vdev_nparity == 2 &&
- spa_version(spa) >= ZFS_VERSION_RAID6));
+ spa_version(spa) >= SPA_VERSION_RAID6));
/*
* Note that we'll add the nparity tag even on storage pools
@@ -240,7 +264,7 @@ vdev_config_generate(spa_t *spa, vdev_t *vd, boolean_t getstats,
if (vd->vdev_isspare)
VERIFY(nvlist_add_uint64(nv, ZPOOL_CONFIG_IS_SPARE, 1) == 0);
- if (!isspare && vd == vd->vdev_top) {
+ if (!isspare && !isl2cache && vd == vd->vdev_top) {
VERIFY(nvlist_add_uint64(nv, ZPOOL_CONFIG_METASLAB_ARRAY,
vd->vdev_ms_array) == 0);
VERIFY(nvlist_add_uint64(nv, ZPOOL_CONFIG_METASLAB_SHIFT,
@@ -249,6 +273,8 @@ vdev_config_generate(spa_t *spa, vdev_t *vd, boolean_t getstats,
vd->vdev_ashift) == 0);
VERIFY(nvlist_add_uint64(nv, ZPOOL_CONFIG_ASIZE,
vd->vdev_asize) == 0);
+ VERIFY(nvlist_add_uint64(nv, ZPOOL_CONFIG_IS_LOG,
+ vd->vdev_islog) == 0);
}
if (vd->vdev_dtl.smo_object != 0)
@@ -271,7 +297,7 @@ vdev_config_generate(spa_t *spa, vdev_t *vd, boolean_t getstats,
for (c = 0; c < vd->vdev_children; c++)
child[c] = vdev_config_generate(spa, vd->vdev_child[c],
- getstats, isspare);
+ getstats, isspare, isl2cache);
VERIFY(nvlist_add_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
child, vd->vdev_children) == 0);
@@ -285,9 +311,18 @@ vdev_config_generate(spa_t *spa, vdev_t *vd, boolean_t getstats,
if (vd->vdev_offline && !vd->vdev_tmpoffline)
VERIFY(nvlist_add_uint64(nv, ZPOOL_CONFIG_OFFLINE,
B_TRUE) == 0);
- else
- (void) nvlist_remove(nv, ZPOOL_CONFIG_OFFLINE,
- DATA_TYPE_UINT64);
+ if (vd->vdev_faulted)
+ VERIFY(nvlist_add_uint64(nv, ZPOOL_CONFIG_FAULTED,
+ B_TRUE) == 0);
+ if (vd->vdev_degraded)
+ VERIFY(nvlist_add_uint64(nv, ZPOOL_CONFIG_DEGRADED,
+ B_TRUE) == 0);
+ if (vd->vdev_removed)
+ VERIFY(nvlist_add_uint64(nv, ZPOOL_CONFIG_REMOVED,
+ B_TRUE) == 0);
+ if (vd->vdev_unspare)
+ VERIFY(nvlist_add_uint64(nv, ZPOOL_CONFIG_UNSPARE,
+ B_TRUE) == 0);
}
return (nv);
@@ -300,23 +335,23 @@ vdev_label_read_config(vdev_t *vd)
nvlist_t *config = NULL;
vdev_phys_t *vp;
zio_t *zio;
- int l;
+ int flags =
+ ZIO_FLAG_CONFIG_WRITER | ZIO_FLAG_CANFAIL | ZIO_FLAG_SPECULATIVE;
- ASSERT(spa_config_held(spa, RW_READER));
+ ASSERT(spa_config_held(spa, SCL_STATE_ALL, RW_WRITER) == SCL_STATE_ALL);
- if (vdev_is_dead(vd))
+ if (!vdev_readable(vd))
return (NULL);
vp = zio_buf_alloc(sizeof (vdev_phys_t));
- for (l = 0; l < VDEV_LABELS; l++) {
+ for (int l = 0; l < VDEV_LABELS; l++) {
- zio = zio_root(spa, NULL, NULL, ZIO_FLAG_CANFAIL |
- ZIO_FLAG_SPECULATIVE | ZIO_FLAG_CONFIG_HELD);
+ zio = zio_root(spa, NULL, NULL, flags);
vdev_label_read(zio, vd, l, vp,
offsetof(vdev_label_t, vl_vdev_phys),
- sizeof (vdev_phys_t), NULL, NULL);
+ sizeof (vdev_phys_t), NULL, NULL, flags);
if (zio_wait(zio) == 0 &&
nvlist_unpack(vp->vp_nvlist, sizeof (vp->vp_nvlist),
@@ -340,7 +375,7 @@ vdev_label_read_config(vdev_t *vd)
*/
static boolean_t
vdev_inuse(vdev_t *vd, uint64_t crtxg, vdev_labeltype_t reason,
- uint64_t *spare_guid)
+ uint64_t *spare_guid, uint64_t *l2cache_guid)
{
spa_t *spa = vd->vdev_spa;
uint64_t state, pool_guid, device_guid, txg, spare_pool;
@@ -349,6 +384,8 @@ vdev_inuse(vdev_t *vd, uint64_t crtxg, vdev_labeltype_t reason,
if (spare_guid)
*spare_guid = 0ULL;
+ if (l2cache_guid)
+ *l2cache_guid = 0ULL;
/*
* Read the label, if any, and perform some basic sanity checks.
@@ -367,7 +404,7 @@ vdev_inuse(vdev_t *vd, uint64_t crtxg, vdev_labeltype_t reason,
return (B_FALSE);
}
- if (state != POOL_STATE_SPARE &&
+ if (state != POOL_STATE_SPARE && state != POOL_STATE_L2CACHE &&
(nvlist_lookup_uint64(label, ZPOOL_CONFIG_POOL_GUID,
&pool_guid) != 0 ||
nvlist_lookup_uint64(label, ZPOOL_CONFIG_POOL_TXG,
@@ -383,9 +420,10 @@ vdev_inuse(vdev_t *vd, uint64_t crtxg, vdev_labeltype_t reason,
* be a part of. The only way this is allowed is if the device is a hot
* spare (which we check for later on).
*/
- if (state != POOL_STATE_SPARE &&
+ if (state != POOL_STATE_SPARE && state != POOL_STATE_L2CACHE &&
!spa_guid_exists(pool_guid, device_guid) &&
- !spa_spare_exists(device_guid, NULL))
+ !spa_spare_exists(device_guid, NULL, NULL) &&
+ !spa_l2cache_exists(device_guid, NULL))
return (B_FALSE);
/*
@@ -395,21 +433,23 @@ vdev_inuse(vdev_t *vd, uint64_t crtxg, vdev_labeltype_t reason,
* user has attempted to add the same vdev multiple times in the same
* transaction.
*/
- if (state != POOL_STATE_SPARE && txg == 0 && vdtxg == crtxg)
+ if (state != POOL_STATE_SPARE && state != POOL_STATE_L2CACHE &&
+ txg == 0 && vdtxg == crtxg)
return (B_TRUE);
/*
* Check to see if this is a spare device. We do an explicit check for
* spa_has_spare() here because it may be on our pending list of spares
- * to add.
+ * to add. We also check if it is an l2cache device.
*/
- if (spa_spare_exists(device_guid, &spare_pool) ||
+ if (spa_spare_exists(device_guid, &spare_pool, NULL) ||
spa_has_spare(spa, device_guid)) {
if (spare_guid)
*spare_guid = device_guid;
switch (reason) {
case VDEV_LABEL_CREATE:
+ case VDEV_LABEL_L2CACHE:
return (B_TRUE);
case VDEV_LABEL_REPLACE:
@@ -422,6 +462,12 @@ vdev_inuse(vdev_t *vd, uint64_t crtxg, vdev_labeltype_t reason,
}
/*
+ * Check to see if this is an l2cache device.
+ */
+ if (spa_l2cache_exists(device_guid, NULL))
+ return (B_TRUE);
+
+ /*
* If the device is marked ACTIVE, then this device is in use by another
* pool on the system.
*/
@@ -445,15 +491,15 @@ vdev_label_init(vdev_t *vd, uint64_t crtxg, vdev_labeltype_t reason)
vdev_boot_header_t *vb;
uberblock_t *ub;
zio_t *zio;
- int l, c, n;
char *buf;
size_t buflen;
int error;
- uint64_t spare_guid;
+ uint64_t spare_guid, l2cache_guid;
+ int flags = ZIO_FLAG_CONFIG_WRITER | ZIO_FLAG_CANFAIL;
- ASSERT(spa_config_held(spa, RW_WRITER));
+ ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL);
- for (c = 0; c < vd->vdev_children; c++)
+ for (int c = 0; c < vd->vdev_children; c++)
if ((error = vdev_label_init(vd->vdev_child[c],
crtxg, reason)) != 0)
return (error);
@@ -471,38 +517,56 @@ vdev_label_init(vdev_t *vd, uint64_t crtxg, vdev_labeltype_t reason)
* Determine if the vdev is in use.
*/
if (reason != VDEV_LABEL_REMOVE &&
- vdev_inuse(vd, crtxg, reason, &spare_guid))
+ vdev_inuse(vd, crtxg, reason, &spare_guid, &l2cache_guid))
return (EBUSY);
ASSERT(reason != VDEV_LABEL_REMOVE ||
- vdev_inuse(vd, crtxg, reason, NULL));
+ vdev_inuse(vd, crtxg, reason, NULL, NULL));
/*
- * If this is a request to add or replace a spare that is in use
- * elsewhere on the system, then we must update the guid (which was
- * initialized to a random value) to reflect the actual GUID (which is
- * shared between multiple pools).
+ * If this is a request to add or replace a spare or l2cache device
+ * that is in use elsewhere on the system, then we must update the
+ * guid (which was initialized to a random value) to reflect the
+ * actual GUID (which is shared between multiple pools).
*/
- if (reason != VDEV_LABEL_REMOVE && spare_guid != 0ULL) {
- vdev_t *pvd = vd->vdev_parent;
+ if (reason != VDEV_LABEL_REMOVE && reason != VDEV_LABEL_L2CACHE &&
+ spare_guid != 0ULL) {
+ uint64_t guid_delta = spare_guid - vd->vdev_guid;
- for (; pvd != NULL; pvd = pvd->vdev_parent) {
- pvd->vdev_guid_sum -= vd->vdev_guid;
- pvd->vdev_guid_sum += spare_guid;
- }
+ vd->vdev_guid += guid_delta;
- vd->vdev_guid = vd->vdev_guid_sum = spare_guid;
+ for (vdev_t *pvd = vd; pvd != NULL; pvd = pvd->vdev_parent)
+ pvd->vdev_guid_sum += guid_delta;
/*
* If this is a replacement, then we want to fallthrough to the
* rest of the code. If we're adding a spare, then it's already
- * labelled appropriately and we can just return.
+ * labeled appropriately and we can just return.
*/
if (reason == VDEV_LABEL_SPARE)
return (0);
ASSERT(reason == VDEV_LABEL_REPLACE);
}
+ if (reason != VDEV_LABEL_REMOVE && reason != VDEV_LABEL_SPARE &&
+ l2cache_guid != 0ULL) {
+ uint64_t guid_delta = l2cache_guid - vd->vdev_guid;
+
+ vd->vdev_guid += guid_delta;
+
+ for (vdev_t *pvd = vd; pvd != NULL; pvd = pvd->vdev_parent)
+ pvd->vdev_guid_sum += guid_delta;
+
+ /*
+ * If this is a replacement, then we want to fallthrough to the
+ * rest of the code. If we're adding an l2cache, then it's
+ * already labeled appropriately and we can just return.
+ */
+ if (reason == VDEV_LABEL_L2CACHE)
+ return (0);
+ ASSERT(reason == VDEV_LABEL_REPLACE);
+ }
+
/*
* Initialize its label.
*/
@@ -532,6 +596,19 @@ vdev_label_init(vdev_t *vd, uint64_t crtxg, vdev_labeltype_t reason)
POOL_STATE_SPARE) == 0);
VERIFY(nvlist_add_uint64(label, ZPOOL_CONFIG_GUID,
vd->vdev_guid) == 0);
+ } else if (reason == VDEV_LABEL_L2CACHE ||
+ (reason == VDEV_LABEL_REMOVE && vd->vdev_isl2cache)) {
+ /*
+ * For level 2 ARC devices, add a special label.
+ */
+ VERIFY(nvlist_alloc(&label, NV_UNIQUE_NAME, KM_SLEEP) == 0);
+
+ VERIFY(nvlist_add_uint64(label, ZPOOL_CONFIG_VERSION,
+ spa_version(spa)) == 0);
+ VERIFY(nvlist_add_uint64(label, ZPOOL_CONFIG_POOL_STATE,
+ POOL_STATE_L2CACHE) == 0);
+ VERIFY(nvlist_add_uint64(label, ZPOOL_CONFIG_GUID,
+ vd->vdev_guid) == 0);
} else {
label = spa_config_generate(spa, vd, 0ULL, B_FALSE);
@@ -576,23 +653,22 @@ vdev_label_init(vdev_t *vd, uint64_t crtxg, vdev_labeltype_t reason)
/*
* Write everything in parallel.
*/
- zio = zio_root(spa, NULL, NULL,
- ZIO_FLAG_CONFIG_HELD | ZIO_FLAG_CANFAIL);
+ zio = zio_root(spa, NULL, NULL, flags);
- for (l = 0; l < VDEV_LABELS; l++) {
+ for (int l = 0; l < VDEV_LABELS; l++) {
vdev_label_write(zio, vd, l, vp,
offsetof(vdev_label_t, vl_vdev_phys),
- sizeof (vdev_phys_t), NULL, NULL);
+ sizeof (vdev_phys_t), NULL, NULL, flags);
vdev_label_write(zio, vd, l, vb,
offsetof(vdev_label_t, vl_boot_header),
- sizeof (vdev_boot_header_t), NULL, NULL);
+ sizeof (vdev_boot_header_t), NULL, NULL, flags);
- for (n = 0; n < VDEV_UBERBLOCK_COUNT(vd); n++) {
+ for (int n = 0; n < VDEV_UBERBLOCK_COUNT(vd); n++) {
vdev_label_write(zio, vd, l, ub,
VDEV_UBERBLOCK_OFFSET(vd, n),
- VDEV_UBERBLOCK_SIZE(vd), NULL, NULL);
+ VDEV_UBERBLOCK_SIZE(vd), NULL, NULL, flags);
}
}
@@ -605,14 +681,20 @@ vdev_label_init(vdev_t *vd, uint64_t crtxg, vdev_labeltype_t reason)
/*
* If this vdev hasn't been previously identified as a spare, then we
- * mark it as such only if a) we are labelling it as a spare, or b) it
- * exists as a spare elsewhere in the system.
+ * mark it as such only if a) we are labeling it as a spare, or b) it
+ * exists as a spare elsewhere in the system. Do the same for
+ * level 2 ARC devices.
*/
if (error == 0 && !vd->vdev_isspare &&
(reason == VDEV_LABEL_SPARE ||
- spa_spare_exists(vd->vdev_guid, NULL)))
+ spa_spare_exists(vd->vdev_guid, NULL, NULL)))
spa_spare_add(vd);
+ if (error == 0 && !vd->vdev_isl2cache &&
+ (reason == VDEV_LABEL_L2CACHE ||
+ spa_l2cache_exists(vd->vdev_guid, NULL)))
+ spa_l2cache_add(vd);
+
return (error);
}
@@ -651,17 +733,17 @@ vdev_uberblock_compare(uberblock_t *ub1, uberblock_t *ub2)
static void
vdev_uberblock_load_done(zio_t *zio)
{
+ zio_t *rio = zio->io_private;
uberblock_t *ub = zio->io_data;
- uberblock_t *ubbest = zio->io_private;
- spa_t *spa = zio->io_spa;
+ uberblock_t *ubbest = rio->io_private;
ASSERT3U(zio->io_size, ==, VDEV_UBERBLOCK_SIZE(zio->io_vd));
if (zio->io_error == 0 && uberblock_verify(ub) == 0) {
- mutex_enter(&spa->spa_uberblock_lock);
+ mutex_enter(&rio->io_lock);
if (vdev_uberblock_compare(ub, ubbest) > 0)
*ubbest = *ub;
- mutex_exit(&spa->spa_uberblock_lock);
+ mutex_exit(&rio->io_lock);
}
zio_buf_free(zio->io_data, zio->io_size);
@@ -670,136 +752,169 @@ vdev_uberblock_load_done(zio_t *zio)
void
vdev_uberblock_load(zio_t *zio, vdev_t *vd, uberblock_t *ubbest)
{
- int l, c, n;
-
- for (c = 0; c < vd->vdev_children; c++)
- vdev_uberblock_load(zio, vd->vdev_child[c], ubbest);
+ spa_t *spa = vd->vdev_spa;
+ vdev_t *rvd = spa->spa_root_vdev;
+ int flags =
+ ZIO_FLAG_CONFIG_WRITER | ZIO_FLAG_CANFAIL | ZIO_FLAG_SPECULATIVE;
+
+ if (vd == rvd) {
+ ASSERT(zio == NULL);
+ spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
+ zio = zio_root(spa, NULL, ubbest, flags);
+ bzero(ubbest, sizeof (uberblock_t));
+ }
- if (!vd->vdev_ops->vdev_op_leaf)
- return;
+ ASSERT(zio != NULL);
- if (vdev_is_dead(vd))
- return;
+ for (int c = 0; c < vd->vdev_children; c++)
+ vdev_uberblock_load(zio, vd->vdev_child[c], ubbest);
- for (l = 0; l < VDEV_LABELS; l++) {
- for (n = 0; n < VDEV_UBERBLOCK_COUNT(vd); n++) {
- vdev_label_read(zio, vd, l,
- zio_buf_alloc(VDEV_UBERBLOCK_SIZE(vd)),
- VDEV_UBERBLOCK_OFFSET(vd, n),
- VDEV_UBERBLOCK_SIZE(vd),
- vdev_uberblock_load_done, ubbest);
+ if (vd->vdev_ops->vdev_op_leaf && vdev_readable(vd)) {
+ for (int l = 0; l < VDEV_LABELS; l++) {
+ for (int n = 0; n < VDEV_UBERBLOCK_COUNT(vd); n++) {
+ vdev_label_read(zio, vd, l,
+ zio_buf_alloc(VDEV_UBERBLOCK_SIZE(vd)),
+ VDEV_UBERBLOCK_OFFSET(vd, n),
+ VDEV_UBERBLOCK_SIZE(vd),
+ vdev_uberblock_load_done, zio, flags);
+ }
}
}
+
+ if (vd == rvd) {
+ (void) zio_wait(zio);
+ spa_config_exit(spa, SCL_ALL, FTAG);
+ }
}
/*
- * Write the uberblock to both labels of all leaves of the specified vdev.
+ * On success, increment root zio's count of good writes.
* We only get credit for writes to known-visible vdevs; see spa_vdev_add().
*/
static void
vdev_uberblock_sync_done(zio_t *zio)
{
- uint64_t *good_writes = zio->io_root->io_private;
+ uint64_t *good_writes = zio->io_private;
if (zio->io_error == 0 && zio->io_vd->vdev_top->vdev_ms_array != 0)
atomic_add_64(good_writes, 1);
}
+/*
+ * Write the uberblock to all labels of all leaves of the specified vdev.
+ */
static void
-vdev_uberblock_sync(zio_t *zio, uberblock_t *ub, vdev_t *vd, uint64_t txg)
+vdev_uberblock_sync(zio_t *zio, uberblock_t *ub, vdev_t *vd, int flags)
{
- int l, c, n;
+ uberblock_t *ubbuf;
+ int n;
- for (c = 0; c < vd->vdev_children; c++)
- vdev_uberblock_sync(zio, ub, vd->vdev_child[c], txg);
+ for (int c = 0; c < vd->vdev_children; c++)
+ vdev_uberblock_sync(zio, ub, vd->vdev_child[c], flags);
if (!vd->vdev_ops->vdev_op_leaf)
return;
- if (vdev_is_dead(vd))
+ if (!vdev_writeable(vd))
return;
- n = txg & (VDEV_UBERBLOCK_COUNT(vd) - 1);
+ n = ub->ub_txg & (VDEV_UBERBLOCK_COUNT(vd) - 1);
- ASSERT(ub->ub_txg == txg);
+ ubbuf = zio_buf_alloc(VDEV_UBERBLOCK_SIZE(vd));
+ bzero(ubbuf, VDEV_UBERBLOCK_SIZE(vd));
+ *ubbuf = *ub;
- for (l = 0; l < VDEV_LABELS; l++)
- vdev_label_write(zio, vd, l, ub,
- VDEV_UBERBLOCK_OFFSET(vd, n),
- VDEV_UBERBLOCK_SIZE(vd),
- vdev_uberblock_sync_done, NULL);
+ for (int l = 0; l < VDEV_LABELS; l++)
+ vdev_label_write(zio, vd, l, ubbuf,
+ VDEV_UBERBLOCK_OFFSET(vd, n), VDEV_UBERBLOCK_SIZE(vd),
+ vdev_uberblock_sync_done, zio->io_private,
+ flags | ZIO_FLAG_DONT_PROPAGATE);
- dprintf("vdev %s in txg %llu\n", vdev_description(vd), txg);
+ zio_buf_free(ubbuf, VDEV_UBERBLOCK_SIZE(vd));
}
-static int
-vdev_uberblock_sync_tree(spa_t *spa, uberblock_t *ub, vdev_t *vd, uint64_t txg)
+int
+vdev_uberblock_sync_list(vdev_t **svd, int svdcount, uberblock_t *ub, int flags)
{
- uberblock_t *ubbuf;
- size_t size = vd->vdev_top ? VDEV_UBERBLOCK_SIZE(vd) : SPA_MAXBLOCKSIZE;
- uint64_t *good_writes;
+ spa_t *spa = svd[0]->vdev_spa;
zio_t *zio;
- int error;
-
- ubbuf = zio_buf_alloc(size);
- bzero(ubbuf, size);
- *ubbuf = *ub;
+ uint64_t good_writes = 0;
- good_writes = kmem_zalloc(sizeof (uint64_t), KM_SLEEP);
+ zio = zio_root(spa, NULL, &good_writes, flags);
- zio = zio_root(spa, NULL, good_writes,
- ZIO_FLAG_CONFIG_HELD | ZIO_FLAG_CANFAIL);
+ for (int v = 0; v < svdcount; v++)
+ vdev_uberblock_sync(zio, ub, svd[v], flags);
- vdev_uberblock_sync(zio, ubbuf, vd, txg);
-
- error = zio_wait(zio);
-
- if (error && *good_writes != 0) {
- dprintf("partial success: good_writes = %llu\n", *good_writes);
- error = 0;
- }
+ (void) zio_wait(zio);
/*
- * It's possible to have no good writes and no error if every vdev is in
- * the CANT_OPEN state.
+ * Flush the uberblocks to disk. This ensures that the odd labels
+ * are no longer needed (because the new uberblocks and the even
+ * labels are safely on disk), so it is safe to overwrite them.
*/
- if (*good_writes == 0 && error == 0)
- error = EIO;
+ zio = zio_root(spa, NULL, NULL, flags);
- kmem_free(good_writes, sizeof (uint64_t));
- zio_buf_free(ubbuf, size);
+ for (int v = 0; v < svdcount; v++)
+ zio_flush(zio, svd[v]);
- return (error);
+ (void) zio_wait(zio);
+
+ return (good_writes >= 1 ? 0 : EIO);
}
/*
- * Sync out an individual vdev.
+ * On success, increment the count of good writes for our top-level vdev.
*/
static void
-vdev_sync_label_done(zio_t *zio)
+vdev_label_sync_done(zio_t *zio)
{
- uint64_t *good_writes = zio->io_root->io_private;
+ uint64_t *good_writes = zio->io_private;
if (zio->io_error == 0)
atomic_add_64(good_writes, 1);
}
+/*
+ * If there weren't enough good writes, indicate failure to the parent.
+ */
+static void
+vdev_label_sync_top_done(zio_t *zio)
+{
+ uint64_t *good_writes = zio->io_private;
+
+ if (*good_writes == 0)
+ zio->io_error = EIO;
+
+ kmem_free(good_writes, sizeof (uint64_t));
+}
+
+/*
+ * We ignore errors for log and cache devices, simply free the private data.
+ */
+static void
+vdev_label_sync_ignore_done(zio_t *zio)
+{
+ kmem_free(zio->io_private, sizeof (uint64_t));
+}
+
+/*
+ * Write all even or odd labels to all leaves of the specified vdev.
+ */
static void
-vdev_sync_label(zio_t *zio, vdev_t *vd, int l, uint64_t txg)
+vdev_label_sync(zio_t *zio, vdev_t *vd, int l, uint64_t txg, int flags)
{
nvlist_t *label;
vdev_phys_t *vp;
char *buf;
size_t buflen;
- int c;
- for (c = 0; c < vd->vdev_children; c++)
- vdev_sync_label(zio, vd->vdev_child[c], l, txg);
+ for (int c = 0; c < vd->vdev_children; c++)
+ vdev_label_sync(zio, vd->vdev_child[c], l, txg, flags);
if (!vd->vdev_ops->vdev_op_leaf)
return;
- if (vdev_is_dead(vd))
+ if (!vdev_writeable(vd))
return;
/*
@@ -813,107 +928,110 @@ vdev_sync_label(zio_t *zio, vdev_t *vd, int l, uint64_t txg)
buf = vp->vp_nvlist;
buflen = sizeof (vp->vp_nvlist);
- if (nvlist_pack(label, &buf, &buflen, NV_ENCODE_XDR, KM_SLEEP) == 0)
- vdev_label_write(zio, vd, l, vp,
- offsetof(vdev_label_t, vl_vdev_phys), sizeof (vdev_phys_t),
- vdev_sync_label_done, NULL);
+ if (nvlist_pack(label, &buf, &buflen, NV_ENCODE_XDR, KM_SLEEP) == 0) {
+ for (; l < VDEV_LABELS; l += 2) {
+ vdev_label_write(zio, vd, l, vp,
+ offsetof(vdev_label_t, vl_vdev_phys),
+ sizeof (vdev_phys_t),
+ vdev_label_sync_done, zio->io_private,
+ flags | ZIO_FLAG_DONT_PROPAGATE);
+ }
+ }
zio_buf_free(vp, sizeof (vdev_phys_t));
nvlist_free(label);
-
- dprintf("%s label %d txg %llu\n", vdev_description(vd), l, txg);
}
-static int
-vdev_sync_labels(vdev_t *vd, int l, uint64_t txg)
+int
+vdev_label_sync_list(spa_t *spa, int l, uint64_t txg, int flags)
{
- uint64_t *good_writes;
+ list_t *dl = &spa->spa_config_dirty_list;
+ vdev_t *vd;
zio_t *zio;
int error;
- ASSERT(vd == vd->vdev_top);
-
- good_writes = kmem_zalloc(sizeof (uint64_t), KM_SLEEP);
-
- zio = zio_root(vd->vdev_spa, NULL, good_writes,
- ZIO_FLAG_CONFIG_HELD | ZIO_FLAG_CANFAIL);
-
/*
- * Recursively kick off writes to all labels.
+ * Write the new labels to disk.
*/
- vdev_sync_label(zio, vd, l, txg);
+ zio = zio_root(spa, NULL, NULL, flags);
+
+ for (vd = list_head(dl); vd != NULL; vd = list_next(dl, vd)) {
+ uint64_t *good_writes = kmem_zalloc(sizeof (uint64_t),
+ KM_SLEEP);
+ zio_t *vio = zio_null(zio, spa,
+ (vd->vdev_islog || vd->vdev_aux != NULL) ?
+ vdev_label_sync_ignore_done : vdev_label_sync_top_done,
+ good_writes, flags);
+ vdev_label_sync(vio, vd, l, txg, flags);
+ zio_nowait(vio);
+ }
error = zio_wait(zio);
- if (error && *good_writes != 0) {
- dprintf("partial success: good_writes = %llu\n", *good_writes);
- error = 0;
- }
+ /*
+ * Flush the new labels to disk.
+ */
+ zio = zio_root(spa, NULL, NULL, flags);
- if (*good_writes == 0 && error == 0)
- error = ENODEV;
+ for (vd = list_head(dl); vd != NULL; vd = list_next(dl, vd))
+ zio_flush(zio, vd);
- kmem_free(good_writes, sizeof (uint64_t));
+ (void) zio_wait(zio);
return (error);
}
/*
- * Sync the entire vdev configuration.
+ * Sync the uberblock and any changes to the vdev configuration.
*
* The order of operations is carefully crafted to ensure that
* if the system panics or loses power at any time, the state on disk
* is still transactionally consistent. The in-line comments below
* describe the failure semantics at each stage.
*
- * Moreover, it is designed to be idempotent: if spa_sync_labels() fails
+ * Moreover, vdev_config_sync() is designed to be idempotent: if it fails
* at any time, you can just call it again, and it will resume its work.
*/
int
-vdev_config_sync(vdev_t *uvd, uint64_t txg)
+vdev_config_sync(vdev_t **svd, int svdcount, uint64_t txg)
{
- spa_t *spa = uvd->vdev_spa;
+ spa_t *spa = svd[0]->vdev_spa;
uberblock_t *ub = &spa->spa_uberblock;
- vdev_t *rvd = spa->spa_root_vdev;
vdev_t *vd;
zio_t *zio;
- int l, error;
+ int error;
+ int flags = ZIO_FLAG_CONFIG_WRITER | ZIO_FLAG_CANFAIL;
ASSERT(ub->ub_txg <= txg);
/*
- * If this isn't a resync due to I/O errors, and nothing changed
- * in this transaction group, and the vdev configuration hasn't changed,
+ * If this isn't a resync due to I/O errors,
+ * and nothing changed in this transaction group,
+ * and the vdev configuration hasn't changed,
* then there's nothing to do.
*/
- if (ub->ub_txg < txg && uberblock_update(ub, rvd, txg) == B_FALSE &&
- list_is_empty(&spa->spa_dirty_list)) {
- dprintf("nothing to sync in %s in txg %llu\n",
- spa_name(spa), txg);
+ if (ub->ub_txg < txg &&
+ uberblock_update(ub, spa->spa_root_vdev, txg) == B_FALSE &&
+ list_is_empty(&spa->spa_config_dirty_list))
return (0);
- }
if (txg > spa_freeze_txg(spa))
return (0);
ASSERT(txg <= spa->spa_final_txg);
- dprintf("syncing %s txg %llu\n", spa_name(spa), txg);
-
/*
* Flush the write cache of every disk that's been written to
* in this transaction group. This ensures that all blocks
* written in this txg will be committed to stable storage
* before any uberblock that references them.
*/
- zio = zio_root(spa, NULL, NULL,
- ZIO_FLAG_CONFIG_HELD | ZIO_FLAG_CANFAIL);
+ zio = zio_root(spa, NULL, NULL, flags);
+
for (vd = txg_list_head(&spa->spa_vdev_txg_list, TXG_CLEAN(txg)); vd;
- vd = txg_list_next(&spa->spa_vdev_txg_list, vd, TXG_CLEAN(txg))) {
- zio_nowait(zio_ioctl(zio, spa, vd, DKIOCFLUSHWRITECACHE,
- NULL, NULL, ZIO_PRIORITY_NOW,
- ZIO_FLAG_CANFAIL | ZIO_FLAG_DONT_RETRY));
- }
+ vd = txg_list_next(&spa->spa_vdev_txg_list, vd, TXG_CLEAN(txg)))
+ zio_flush(zio, vd);
+
(void) zio_wait(zio);
/*
@@ -921,34 +1039,15 @@ vdev_config_sync(vdev_t *uvd, uint64_t txg)
* system dies in the middle of this process, that's OK: all of the
* even labels that made it to disk will be newer than any uberblock,
* and will therefore be considered invalid. The odd labels (L1, L3),
- * which have not yet been touched, will still be valid.
- */
- for (vd = list_head(&spa->spa_dirty_list); vd != NULL;
- vd = list_next(&spa->spa_dirty_list, vd)) {
- for (l = 0; l < VDEV_LABELS; l++) {
- if (l & 1)
- continue;
- if ((error = vdev_sync_labels(vd, l, txg)) != 0)
- return (error);
- }
- }
-
- /*
- * Flush the new labels to disk. This ensures that all even-label
- * updates are committed to stable storage before the uberblock update.
+ * which have not yet been touched, will still be valid. We flush
+ * the new labels to disk to ensure that all even-label updates
+ * are committed to stable storage before the uberblock update.
*/
- zio = zio_root(spa, NULL, NULL,
- ZIO_FLAG_CONFIG_HELD | ZIO_FLAG_CANFAIL);
- for (vd = list_head(&spa->spa_dirty_list); vd != NULL;
- vd = list_next(&spa->spa_dirty_list, vd)) {
- zio_nowait(zio_ioctl(zio, spa, vd, DKIOCFLUSHWRITECACHE,
- NULL, NULL, ZIO_PRIORITY_NOW,
- ZIO_FLAG_CANFAIL | ZIO_FLAG_DONT_RETRY));
- }
- (void) zio_wait(zio);
+ if ((error = vdev_label_sync_list(spa, 0, txg, flags)) != 0)
+ return (error);
/*
- * Sync the uberblocks to all vdevs in the tree specified by uvd.
+ * Sync the uberblocks to all vdevs in svd[].
* If the system dies in the middle of this step, there are two cases
* to consider, and the on-disk state is consistent either way:
*
@@ -962,50 +1061,18 @@ vdev_config_sync(vdev_t *uvd, uint64_t txg)
* been successfully committed) will be valid with respect
* to the new uberblocks.
*/
- if ((error = vdev_uberblock_sync_tree(spa, ub, uvd, txg)) != 0)
+ if ((error = vdev_uberblock_sync_list(svd, svdcount, ub, flags)) != 0)
return (error);
/*
- * Flush the uberblocks to disk. This ensures that the odd labels
- * are no longer needed (because the new uberblocks and the even
- * labels are safely on disk), so it is safe to overwrite them.
- */
- (void) zio_wait(zio_ioctl(NULL, spa, uvd, DKIOCFLUSHWRITECACHE,
- NULL, NULL, ZIO_PRIORITY_NOW,
- ZIO_FLAG_CONFIG_HELD | ZIO_FLAG_CANFAIL | ZIO_FLAG_DONT_RETRY));
-
- /*
* Sync out odd labels for every dirty vdev. If the system dies
* in the middle of this process, the even labels and the new
* uberblocks will suffice to open the pool. The next time
* the pool is opened, the first thing we'll do -- before any
* user data is modified -- is mark every vdev dirty so that
- * all labels will be brought up to date.
+ * all labels will be brought up to date. We flush the new labels
+ * to disk to ensure that all odd-label updates are committed to
+ * stable storage before the next transaction group begins.
*/
- for (vd = list_head(&spa->spa_dirty_list); vd != NULL;
- vd = list_next(&spa->spa_dirty_list, vd)) {
- for (l = 0; l < VDEV_LABELS; l++) {
- if ((l & 1) == 0)
- continue;
- if ((error = vdev_sync_labels(vd, l, txg)) != 0)
- return (error);
- }
- }
-
- /*
- * Flush the new labels to disk. This ensures that all odd-label
- * updates are committed to stable storage before the next
- * transaction group begins.
- */
- zio = zio_root(spa, NULL, NULL,
- ZIO_FLAG_CONFIG_HELD | ZIO_FLAG_CANFAIL);
- for (vd = list_head(&spa->spa_dirty_list); vd != NULL;
- vd = list_next(&spa->spa_dirty_list, vd)) {
- zio_nowait(zio_ioctl(zio, spa, vd, DKIOCFLUSHWRITECACHE,
- NULL, NULL, ZIO_PRIORITY_NOW,
- ZIO_FLAG_CANFAIL | ZIO_FLAG_DONT_RETRY));
- }
- (void) zio_wait(zio);
-
- return (0);
+ return (vdev_label_sync_list(spa, 1, txg, flags));
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_mirror.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_mirror.c
index 73d1a83..c4629ff 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_mirror.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_mirror.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/zfs_context.h>
#include <sys/spa.h>
#include <sys/vdev_impl.h>
@@ -39,8 +37,9 @@ typedef struct mirror_child {
vdev_t *mc_vd;
uint64_t mc_offset;
int mc_error;
- short mc_tried;
- short mc_skipped;
+ uint8_t mc_tried;
+ uint8_t mc_skipped;
+ uint8_t mc_speculative;
} mirror_child_t;
typedef struct mirror_map {
@@ -53,6 +52,14 @@ typedef struct mirror_map {
int vdev_mirror_shift = 21;
+static void
+vdev_mirror_map_free(zio_t *zio)
+{
+ mirror_map_t *mm = zio->io_vsd;
+
+ kmem_free(mm, offsetof(mirror_map_t, mm_child[mm->mm_children]));
+}
+
static mirror_map_t *
vdev_mirror_map_alloc(zio_t *zio)
{
@@ -110,18 +117,10 @@ vdev_mirror_map_alloc(zio_t *zio)
}
zio->io_vsd = mm;
+ zio->io_vsd_free = vdev_mirror_map_free;
return (mm);
}
-static void
-vdev_mirror_map_free(zio_t *zio)
-{
- mirror_map_t *mm = zio->io_vsd;
-
- kmem_free(mm, offsetof(mirror_map_t, mm_child[mm->mm_children]));
- zio->io_vsd = NULL;
-}
-
static int
vdev_mirror_open(vdev_t *vd, uint64_t *asize, uint64_t *ashift)
{
@@ -195,13 +194,6 @@ vdev_mirror_scrub_done(zio_t *zio)
mc->mc_skipped = 0;
}
-static void
-vdev_mirror_repair_done(zio_t *zio)
-{
- ASSERT(zio->io_private == zio->io_parent);
- vdev_mirror_map_free(zio->io_private);
-}
-
/*
* Try to find a child whose DTL doesn't contain the block we want to read.
* If we can't, try the read on any vdev we haven't already tried.
@@ -219,7 +211,7 @@ vdev_mirror_child_select(zio_t *zio)
/*
* Try to find a child whose DTL doesn't contain the block to read.
* If a child is known to be completely inaccessible (indicated by
- * vdev_is_dead() returning B_TRUE), don't even try.
+ * vdev_readable() returning B_FALSE), don't even try.
*/
for (i = 0, c = mm->mm_preferred; i < mm->mm_children; i++, c++) {
if (c >= mm->mm_children)
@@ -227,7 +219,7 @@ vdev_mirror_child_select(zio_t *zio)
mc = &mm->mm_child[c];
if (mc->mc_tried || mc->mc_skipped)
continue;
- if (vdev_is_dead(mc->mc_vd)) {
+ if (!vdev_readable(mc->mc_vd)) {
mc->mc_error = ENXIO;
mc->mc_tried = 1; /* don't even try */
mc->mc_skipped = 1;
@@ -237,6 +229,7 @@ vdev_mirror_child_select(zio_t *zio)
return (c);
mc->mc_error = ESTALE;
mc->mc_skipped = 1;
+ mc->mc_speculative = 1;
}
/*
@@ -253,7 +246,7 @@ vdev_mirror_child_select(zio_t *zio)
return (-1);
}
-static void
+static int
vdev_mirror_io_start(zio_t *zio)
{
mirror_map_t *mm;
@@ -275,12 +268,10 @@ vdev_mirror_io_start(zio_t *zio)
zio_nowait(zio_vdev_child_io(zio, zio->io_bp,
mc->mc_vd, mc->mc_offset,
zio_buf_alloc(zio->io_size), zio->io_size,
- zio->io_type, zio->io_priority,
- ZIO_FLAG_CANFAIL,
+ zio->io_type, zio->io_priority, 0,
vdev_mirror_scrub_done, mc));
}
- zio_wait_children_done(zio);
- return;
+ return (ZIO_PIPELINE_CONTINUE);
}
/*
* For normal reads just pick one child.
@@ -310,13 +301,27 @@ vdev_mirror_io_start(zio_t *zio)
while (children--) {
mc = &mm->mm_child[c];
zio_nowait(zio_vdev_child_io(zio, zio->io_bp,
- mc->mc_vd, mc->mc_offset,
- zio->io_data, zio->io_size, zio->io_type, zio->io_priority,
- ZIO_FLAG_CANFAIL, vdev_mirror_child_done, mc));
+ mc->mc_vd, mc->mc_offset, zio->io_data, zio->io_size,
+ zio->io_type, zio->io_priority, 0,
+ vdev_mirror_child_done, mc));
c++;
}
- zio_wait_children_done(zio);
+ return (ZIO_PIPELINE_CONTINUE);
+}
+
+static int
+vdev_mirror_worst_error(mirror_map_t *mm)
+{
+ int error[2] = { 0, 0 };
+
+ for (int c = 0; c < mm->mm_children; c++) {
+ mirror_child_t *mc = &mm->mm_child[c];
+ int s = mc->mc_speculative;
+ error[s] = zio_worst_error(error[s], mc->mc_error);
+ }
+
+ return (error[0] ? error[0] : error[1]);
}
static void
@@ -328,41 +333,45 @@ vdev_mirror_io_done(zio_t *zio)
int good_copies = 0;
int unexpected_errors = 0;
- zio->io_error = 0;
- zio->io_numerrors = 0;
-
for (c = 0; c < mm->mm_children; c++) {
mc = &mm->mm_child[c];
- if (mc->mc_tried && mc->mc_error == 0) {
- good_copies++;
- continue;
- }
-
- /*
- * We preserve any EIOs because those may be worth retrying;
- * whereas ECKSUM and ENXIO are more likely to be persistent.
- */
if (mc->mc_error) {
- if (zio->io_error != EIO)
- zio->io_error = mc->mc_error;
if (!mc->mc_skipped)
unexpected_errors++;
- zio->io_numerrors++;
+ } else if (mc->mc_tried) {
+ good_copies++;
}
}
if (zio->io_type == ZIO_TYPE_WRITE) {
/*
* XXX -- for now, treat partial writes as success.
- * XXX -- For a replacing vdev, we need to make sure the
- * new child succeeds.
+ *
+ * Now that we support write reallocation, it would be better
+ * to treat partial failure as real failure unless there are
+ * no non-degraded top-level vdevs left, and not update DTLs
+ * if we intend to reallocate.
*/
/* XXPOLICY */
- if (good_copies != 0)
- zio->io_error = 0;
- vdev_mirror_map_free(zio);
- zio_next_stage(zio);
+ if (good_copies != mm->mm_children) {
+ /*
+ * Always require at least one good copy.
+ *
+ * For ditto blocks (io_vd == NULL), require
+ * all copies to be good.
+ *
+ * XXX -- for replacing vdevs, there's no great answer.
+ * If the old device is really dead, we may not even
+ * be able to access it -- so we only want to
+ * require good writes to the new device. But if
+ * the new device turns out to be flaky, we want
+ * to be able to detach it -- which requires all
+ * writes to the old device to have succeeded.
+ */
+ if (good_copies == 0 || zio->io_vd == NULL)
+ zio->io_error = vdev_mirror_worst_error(mm);
+ }
return;
}
@@ -375,40 +384,27 @@ vdev_mirror_io_done(zio_t *zio)
if (good_copies == 0 && (c = vdev_mirror_child_select(zio)) != -1) {
ASSERT(c >= 0 && c < mm->mm_children);
mc = &mm->mm_child[c];
- dprintf("retrying i/o (err=%d) on child %s\n",
- zio->io_error, vdev_description(mc->mc_vd));
- zio->io_error = 0;
zio_vdev_io_redone(zio);
zio_nowait(zio_vdev_child_io(zio, zio->io_bp,
mc->mc_vd, mc->mc_offset, zio->io_data, zio->io_size,
- ZIO_TYPE_READ, zio->io_priority, ZIO_FLAG_CANFAIL,
+ ZIO_TYPE_READ, zio->io_priority, 0,
vdev_mirror_child_done, mc));
- zio_wait_children_done(zio);
return;
}
/* XXPOLICY */
- if (good_copies)
- zio->io_error = 0;
- else
+ if (good_copies == 0) {
+ zio->io_error = vdev_mirror_worst_error(mm);
ASSERT(zio->io_error != 0);
+ }
if (good_copies && (spa_mode & FWRITE) &&
(unexpected_errors ||
(zio->io_flags & ZIO_FLAG_RESILVER) ||
((zio->io_flags & ZIO_FLAG_SCRUB) && mm->mm_replacing))) {
- zio_t *rio;
-
/*
* Use the good data we have in hand to repair damaged children.
- *
- * We issue all repair I/Os as children of 'rio' to arrange
- * that vdev_mirror_map_free(zio) will be invoked after all
- * repairs complete, but before we advance to the next stage.
*/
- rio = zio_null(zio, zio->io_spa,
- vdev_mirror_repair_done, zio, ZIO_FLAG_CANFAIL);
-
for (c = 0; c < mm->mm_children; c++) {
/*
* Don't rewrite known good children.
@@ -429,24 +425,13 @@ vdev_mirror_io_done(zio_t *zio)
mc->mc_error = ESTALE;
}
- dprintf("resilvered %s @ 0x%llx error %d\n",
- vdev_description(mc->mc_vd), mc->mc_offset,
- mc->mc_error);
-
- zio_nowait(zio_vdev_child_io(rio, zio->io_bp, mc->mc_vd,
- mc->mc_offset, zio->io_data, zio->io_size,
+ zio_nowait(zio_vdev_child_io(zio, zio->io_bp,
+ mc->mc_vd, mc->mc_offset,
+ zio->io_data, zio->io_size,
ZIO_TYPE_WRITE, zio->io_priority,
- ZIO_FLAG_IO_REPAIR | ZIO_FLAG_CANFAIL |
- ZIO_FLAG_DONT_PROPAGATE, NULL, NULL));
+ ZIO_FLAG_IO_REPAIR, NULL, NULL));
}
-
- zio_nowait(rio);
- zio_wait_children_done(zio);
- return;
}
-
- vdev_mirror_map_free(zio);
- zio_next_stage(zio);
}
static void
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_missing.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_missing.c
index b35f4a5..731f7d3 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_missing.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_missing.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* The 'missing' vdev is a special vdev type used only during import. It
* signifies a placeholder in the root vdev for some vdev that we know is
@@ -63,18 +60,17 @@ vdev_missing_close(vdev_t *vd)
}
/* ARGSUSED */
-static void
+static int
vdev_missing_io_start(zio_t *zio)
{
zio->io_error = ENOTSUP;
- zio_next_stage_async(zio);
+ return (ZIO_PIPELINE_CONTINUE);
}
/* ARGSUSED */
static void
vdev_missing_io_done(zio_t *zio)
{
- zio_next_stage(zio);
}
vdev_ops_t vdev_missing_ops = {
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_queue.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_queue.c
index 8ef524f..cd4d5ae 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_queue.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_queue.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/zfs_context.h>
#include <sys/spa.h>
#include <sys/vdev_impl.h>
@@ -55,6 +53,25 @@ int zfs_vdev_ramp_rate = 2;
*/
int zfs_vdev_aggregation_limit = SPA_MAXBLOCKSIZE;
+SYSCTL_DECL(_vfs_zfs_vdev);
+TUNABLE_INT("vfs.zfs.vdev.max_pending", &zfs_vdev_max_pending);
+SYSCTL_INT(_vfs_zfs_vdev, OID_AUTO, max_pending, CTLFLAG_RDTUN,
+ &zfs_vdev_max_pending, 0, "Maximum I/O requests pending on each device");
+TUNABLE_INT("vfs.zfs.vdev.min_pending", &zfs_vdev_min_pending);
+SYSCTL_INT(_vfs_zfs_vdev, OID_AUTO, min_pending, CTLFLAG_RDTUN,
+ &zfs_vdev_min_pending, 0,
+ "Initial number of I/O requests pending to each device");
+TUNABLE_INT("vfs.zfs.vdev.time_shift", &zfs_vdev_time_shift);
+SYSCTL_INT(_vfs_zfs_vdev, OID_AUTO, time_shift, CTLFLAG_RDTUN,
+ &zfs_vdev_time_shift, 0, "Used for calculating I/O request deadline");
+TUNABLE_INT("vfs.zfs.vdev.ramp_rate", &zfs_vdev_ramp_rate);
+SYSCTL_INT(_vfs_zfs_vdev, OID_AUTO, ramp_rate, CTLFLAG_RDTUN,
+ &zfs_vdev_ramp_rate, 0, "Exponential I/O issue ramp-up rate");
+TUNABLE_INT("vfs.zfs.vdev.aggregation_limit", &zfs_vdev_aggregation_limit);
+SYSCTL_INT(_vfs_zfs_vdev, OID_AUTO, aggregation_limit, CTLFLAG_RDTUN,
+ &zfs_vdev_aggregation_limit, 0,
+ "I/O requests are aggregated up to this size");
+
/*
* Virtual device vector for disk I/O scheduling.
*/
@@ -162,7 +179,7 @@ vdev_queue_agg_io_done(zio_t *aio)
aio->io_delegate_list = dio->io_delegate_next;
dio->io_delegate_next = NULL;
dio->io_error = aio->io_error;
- zio_next_stage(dio);
+ zio_execute(dio);
}
ASSERT3U(offset, ==, aio->io_size);
@@ -172,11 +189,8 @@ vdev_queue_agg_io_done(zio_t *aio)
#define IS_ADJACENT(io, nio) \
((io)->io_offset + (io)->io_size == (nio)->io_offset)
-typedef void zio_issue_func_t(zio_t *);
-
static zio_t *
-vdev_queue_io_to_issue(vdev_queue_t *vq, uint64_t pending_limit,
- zio_issue_func_t **funcp)
+vdev_queue_io_to_issue(vdev_queue_t *vq, uint64_t pending_limit)
{
zio_t *fio, *lio, *aio, *dio;
avl_tree_t *tree;
@@ -184,8 +198,6 @@ vdev_queue_io_to_issue(vdev_queue_t *vq, uint64_t pending_limit,
ASSERT(MUTEX_HELD(&vq->vq_lock));
- *funcp = NULL;
-
if (avl_numnodes(&vq->vq_pending_tree) >= pending_limit ||
avl_numnodes(&vq->vq_deadline_tree) == 0)
return (NULL);
@@ -196,6 +208,7 @@ vdev_queue_io_to_issue(vdev_queue_t *vq, uint64_t pending_limit,
size = fio->io_size;
while ((dio = AVL_PREV(tree, fio)) != NULL && IS_ADJACENT(dio, fio) &&
+ !((dio->io_flags | fio->io_flags) & ZIO_FLAG_DONT_AGGREGATE) &&
size + dio->io_size <= zfs_vdev_aggregation_limit) {
dio->io_delegate_next = fio;
fio = dio;
@@ -203,6 +216,7 @@ vdev_queue_io_to_issue(vdev_queue_t *vq, uint64_t pending_limit,
}
while ((dio = AVL_NEXT(tree, lio)) != NULL && IS_ADJACENT(lio, dio) &&
+ !((lio->io_flags | dio->io_flags) & ZIO_FLAG_DONT_AGGREGATE) &&
size + dio->io_size <= zfs_vdev_aggregation_limit) {
lio->io_delegate_next = dio;
lio = dio;
@@ -212,15 +226,12 @@ vdev_queue_io_to_issue(vdev_queue_t *vq, uint64_t pending_limit,
if (fio != lio) {
char *buf = zio_buf_alloc(size);
uint64_t offset = 0;
- int nagg = 0;
ASSERT(size <= zfs_vdev_aggregation_limit);
- aio = zio_vdev_child_io(fio, NULL, fio->io_vd,
- fio->io_offset, buf, size, fio->io_type,
- ZIO_PRIORITY_NOW, ZIO_FLAG_DONT_QUEUE |
- ZIO_FLAG_DONT_CACHE | ZIO_FLAG_DONT_PROPAGATE |
- ZIO_FLAG_NOBOOKMARK,
+ aio = zio_vdev_delegated_io(fio->io_vd, fio->io_offset,
+ buf, size, fio->io_type, ZIO_PRIORITY_NOW,
+ ZIO_FLAG_DONT_CACHE | ZIO_FLAG_DONT_QUEUE,
vdev_queue_agg_io_done, NULL);
aio->io_delegate_list = fio;
@@ -233,19 +244,12 @@ vdev_queue_io_to_issue(vdev_queue_t *vq, uint64_t pending_limit,
offset += dio->io_size;
vdev_queue_io_remove(vq, dio);
zio_vdev_io_bypass(dio);
- nagg++;
}
ASSERT(offset == size);
- dprintf("%5s T=%llu off=%8llx agg=%3d "
- "old=%5llx new=%5llx\n",
- zio_type_name[fio->io_type],
- fio->io_deadline, fio->io_offset, nagg, fio->io_size, size);
-
avl_add(&vq->vq_pending_tree, aio);
- *funcp = zio_nowait;
return (aio);
}
@@ -254,8 +258,6 @@ vdev_queue_io_to_issue(vdev_queue_t *vq, uint64_t pending_limit,
avl_add(&vq->vq_pending_tree, fio);
- *funcp = zio_next_stage;
-
return (fio);
}
@@ -264,7 +266,6 @@ vdev_queue_io(zio_t *zio)
{
vdev_queue_t *vq = &zio->io_vd->vdev_queue;
zio_t *nio;
- zio_issue_func_t *func;
ASSERT(zio->io_type == ZIO_TYPE_READ || zio->io_type == ZIO_TYPE_WRITE);
@@ -280,42 +281,45 @@ vdev_queue_io(zio_t *zio)
mutex_enter(&vq->vq_lock);
- zio->io_deadline = (zio->io_timestamp >> zfs_vdev_time_shift) +
- zio->io_priority;
+ zio->io_deadline = (lbolt64 >> zfs_vdev_time_shift) + zio->io_priority;
vdev_queue_io_add(vq, zio);
- nio = vdev_queue_io_to_issue(vq, zfs_vdev_min_pending, &func);
+ nio = vdev_queue_io_to_issue(vq, zfs_vdev_min_pending);
mutex_exit(&vq->vq_lock);
- if (nio == NULL || func != zio_nowait)
- return (nio);
+ if (nio == NULL)
+ return (NULL);
+
+ if (nio->io_done == vdev_queue_agg_io_done) {
+ zio_nowait(nio);
+ return (NULL);
+ }
- func(nio);
- return (NULL);
+ return (nio);
}
void
vdev_queue_io_done(zio_t *zio)
{
vdev_queue_t *vq = &zio->io_vd->vdev_queue;
- zio_t *nio;
- zio_issue_func_t *func;
- int i;
mutex_enter(&vq->vq_lock);
avl_remove(&vq->vq_pending_tree, zio);
- for (i = 0; i < zfs_vdev_ramp_rate; i++) {
- nio = vdev_queue_io_to_issue(vq, zfs_vdev_max_pending, &func);
+ for (int i = 0; i < zfs_vdev_ramp_rate; i++) {
+ zio_t *nio = vdev_queue_io_to_issue(vq, zfs_vdev_max_pending);
if (nio == NULL)
break;
mutex_exit(&vq->vq_lock);
- if (func == zio_next_stage)
+ if (nio->io_done == vdev_queue_agg_io_done) {
+ zio_nowait(nio);
+ } else {
zio_vdev_io_reissue(nio);
- func(nio);
+ zio_execute(nio);
+ }
mutex_enter(&vq->vq_lock);
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_raidz.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_raidz.c
index 0c86630..69e3144 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_raidz.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_raidz.c
@@ -20,12 +20,10 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/zfs_context.h>
#include <sys/spa.h>
#include <sys/vdev_impl.h>
@@ -194,6 +192,18 @@ vdev_raidz_exp2(uint_t a, int exp)
return (vdev_raidz_pow2[exp]);
}
+static void
+vdev_raidz_map_free(zio_t *zio)
+{
+ raidz_map_t *rm = zio->io_vsd;
+ int c;
+
+ for (c = 0; c < rm->rm_firstdatacol; c++)
+ zio_buf_free(rm->rm_col[c].rc_data, rm->rm_col[c].rc_size);
+
+ kmem_free(rm, offsetof(raidz_map_t, rm_col[rm->rm_cols]));
+}
+
static raidz_map_t *
vdev_raidz_map_alloc(zio_t *zio, uint64_t unit_shift, uint64_t dcols,
uint64_t nparity)
@@ -276,23 +286,11 @@ vdev_raidz_map_alloc(zio_t *zio, uint64_t unit_shift, uint64_t dcols,
}
zio->io_vsd = rm;
+ zio->io_vsd_free = vdev_raidz_map_free;
return (rm);
}
static void
-vdev_raidz_map_free(zio_t *zio)
-{
- raidz_map_t *rm = zio->io_vsd;
- int c;
-
- for (c = 0; c < rm->rm_firstdatacol; c++)
- zio_buf_free(rm->rm_col[c].rc_data, rm->rm_col[c].rc_size);
-
- kmem_free(rm, offsetof(raidz_map_t, rm_col[rm->rm_cols]));
- zio->io_vsd = NULL;
-}
-
-static void
vdev_raidz_generate_parity_p(raidz_map_t *rm)
{
uint64_t *p, *src, pcount, ccount, i;
@@ -632,14 +630,7 @@ vdev_raidz_child_done(zio_t *zio)
rc->rc_skipped = 0;
}
-static void
-vdev_raidz_repair_done(zio_t *zio)
-{
- ASSERT(zio->io_private == zio->io_parent);
- vdev_raidz_map_free(zio->io_private);
-}
-
-static void
+static int
vdev_raidz_io_start(zio_t *zio)
{
vdev_t *vd = zio->io_vd;
@@ -669,11 +660,11 @@ vdev_raidz_io_start(zio_t *zio)
cvd = vd->vdev_child[rc->rc_devidx];
zio_nowait(zio_vdev_child_io(zio, NULL, cvd,
rc->rc_offset, rc->rc_data, rc->rc_size,
- zio->io_type, zio->io_priority, ZIO_FLAG_CANFAIL,
+ zio->io_type, zio->io_priority, 0,
vdev_raidz_child_done, rc));
}
- zio_wait_children_done(zio);
- return;
+
+ return (ZIO_PIPELINE_CONTINUE);
}
ASSERT(zio->io_type == ZIO_TYPE_READ);
@@ -686,7 +677,7 @@ vdev_raidz_io_start(zio_t *zio)
for (c = rm->rm_cols - 1; c >= 0; c--) {
rc = &rm->rm_col[c];
cvd = vd->vdev_child[rc->rc_devidx];
- if (vdev_is_dead(cvd)) {
+ if (!vdev_readable(cvd)) {
if (c >= rm->rm_firstdatacol)
rm->rm_missingdata++;
else
@@ -709,12 +700,12 @@ vdev_raidz_io_start(zio_t *zio)
(zio->io_flags & ZIO_FLAG_SCRUB)) {
zio_nowait(zio_vdev_child_io(zio, NULL, cvd,
rc->rc_offset, rc->rc_data, rc->rc_size,
- zio->io_type, zio->io_priority, ZIO_FLAG_CANFAIL,
+ zio->io_type, zio->io_priority, 0,
vdev_raidz_child_done, rc));
}
}
- zio_wait_children_done(zio);
+ return (ZIO_PIPELINE_CONTINUE);
}
/*
@@ -724,8 +715,6 @@ static void
raidz_checksum_error(zio_t *zio, raidz_col_t *rc)
{
vdev_t *vd = zio->io_vd->vdev_child[rc->rc_devidx];
- dprintf_bp(zio->io_bp, "imputed checksum error on %s: ",
- vdev_description(vd));
if (!(zio->io_flags & ZIO_FLAG_SPECULATIVE)) {
mutex_enter(&vd->vdev_stat_lock);
@@ -783,6 +772,17 @@ static uint64_t raidz_corrected_p;
static uint64_t raidz_corrected_q;
static uint64_t raidz_corrected_pq;
+static int
+vdev_raidz_worst_error(raidz_map_t *rm)
+{
+ int error = 0;
+
+ for (int c = 0; c < rm->rm_cols; c++)
+ error = zio_worst_error(error, rm->rm_col[c].rc_error);
+
+ return (error);
+}
+
static void
vdev_raidz_io_done(zio_t *zio)
{
@@ -794,26 +794,19 @@ vdev_raidz_io_done(zio_t *zio)
int parity_errors = 0;
int parity_untried = 0;
int data_errors = 0;
+ int total_errors = 0;
int n, c, c1;
ASSERT(zio->io_bp != NULL); /* XXX need to add code to enforce this */
- zio->io_error = 0;
- zio->io_numerrors = 0;
-
ASSERT(rm->rm_missingparity <= rm->rm_firstdatacol);
ASSERT(rm->rm_missingdata <= rm->rm_cols - rm->rm_firstdatacol);
for (c = 0; c < rm->rm_cols; c++) {
rc = &rm->rm_col[c];
- /*
- * We preserve any EIOs because those may be worth retrying;
- * whereas ECKSUM and ENXIO are more likely to be persistent.
- */
if (rc->rc_error) {
- if (zio->io_error != EIO)
- zio->io_error = rc->rc_error;
+ ASSERT(rc->rc_error != ECKSUM); /* child has no bp */
if (c < rm->rm_firstdatacol)
parity_errors++;
@@ -823,7 +816,7 @@ vdev_raidz_io_done(zio_t *zio)
if (!rc->rc_skipped)
unexpected_errors++;
- zio->io_numerrors++;
+ total_errors++;
} else if (c < rm->rm_firstdatacol && !rc->rc_tried) {
parity_untried++;
}
@@ -831,16 +824,19 @@ vdev_raidz_io_done(zio_t *zio)
if (zio->io_type == ZIO_TYPE_WRITE) {
/*
- * If this is not a failfast write, and we were able to
- * write enough columns to reconstruct the data, good enough.
+ * XXX -- for now, treat partial writes as a success.
+ * (If we couldn't write enough columns to reconstruct
+ * the data, the I/O failed. Otherwise, good enough.)
+ *
+ * Now that we support write reallocation, it would be better
+ * to treat partial failure as real failure unless there are
+ * no non-degraded top-level vdevs left, and not update DTLs
+ * if we intend to reallocate.
*/
/* XXPOLICY */
- if (zio->io_numerrors <= rm->rm_firstdatacol &&
- !(zio->io_flags & ZIO_FLAG_FAILFAST))
- zio->io_error = 0;
+ if (total_errors > rm->rm_firstdatacol)
+ zio->io_error = vdev_raidz_worst_error(rm);
- vdev_raidz_map_free(zio);
- zio_next_stage(zio);
return;
}
@@ -862,12 +858,10 @@ vdev_raidz_io_done(zio_t *zio)
* has a valid checksum. Naturally, this case applies in the absence of
* any errors.
*/
- if (zio->io_numerrors <= rm->rm_firstdatacol - parity_untried) {
+ if (total_errors <= rm->rm_firstdatacol - parity_untried) {
switch (data_errors) {
case 0:
if (zio_checksum_error(zio) == 0) {
- zio->io_error = 0;
-
/*
* If we read parity information (unnecessarily
* as it happens since no reconstruction was
@@ -919,7 +913,6 @@ vdev_raidz_io_done(zio_t *zio)
}
if (zio_checksum_error(zio) == 0) {
- zio->io_error = 0;
if (rm->rm_col[VDEV_RAIDZ_P].rc_error == 0)
atomic_inc_64(&raidz_corrected_p);
else
@@ -981,9 +974,7 @@ vdev_raidz_io_done(zio_t *zio)
vdev_raidz_reconstruct_pq(rm, c1, c);
if (zio_checksum_error(zio) == 0) {
- zio->io_error = 0;
atomic_inc_64(&raidz_corrected_pq);
-
goto done;
}
break;
@@ -1009,7 +1000,6 @@ vdev_raidz_io_done(zio_t *zio)
if (rm->rm_col[c].rc_tried)
continue;
- zio->io_error = 0;
zio_vdev_io_redone(zio);
do {
rc = &rm->rm_col[c];
@@ -1018,11 +1008,10 @@ vdev_raidz_io_done(zio_t *zio)
zio_nowait(zio_vdev_child_io(zio, NULL,
vd->vdev_child[rc->rc_devidx],
rc->rc_offset, rc->rc_data, rc->rc_size,
- zio->io_type, zio->io_priority, ZIO_FLAG_CANFAIL,
+ zio->io_type, zio->io_priority, 0,
vdev_raidz_child_done, rc));
} while (++c < rm->rm_cols);
- dprintf("rereading\n");
- zio_wait_children_done(zio);
+
return;
}
@@ -1034,8 +1023,15 @@ vdev_raidz_io_done(zio_t *zio)
* in absent data. Before we attempt combinatorial reconstruction make
* sure we have a chance of coming up with the right answer.
*/
- if (zio->io_numerrors >= rm->rm_firstdatacol) {
- ASSERT(zio->io_error != 0);
+ if (total_errors >= rm->rm_firstdatacol) {
+ zio->io_error = vdev_raidz_worst_error(rm);
+ /*
+ * If there were exactly as many device errors as parity
+ * columns, yet we couldn't reconstruct the data, then at
+ * least one device must have returned bad data silently.
+ */
+ if (total_errors == rm->rm_firstdatacol)
+ zio->io_error = zio_worst_error(zio->io_error, ECKSUM);
goto done;
}
@@ -1053,7 +1049,6 @@ vdev_raidz_io_done(zio_t *zio)
if (zio_checksum_error(zio) == 0) {
zio_buf_free(orig, rc->rc_size);
- zio->io_error = 0;
atomic_inc_64(&raidz_corrected_p);
/*
@@ -1085,7 +1080,6 @@ vdev_raidz_io_done(zio_t *zio)
if (zio_checksum_error(zio) == 0) {
zio_buf_free(orig, rc->rc_size);
- zio->io_error = 0;
atomic_inc_64(&raidz_corrected_q);
/*
@@ -1127,7 +1121,6 @@ vdev_raidz_io_done(zio_t *zio)
if (zio_checksum_error(zio) == 0) {
zio_buf_free(orig, rc->rc_size);
zio_buf_free(orig1, rc1->rc_size);
- zio->io_error = 0;
atomic_inc_64(&raidz_corrected_pq);
/*
@@ -1159,6 +1152,7 @@ vdev_raidz_io_done(zio_t *zio)
* all children.
*/
zio->io_error = ECKSUM;
+
if (!(zio->io_flags & ZIO_FLAG_SPECULATIVE)) {
for (c = 0; c < rm->rm_cols; c++) {
rc = &rm->rm_col[c];
@@ -1173,18 +1167,9 @@ done:
if (zio->io_error == 0 && (spa_mode & FWRITE) &&
(unexpected_errors || (zio->io_flags & ZIO_FLAG_RESILVER))) {
- zio_t *rio;
-
/*
* Use the good data we have in hand to repair damaged children.
- *
- * We issue all repair I/Os as children of 'rio' to arrange
- * that vdev_raidz_map_free(zio) will be invoked after all
- * repairs complete, but before we advance to the next stage.
*/
- rio = zio_null(zio, zio->io_spa,
- vdev_raidz_repair_done, zio, ZIO_FLAG_CANFAIL);
-
for (c = 0; c < rm->rm_cols; c++) {
rc = &rm->rm_col[c];
cvd = vd->vdev_child[rc->rc_devidx];
@@ -1192,25 +1177,12 @@ done:
if (rc->rc_error == 0)
continue;
- dprintf("%s resilvered %s @ 0x%llx error %d\n",
- vdev_description(vd),
- vdev_description(cvd),
- zio->io_offset, rc->rc_error);
-
- zio_nowait(zio_vdev_child_io(rio, NULL, cvd,
+ zio_nowait(zio_vdev_child_io(zio, NULL, cvd,
rc->rc_offset, rc->rc_data, rc->rc_size,
ZIO_TYPE_WRITE, zio->io_priority,
- ZIO_FLAG_IO_REPAIR | ZIO_FLAG_DONT_PROPAGATE |
- ZIO_FLAG_CANFAIL, NULL, NULL));
+ ZIO_FLAG_IO_REPAIR, NULL, NULL));
}
-
- zio_nowait(rio);
- zio_wait_children_done(zio);
- return;
}
-
- vdev_raidz_map_free(zio);
- zio_next_stage(zio);
}
static void
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_root.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_root.c
index 0e8752c..88383f0 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_root.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_root.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/zfs_context.h>
#include <sys/spa.h>
#include <sys/vdev_impl.h>
@@ -44,18 +42,17 @@
* probably fine. Adding bean counters during alloc/free can make this
* future guesswork more accurate.
*/
-/*ARGSUSED*/
static int
too_many_errors(vdev_t *vd, int numerrors)
{
+ ASSERT3U(numerrors, <=, vd->vdev_children);
return (numerrors > 0);
}
static int
vdev_root_open(vdev_t *vd, uint64_t *asize, uint64_t *ashift)
{
- vdev_t *cvd;
- int c, error;
+ int c;
int lasterror = 0;
int numerrors = 0;
@@ -65,9 +62,11 @@ vdev_root_open(vdev_t *vd, uint64_t *asize, uint64_t *ashift)
}
for (c = 0; c < vd->vdev_children; c++) {
- cvd = vd->vdev_child[c];
+ vdev_t *cvd = vd->vdev_child[c];
+ int error;
- if ((error = vdev_open(cvd)) != 0) {
+ if ((error = vdev_open(cvd)) != 0 &&
+ !cvd->vdev_islog) {
lasterror = error;
numerrors++;
continue;
@@ -97,13 +96,14 @@ vdev_root_close(vdev_t *vd)
static void
vdev_root_state_change(vdev_t *vd, int faulted, int degraded)
{
- if (too_many_errors(vd, faulted))
+ if (too_many_errors(vd, faulted)) {
vdev_set_state(vd, B_FALSE, VDEV_STATE_CANT_OPEN,
VDEV_AUX_NO_REPLICAS);
- else if (degraded != 0)
+ } else if (degraded) {
vdev_set_state(vd, B_FALSE, VDEV_STATE_DEGRADED, VDEV_AUX_NONE);
- else
+ } else {
vdev_set_state(vd, B_FALSE, VDEV_STATE_HEALTHY, VDEV_AUX_NONE);
+ }
}
vdev_ops_t vdev_root_ops = {
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zap.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zap.c
index 4246ec0..90fe3d0 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zap.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zap.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -44,6 +44,7 @@
#include <sys/spa.h>
#include <sys/dmu.h>
#include <sys/zfs_context.h>
+#include <sys/zfs_znode.h>
#include <sys/zap.h>
#include <sys/refcount.h>
#include <sys/zap_impl.h>
@@ -103,6 +104,7 @@ fzap_upgrade(zap_t *zap, dmu_tx_t *tx)
zp->zap_num_leafs = 1;
zp->zap_num_entries = 0;
zp->zap_salt = zap->zap_salt;
+ zp->zap_normflags = zap->zap_normflags;
/* block 1 will be the first leaf */
for (i = 0; i < (1<<zp->zap_ptrtbl.zt_shift); i++)
@@ -119,7 +121,7 @@ fzap_upgrade(zap_t *zap, dmu_tx_t *tx)
l->l_dbuf = db;
l->l_phys = db->db_data;
- zap_leaf_init(l);
+ zap_leaf_init(l, zp->zap_normflags != 0);
kmem_free(l, sizeof (zap_leaf_t));
dmu_buf_rele(db, FTAG);
@@ -399,7 +401,7 @@ zap_create_leaf(zap_t *zap, dmu_tx_t *tx)
ASSERT(winner == NULL);
dmu_buf_will_dirty(l->l_dbuf, tx);
- zap_leaf_init(l);
+ zap_leaf_init(l, zap->zap_normflags != 0);
zap->zap_f.zap_phys->zap_num_leafs++;
@@ -580,9 +582,10 @@ zap_deref_leaf(zap_t *zap, uint64_t h, dmu_tx_t *tx, krw_t lt, zap_leaf_t **lp)
}
static int
-zap_expand_leaf(zap_t *zap, zap_leaf_t *l, uint64_t hash, dmu_tx_t *tx,
- zap_leaf_t **lp)
+zap_expand_leaf(zap_name_t *zn, zap_leaf_t *l, dmu_tx_t *tx, zap_leaf_t **lp)
{
+ zap_t *zap = zn->zn_zap;
+ uint64_t hash = zn->zn_hash;
zap_leaf_t *nl;
int prefix_diff, i, err;
uint64_t sibling;
@@ -602,7 +605,9 @@ zap_expand_leaf(zap_t *zap, zap_leaf_t *l, uint64_t hash, dmu_tx_t *tx,
zap_put_leaf(l);
zap_unlockdir(zap);
- err = zap_lockdir(os, object, tx, RW_WRITER, FALSE, &zap);
+ err = zap_lockdir(os, object, tx, RW_WRITER,
+ FALSE, FALSE, &zn->zn_zap);
+ zap = zn->zn_zap;
if (err)
return (err);
ASSERT(!zap->zap_ismicro);
@@ -643,7 +648,7 @@ zap_expand_leaf(zap_t *zap, zap_leaf_t *l, uint64_t hash, dmu_tx_t *tx,
}
nl = zap_create_leaf(zap, tx);
- zap_leaf_split(l, nl);
+ zap_leaf_split(l, nl, zap->zap_normflags != 0);
/* set sibling pointers */
for (i = 0; i < (1ULL<<prefix_diff); i++) {
@@ -664,8 +669,9 @@ zap_expand_leaf(zap_t *zap, zap_leaf_t *l, uint64_t hash, dmu_tx_t *tx,
}
static void
-zap_put_leaf_maybe_grow_ptrtbl(zap_t *zap, zap_leaf_t *l, dmu_tx_t *tx)
+zap_put_leaf_maybe_grow_ptrtbl(zap_name_t *zn, zap_leaf_t *l, dmu_tx_t *tx)
{
+ zap_t *zap = zn->zn_zap;
int shift = zap->zap_f.zap_phys->zap_ptrtbl.zt_shift;
int leaffull = (l->l_phys->l_hdr.lh_prefix_len == shift &&
l->l_phys->l_hdr.lh_nfree < ZAP_LEAF_LOW_WATER);
@@ -685,7 +691,8 @@ zap_put_leaf_maybe_grow_ptrtbl(zap_t *zap, zap_leaf_t *l, dmu_tx_t *tx)
zap_unlockdir(zap);
err = zap_lockdir(os, zapobj, tx,
- RW_WRITER, FALSE, &zap);
+ RW_WRITER, FALSE, FALSE, &zn->zn_zap);
+ zap = zn->zn_zap;
if (err)
return;
}
@@ -721,53 +728,58 @@ fzap_checksize(const char *name, uint64_t integer_size, uint64_t num_integers)
}
/*
- * Routines for maniplulating attributes.
+ * Routines for manipulating attributes.
*/
int
-fzap_lookup(zap_t *zap, const char *name,
- uint64_t integer_size, uint64_t num_integers, void *buf)
+fzap_lookup(zap_name_t *zn,
+ uint64_t integer_size, uint64_t num_integers, void *buf,
+ char *realname, int rn_len, boolean_t *ncp)
{
zap_leaf_t *l;
int err;
- uint64_t hash;
zap_entry_handle_t zeh;
- err = fzap_checksize(name, integer_size, num_integers);
+ err = fzap_checksize(zn->zn_name_orij, integer_size, num_integers);
if (err != 0)
return (err);
- hash = zap_hash(zap, name);
- err = zap_deref_leaf(zap, hash, NULL, RW_READER, &l);
+ err = zap_deref_leaf(zn->zn_zap, zn->zn_hash, NULL, RW_READER, &l);
if (err != 0)
return (err);
- err = zap_leaf_lookup(l, name, hash, &zeh);
- if (err == 0)
+ err = zap_leaf_lookup(l, zn, &zeh);
+ if (err == 0) {
err = zap_entry_read(&zeh, integer_size, num_integers, buf);
+ (void) zap_entry_read_name(&zeh, rn_len, realname);
+ if (ncp) {
+ *ncp = zap_entry_normalization_conflict(&zeh,
+ zn, NULL, zn->zn_zap);
+ }
+ }
zap_put_leaf(l);
return (err);
}
int
-fzap_add_cd(zap_t *zap, const char *name,
+fzap_add_cd(zap_name_t *zn,
uint64_t integer_size, uint64_t num_integers,
const void *val, uint32_t cd, dmu_tx_t *tx)
{
zap_leaf_t *l;
- uint64_t hash;
int err;
zap_entry_handle_t zeh;
+ zap_t *zap = zn->zn_zap;
ASSERT(RW_LOCK_HELD(&zap->zap_rwlock));
ASSERT(!zap->zap_ismicro);
- ASSERT(fzap_checksize(name, integer_size, num_integers) == 0);
+ ASSERT(fzap_checksize(zn->zn_name_orij,
+ integer_size, num_integers) == 0);
- hash = zap_hash(zap, name);
- err = zap_deref_leaf(zap, hash, tx, RW_WRITER, &l);
+ err = zap_deref_leaf(zap, zn->zn_hash, tx, RW_WRITER, &l);
if (err != 0)
return (err);
retry:
- err = zap_leaf_lookup(l, name, hash, &zeh);
+ err = zap_leaf_lookup(l, zn, &zeh);
if (err == 0) {
err = EEXIST;
goto out;
@@ -775,63 +787,62 @@ retry:
if (err != ENOENT)
goto out;
- err = zap_entry_create(l, name, hash, cd,
+ err = zap_entry_create(l, zn->zn_name_orij, zn->zn_hash, cd,
integer_size, num_integers, val, &zeh);
if (err == 0) {
zap_increment_num_entries(zap, 1, tx);
} else if (err == EAGAIN) {
- err = zap_expand_leaf(zap, l, hash, tx, &l);
+ err = zap_expand_leaf(zn, l, tx, &l);
+ zap = zn->zn_zap; /* zap_expand_leaf() may change zap */
if (err == 0)
goto retry;
}
out:
- zap_put_leaf_maybe_grow_ptrtbl(zap, l, tx);
+ if (zap != NULL)
+ zap_put_leaf_maybe_grow_ptrtbl(zn, l, tx);
return (err);
}
int
-fzap_add(zap_t *zap, const char *name,
+fzap_add(zap_name_t *zn,
uint64_t integer_size, uint64_t num_integers,
const void *val, dmu_tx_t *tx)
{
- int err = fzap_checksize(name, integer_size, num_integers);
+ int err = fzap_checksize(zn->zn_name_orij, integer_size, num_integers);
if (err != 0)
return (err);
- return (fzap_add_cd(zap, name, integer_size, num_integers,
+ return (fzap_add_cd(zn, integer_size, num_integers,
val, ZAP_MAXCD, tx));
}
int
-fzap_update(zap_t *zap, const char *name,
+fzap_update(zap_name_t *zn,
int integer_size, uint64_t num_integers, const void *val, dmu_tx_t *tx)
{
zap_leaf_t *l;
- uint64_t hash;
int err, create;
zap_entry_handle_t zeh;
+ zap_t *zap = zn->zn_zap;
ASSERT(RW_LOCK_HELD(&zap->zap_rwlock));
- err = fzap_checksize(name, integer_size, num_integers);
+ err = fzap_checksize(zn->zn_name_orij, integer_size, num_integers);
if (err != 0)
return (err);
- hash = zap_hash(zap, name);
- err = zap_deref_leaf(zap, hash, tx, RW_WRITER, &l);
+ err = zap_deref_leaf(zap, zn->zn_hash, tx, RW_WRITER, &l);
if (err != 0)
return (err);
retry:
- err = zap_leaf_lookup(l, name, hash, &zeh);
+ err = zap_leaf_lookup(l, zn, &zeh);
create = (err == ENOENT);
ASSERT(err == 0 || err == ENOENT);
- /* XXX If this leaf is chained, split it if we can. */
-
if (create) {
- err = zap_entry_create(l, name, hash, ZAP_MAXCD,
- integer_size, num_integers, val, &zeh);
+ err = zap_entry_create(l, zn->zn_name_orij, zn->zn_hash,
+ ZAP_MAXCD, integer_size, num_integers, val, &zeh);
if (err == 0)
zap_increment_num_entries(zap, 1, tx);
} else {
@@ -839,29 +850,29 @@ retry:
}
if (err == EAGAIN) {
- err = zap_expand_leaf(zap, l, hash, tx, &l);
+ err = zap_expand_leaf(zn, l, tx, &l);
+ zap = zn->zn_zap; /* zap_expand_leaf() may change zap */
if (err == 0)
goto retry;
}
- zap_put_leaf_maybe_grow_ptrtbl(zap, l, tx);
+ if (zap != NULL)
+ zap_put_leaf_maybe_grow_ptrtbl(zn, l, tx);
return (err);
}
int
-fzap_length(zap_t *zap, const char *name,
+fzap_length(zap_name_t *zn,
uint64_t *integer_size, uint64_t *num_integers)
{
zap_leaf_t *l;
int err;
- uint64_t hash;
zap_entry_handle_t zeh;
- hash = zap_hash(zap, name);
- err = zap_deref_leaf(zap, hash, NULL, RW_READER, &l);
+ err = zap_deref_leaf(zn->zn_zap, zn->zn_hash, NULL, RW_READER, &l);
if (err != 0)
return (err);
- err = zap_leaf_lookup(l, name, hash, &zeh);
+ err = zap_leaf_lookup(l, zn, &zeh);
if (err != 0)
goto out;
@@ -875,40 +886,44 @@ out:
}
int
-fzap_remove(zap_t *zap, const char *name, dmu_tx_t *tx)
+fzap_remove(zap_name_t *zn, dmu_tx_t *tx)
{
zap_leaf_t *l;
- uint64_t hash;
int err;
zap_entry_handle_t zeh;
- hash = zap_hash(zap, name);
- err = zap_deref_leaf(zap, hash, tx, RW_WRITER, &l);
+ err = zap_deref_leaf(zn->zn_zap, zn->zn_hash, tx, RW_WRITER, &l);
if (err != 0)
return (err);
- err = zap_leaf_lookup(l, name, hash, &zeh);
+ err = zap_leaf_lookup(l, zn, &zeh);
if (err == 0) {
zap_entry_remove(&zeh);
- zap_increment_num_entries(zap, -1, tx);
+ zap_increment_num_entries(zn->zn_zap, -1, tx);
}
zap_put_leaf(l);
- dprintf("fzap_remove: ds=%p obj=%llu name=%s err=%d\n",
- zap->zap_objset, zap->zap_object, name, err);
return (err);
}
+/*
+ * Helper functions for consumers.
+ */
+
int
-zap_value_search(objset_t *os, uint64_t zapobj, uint64_t value, char *name)
+zap_value_search(objset_t *os, uint64_t zapobj, uint64_t value, uint64_t mask,
+ char *name)
{
zap_cursor_t zc;
zap_attribute_t *za;
int err;
+ if (mask == 0)
+ mask = -1ULL;
+
za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP);
for (zap_cursor_init(&zc, os, zapobj);
(err = zap_cursor_retrieve(&zc, za)) == 0;
zap_cursor_advance(&zc)) {
- if (ZFS_DIRENT_OBJ(za->za_first_integer) == value) {
+ if ((za->za_first_integer & mask) == (value & mask)) {
(void) strcpy(name, za->za_name);
break;
}
@@ -918,6 +933,53 @@ zap_value_search(objset_t *os, uint64_t zapobj, uint64_t value, char *name)
return (err);
}
+int
+zap_join(objset_t *os, uint64_t fromobj, uint64_t intoobj, dmu_tx_t *tx)
+{
+ zap_cursor_t zc;
+ zap_attribute_t za;
+ int err;
+
+ for (zap_cursor_init(&zc, os, fromobj);
+ zap_cursor_retrieve(&zc, &za) == 0;
+ (void) zap_cursor_advance(&zc)) {
+ if (za.za_integer_length != 8 || za.za_num_integers != 1)
+ return (EINVAL);
+ err = zap_add(os, intoobj, za.za_name,
+ 8, 1, &za.za_first_integer, tx);
+ if (err)
+ return (err);
+ }
+ zap_cursor_fini(&zc);
+ return (0);
+}
+
+int
+zap_add_int(objset_t *os, uint64_t obj, uint64_t value, dmu_tx_t *tx)
+{
+ char name[20];
+
+ (void) snprintf(name, sizeof (name), "%llx", (longlong_t)value);
+ return (zap_add(os, obj, name, 8, 1, &value, tx));
+}
+
+int
+zap_remove_int(objset_t *os, uint64_t obj, uint64_t value, dmu_tx_t *tx)
+{
+ char name[20];
+
+ (void) snprintf(name, sizeof (name), "%llx", (longlong_t)value);
+ return (zap_remove(os, obj, name, tx));
+}
+
+int
+zap_lookup_int(objset_t *os, uint64_t obj, uint64_t value)
+{
+ char name[20];
+
+ (void) snprintf(name, sizeof (name), "%llx", (longlong_t)value);
+ return (zap_lookup(os, obj, name, 8, 1, &value));
+}
/*
* Routines for iterating over the attributes.
@@ -983,6 +1045,10 @@ again:
err = zap_entry_read_name(&zeh,
sizeof (za->za_name), za->za_name);
ASSERT(err == 0);
+
+ za->za_normalization_conflict =
+ zap_entry_normalization_conflict(&zeh,
+ NULL, za->za_name, zap);
}
rw_exit(&zc->zc_leaf->l_rwlock);
return (err);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zap_leaf.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zap_leaf.c
index 5dff514..da498b6 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zap_leaf.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zap_leaf.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -38,6 +38,8 @@
#include <sys/spa.h>
#include <sys/dmu.h>
+static uint16_t *zap_leaf_rehash_entry(zap_leaf_t *l, uint16_t entry);
+
#define CHAIN_END 0xffff /* end of the chunk chain */
/* half the (current) minimum block size */
@@ -150,7 +152,7 @@ zap_leaf_byteswap(zap_leaf_phys_t *buf, int size)
}
void
-zap_leaf_init(zap_leaf_t *l)
+zap_leaf_init(zap_leaf_t *l, boolean_t sort)
{
int i;
@@ -165,6 +167,8 @@ zap_leaf_init(zap_leaf_t *l)
l->l_phys->l_hdr.lh_block_type = ZBT_LEAF;
l->l_phys->l_hdr.lh_magic = ZAP_LEAF_MAGIC;
l->l_phys->l_hdr.lh_nfree = ZAP_LEAF_NUMCHUNKS(l);
+ if (sort)
+ l->l_phys->l_hdr.lh_flags |= ZLF_ENTRIES_CDSORTED;
}
/*
@@ -327,19 +331,30 @@ zap_leaf_array_read(zap_leaf_t *l, uint16_t chunk,
/*
* Only to be used on 8-bit arrays.
* array_len is actual len in bytes (not encoded le_value_length).
- * buf is null-terminated.
+ * namenorm is null-terminated.
*/
-static int
-zap_leaf_array_equal(zap_leaf_t *l, int chunk,
- int array_len, const char *buf)
+static boolean_t
+zap_leaf_array_match(zap_leaf_t *l, zap_name_t *zn, int chunk, int array_len)
{
int bseen = 0;
+ if (zn->zn_matchtype == MT_FIRST) {
+ char *thisname = kmem_alloc(array_len, KM_SLEEP);
+ boolean_t match;
+
+ zap_leaf_array_read(l, chunk, 1, array_len, 1,
+ array_len, thisname);
+ match = zap_match(zn, thisname);
+ kmem_free(thisname, array_len);
+ return (match);
+ }
+
+ /* Fast path for exact matching */
while (bseen < array_len) {
struct zap_leaf_array *la = &ZAP_LEAF_CHUNK(l, chunk).l_array;
int toread = MIN(array_len - bseen, ZAP_LEAF_ARRAY_BYTES);
ASSERT3U(chunk, <, ZAP_LEAF_NUMCHUNKS(l));
- if (bcmp(la->la_array, buf + bseen, toread))
+ if (bcmp(la->la_array, zn->zn_name_orij + bseen, toread))
break;
chunk = la->la_next;
bseen += toread;
@@ -352,15 +367,15 @@ zap_leaf_array_equal(zap_leaf_t *l, int chunk,
*/
int
-zap_leaf_lookup(zap_leaf_t *l,
- const char *name, uint64_t h, zap_entry_handle_t *zeh)
+zap_leaf_lookup(zap_leaf_t *l, zap_name_t *zn, zap_entry_handle_t *zeh)
{
uint16_t *chunkp;
struct zap_leaf_entry *le;
ASSERT3U(l->l_phys->l_hdr.lh_magic, ==, ZAP_LEAF_MAGIC);
- for (chunkp = LEAF_HASH_ENTPTR(l, h);
+again:
+ for (chunkp = LEAF_HASH_ENTPTR(l, zn->zn_hash);
*chunkp != CHAIN_END; chunkp = &le->le_next) {
uint16_t chunk = *chunkp;
le = ZAP_LEAF_ENTRY(l, chunk);
@@ -368,11 +383,18 @@ zap_leaf_lookup(zap_leaf_t *l,
ASSERT3U(chunk, <, ZAP_LEAF_NUMCHUNKS(l));
ASSERT3U(le->le_type, ==, ZAP_CHUNK_ENTRY);
- if (le->le_hash != h)
+ if (le->le_hash != zn->zn_hash)
continue;
- if (zap_leaf_array_equal(l, le->le_name_chunk,
- le->le_name_length, name)) {
+ /*
+ * NB: the entry chain is always sorted by cd on
+ * normalized zap objects, so this will find the
+ * lowest-cd match for MT_FIRST.
+ */
+ ASSERT(zn->zn_matchtype == MT_EXACT ||
+ (l->l_phys->l_hdr.lh_flags & ZLF_ENTRIES_CDSORTED));
+ if (zap_leaf_array_match(l, zn, le->le_name_chunk,
+ le->le_name_length)) {
zeh->zeh_num_integers = le->le_value_length;
zeh->zeh_integer_size = le->le_int_size;
zeh->zeh_cd = le->le_cd;
@@ -383,6 +405,15 @@ zap_leaf_lookup(zap_leaf_t *l,
}
}
+ /*
+ * NB: we could of course do this in one pass, but that would be
+ * a pain. We'll see if MT_BEST is even used much.
+ */
+ if (zn->zn_matchtype == MT_BEST) {
+ zn->zn_matchtype = MT_FIRST;
+ goto again;
+ }
+
return (ENOENT);
}
@@ -539,22 +570,41 @@ zap_entry_create(zap_leaf_t *l, const char *name, uint64_t h, uint32_t cd,
return (E2BIG);
if (cd == ZAP_MAXCD) {
- for (cd = 0; cd < ZAP_MAXCD; cd++) {
+ /* find the lowest unused cd */
+ if (l->l_phys->l_hdr.lh_flags & ZLF_ENTRIES_CDSORTED) {
+ cd = 0;
+
for (chunk = *LEAF_HASH_ENTPTR(l, h);
chunk != CHAIN_END; chunk = le->le_next) {
le = ZAP_LEAF_ENTRY(l, chunk);
- if (le->le_hash == h &&
- le->le_cd == cd) {
+ if (le->le_cd > cd)
break;
+ if (le->le_hash == h) {
+ ASSERT3U(cd, ==, le->le_cd);
+ cd++;
}
}
- /* If this cd is not in use, we are good. */
- if (chunk == CHAIN_END)
- break;
+ } else {
+ /* old unsorted format; do it the O(n^2) way */
+ for (cd = 0; cd < ZAP_MAXCD; cd++) {
+ for (chunk = *LEAF_HASH_ENTPTR(l, h);
+ chunk != CHAIN_END; chunk = le->le_next) {
+ le = ZAP_LEAF_ENTRY(l, chunk);
+ if (le->le_hash == h &&
+ le->le_cd == cd) {
+ break;
+ }
+ }
+ /* If this cd is not in use, we are good. */
+ if (chunk == CHAIN_END)
+ break;
+ }
}
- /* If we tried all the cd's, we lose. */
- if (cd == ZAP_MAXCD)
- return (ENOSPC);
+ /*
+ * we would run out of space in a block before we could
+ * have ZAP_MAXCD entries
+ */
+ ASSERT3U(cd, <, ZAP_MAXCD);
}
if (l->l_phys->l_hdr.lh_nfree < numchunks)
@@ -574,9 +624,8 @@ zap_entry_create(zap_leaf_t *l, const char *name, uint64_t h, uint32_t cd,
le->le_cd = cd;
/* link it into the hash chain */
- chunkp = LEAF_HASH_ENTPTR(l, h);
- le->le_next = *chunkp;
- *chunkp = chunk;
+ /* XXX if we did the search above, we could just use that */
+ chunkp = zap_leaf_rehash_entry(l, chunk);
l->l_phys->l_hdr.lh_nentries++;
@@ -591,16 +640,76 @@ zap_entry_create(zap_leaf_t *l, const char *name, uint64_t h, uint32_t cd,
}
/*
+ * Determine if there is another entry with the same normalized form.
+ * For performance purposes, either zn or name must be provided (the
+ * other can be NULL). Note, there usually won't be any hash
+ * conflicts, in which case we don't need the concatenated/normalized
+ * form of the name. But all callers have one of these on hand anyway,
+ * so might as well take advantage. A cleaner but slower interface
+ * would accept neither argument, and compute the normalized name as
+ * needed (using zap_name_alloc(zap_entry_read_name(zeh))).
+ */
+boolean_t
+zap_entry_normalization_conflict(zap_entry_handle_t *zeh, zap_name_t *zn,
+ const char *name, zap_t *zap)
+{
+ uint64_t chunk;
+ struct zap_leaf_entry *le;
+ boolean_t allocdzn = B_FALSE;
+
+ if (zap->zap_normflags == 0)
+ return (B_FALSE);
+
+ for (chunk = *LEAF_HASH_ENTPTR(zeh->zeh_leaf, zeh->zeh_hash);
+ chunk != CHAIN_END; chunk = le->le_next) {
+ le = ZAP_LEAF_ENTRY(zeh->zeh_leaf, chunk);
+ if (le->le_hash != zeh->zeh_hash)
+ continue;
+ if (le->le_cd == zeh->zeh_cd)
+ continue;
+
+ if (zn == NULL) {
+ zn = zap_name_alloc(zap, name, MT_FIRST);
+ allocdzn = B_TRUE;
+ }
+ if (zap_leaf_array_match(zeh->zeh_leaf, zn,
+ le->le_name_chunk, le->le_name_length)) {
+ if (allocdzn)
+ zap_name_free(zn);
+ return (B_TRUE);
+ }
+ }
+ if (allocdzn)
+ zap_name_free(zn);
+ return (B_FALSE);
+}
+
+/*
* Routines for transferring entries between leafs.
*/
-static void
+static uint16_t *
zap_leaf_rehash_entry(zap_leaf_t *l, uint16_t entry)
{
struct zap_leaf_entry *le = ZAP_LEAF_ENTRY(l, entry);
- uint16_t *ptr = LEAF_HASH_ENTPTR(l, le->le_hash);
- le->le_next = *ptr;
- *ptr = entry;
+ struct zap_leaf_entry *le2;
+ uint16_t *chunkp;
+
+ /*
+ * keep the entry chain sorted by cd
+ * NB: this will not cause problems for unsorted leafs, though
+ * it is unnecessary there.
+ */
+ for (chunkp = LEAF_HASH_ENTPTR(l, le->le_hash);
+ *chunkp != CHAIN_END; chunkp = &le2->le_next) {
+ le2 = ZAP_LEAF_ENTRY(l, *chunkp);
+ if (le2->le_cd > le->le_cd)
+ break;
+ }
+
+ le->le_next = *chunkp;
+ *chunkp = entry;
+ return (chunkp);
}
static uint16_t
@@ -644,7 +753,7 @@ zap_leaf_transfer_entry(zap_leaf_t *l, int entry, zap_leaf_t *nl)
nle = ZAP_LEAF_ENTRY(nl, chunk);
*nle = *le; /* structure assignment */
- zap_leaf_rehash_entry(nl, chunk);
+ (void) zap_leaf_rehash_entry(nl, chunk);
nle->le_name_chunk = zap_leaf_transfer_array(l, le->le_name_chunk, nl);
nle->le_value_chunk =
@@ -660,7 +769,7 @@ zap_leaf_transfer_entry(zap_leaf_t *l, int entry, zap_leaf_t *nl)
* Transfer the entries whose hash prefix ends in 1 to the new leaf.
*/
void
-zap_leaf_split(zap_leaf_t *l, zap_leaf_t *nl)
+zap_leaf_split(zap_leaf_t *l, zap_leaf_t *nl, boolean_t sort)
{
int i;
int bit = 64 - 1 - l->l_phys->l_hdr.lh_prefix_len;
@@ -674,6 +783,9 @@ zap_leaf_split(zap_leaf_t *l, zap_leaf_t *nl)
/* break existing hash chains */
zap_memset(l->l_phys->l_hash, CHAIN_END, 2*ZAP_LEAF_HASH_NUMENTRIES(l));
+ if (sort)
+ l->l_phys->l_hdr.lh_flags |= ZLF_ENTRIES_CDSORTED;
+
/*
* Transfer entries whose hash bit 'bit' is set to nl; rehash
* the remaining entries
@@ -691,7 +803,7 @@ zap_leaf_split(zap_leaf_t *l, zap_leaf_t *nl)
if (le->le_hash & (1ULL << bit))
zap_leaf_transfer_entry(l, i, nl);
else
- zap_leaf_rehash_entry(l, i);
+ (void) zap_leaf_rehash_entry(l, i);
}
}
@@ -726,7 +838,7 @@ zap_leaf_stats(zap_t *zap, zap_leaf_t *l, zap_stats_t *zs)
n = 1 + ZAP_LEAF_ARRAY_NCHUNKS(le->le_name_length) +
ZAP_LEAF_ARRAY_NCHUNKS(le->le_value_length *
- le->le_int_size);
+ le->le_int_size);
n = MIN(n, ZAP_HISTOGRAM_SIZE-1);
zs->zs_entries_using_n_chunks[n]++;
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zap_micro.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zap_micro.c
index 9a882a5..75b43a6 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zap_micro.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zap_micro.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -34,9 +34,104 @@
#include <sys/zap_leaf.h>
#include <sys/avl.h>
+#ifdef _KERNEL
+#include <sys/sunddi.h>
+#endif
+
+static int mzap_upgrade(zap_t **zapp, dmu_tx_t *tx);
+
+
+static uint64_t
+zap_hash(zap_t *zap, const char *normname)
+{
+ const uint8_t *cp;
+ uint8_t c;
+ uint64_t crc = zap->zap_salt;
+
+ /* NB: name must already be normalized, if necessary */
+
+ ASSERT(crc != 0);
+ ASSERT(zfs_crc64_table[128] == ZFS_CRC64_POLY);
+ for (cp = (const uint8_t *)normname; (c = *cp) != '\0'; cp++) {
+ crc = (crc >> 8) ^ zfs_crc64_table[(crc ^ c) & 0xFF];
+ }
+
+ /*
+ * Only use 28 bits, since we need 4 bits in the cookie for the
+ * collision differentiator. We MUST use the high bits, since
+ * those are the ones that we first pay attention to when
+ * chosing the bucket.
+ */
+ crc &= ~((1ULL << (64 - ZAP_HASHBITS)) - 1);
-static void mzap_upgrade(zap_t *zap, dmu_tx_t *tx);
+ return (crc);
+}
+
+static int
+zap_normalize(zap_t *zap, const char *name, char *namenorm)
+{
+ size_t inlen, outlen;
+ int err;
+
+ inlen = strlen(name) + 1;
+ outlen = ZAP_MAXNAMELEN;
+
+ err = 0;
+ (void) u8_textprep_str((char *)name, &inlen, namenorm, &outlen,
+ zap->zap_normflags | U8_TEXTPREP_IGNORE_NULL, U8_UNICODE_LATEST,
+ &err);
+
+ return (err);
+}
+
+boolean_t
+zap_match(zap_name_t *zn, const char *matchname)
+{
+ if (zn->zn_matchtype == MT_FIRST) {
+ char norm[ZAP_MAXNAMELEN];
+ if (zap_normalize(zn->zn_zap, matchname, norm) != 0)
+ return (B_FALSE);
+
+ return (strcmp(zn->zn_name_norm, norm) == 0);
+ } else {
+ /* MT_BEST or MT_EXACT */
+ return (strcmp(zn->zn_name_orij, matchname) == 0);
+ }
+}
+
+void
+zap_name_free(zap_name_t *zn)
+{
+ kmem_free(zn, sizeof (zap_name_t));
+}
+
+/* XXX combine this with zap_lockdir()? */
+zap_name_t *
+zap_name_alloc(zap_t *zap, const char *name, matchtype_t mt)
+{
+ zap_name_t *zn = kmem_alloc(sizeof (zap_name_t), KM_SLEEP);
+
+ zn->zn_zap = zap;
+ zn->zn_name_orij = name;
+ zn->zn_matchtype = mt;
+ if (zap->zap_normflags) {
+ if (zap_normalize(zap, name, zn->zn_normbuf) != 0) {
+ zap_name_free(zn);
+ return (NULL);
+ }
+ zn->zn_name_norm = zn->zn_normbuf;
+ } else {
+ if (mt != MT_EXACT) {
+ zap_name_free(zn);
+ return (NULL);
+ }
+ zn->zn_name_norm = zn->zn_name_orij;
+ }
+
+ zn->zn_hash = zap_hash(zap, zn->zn_name_norm);
+ return (zn);
+}
static void
mzap_byteswap(mzap_phys_t *buf, size_t size)
@@ -44,6 +139,7 @@ mzap_byteswap(mzap_phys_t *buf, size_t size)
int i, max;
buf->mz_block_type = BSWAP_64(buf->mz_block_type);
buf->mz_salt = BSWAP_64(buf->mz_salt);
+ buf->mz_normflags = BSWAP_64(buf->mz_normflags);
max = (size / MZAP_ENT_LEN) - 1;
for (i = 0; i < max; i++) {
buf->mz_chunk[i].mze_value =
@@ -93,7 +189,6 @@ mze_insert(zap_t *zap, int chunkid, uint64_t hash, mzap_ent_phys_t *mzep)
ASSERT(zap->zap_ismicro);
ASSERT(RW_WRITE_HELD(&zap->zap_rwlock));
ASSERT(mzep->mze_cd < ZAP_MAXCD);
- ASSERT3U(zap_hash(zap, mzep->mze_name), ==, hash);
mze = kmem_alloc(sizeof (mzap_ent_t), KM_SLEEP);
mze->mze_chunkid = chunkid;
@@ -103,30 +198,34 @@ mze_insert(zap_t *zap, int chunkid, uint64_t hash, mzap_ent_phys_t *mzep)
}
static mzap_ent_t *
-mze_find(zap_t *zap, const char *name, uint64_t hash)
+mze_find(zap_name_t *zn)
{
mzap_ent_t mze_tofind;
mzap_ent_t *mze;
avl_index_t idx;
- avl_tree_t *avl = &zap->zap_m.zap_avl;
+ avl_tree_t *avl = &zn->zn_zap->zap_m.zap_avl;
- ASSERT(zap->zap_ismicro);
- ASSERT(RW_LOCK_HELD(&zap->zap_rwlock));
- ASSERT3U(zap_hash(zap, name), ==, hash);
+ ASSERT(zn->zn_zap->zap_ismicro);
+ ASSERT(RW_LOCK_HELD(&zn->zn_zap->zap_rwlock));
- if (strlen(name) >= sizeof (mze_tofind.mze_phys.mze_name))
+ if (strlen(zn->zn_name_norm) >= sizeof (mze_tofind.mze_phys.mze_name))
return (NULL);
- mze_tofind.mze_hash = hash;
+ mze_tofind.mze_hash = zn->zn_hash;
mze_tofind.mze_phys.mze_cd = 0;
+again:
mze = avl_find(avl, &mze_tofind, &idx);
if (mze == NULL)
mze = avl_nearest(avl, idx, AVL_AFTER);
- for (; mze && mze->mze_hash == hash; mze = AVL_NEXT(avl, mze)) {
- if (strcmp(name, mze->mze_phys.mze_name) == 0)
+ for (; mze && mze->mze_hash == zn->zn_hash; mze = AVL_NEXT(avl, mze)) {
+ if (zap_match(zn, mze->mze_phys.mze_name))
return (mze);
}
+ if (zn->zn_matchtype == MT_BEST) {
+ zn->zn_matchtype = MT_FIRST;
+ goto again;
+ }
return (NULL);
}
@@ -193,7 +292,7 @@ mzap_open(objset_t *os, uint64_t obj, dmu_buf_t *db)
zap->zap_object = obj;
zap->zap_dbuf = db;
- if (((uint64_t *)db->db_data)[0] != ZBT_MICRO) {
+ if (*(uint64_t *)db->db_data != ZBT_MICRO) {
mutex_init(&zap->zap_f.zap_num_entries_mtx, NULL,
MUTEX_DEFAULT, 0);
zap->zap_f.zap_block_shift = highbit(db->db_size) - 1;
@@ -219,6 +318,7 @@ mzap_open(objset_t *os, uint64_t obj, dmu_buf_t *db)
if (zap->zap_ismicro) {
zap->zap_salt = zap->zap_m.zap_phys->mz_salt;
+ zap->zap_normflags = zap->zap_m.zap_phys->mz_normflags;
zap->zap_m.zap_num_chunks = db->db_size / MZAP_ENT_LEN - 1;
avl_create(&zap->zap_m.zap_avl, mze_compare,
sizeof (mzap_ent_t), offsetof(mzap_ent_t, mze_node));
@@ -227,13 +327,18 @@ mzap_open(objset_t *os, uint64_t obj, dmu_buf_t *db)
mzap_ent_phys_t *mze =
&zap->zap_m.zap_phys->mz_chunk[i];
if (mze->mze_name[0]) {
+ zap_name_t *zn;
+
zap->zap_m.zap_num_entries++;
- mze_insert(zap, i,
- zap_hash(zap, mze->mze_name), mze);
+ zn = zap_name_alloc(zap, mze->mze_name,
+ MT_EXACT);
+ mze_insert(zap, i, zn->zn_hash, mze);
+ zap_name_free(zn);
}
}
} else {
zap->zap_salt = zap->zap_f.zap_phys->zap_salt;
+ zap->zap_normflags = zap->zap_f.zap_phys->zap_normflags;
ASSERT3U(sizeof (struct zap_leaf_header), ==,
2*ZAP_LEAF_CHUNKSIZE);
@@ -260,7 +365,7 @@ mzap_open(objset_t *os, uint64_t obj, dmu_buf_t *db)
int
zap_lockdir(objset_t *os, uint64_t obj, dmu_tx_t *tx,
- krw_t lti, int fatreader, zap_t **zapp)
+ krw_t lti, boolean_t fatreader, boolean_t adding, zap_t **zapp)
{
zap_t *zap;
dmu_buf_t *db;
@@ -312,15 +417,14 @@ zap_lockdir(objset_t *os, uint64_t obj, dmu_tx_t *tx,
ASSERT(!zap->zap_ismicro ||
zap->zap_m.zap_num_entries <= zap->zap_m.zap_num_chunks);
- if (zap->zap_ismicro && tx &&
+ if (zap->zap_ismicro && tx && adding &&
zap->zap_m.zap_num_entries == zap->zap_m.zap_num_chunks) {
uint64_t newsz = db->db_size + SPA_MINBLOCKSIZE;
if (newsz > MZAP_MAX_BLKSZ) {
dprintf("upgrading obj %llu: num_entries=%u\n",
obj, zap->zap_m.zap_num_entries);
- mzap_upgrade(zap, tx);
*zapp = zap;
- return (0);
+ return (mzap_upgrade(zapp, tx));
}
err = dmu_object_set_blocksize(os, obj, newsz, 0, tx);
ASSERT3U(err, ==, 0);
@@ -339,11 +443,12 @@ zap_unlockdir(zap_t *zap)
dmu_buf_rele(zap->zap_dbuf, NULL);
}
-static void
-mzap_upgrade(zap_t *zap, dmu_tx_t *tx)
+static int
+mzap_upgrade(zap_t **zapp, dmu_tx_t *tx)
{
mzap_phys_t *mzp;
int i, sz, nchunks, err;
+ zap_t *zap = *zapp;
ASSERT(RW_WRITE_HELD(&zap->zap_rwlock));
@@ -354,10 +459,14 @@ mzap_upgrade(zap_t *zap, dmu_tx_t *tx)
err = dmu_object_set_blocksize(zap->zap_objset, zap->zap_object,
1ULL << fzap_default_block_shift, 0, tx);
- ASSERT(err == 0);
+ if (err) {
+ kmem_free(mzp, sz);
+ return (err);
+ }
dprintf("upgrading obj=%llu with %u chunks\n",
zap->zap_object, nchunks);
+ /* XXX destroy the avl later, so we can use the stored hash value */
mze_destroy(zap);
fzap_upgrade(zap, tx);
@@ -365,44 +474,25 @@ mzap_upgrade(zap_t *zap, dmu_tx_t *tx)
for (i = 0; i < nchunks; i++) {
int err;
mzap_ent_phys_t *mze = &mzp->mz_chunk[i];
+ zap_name_t *zn;
if (mze->mze_name[0] == 0)
continue;
dprintf("adding %s=%llu\n",
mze->mze_name, mze->mze_value);
- err = fzap_add_cd(zap,
- mze->mze_name, 8, 1, &mze->mze_value,
- mze->mze_cd, tx);
- ASSERT3U(err, ==, 0);
+ zn = zap_name_alloc(zap, mze->mze_name, MT_EXACT);
+ err = fzap_add_cd(zn, 8, 1, &mze->mze_value, mze->mze_cd, tx);
+ zap = zn->zn_zap; /* fzap_add_cd() may change zap */
+ zap_name_free(zn);
+ if (err)
+ break;
}
kmem_free(mzp, sz);
+ *zapp = zap;
+ return (err);
}
-uint64_t
-zap_hash(zap_t *zap, const char *name)
-{
- const uint8_t *cp;
- uint8_t c;
- uint64_t crc = zap->zap_salt;
-
- ASSERT(crc != 0);
- ASSERT(zfs_crc64_table[128] == ZFS_CRC64_POLY);
- for (cp = (const uint8_t *)name; (c = *cp) != '\0'; cp++)
- crc = (crc >> 8) ^ zfs_crc64_table[(crc ^ c) & 0xFF];
-
- /*
- * Only use 28 bits, since we need 4 bits in the cookie for the
- * collision differentiator. We MUST use the high bits, since
- * those are the onces that we first pay attention to when
- * chosing the bucket.
- */
- crc &= ~((1ULL << (64 - ZAP_HASHBITS)) - 1);
-
- return (crc);
-}
-
-
static void
-mzap_create_impl(objset_t *os, uint64_t obj, dmu_tx_t *tx)
+mzap_create_impl(objset_t *os, uint64_t obj, int normflags, dmu_tx_t *tx)
{
dmu_buf_t *db;
mzap_phys_t *zp;
@@ -421,7 +511,7 @@ mzap_create_impl(objset_t *os, uint64_t obj, dmu_tx_t *tx)
zp = db->db_data;
zp->mz_block_type = ZBT_MICRO;
zp->mz_salt = ((uintptr_t)db ^ (uintptr_t)tx ^ (obj << 1)) | 1ULL;
- ASSERT(zp->mz_salt != 0);
+ zp->mz_normflags = normflags;
dmu_buf_rele(db, FTAG);
}
@@ -429,12 +519,21 @@ int
zap_create_claim(objset_t *os, uint64_t obj, dmu_object_type_t ot,
dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx)
{
+ return (zap_create_claim_norm(os, obj,
+ 0, ot, bonustype, bonuslen, tx));
+}
+
+int
+zap_create_claim_norm(objset_t *os, uint64_t obj, int normflags,
+ dmu_object_type_t ot,
+ dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx)
+{
int err;
err = dmu_object_claim(os, obj, ot, 0, bonustype, bonuslen, tx);
if (err != 0)
return (err);
- mzap_create_impl(os, obj, tx);
+ mzap_create_impl(os, obj, normflags, tx);
return (0);
}
@@ -442,9 +541,16 @@ uint64_t
zap_create(objset_t *os, dmu_object_type_t ot,
dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx)
{
+ return (zap_create_norm(os, 0, ot, bonustype, bonuslen, tx));
+}
+
+uint64_t
+zap_create_norm(objset_t *os, int normflags, dmu_object_type_t ot,
+ dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx)
+{
uint64_t obj = dmu_object_alloc(os, ot, 0, bonustype, bonuslen, tx);
- mzap_create_impl(os, obj, tx);
+ mzap_create_impl(os, obj, normflags, tx);
return (obj);
}
@@ -482,7 +588,7 @@ zap_count(objset_t *os, uint64_t zapobj, uint64_t *count)
zap_t *zap;
int err;
- err = zap_lockdir(os, zapobj, NULL, RW_READER, TRUE, &zap);
+ err = zap_lockdir(os, zapobj, NULL, RW_READER, TRUE, FALSE, &zap);
if (err)
return (err);
if (!zap->zap_ismicro) {
@@ -495,36 +601,102 @@ zap_count(objset_t *os, uint64_t zapobj, uint64_t *count)
}
/*
- * Routines for maniplulating attributes.
+ * zn may be NULL; if not specified, it will be computed if needed.
+ * See also the comment above zap_entry_normalization_conflict().
+ */
+static boolean_t
+mzap_normalization_conflict(zap_t *zap, zap_name_t *zn, mzap_ent_t *mze)
+{
+ mzap_ent_t *other;
+ int direction = AVL_BEFORE;
+ boolean_t allocdzn = B_FALSE;
+
+ if (zap->zap_normflags == 0)
+ return (B_FALSE);
+
+again:
+ for (other = avl_walk(&zap->zap_m.zap_avl, mze, direction);
+ other && other->mze_hash == mze->mze_hash;
+ other = avl_walk(&zap->zap_m.zap_avl, other, direction)) {
+
+ if (zn == NULL) {
+ zn = zap_name_alloc(zap, mze->mze_phys.mze_name,
+ MT_FIRST);
+ allocdzn = B_TRUE;
+ }
+ if (zap_match(zn, other->mze_phys.mze_name)) {
+ if (allocdzn)
+ zap_name_free(zn);
+ return (B_TRUE);
+ }
+ }
+
+ if (direction == AVL_BEFORE) {
+ direction = AVL_AFTER;
+ goto again;
+ }
+
+ if (allocdzn)
+ zap_name_free(zn);
+ return (B_FALSE);
+}
+
+/*
+ * Routines for manipulating attributes.
*/
int
zap_lookup(objset_t *os, uint64_t zapobj, const char *name,
uint64_t integer_size, uint64_t num_integers, void *buf)
{
+ return (zap_lookup_norm(os, zapobj, name, integer_size,
+ num_integers, buf, MT_EXACT, NULL, 0, NULL));
+}
+
+int
+zap_lookup_norm(objset_t *os, uint64_t zapobj, const char *name,
+ uint64_t integer_size, uint64_t num_integers, void *buf,
+ matchtype_t mt, char *realname, int rn_len,
+ boolean_t *ncp)
+{
zap_t *zap;
int err;
mzap_ent_t *mze;
+ zap_name_t *zn;
- err = zap_lockdir(os, zapobj, NULL, RW_READER, TRUE, &zap);
+ err = zap_lockdir(os, zapobj, NULL, RW_READER, TRUE, FALSE, &zap);
if (err)
return (err);
+ zn = zap_name_alloc(zap, name, mt);
+ if (zn == NULL) {
+ zap_unlockdir(zap);
+ return (ENOTSUP);
+ }
+
if (!zap->zap_ismicro) {
- err = fzap_lookup(zap, name,
- integer_size, num_integers, buf);
+ err = fzap_lookup(zn, integer_size, num_integers, buf,
+ realname, rn_len, ncp);
} else {
- mze = mze_find(zap, name, zap_hash(zap, name));
+ mze = mze_find(zn);
if (mze == NULL) {
err = ENOENT;
} else {
- if (num_integers < 1)
+ if (num_integers < 1) {
err = EOVERFLOW;
- else if (integer_size != 8)
+ } else if (integer_size != 8) {
err = EINVAL;
- else
+ } else {
*(uint64_t *)buf = mze->mze_phys.mze_value;
+ (void) strlcpy(realname,
+ mze->mze_phys.mze_name, rn_len);
+ if (ncp) {
+ *ncp = mzap_normalization_conflict(zap,
+ zn, mze);
+ }
+ }
}
}
+ zap_name_free(zn);
zap_unlockdir(zap);
return (err);
}
@@ -536,14 +708,20 @@ zap_length(objset_t *os, uint64_t zapobj, const char *name,
zap_t *zap;
int err;
mzap_ent_t *mze;
+ zap_name_t *zn;
- err = zap_lockdir(os, zapobj, NULL, RW_READER, TRUE, &zap);
+ err = zap_lockdir(os, zapobj, NULL, RW_READER, TRUE, FALSE, &zap);
if (err)
return (err);
+ zn = zap_name_alloc(zap, name, MT_EXACT);
+ if (zn == NULL) {
+ zap_unlockdir(zap);
+ return (ENOTSUP);
+ }
if (!zap->zap_ismicro) {
- err = fzap_length(zap, name, integer_size, num_integers);
+ err = fzap_length(zn, integer_size, num_integers);
} else {
- mze = mze_find(zap, name, zap_hash(zap, name));
+ mze = mze_find(zn);
if (mze == NULL) {
err = ENOENT;
} else {
@@ -553,28 +731,31 @@ zap_length(objset_t *os, uint64_t zapobj, const char *name,
*num_integers = 1;
}
}
+ zap_name_free(zn);
zap_unlockdir(zap);
return (err);
}
static void
-mzap_addent(zap_t *zap, const char *name, uint64_t hash, uint64_t value)
+mzap_addent(zap_name_t *zn, uint64_t value)
{
int i;
+ zap_t *zap = zn->zn_zap;
int start = zap->zap_m.zap_alloc_next;
uint32_t cd;
- dprintf("obj=%llu %s=%llu\n", zap->zap_object, name, value);
+ dprintf("obj=%llu %s=%llu\n", zap->zap_object,
+ zn->zn_name_orij, value);
ASSERT(RW_WRITE_HELD(&zap->zap_rwlock));
#ifdef ZFS_DEBUG
for (i = 0; i < zap->zap_m.zap_num_chunks; i++) {
mzap_ent_phys_t *mze = &zap->zap_m.zap_phys->mz_chunk[i];
- ASSERT(strcmp(name, mze->mze_name) != 0);
+ ASSERT(strcmp(zn->zn_name_orij, mze->mze_name) != 0);
}
#endif
- cd = mze_find_unused_cd(zap, hash);
+ cd = mze_find_unused_cd(zap, zn->zn_hash);
/* given the limited size of the microzap, this can't happen */
ASSERT(cd != ZAP_MAXCD);
@@ -584,13 +765,13 @@ again:
if (mze->mze_name[0] == 0) {
mze->mze_value = value;
mze->mze_cd = cd;
- (void) strcpy(mze->mze_name, name);
+ (void) strcpy(mze->mze_name, zn->zn_name_orij);
zap->zap_m.zap_num_entries++;
zap->zap_m.zap_alloc_next = i+1;
if (zap->zap_m.zap_alloc_next ==
zap->zap_m.zap_num_chunks)
zap->zap_m.zap_alloc_next = 0;
- mze_insert(zap, i, hash, mze);
+ mze_insert(zap, i, zn->zn_hash, mze);
return;
}
}
@@ -610,29 +791,39 @@ zap_add(objset_t *os, uint64_t zapobj, const char *name,
int err;
mzap_ent_t *mze;
const uint64_t *intval = val;
- uint64_t hash;
+ zap_name_t *zn;
- err = zap_lockdir(os, zapobj, tx, RW_WRITER, TRUE, &zap);
+ err = zap_lockdir(os, zapobj, tx, RW_WRITER, TRUE, TRUE, &zap);
if (err)
return (err);
+ zn = zap_name_alloc(zap, name, MT_EXACT);
+ if (zn == NULL) {
+ zap_unlockdir(zap);
+ return (ENOTSUP);
+ }
if (!zap->zap_ismicro) {
- err = fzap_add(zap, name, integer_size, num_integers, val, tx);
+ err = fzap_add(zn, integer_size, num_integers, val, tx);
+ zap = zn->zn_zap; /* fzap_add() may change zap */
} else if (integer_size != 8 || num_integers != 1 ||
strlen(name) >= MZAP_NAME_LEN) {
dprintf("upgrading obj %llu: intsz=%u numint=%llu name=%s\n",
zapobj, integer_size, num_integers, name);
- mzap_upgrade(zap, tx);
- err = fzap_add(zap, name, integer_size, num_integers, val, tx);
+ err = mzap_upgrade(&zn->zn_zap, tx);
+ if (err == 0)
+ err = fzap_add(zn, integer_size, num_integers, val, tx);
+ zap = zn->zn_zap; /* fzap_add() may change zap */
} else {
- hash = zap_hash(zap, name);
- mze = mze_find(zap, name, hash);
+ mze = mze_find(zn);
if (mze != NULL) {
err = EEXIST;
} else {
- mzap_addent(zap, name, hash, *intval);
+ mzap_addent(zn, *intval);
}
}
- zap_unlockdir(zap);
+ ASSERT(zap == zn->zn_zap);
+ zap_name_free(zn);
+ if (zap != NULL) /* may be NULL if fzap_add() failed */
+ zap_unlockdir(zap);
return (err);
}
@@ -643,68 +834,87 @@ zap_update(objset_t *os, uint64_t zapobj, const char *name,
zap_t *zap;
mzap_ent_t *mze;
const uint64_t *intval = val;
- uint64_t hash;
+ zap_name_t *zn;
int err;
- err = zap_lockdir(os, zapobj, tx, RW_WRITER, TRUE, &zap);
+ err = zap_lockdir(os, zapobj, tx, RW_WRITER, TRUE, TRUE, &zap);
if (err)
return (err);
- ASSERT(RW_LOCK_HELD(&zap->zap_rwlock));
+ zn = zap_name_alloc(zap, name, MT_EXACT);
+ if (zn == NULL) {
+ zap_unlockdir(zap);
+ return (ENOTSUP);
+ }
if (!zap->zap_ismicro) {
- err = fzap_update(zap, name,
- integer_size, num_integers, val, tx);
+ err = fzap_update(zn, integer_size, num_integers, val, tx);
+ zap = zn->zn_zap; /* fzap_update() may change zap */
} else if (integer_size != 8 || num_integers != 1 ||
strlen(name) >= MZAP_NAME_LEN) {
dprintf("upgrading obj %llu: intsz=%u numint=%llu name=%s\n",
zapobj, integer_size, num_integers, name);
- mzap_upgrade(zap, tx);
- err = fzap_update(zap, name,
- integer_size, num_integers, val, tx);
+ err = mzap_upgrade(&zn->zn_zap, tx);
+ if (err == 0)
+ err = fzap_update(zn, integer_size, num_integers,
+ val, tx);
+ zap = zn->zn_zap; /* fzap_update() may change zap */
} else {
- hash = zap_hash(zap, name);
- mze = mze_find(zap, name, hash);
+ mze = mze_find(zn);
if (mze != NULL) {
mze->mze_phys.mze_value = *intval;
zap->zap_m.zap_phys->mz_chunk
[mze->mze_chunkid].mze_value = *intval;
} else {
- mzap_addent(zap, name, hash, *intval);
+ mzap_addent(zn, *intval);
}
}
- zap_unlockdir(zap);
+ ASSERT(zap == zn->zn_zap);
+ zap_name_free(zn);
+ if (zap != NULL) /* may be NULL if fzap_upgrade() failed */
+ zap_unlockdir(zap);
return (err);
}
int
zap_remove(objset_t *os, uint64_t zapobj, const char *name, dmu_tx_t *tx)
{
+ return (zap_remove_norm(os, zapobj, name, MT_EXACT, tx));
+}
+
+int
+zap_remove_norm(objset_t *os, uint64_t zapobj, const char *name,
+ matchtype_t mt, dmu_tx_t *tx)
+{
zap_t *zap;
int err;
mzap_ent_t *mze;
+ zap_name_t *zn;
- err = zap_lockdir(os, zapobj, tx, RW_WRITER, TRUE, &zap);
+ err = zap_lockdir(os, zapobj, tx, RW_WRITER, TRUE, FALSE, &zap);
if (err)
return (err);
+ zn = zap_name_alloc(zap, name, mt);
+ if (zn == NULL) {
+ zap_unlockdir(zap);
+ return (ENOTSUP);
+ }
if (!zap->zap_ismicro) {
- err = fzap_remove(zap, name, tx);
+ err = fzap_remove(zn, tx);
} else {
- mze = mze_find(zap, name, zap_hash(zap, name));
+ mze = mze_find(zn);
if (mze == NULL) {
- dprintf("fail: %s\n", name);
err = ENOENT;
} else {
- dprintf("success: %s\n", name);
zap->zap_m.zap_num_entries--;
bzero(&zap->zap_m.zap_phys->mz_chunk[mze->mze_chunkid],
sizeof (mzap_ent_phys_t));
mze_remove(zap, mze);
}
}
+ zap_name_free(zn);
zap_unlockdir(zap);
return (err);
}
-
/*
* Routines for iterating over the attributes.
*/
@@ -781,7 +991,7 @@ zap_cursor_retrieve(zap_cursor_t *zc, zap_attribute_t *za)
if (zc->zc_zap == NULL) {
err = zap_lockdir(zc->zc_objset, zc->zc_zapobj, NULL,
- RW_READER, TRUE, &zc->zc_zap);
+ RW_READER, TRUE, FALSE, &zc->zc_zap);
if (err)
return (err);
} else {
@@ -796,14 +1006,17 @@ zap_cursor_retrieve(zap_cursor_t *zc, zap_attribute_t *za)
mze_tofind.mze_phys.mze_cd = zc->zc_cd;
mze = avl_find(&zc->zc_zap->zap_m.zap_avl, &mze_tofind, &idx);
- ASSERT(mze == NULL || 0 == bcmp(&mze->mze_phys,
- &zc->zc_zap->zap_m.zap_phys->mz_chunk[mze->mze_chunkid],
- sizeof (mze->mze_phys)));
if (mze == NULL) {
mze = avl_nearest(&zc->zc_zap->zap_m.zap_avl,
idx, AVL_AFTER);
}
if (mze) {
+ ASSERT(0 == bcmp(&mze->mze_phys,
+ &zc->zc_zap->zap_m.zap_phys->mz_chunk
+ [mze->mze_chunkid], sizeof (mze->mze_phys)));
+
+ za->za_normalization_conflict =
+ mzap_normalization_conflict(zc->zc_zap, NULL, mze);
za->za_integer_length = 8;
za->za_num_integers = 1;
za->za_first_integer = mze->mze_phys.mze_value;
@@ -839,7 +1052,7 @@ zap_get_stats(objset_t *os, uint64_t zapobj, zap_stats_t *zs)
int err;
zap_t *zap;
- err = zap_lockdir(os, zapobj, NULL, RW_READER, TRUE, &zap);
+ err = zap_lockdir(os, zapobj, NULL, RW_READER, TRUE, FALSE, &zap);
if (err)
return (err);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_acl.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_acl.c
index 33c2909..ec7d29e 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_acl.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_acl.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
@@ -43,15 +41,19 @@
#include <sys/fs/zfs.h>
#include <sys/policy.h>
#include <sys/zfs_znode.h>
+#include <sys/zfs_fuid.h>
#include <sys/zfs_acl.h>
#include <sys/zfs_dir.h>
#include <sys/zfs_vfsops.h>
#include <sys/dmu.h>
+#include <sys/dnode.h>
#include <sys/zap.h>
#include <acl/acl_common.h>
#define ALLOW ACE_ACCESS_ALLOWED_ACE_TYPE
#define DENY ACE_ACCESS_DENIED_ACE_TYPE
+#define MAX_ACE_TYPE ACE_SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE
+#define MIN_ACE_TYPE ALLOW
#define OWNING_GROUP (ACE_GROUP|ACE_IDENTIFIER_GROUP)
#define EVERYONE_ALLOW_MASK (ACE_READ_ACL|ACE_READ_ATTRIBUTES | \
@@ -60,8 +62,15 @@
ACE_WRITE_ATTRIBUTES|ACE_WRITE_NAMED_ATTRS)
#define OWNER_ALLOW_MASK (ACE_WRITE_ACL | ACE_WRITE_OWNER | \
ACE_WRITE_ATTRIBUTES|ACE_WRITE_NAMED_ATTRS)
-#define WRITE_MASK (ACE_WRITE_DATA|ACE_APPEND_DATA|ACE_WRITE_NAMED_ATTRS| \
- ACE_WRITE_ATTRIBUTES|ACE_WRITE_ACL|ACE_WRITE_OWNER)
+#define WRITE_MASK_DATA (ACE_WRITE_DATA|ACE_APPEND_DATA|ACE_WRITE_NAMED_ATTRS)
+
+#define ZFS_CHECKED_MASKS (ACE_READ_ACL|ACE_READ_ATTRIBUTES|ACE_READ_DATA| \
+ ACE_READ_NAMED_ATTRS|ACE_WRITE_DATA|ACE_WRITE_ATTRIBUTES| \
+ ACE_WRITE_NAMED_ATTRS|ACE_APPEND_DATA|ACE_EXECUTE|ACE_WRITE_OWNER| \
+ ACE_WRITE_ACL|ACE_DELETE|ACE_DELETE_CHILD|ACE_SYNCHRONIZE)
+
+#define WRITE_MASK (WRITE_MASK_DATA|ACE_WRITE_ATTRIBUTES|ACE_WRITE_ACL|\
+ ACE_WRITE_OWNER|ACE_DELETE|ACE_DELETE_CHILD)
#define OGE_CLEAR (ACE_READ_DATA|ACE_LIST_DIRECTORY|ACE_WRITE_DATA| \
ACE_ADD_FILE|ACE_APPEND_DATA|ACE_ADD_SUBDIRECTORY|ACE_EXECUTE)
@@ -70,59 +79,656 @@
ACE_ADD_FILE|ACE_APPEND_DATA|ACE_ADD_SUBDIRECTORY|ACE_EXECUTE)
#define ALL_INHERIT (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE | \
- ACE_NO_PROPAGATE_INHERIT_ACE|ACE_INHERIT_ONLY_ACE)
+ ACE_NO_PROPAGATE_INHERIT_ACE|ACE_INHERIT_ONLY_ACE|ACE_INHERITED_ACE)
+
+#define RESTRICTED_CLEAR (ACE_WRITE_ACL|ACE_WRITE_OWNER)
+
+#define V4_ACL_WIDE_FLAGS (ZFS_ACL_AUTO_INHERIT|ZFS_ACL_DEFAULTED|\
+ ZFS_ACL_PROTECTED)
+
+#define ZFS_ACL_WIDE_FLAGS (V4_ACL_WIDE_FLAGS|ZFS_ACL_TRIVIAL|ZFS_INHERIT_ACE|\
+ ZFS_ACL_OBJ_ACE)
+
+static uint16_t
+zfs_ace_v0_get_type(void *acep)
+{
+ return (((zfs_oldace_t *)acep)->z_type);
+}
+
+static uint16_t
+zfs_ace_v0_get_flags(void *acep)
+{
+ return (((zfs_oldace_t *)acep)->z_flags);
+}
+
+static uint32_t
+zfs_ace_v0_get_mask(void *acep)
+{
+ return (((zfs_oldace_t *)acep)->z_access_mask);
+}
+
+static uint64_t
+zfs_ace_v0_get_who(void *acep)
+{
+ return (((zfs_oldace_t *)acep)->z_fuid);
+}
+
+static void
+zfs_ace_v0_set_type(void *acep, uint16_t type)
+{
+ ((zfs_oldace_t *)acep)->z_type = type;
+}
-#define SECURE_CLEAR (ACE_WRITE_ACL|ACE_WRITE_OWNER)
+static void
+zfs_ace_v0_set_flags(void *acep, uint16_t flags)
+{
+ ((zfs_oldace_t *)acep)->z_flags = flags;
+}
-#define OGE_PAD 6 /* traditional owner/group/everyone ACES */
+static void
+zfs_ace_v0_set_mask(void *acep, uint32_t mask)
+{
+ ((zfs_oldace_t *)acep)->z_access_mask = mask;
+}
-static int zfs_ace_can_use(znode_t *zp, ace_t *);
+static void
+zfs_ace_v0_set_who(void *acep, uint64_t who)
+{
+ ((zfs_oldace_t *)acep)->z_fuid = who;
+}
+
+/*ARGSUSED*/
+static size_t
+zfs_ace_v0_size(void *acep)
+{
+ return (sizeof (zfs_oldace_t));
+}
+
+static size_t
+zfs_ace_v0_abstract_size(void)
+{
+ return (sizeof (zfs_oldace_t));
+}
+
+static int
+zfs_ace_v0_mask_off(void)
+{
+ return (offsetof(zfs_oldace_t, z_access_mask));
+}
+
+/*ARGSUSED*/
+static int
+zfs_ace_v0_data(void *acep, void **datap)
+{
+ *datap = NULL;
+ return (0);
+}
+
+static acl_ops_t zfs_acl_v0_ops = {
+ zfs_ace_v0_get_mask,
+ zfs_ace_v0_set_mask,
+ zfs_ace_v0_get_flags,
+ zfs_ace_v0_set_flags,
+ zfs_ace_v0_get_type,
+ zfs_ace_v0_set_type,
+ zfs_ace_v0_get_who,
+ zfs_ace_v0_set_who,
+ zfs_ace_v0_size,
+ zfs_ace_v0_abstract_size,
+ zfs_ace_v0_mask_off,
+ zfs_ace_v0_data
+};
+
+static uint16_t
+zfs_ace_fuid_get_type(void *acep)
+{
+ return (((zfs_ace_hdr_t *)acep)->z_type);
+}
+
+static uint16_t
+zfs_ace_fuid_get_flags(void *acep)
+{
+ return (((zfs_ace_hdr_t *)acep)->z_flags);
+}
+
+static uint32_t
+zfs_ace_fuid_get_mask(void *acep)
+{
+ return (((zfs_ace_hdr_t *)acep)->z_access_mask);
+}
+
+static uint64_t
+zfs_ace_fuid_get_who(void *args)
+{
+ uint16_t entry_type;
+ zfs_ace_t *acep = args;
+
+ entry_type = acep->z_hdr.z_flags & ACE_TYPE_FLAGS;
+
+ if (entry_type == ACE_OWNER || entry_type == OWNING_GROUP ||
+ entry_type == ACE_EVERYONE)
+ return (-1);
+ return (((zfs_ace_t *)acep)->z_fuid);
+}
+
+static void
+zfs_ace_fuid_set_type(void *acep, uint16_t type)
+{
+ ((zfs_ace_hdr_t *)acep)->z_type = type;
+}
+
+static void
+zfs_ace_fuid_set_flags(void *acep, uint16_t flags)
+{
+ ((zfs_ace_hdr_t *)acep)->z_flags = flags;
+}
+
+static void
+zfs_ace_fuid_set_mask(void *acep, uint32_t mask)
+{
+ ((zfs_ace_hdr_t *)acep)->z_access_mask = mask;
+}
+
+static void
+zfs_ace_fuid_set_who(void *arg, uint64_t who)
+{
+ zfs_ace_t *acep = arg;
+
+ uint16_t entry_type = acep->z_hdr.z_flags & ACE_TYPE_FLAGS;
+
+ if (entry_type == ACE_OWNER || entry_type == OWNING_GROUP ||
+ entry_type == ACE_EVERYONE)
+ return;
+ acep->z_fuid = who;
+}
+
+static size_t
+zfs_ace_fuid_size(void *acep)
+{
+ zfs_ace_hdr_t *zacep = acep;
+ uint16_t entry_type;
+
+ switch (zacep->z_type) {
+ case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
+ case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
+ case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
+ case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
+ return (sizeof (zfs_object_ace_t));
+ case ALLOW:
+ case DENY:
+ entry_type =
+ (((zfs_ace_hdr_t *)acep)->z_flags & ACE_TYPE_FLAGS);
+ if (entry_type == ACE_OWNER ||
+ entry_type == OWNING_GROUP ||
+ entry_type == ACE_EVERYONE)
+ return (sizeof (zfs_ace_hdr_t));
+ /*FALLTHROUGH*/
+ default:
+ return (sizeof (zfs_ace_t));
+ }
+}
+
+static size_t
+zfs_ace_fuid_abstract_size(void)
+{
+ return (sizeof (zfs_ace_hdr_t));
+}
+
+static int
+zfs_ace_fuid_mask_off(void)
+{
+ return (offsetof(zfs_ace_hdr_t, z_access_mask));
+}
+
+static int
+zfs_ace_fuid_data(void *acep, void **datap)
+{
+ zfs_ace_t *zacep = acep;
+ zfs_object_ace_t *zobjp;
+
+ switch (zacep->z_hdr.z_type) {
+ case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
+ case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
+ case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
+ case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
+ zobjp = acep;
+ *datap = (caddr_t)zobjp + sizeof (zfs_ace_t);
+ return (sizeof (zfs_object_ace_t) - sizeof (zfs_ace_t));
+ default:
+ *datap = NULL;
+ return (0);
+ }
+}
+
+static acl_ops_t zfs_acl_fuid_ops = {
+ zfs_ace_fuid_get_mask,
+ zfs_ace_fuid_set_mask,
+ zfs_ace_fuid_get_flags,
+ zfs_ace_fuid_set_flags,
+ zfs_ace_fuid_get_type,
+ zfs_ace_fuid_set_type,
+ zfs_ace_fuid_get_who,
+ zfs_ace_fuid_set_who,
+ zfs_ace_fuid_size,
+ zfs_ace_fuid_abstract_size,
+ zfs_ace_fuid_mask_off,
+ zfs_ace_fuid_data
+};
+
+static int
+zfs_acl_version(int version)
+{
+ if (version < ZPL_VERSION_FUID)
+ return (ZFS_ACL_VERSION_INITIAL);
+ else
+ return (ZFS_ACL_VERSION_FUID);
+}
+
+static int
+zfs_acl_version_zp(znode_t *zp)
+{
+ return (zfs_acl_version(zp->z_zfsvfs->z_version));
+}
static zfs_acl_t *
-zfs_acl_alloc(int slots)
+zfs_acl_alloc(int vers)
{
zfs_acl_t *aclp;
aclp = kmem_zalloc(sizeof (zfs_acl_t), KM_SLEEP);
- if (slots != 0) {
- aclp->z_acl = kmem_alloc(ZFS_ACL_SIZE(slots), KM_SLEEP);
- aclp->z_acl_count = 0;
- aclp->z_state = ACL_DATA_ALLOCED;
- } else {
- aclp->z_state = 0;
- }
- aclp->z_slots = slots;
+ list_create(&aclp->z_acl, sizeof (zfs_acl_node_t),
+ offsetof(zfs_acl_node_t, z_next));
+ aclp->z_version = vers;
+ if (vers == ZFS_ACL_VERSION_FUID)
+ aclp->z_ops = zfs_acl_fuid_ops;
+ else
+ aclp->z_ops = zfs_acl_v0_ops;
return (aclp);
}
+static zfs_acl_node_t *
+zfs_acl_node_alloc(size_t bytes)
+{
+ zfs_acl_node_t *aclnode;
+
+ aclnode = kmem_zalloc(sizeof (zfs_acl_node_t), KM_SLEEP);
+ if (bytes) {
+ aclnode->z_acldata = kmem_alloc(bytes, KM_SLEEP);
+ aclnode->z_allocdata = aclnode->z_acldata;
+ aclnode->z_allocsize = bytes;
+ aclnode->z_size = bytes;
+ }
+
+ return (aclnode);
+}
+
+static void
+zfs_acl_node_free(zfs_acl_node_t *aclnode)
+{
+ if (aclnode->z_allocsize)
+ kmem_free(aclnode->z_allocdata, aclnode->z_allocsize);
+ kmem_free(aclnode, sizeof (zfs_acl_node_t));
+}
+
+static void
+zfs_acl_release_nodes(zfs_acl_t *aclp)
+{
+ zfs_acl_node_t *aclnode;
+
+ while (aclnode = list_head(&aclp->z_acl)) {
+ list_remove(&aclp->z_acl, aclnode);
+ zfs_acl_node_free(aclnode);
+ }
+ aclp->z_acl_count = 0;
+ aclp->z_acl_bytes = 0;
+}
+
void
zfs_acl_free(zfs_acl_t *aclp)
{
- if (aclp->z_state == ACL_DATA_ALLOCED) {
- kmem_free(aclp->z_acl, ZFS_ACL_SIZE(aclp->z_slots));
- }
+ zfs_acl_release_nodes(aclp);
+ list_destroy(&aclp->z_acl);
kmem_free(aclp, sizeof (zfs_acl_t));
}
-static uint32_t
-zfs_v4_to_unix(uint32_t access_mask)
+static boolean_t
+zfs_acl_valid_ace_type(uint_t type, uint_t flags)
{
- uint32_t new_mask = 0;
+ uint16_t entry_type;
+
+ switch (type) {
+ case ALLOW:
+ case DENY:
+ case ACE_SYSTEM_AUDIT_ACE_TYPE:
+ case ACE_SYSTEM_ALARM_ACE_TYPE:
+ entry_type = flags & ACE_TYPE_FLAGS;
+ return (entry_type == ACE_OWNER ||
+ entry_type == OWNING_GROUP ||
+ entry_type == ACE_EVERYONE || entry_type == 0 ||
+ entry_type == ACE_IDENTIFIER_GROUP);
+ default:
+ if (type >= MIN_ACE_TYPE && type <= MAX_ACE_TYPE)
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+static boolean_t
+zfs_ace_valid(vtype_t obj_type, zfs_acl_t *aclp, uint16_t type, uint16_t iflags)
+{
/*
- * This is used for mapping v4 permissions into permissions
- * that can be passed to secpolicy_vnode_access()
+ * first check type of entry
*/
- if (access_mask & (ACE_READ_DATA | ACE_LIST_DIRECTORY |
- ACE_READ_ATTRIBUTES | ACE_READ_ACL))
- new_mask |= S_IROTH;
- if (access_mask & (ACE_WRITE_DATA | ACE_APPEND_DATA |
- ACE_WRITE_ATTRIBUTES | ACE_ADD_FILE | ACE_WRITE_NAMED_ATTRS))
- new_mask |= S_IWOTH;
- if (access_mask & (ACE_EXECUTE | ACE_READ_NAMED_ATTRS))
- new_mask |= S_IXOTH;
- return (new_mask);
+ if (!zfs_acl_valid_ace_type(type, iflags))
+ return (B_FALSE);
+
+ switch (type) {
+ case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
+ case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
+ case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
+ case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
+ if (aclp->z_version < ZFS_ACL_VERSION_FUID)
+ return (B_FALSE);
+ aclp->z_hints |= ZFS_ACL_OBJ_ACE;
+ }
+
+ /*
+ * next check inheritance level flags
+ */
+
+ if (obj_type == VDIR &&
+ (iflags & (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE)))
+ aclp->z_hints |= ZFS_INHERIT_ACE;
+
+ if (iflags & (ACE_INHERIT_ONLY_ACE|ACE_NO_PROPAGATE_INHERIT_ACE)) {
+ if ((iflags & (ACE_FILE_INHERIT_ACE|
+ ACE_DIRECTORY_INHERIT_ACE)) == 0) {
+ return (B_FALSE);
+ }
+ }
+
+ return (B_TRUE);
+}
+
+static void *
+zfs_acl_next_ace(zfs_acl_t *aclp, void *start, uint64_t *who,
+ uint32_t *access_mask, uint16_t *iflags, uint16_t *type)
+{
+ zfs_acl_node_t *aclnode;
+
+ if (start == NULL) {
+ aclnode = list_head(&aclp->z_acl);
+ if (aclnode == NULL)
+ return (NULL);
+
+ aclp->z_next_ace = aclnode->z_acldata;
+ aclp->z_curr_node = aclnode;
+ aclnode->z_ace_idx = 0;
+ }
+
+ aclnode = aclp->z_curr_node;
+
+ if (aclnode == NULL)
+ return (NULL);
+
+ if (aclnode->z_ace_idx >= aclnode->z_ace_count) {
+ aclnode = list_next(&aclp->z_acl, aclnode);
+ if (aclnode == NULL)
+ return (NULL);
+ else {
+ aclp->z_curr_node = aclnode;
+ aclnode->z_ace_idx = 0;
+ aclp->z_next_ace = aclnode->z_acldata;
+ }
+ }
+
+ if (aclnode->z_ace_idx < aclnode->z_ace_count) {
+ void *acep = aclp->z_next_ace;
+ size_t ace_size;
+
+ /*
+ * Make sure we don't overstep our bounds
+ */
+ ace_size = aclp->z_ops.ace_size(acep);
+
+ if (((caddr_t)acep + ace_size) >
+ ((caddr_t)aclnode->z_acldata + aclnode->z_size)) {
+ return (NULL);
+ }
+
+ *iflags = aclp->z_ops.ace_flags_get(acep);
+ *type = aclp->z_ops.ace_type_get(acep);
+ *access_mask = aclp->z_ops.ace_mask_get(acep);
+ *who = aclp->z_ops.ace_who_get(acep);
+ aclp->z_next_ace = (caddr_t)aclp->z_next_ace + ace_size;
+ aclnode->z_ace_idx++;
+ return ((void *)acep);
+ }
+ return (NULL);
+}
+
+/*ARGSUSED*/
+static uint64_t
+zfs_ace_walk(void *datap, uint64_t cookie, int aclcnt,
+ uint16_t *flags, uint16_t *type, uint32_t *mask)
+{
+ zfs_acl_t *aclp = datap;
+ zfs_ace_hdr_t *acep = (zfs_ace_hdr_t *)(uintptr_t)cookie;
+ uint64_t who;
+
+ acep = zfs_acl_next_ace(aclp, acep, &who, mask,
+ flags, type);
+ return ((uint64_t)(uintptr_t)acep);
+}
+
+static zfs_acl_node_t *
+zfs_acl_curr_node(zfs_acl_t *aclp)
+{
+ ASSERT(aclp->z_curr_node);
+ return (aclp->z_curr_node);
+}
+
+/*
+ * Copy ACE to internal ZFS format.
+ * While processing the ACL each ACE will be validated for correctness.
+ * ACE FUIDs will be created later.
+ */
+int
+zfs_copy_ace_2_fuid(vtype_t obj_type, zfs_acl_t *aclp, void *datap,
+ zfs_ace_t *z_acl, int aclcnt, size_t *size)
+{
+ int i;
+ uint16_t entry_type;
+ zfs_ace_t *aceptr = z_acl;
+ ace_t *acep = datap;
+ zfs_object_ace_t *zobjacep;
+ ace_object_t *aceobjp;
+
+ for (i = 0; i != aclcnt; i++) {
+ aceptr->z_hdr.z_access_mask = acep->a_access_mask;
+ aceptr->z_hdr.z_flags = acep->a_flags;
+ aceptr->z_hdr.z_type = acep->a_type;
+ entry_type = aceptr->z_hdr.z_flags & ACE_TYPE_FLAGS;
+ if (entry_type != ACE_OWNER && entry_type != OWNING_GROUP &&
+ entry_type != ACE_EVERYONE) {
+ if (!aclp->z_has_fuids)
+ aclp->z_has_fuids = IS_EPHEMERAL(acep->a_who);
+ aceptr->z_fuid = (uint64_t)acep->a_who;
+ }
+
+ /*
+ * Make sure ACE is valid
+ */
+ if (zfs_ace_valid(obj_type, aclp, aceptr->z_hdr.z_type,
+ aceptr->z_hdr.z_flags) != B_TRUE)
+ return (EINVAL);
+
+ switch (acep->a_type) {
+ case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
+ case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
+ case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
+ case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
+ zobjacep = (zfs_object_ace_t *)aceptr;
+ aceobjp = (ace_object_t *)acep;
+
+ bcopy(aceobjp->a_obj_type, zobjacep->z_object_type,
+ sizeof (aceobjp->a_obj_type));
+ bcopy(aceobjp->a_inherit_obj_type,
+ zobjacep->z_inherit_type,
+ sizeof (aceobjp->a_inherit_obj_type));
+ acep = (ace_t *)((caddr_t)acep + sizeof (ace_object_t));
+ break;
+ default:
+ acep = (ace_t *)((caddr_t)acep + sizeof (ace_t));
+ }
+
+ aceptr = (zfs_ace_t *)((caddr_t)aceptr +
+ aclp->z_ops.ace_size(aceptr));
+ }
+
+ *size = (caddr_t)aceptr - (caddr_t)z_acl;
+
+ return (0);
+}
+
+/*
+ * Copy ZFS ACEs to fixed size ace_t layout
+ */
+static void
+zfs_copy_fuid_2_ace(zfsvfs_t *zfsvfs, zfs_acl_t *aclp, cred_t *cr,
+ void *datap, int filter)
+{
+ uint64_t who;
+ uint32_t access_mask;
+ uint16_t iflags, type;
+ zfs_ace_hdr_t *zacep = NULL;
+ ace_t *acep = datap;
+ ace_object_t *objacep;
+ zfs_object_ace_t *zobjacep;
+ size_t ace_size;
+ uint16_t entry_type;
+
+ while (zacep = zfs_acl_next_ace(aclp, zacep,
+ &who, &access_mask, &iflags, &type)) {
+
+ switch (type) {
+ case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
+ case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
+ case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
+ case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
+ if (filter) {
+ continue;
+ }
+ zobjacep = (zfs_object_ace_t *)zacep;
+ objacep = (ace_object_t *)acep;
+ bcopy(zobjacep->z_object_type,
+ objacep->a_obj_type,
+ sizeof (zobjacep->z_object_type));
+ bcopy(zobjacep->z_inherit_type,
+ objacep->a_inherit_obj_type,
+ sizeof (zobjacep->z_inherit_type));
+ ace_size = sizeof (ace_object_t);
+ break;
+ default:
+ ace_size = sizeof (ace_t);
+ break;
+ }
+
+ entry_type = (iflags & ACE_TYPE_FLAGS);
+ if ((entry_type != ACE_OWNER &&
+ entry_type != OWNING_GROUP &&
+ entry_type != ACE_EVERYONE)) {
+ acep->a_who = zfs_fuid_map_id(zfsvfs, who,
+ cr, (entry_type & ACE_IDENTIFIER_GROUP) ?
+ ZFS_ACE_GROUP : ZFS_ACE_USER);
+ } else {
+ acep->a_who = (uid_t)(int64_t)who;
+ }
+ acep->a_access_mask = access_mask;
+ acep->a_flags = iflags;
+ acep->a_type = type;
+ acep = (ace_t *)((caddr_t)acep + ace_size);
+ }
+}
+
+static int
+zfs_copy_ace_2_oldace(vtype_t obj_type, zfs_acl_t *aclp, ace_t *acep,
+ zfs_oldace_t *z_acl, int aclcnt, size_t *size)
+{
+ int i;
+ zfs_oldace_t *aceptr = z_acl;
+
+ for (i = 0; i != aclcnt; i++, aceptr++) {
+ aceptr->z_access_mask = acep[i].a_access_mask;
+ aceptr->z_type = acep[i].a_type;
+ aceptr->z_flags = acep[i].a_flags;
+ aceptr->z_fuid = acep[i].a_who;
+ /*
+ * Make sure ACE is valid
+ */
+ if (zfs_ace_valid(obj_type, aclp, aceptr->z_type,
+ aceptr->z_flags) != B_TRUE)
+ return (EINVAL);
+ }
+ *size = (caddr_t)aceptr - (caddr_t)z_acl;
+ return (0);
+}
+
+/*
+ * convert old ACL format to new
+ */
+void
+zfs_acl_xform(znode_t *zp, zfs_acl_t *aclp)
+{
+ zfs_oldace_t *oldaclp;
+ int i;
+ uint16_t type, iflags;
+ uint32_t access_mask;
+ uint64_t who;
+ void *cookie = NULL;
+ zfs_acl_node_t *newaclnode;
+
+ ASSERT(aclp->z_version == ZFS_ACL_VERSION_INITIAL);
+ /*
+ * First create the ACE in a contiguous piece of memory
+ * for zfs_copy_ace_2_fuid().
+ *
+ * We only convert an ACL once, so this won't happen
+ * everytime.
+ */
+ oldaclp = kmem_alloc(sizeof (zfs_oldace_t) * aclp->z_acl_count,
+ KM_SLEEP);
+ i = 0;
+ while (cookie = zfs_acl_next_ace(aclp, cookie, &who,
+ &access_mask, &iflags, &type)) {
+ oldaclp[i].z_flags = iflags;
+ oldaclp[i].z_type = type;
+ oldaclp[i].z_fuid = who;
+ oldaclp[i++].z_access_mask = access_mask;
+ }
+
+ newaclnode = zfs_acl_node_alloc(aclp->z_acl_count *
+ sizeof (zfs_object_ace_t));
+ aclp->z_ops = zfs_acl_fuid_ops;
+ VERIFY(zfs_copy_ace_2_fuid(ZTOV(zp)->v_type, aclp, oldaclp,
+ newaclnode->z_acldata, aclp->z_acl_count,
+ &newaclnode->z_size) == 0);
+ newaclnode->z_ace_count = aclp->z_acl_count;
+ aclp->z_version = ZFS_ACL_VERSION;
+ kmem_free(oldaclp, aclp->z_acl_count * sizeof (zfs_oldace_t));
+
+ /*
+ * Release all previous ACL nodes
+ */
+
+ zfs_acl_release_nodes(aclp);
+
+ list_insert_head(&aclp->z_acl, newaclnode);
+
+ aclp->z_acl_bytes = newaclnode->z_size;
+ aclp->z_acl_count = newaclnode->z_ace_count;
+
}
/*
@@ -133,157 +739,213 @@ zfs_unix_to_v4(uint32_t access_mask)
{
uint32_t new_mask = 0;
- if (access_mask & 01)
- new_mask |= (ACE_EXECUTE);
- if (access_mask & 02) {
- new_mask |= (ACE_WRITE_DATA);
- } if (access_mask & 04) {
+ if (access_mask & S_IXOTH)
+ new_mask |= ACE_EXECUTE;
+ if (access_mask & S_IWOTH)
+ new_mask |= ACE_WRITE_DATA;
+ if (access_mask & S_IROTH)
new_mask |= ACE_READ_DATA;
- }
return (new_mask);
}
static void
-zfs_set_ace(ace_t *zacep, uint32_t access_mask, int access_type,
- uid_t uid, int entry_type)
+zfs_set_ace(zfs_acl_t *aclp, void *acep, uint32_t access_mask,
+ uint16_t access_type, uint64_t fuid, uint16_t entry_type)
{
- zacep->a_access_mask = access_mask;
- zacep->a_type = access_type;
- zacep->a_who = uid;
- zacep->a_flags = entry_type;
+ uint16_t type = entry_type & ACE_TYPE_FLAGS;
+
+ aclp->z_ops.ace_mask_set(acep, access_mask);
+ aclp->z_ops.ace_type_set(acep, access_type);
+ aclp->z_ops.ace_flags_set(acep, entry_type);
+ if ((type != ACE_OWNER && type != OWNING_GROUP &&
+ type != ACE_EVERYONE))
+ aclp->z_ops.ace_who_set(acep, fuid);
}
+/*
+ * Determine mode of file based on ACL.
+ * Also, create FUIDs for any User/Group ACEs
+ */
static uint64_t
-zfs_mode_compute(znode_t *zp, zfs_acl_t *aclp)
-{
- int i;
- int entry_type;
- mode_t mode = (zp->z_phys->zp_mode &
- (S_IFMT | S_ISUID | S_ISGID | S_ISVTX));
- mode_t seen = 0;
- ace_t *acep;
-
- for (i = 0, acep = aclp->z_acl;
- i != aclp->z_acl_count; i++, acep++) {
- entry_type = (acep->a_flags & ACE_TYPE_FLAGS);
+zfs_mode_fuid_compute(znode_t *zp, zfs_acl_t *aclp, cred_t *cr,
+ zfs_fuid_info_t **fuidp, dmu_tx_t *tx)
+{
+ int entry_type;
+ mode_t mode;
+ mode_t seen = 0;
+ zfs_ace_hdr_t *acep = NULL;
+ uint64_t who;
+ uint16_t iflags, type;
+ uint32_t access_mask;
+
+ mode = (zp->z_phys->zp_mode & (S_IFMT | S_ISUID | S_ISGID | S_ISVTX));
+
+ while (acep = zfs_acl_next_ace(aclp, acep, &who,
+ &access_mask, &iflags, &type)) {
+
+ if (!zfs_acl_valid_ace_type(type, iflags))
+ continue;
+
+ entry_type = (iflags & ACE_TYPE_FLAGS);
+
+ /*
+ * Skip over owner@, group@ or everyone@ inherit only ACEs
+ */
+ if ((iflags & ACE_INHERIT_ONLY_ACE) &&
+ (entry_type == ACE_OWNER || entry_type == ACE_EVERYONE ||
+ entry_type == OWNING_GROUP))
+ continue;
+
if (entry_type == ACE_OWNER) {
- if ((acep->a_access_mask & ACE_READ_DATA) &&
+ if ((access_mask & ACE_READ_DATA) &&
(!(seen & S_IRUSR))) {
seen |= S_IRUSR;
- if (acep->a_type == ALLOW) {
+ if (type == ALLOW) {
mode |= S_IRUSR;
}
}
- if ((acep->a_access_mask & ACE_WRITE_DATA) &&
+ if ((access_mask & ACE_WRITE_DATA) &&
(!(seen & S_IWUSR))) {
seen |= S_IWUSR;
- if (acep->a_type == ALLOW) {
+ if (type == ALLOW) {
mode |= S_IWUSR;
}
}
- if ((acep->a_access_mask & ACE_EXECUTE) &&
+ if ((access_mask & ACE_EXECUTE) &&
(!(seen & S_IXUSR))) {
seen |= S_IXUSR;
- if (acep->a_type == ALLOW) {
+ if (type == ALLOW) {
mode |= S_IXUSR;
}
}
} else if (entry_type == OWNING_GROUP) {
- if ((acep->a_access_mask & ACE_READ_DATA) &&
+ if ((access_mask & ACE_READ_DATA) &&
(!(seen & S_IRGRP))) {
seen |= S_IRGRP;
- if (acep->a_type == ALLOW) {
+ if (type == ALLOW) {
mode |= S_IRGRP;
}
}
- if ((acep->a_access_mask & ACE_WRITE_DATA) &&
+ if ((access_mask & ACE_WRITE_DATA) &&
(!(seen & S_IWGRP))) {
seen |= S_IWGRP;
- if (acep->a_type == ALLOW) {
+ if (type == ALLOW) {
mode |= S_IWGRP;
}
}
- if ((acep->a_access_mask & ACE_EXECUTE) &&
+ if ((access_mask & ACE_EXECUTE) &&
(!(seen & S_IXGRP))) {
seen |= S_IXGRP;
- if (acep->a_type == ALLOW) {
+ if (type == ALLOW) {
mode |= S_IXGRP;
}
}
} else if (entry_type == ACE_EVERYONE) {
- if ((acep->a_access_mask & ACE_READ_DATA)) {
+ if ((access_mask & ACE_READ_DATA)) {
if (!(seen & S_IRUSR)) {
seen |= S_IRUSR;
- if (acep->a_type == ALLOW) {
+ if (type == ALLOW) {
mode |= S_IRUSR;
}
}
if (!(seen & S_IRGRP)) {
seen |= S_IRGRP;
- if (acep->a_type == ALLOW) {
+ if (type == ALLOW) {
mode |= S_IRGRP;
}
}
if (!(seen & S_IROTH)) {
seen |= S_IROTH;
- if (acep->a_type == ALLOW) {
+ if (type == ALLOW) {
mode |= S_IROTH;
}
}
}
- if ((acep->a_access_mask & ACE_WRITE_DATA)) {
+ if ((access_mask & ACE_WRITE_DATA)) {
if (!(seen & S_IWUSR)) {
seen |= S_IWUSR;
- if (acep->a_type == ALLOW) {
+ if (type == ALLOW) {
mode |= S_IWUSR;
}
}
if (!(seen & S_IWGRP)) {
seen |= S_IWGRP;
- if (acep->a_type == ALLOW) {
+ if (type == ALLOW) {
mode |= S_IWGRP;
}
}
if (!(seen & S_IWOTH)) {
seen |= S_IWOTH;
- if (acep->a_type == ALLOW) {
+ if (type == ALLOW) {
mode |= S_IWOTH;
}
}
}
- if ((acep->a_access_mask & ACE_EXECUTE)) {
+ if ((access_mask & ACE_EXECUTE)) {
if (!(seen & S_IXUSR)) {
seen |= S_IXUSR;
- if (acep->a_type == ALLOW) {
+ if (type == ALLOW) {
mode |= S_IXUSR;
}
}
if (!(seen & S_IXGRP)) {
seen |= S_IXGRP;
- if (acep->a_type == ALLOW) {
+ if (type == ALLOW) {
mode |= S_IXGRP;
}
}
if (!(seen & S_IXOTH)) {
seen |= S_IXOTH;
- if (acep->a_type == ALLOW) {
+ if (type == ALLOW) {
mode |= S_IXOTH;
}
}
}
}
+ /*
+ * Now handle FUID create for user/group ACEs
+ */
+ if (entry_type == 0 || entry_type == ACE_IDENTIFIER_GROUP) {
+ aclp->z_ops.ace_who_set(acep,
+ zfs_fuid_create(zp->z_zfsvfs, who, cr,
+ (entry_type == 0) ? ZFS_ACE_USER : ZFS_ACE_GROUP,
+ tx, fuidp));
+ }
}
return (mode);
}
static zfs_acl_t *
-zfs_acl_node_read_internal(znode_t *zp)
+zfs_acl_node_read_internal(znode_t *zp, boolean_t will_modify)
{
zfs_acl_t *aclp;
+ zfs_acl_node_t *aclnode;
- aclp = zfs_acl_alloc(0);
- aclp->z_acl_count = zp->z_phys->zp_acl.z_acl_count;
- aclp->z_acl = &zp->z_phys->zp_acl.z_ace_data[0];
+ aclp = zfs_acl_alloc(zp->z_phys->zp_acl.z_acl_version);
+
+ /*
+ * Version 0 to 1 znode_acl_phys has the size/count fields swapped.
+ * Version 0 didn't have a size field, only a count.
+ */
+ if (zp->z_phys->zp_acl.z_acl_version == ZFS_ACL_VERSION_INITIAL) {
+ aclp->z_acl_count = zp->z_phys->zp_acl.z_acl_size;
+ aclp->z_acl_bytes = ZFS_ACL_SIZE(aclp->z_acl_count);
+ } else {
+ aclp->z_acl_count = zp->z_phys->zp_acl.z_acl_count;
+ aclp->z_acl_bytes = zp->z_phys->zp_acl.z_acl_size;
+ }
+
+ aclnode = zfs_acl_node_alloc(will_modify ? aclp->z_acl_bytes : 0);
+ aclnode->z_ace_count = aclp->z_acl_count;
+ if (will_modify) {
+ bcopy(zp->z_phys->zp_acl.z_ace_data, aclnode->z_acldata,
+ aclp->z_acl_bytes);
+ } else {
+ aclnode->z_size = aclp->z_acl_bytes;
+ aclnode->z_acldata = &zp->z_phys->zp_acl.z_ace_data[0];
+ }
+
+ list_insert_head(&aclp->z_acl, aclnode);
return (aclp);
}
@@ -292,212 +954,176 @@ zfs_acl_node_read_internal(znode_t *zp)
* Read an external acl object.
*/
static int
-zfs_acl_node_read(znode_t *zp, zfs_acl_t **aclpp)
+zfs_acl_node_read(znode_t *zp, zfs_acl_t **aclpp, boolean_t will_modify)
{
uint64_t extacl = zp->z_phys->zp_acl.z_acl_extern_obj;
zfs_acl_t *aclp;
+ size_t aclsize;
+ size_t acl_count;
+ zfs_acl_node_t *aclnode;
int error;
ASSERT(MUTEX_HELD(&zp->z_acl_lock));
if (zp->z_phys->zp_acl.z_acl_extern_obj == 0) {
- *aclpp = zfs_acl_node_read_internal(zp);
+ *aclpp = zfs_acl_node_read_internal(zp, will_modify);
return (0);
}
- aclp = zfs_acl_alloc(zp->z_phys->zp_acl.z_acl_count);
+ aclp = zfs_acl_alloc(zp->z_phys->zp_acl.z_acl_version);
+ if (zp->z_phys->zp_acl.z_acl_version == ZFS_ACL_VERSION_INITIAL) {
+ zfs_acl_phys_v0_t *zacl0 =
+ (zfs_acl_phys_v0_t *)&zp->z_phys->zp_acl;
+ aclsize = ZFS_ACL_SIZE(zacl0->z_acl_count);
+ acl_count = zacl0->z_acl_count;
+ } else {
+ aclsize = zp->z_phys->zp_acl.z_acl_size;
+ acl_count = zp->z_phys->zp_acl.z_acl_count;
+ if (aclsize == 0)
+ aclsize = acl_count * sizeof (zfs_ace_t);
+ }
+ aclnode = zfs_acl_node_alloc(aclsize);
+ list_insert_head(&aclp->z_acl, aclnode);
error = dmu_read(zp->z_zfsvfs->z_os, extacl, 0,
- ZFS_ACL_SIZE(zp->z_phys->zp_acl.z_acl_count), aclp->z_acl);
+ aclsize, aclnode->z_acldata);
+ aclnode->z_ace_count = acl_count;
+ aclp->z_acl_count = acl_count;
+ aclp->z_acl_bytes = aclsize;
+
if (error != 0) {
zfs_acl_free(aclp);
+ /* convert checksum errors into IO errors */
+ if (error == ECKSUM)
+ error = EIO;
return (error);
}
- aclp->z_acl_count = zp->z_phys->zp_acl.z_acl_count;
-
*aclpp = aclp;
return (0);
}
-static boolean_t
-zfs_acl_valid(znode_t *zp, ace_t *uace, int aclcnt, int *inherit)
-{
- ace_t *acep;
- int i;
-
- *inherit = 0;
-
- if (aclcnt > MAX_ACL_ENTRIES || aclcnt <= 0) {
- return (B_FALSE);
- }
-
- for (i = 0, acep = uace; i != aclcnt; i++, acep++) {
-
- /*
- * first check type of entry
- */
-
- switch (acep->a_flags & ACE_TYPE_FLAGS) {
- case ACE_OWNER:
- acep->a_who = -1;
- break;
- case (ACE_IDENTIFIER_GROUP | ACE_GROUP):
- case ACE_IDENTIFIER_GROUP:
- if (acep->a_flags & ACE_GROUP) {
- acep->a_who = -1;
- }
- break;
- case ACE_EVERYONE:
- acep->a_who = -1;
- break;
- }
-
- /*
- * next check inheritance level flags
- */
-
- if (acep->a_type != ALLOW && acep->a_type != DENY)
- return (B_FALSE);
-
- /*
- * Only directories should have inheritance flags.
- */
- if (ZTOV(zp)->v_type != VDIR && (acep->a_flags &
- (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE|
- ACE_INHERIT_ONLY_ACE|ACE_NO_PROPAGATE_INHERIT_ACE))) {
- return (B_FALSE);
- }
-
- if (acep->a_flags &
- (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE))
- *inherit = 1;
-
- if (acep->a_flags &
- (ACE_INHERIT_ONLY_ACE|ACE_NO_PROPAGATE_INHERIT_ACE)) {
- if ((acep->a_flags & (ACE_FILE_INHERIT_ACE|
- ACE_DIRECTORY_INHERIT_ACE)) == 0) {
- return (B_FALSE);
- }
- }
- }
-
- return (B_TRUE);
-}
/*
- * common code for setting acl's.
+ * common code for setting ACLs.
*
* This function is called from zfs_mode_update, zfs_perm_init, and zfs_setacl.
* zfs_setacl passes a non-NULL inherit pointer (ihp) to indicate that it's
* already checked the acl and knows whether to inherit.
*/
int
-zfs_aclset_common(znode_t *zp, zfs_acl_t *aclp, dmu_tx_t *tx, int *ihp)
+zfs_aclset_common(znode_t *zp, zfs_acl_t *aclp, cred_t *cr,
+ zfs_fuid_info_t **fuidp, dmu_tx_t *tx)
{
- int inherit = 0;
int error;
znode_phys_t *zphys = zp->z_phys;
- zfs_znode_acl_t *zacl = &zphys->zp_acl;
- uint32_t acl_phys_size = ZFS_ACL_SIZE(aclp->z_acl_count);
+ zfs_acl_phys_t *zacl = &zphys->zp_acl;
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
uint64_t aoid = zphys->zp_acl.z_acl_extern_obj;
+ uint64_t off = 0;
+ dmu_object_type_t otype;
+ zfs_acl_node_t *aclnode;
ASSERT(MUTEX_HELD(&zp->z_lock));
ASSERT(MUTEX_HELD(&zp->z_acl_lock));
- if (ihp)
- inherit = *ihp; /* already determined by caller */
- else if (!zfs_acl_valid(zp, aclp->z_acl,
- aclp->z_acl_count, &inherit)) {
- return (EINVAL);
- }
-
dmu_buf_will_dirty(zp->z_dbuf, tx);
+ zphys->zp_mode = zfs_mode_fuid_compute(zp, aclp, cr, fuidp, tx);
+
/*
- * Will ACL fit internally?
+ * Decide which opbject type to use. If we are forced to
+ * use old ACL format than transform ACL into zfs_oldace_t
+ * layout.
*/
- if (aclp->z_acl_count > ACE_SLOT_CNT) {
+ if (!zfsvfs->z_use_fuids) {
+ otype = DMU_OT_OLDACL;
+ } else {
+ if ((aclp->z_version == ZFS_ACL_VERSION_INITIAL) &&
+ (zfsvfs->z_version >= ZPL_VERSION_FUID))
+ zfs_acl_xform(zp, aclp);
+ ASSERT(aclp->z_version >= ZFS_ACL_VERSION_FUID);
+ otype = DMU_OT_ACL;
+ }
+
+ if (aclp->z_acl_bytes > ZFS_ACE_SPACE) {
+ /*
+ * If ACL was previously external and we are now
+ * converting to new ACL format then release old
+ * ACL object and create a new one.
+ */
+ if (aoid && aclp->z_version != zacl->z_acl_version) {
+ error = dmu_object_free(zfsvfs->z_os,
+ zp->z_phys->zp_acl.z_acl_extern_obj, tx);
+ if (error)
+ return (error);
+ aoid = 0;
+ }
if (aoid == 0) {
aoid = dmu_object_alloc(zfsvfs->z_os,
- DMU_OT_ACL, acl_phys_size, DMU_OT_NONE, 0, tx);
+ otype, aclp->z_acl_bytes,
+ otype == DMU_OT_ACL ? DMU_OT_SYSACL : DMU_OT_NONE,
+ otype == DMU_OT_ACL ? DN_MAX_BONUSLEN : 0, tx);
} else {
(void) dmu_object_set_blocksize(zfsvfs->z_os, aoid,
- acl_phys_size, 0, tx);
+ aclp->z_acl_bytes, 0, tx);
}
zphys->zp_acl.z_acl_extern_obj = aoid;
- zphys->zp_acl.z_acl_count = aclp->z_acl_count;
- dmu_write(zfsvfs->z_os, aoid, 0,
- acl_phys_size, aclp->z_acl, tx);
+ for (aclnode = list_head(&aclp->z_acl); aclnode;
+ aclnode = list_next(&aclp->z_acl, aclnode)) {
+ if (aclnode->z_ace_count == 0)
+ continue;
+ dmu_write(zfsvfs->z_os, aoid, off,
+ aclnode->z_size, aclnode->z_acldata, tx);
+ off += aclnode->z_size;
+ }
} else {
+ void *start = zacl->z_ace_data;
/*
* Migrating back embedded?
*/
if (zphys->zp_acl.z_acl_extern_obj) {
error = dmu_object_free(zfsvfs->z_os,
- zp->z_phys->zp_acl.z_acl_extern_obj, tx);
+ zp->z_phys->zp_acl.z_acl_extern_obj, tx);
if (error)
return (error);
zphys->zp_acl.z_acl_extern_obj = 0;
}
- bcopy(aclp->z_acl, zacl->z_ace_data,
- aclp->z_acl_count * sizeof (ace_t));
- zacl->z_acl_count = aclp->z_acl_count;
+
+ for (aclnode = list_head(&aclp->z_acl); aclnode;
+ aclnode = list_next(&aclp->z_acl, aclnode)) {
+ if (aclnode->z_ace_count == 0)
+ continue;
+ bcopy(aclnode->z_acldata, start, aclnode->z_size);
+ start = (caddr_t)start + aclnode->z_size;
+ }
}
- zp->z_phys->zp_flags &= ~(ZFS_ACL_TRIVIAL|ZFS_INHERIT_ACE);
- if (inherit) {
- zp->z_phys->zp_flags |= ZFS_INHERIT_ACE;
- } else if (ace_trivial(zacl->z_ace_data, zacl->z_acl_count) == 0) {
- zp->z_phys->zp_flags |= ZFS_ACL_TRIVIAL;
+ /*
+ * If Old version then swap count/bytes to match old
+ * layout of znode_acl_phys_t.
+ */
+ if (aclp->z_version == ZFS_ACL_VERSION_INITIAL) {
+ zphys->zp_acl.z_acl_size = aclp->z_acl_count;
+ zphys->zp_acl.z_acl_count = aclp->z_acl_bytes;
+ } else {
+ zphys->zp_acl.z_acl_size = aclp->z_acl_bytes;
+ zphys->zp_acl.z_acl_count = aclp->z_acl_count;
}
- zphys->zp_mode = zfs_mode_compute(zp, aclp);
- zfs_time_stamper_locked(zp, STATE_CHANGED, tx);
+ zphys->zp_acl.z_acl_version = aclp->z_version;
- return (0);
-}
+ /*
+ * Replace ACL wide bits, but first clear them.
+ */
+ zp->z_phys->zp_flags &= ~ZFS_ACL_WIDE_FLAGS;
-/*
- * Create space for slots_needed ACEs to be append
- * to aclp.
- */
-static void
-zfs_acl_append(zfs_acl_t *aclp, int slots_needed)
-{
- ace_t *newacep;
- ace_t *oldaclp;
- int slot_cnt;
- int slots_left = aclp->z_slots - aclp->z_acl_count;
+ zp->z_phys->zp_flags |= aclp->z_hints;
- if (aclp->z_state == ACL_DATA_ALLOCED)
- ASSERT(aclp->z_slots >= aclp->z_acl_count);
- if (slots_left < slots_needed || aclp->z_state != ACL_DATA_ALLOCED) {
- slot_cnt = aclp->z_slots + 1 + (slots_needed - slots_left);
- newacep = kmem_alloc(ZFS_ACL_SIZE(slot_cnt), KM_SLEEP);
- bcopy(aclp->z_acl, newacep,
- ZFS_ACL_SIZE(aclp->z_acl_count));
- oldaclp = aclp->z_acl;
- if (aclp->z_state == ACL_DATA_ALLOCED)
- kmem_free(oldaclp, ZFS_ACL_SIZE(aclp->z_slots));
- aclp->z_acl = newacep;
- aclp->z_slots = slot_cnt;
- aclp->z_state = ACL_DATA_ALLOCED;
- }
-}
+ if (ace_trivial_common(aclp, 0, zfs_ace_walk) == 0)
+ zp->z_phys->zp_flags |= ZFS_ACL_TRIVIAL;
-/*
- * Remove "slot" ACE from aclp
- */
-static void
-zfs_ace_remove(zfs_acl_t *aclp, int slot)
-{
- if (aclp->z_acl_count > 1) {
- (void) memmove(&aclp->z_acl[slot],
- &aclp->z_acl[slot +1], sizeof (ace_t) *
- (--aclp->z_acl_count - slot));
- } else
- aclp->z_acl_count--;
+ zfs_time_stamper_locked(zp, STATE_CHANGED, tx);
+ return (0);
}
/*
@@ -506,16 +1132,24 @@ zfs_ace_remove(zfs_acl_t *aclp, int slot)
* This applies the "groupmask" value for aclmode property.
*/
static void
-zfs_acl_prepend_fixup(ace_t *acep, ace_t *origacep, mode_t mode, uid_t owner)
+zfs_acl_prepend_fixup(zfs_acl_t *aclp, void *acep, void *origacep,
+ mode_t mode, uint64_t owner)
{
-
int rmask, wmask, xmask;
int user_ace;
+ uint16_t aceflags;
+ uint32_t origmask, acepmask;
+ uint64_t fuid;
- user_ace = (!(acep->a_flags &
+ aceflags = aclp->z_ops.ace_flags_get(acep);
+ fuid = aclp->z_ops.ace_who_get(acep);
+ origmask = aclp->z_ops.ace_mask_get(origacep);
+ acepmask = aclp->z_ops.ace_mask_get(acep);
+
+ user_ace = (!(aceflags &
(ACE_OWNER|ACE_GROUP|ACE_IDENTIFIER_GROUP)));
- if (user_ace && (acep->a_who == owner)) {
+ if (user_ace && (fuid == owner)) {
rmask = S_IRUSR;
wmask = S_IWUSR;
xmask = S_IXUSR;
@@ -525,33 +1159,38 @@ zfs_acl_prepend_fixup(ace_t *acep, ace_t *origacep, mode_t mode, uid_t owner)
xmask = S_IXGRP;
}
- if (origacep->a_access_mask & ACE_READ_DATA) {
- if (mode & rmask)
- acep->a_access_mask &= ~ACE_READ_DATA;
- else
- acep->a_access_mask |= ACE_READ_DATA;
+ if (origmask & ACE_READ_DATA) {
+ if (mode & rmask) {
+ acepmask &= ~ACE_READ_DATA;
+ } else {
+ acepmask |= ACE_READ_DATA;
+ }
}
- if (origacep->a_access_mask & ACE_WRITE_DATA) {
- if (mode & wmask)
- acep->a_access_mask &= ~ACE_WRITE_DATA;
- else
- acep->a_access_mask |= ACE_WRITE_DATA;
+ if (origmask & ACE_WRITE_DATA) {
+ if (mode & wmask) {
+ acepmask &= ~ACE_WRITE_DATA;
+ } else {
+ acepmask |= ACE_WRITE_DATA;
+ }
}
- if (origacep->a_access_mask & ACE_APPEND_DATA) {
- if (mode & wmask)
- acep->a_access_mask &= ~ACE_APPEND_DATA;
- else
- acep->a_access_mask |= ACE_APPEND_DATA;
+ if (origmask & ACE_APPEND_DATA) {
+ if (mode & wmask) {
+ acepmask &= ~ACE_APPEND_DATA;
+ } else {
+ acepmask |= ACE_APPEND_DATA;
+ }
}
- if (origacep->a_access_mask & ACE_EXECUTE) {
- if (mode & xmask)
- acep->a_access_mask &= ~ACE_EXECUTE;
- else
- acep->a_access_mask |= ACE_EXECUTE;
+ if (origmask & ACE_EXECUTE) {
+ if (mode & xmask) {
+ acepmask &= ~ACE_EXECUTE;
+ } else {
+ acepmask |= ACE_EXECUTE;
+ }
}
+ aclp->z_ops.ace_mask_set(acep, acepmask);
}
/*
@@ -560,116 +1199,156 @@ zfs_acl_prepend_fixup(ace_t *acep, ace_t *origacep, mode_t mode, uid_t owner)
static void
zfs_acl_fixup_canonical_six(zfs_acl_t *aclp, mode_t mode)
{
- int cnt;
- ace_t *acep;
+ zfs_acl_node_t *aclnode = list_tail(&aclp->z_acl);
+ void *acep;
+ int maskoff = aclp->z_ops.ace_mask_off();
+ size_t abstract_size = aclp->z_ops.ace_abstract_size();
+
+ ASSERT(aclnode != NULL);
- cnt = aclp->z_acl_count -1;
- acep = aclp->z_acl;
+ acep = (void *)((caddr_t)aclnode->z_acldata +
+ aclnode->z_size - (abstract_size * 6));
/*
* Fixup final ACEs to match the mode
*/
- ASSERT(cnt >= 5);
- adjust_ace_pair(&acep[cnt - 1], mode); /* everyone@ */
- adjust_ace_pair(&acep[cnt - 3], (mode & 0070) >> 3); /* group@ */
- adjust_ace_pair(&acep[cnt - 5], (mode & 0700) >> 6); /* owner@ */
+ adjust_ace_pair_common(acep, maskoff, abstract_size,
+ (mode & 0700) >> 6); /* owner@ */
+
+ acep = (caddr_t)acep + (abstract_size * 2);
+
+ adjust_ace_pair_common(acep, maskoff, abstract_size,
+ (mode & 0070) >> 3); /* group@ */
+
+ acep = (caddr_t)acep + (abstract_size * 2);
+ adjust_ace_pair_common(acep, maskoff,
+ abstract_size, mode); /* everyone@ */
}
static int
-zfs_acl_ace_match(ace_t *acep, int allow_deny, int type, int mask)
+zfs_acl_ace_match(zfs_acl_t *aclp, void *acep, int allow_deny,
+ int entry_type, int accessmask)
{
- return (acep->a_access_mask == mask && acep->a_type == allow_deny &&
- ((acep->a_flags & ACE_TYPE_FLAGS) == type));
+ uint32_t mask = aclp->z_ops.ace_mask_get(acep);
+ uint16_t type = aclp->z_ops.ace_type_get(acep);
+ uint16_t flags = aclp->z_ops.ace_flags_get(acep);
+
+ return (mask == accessmask && type == allow_deny &&
+ ((flags & ACE_TYPE_FLAGS) == entry_type));
}
/*
* Can prepended ACE be reused?
*/
static int
-zfs_reuse_deny(ace_t *acep, int i)
+zfs_reuse_deny(zfs_acl_t *aclp, void *acep, void *prevacep)
{
int okay_masks;
+ uint16_t prevtype;
+ uint16_t prevflags;
+ uint16_t flags;
+ uint32_t mask, prevmask;
- if (i < 1)
+ if (prevacep == NULL)
return (B_FALSE);
- if (acep[i-1].a_type != DENY)
+ prevtype = aclp->z_ops.ace_type_get(prevacep);
+ prevflags = aclp->z_ops.ace_flags_get(prevacep);
+ flags = aclp->z_ops.ace_flags_get(acep);
+ mask = aclp->z_ops.ace_mask_get(acep);
+ prevmask = aclp->z_ops.ace_mask_get(prevacep);
+
+ if (prevtype != DENY)
return (B_FALSE);
- if (acep[i-1].a_flags != (acep[i].a_flags & ACE_IDENTIFIER_GROUP))
+ if (prevflags != (flags & ACE_IDENTIFIER_GROUP))
return (B_FALSE);
- okay_masks = (acep[i].a_access_mask & OKAY_MASK_BITS);
+ okay_masks = (mask & OKAY_MASK_BITS);
- if (acep[i-1].a_access_mask & ~okay_masks)
+ if (prevmask & ~okay_masks)
return (B_FALSE);
return (B_TRUE);
}
+
/*
- * Create space to prepend an ACE
+ * Insert new ACL node into chain of zfs_acl_node_t's
+ *
+ * This will result in two possible results.
+ * 1. If the ACL is currently just a single zfs_acl_node and
+ * we are prepending the entry then current acl node will have
+ * a new node inserted above it.
+ *
+ * 2. If we are inserting in the middle of current acl node then
+ * the current node will be split in two and new node will be inserted
+ * in between the two split nodes.
*/
-static void
-zfs_acl_prepend(zfs_acl_t *aclp, int i)
-{
- ace_t *oldaclp = NULL;
- ace_t *to, *from;
- int slots_left = aclp->z_slots - aclp->z_acl_count;
- int oldslots;
- int need_free = 0;
-
- if (aclp->z_state == ACL_DATA_ALLOCED)
- ASSERT(aclp->z_slots >= aclp->z_acl_count);
-
- if (slots_left == 0 || aclp->z_state != ACL_DATA_ALLOCED) {
-
- to = kmem_alloc(ZFS_ACL_SIZE(aclp->z_acl_count +
- OGE_PAD), KM_SLEEP);
- if (aclp->z_state == ACL_DATA_ALLOCED)
- need_free++;
- from = aclp->z_acl;
- oldaclp = aclp->z_acl;
- (void) memmove(to, from,
- sizeof (ace_t) * aclp->z_acl_count);
- aclp->z_state = ACL_DATA_ALLOCED;
- } else {
- from = aclp->z_acl;
- to = aclp->z_acl;
+static zfs_acl_node_t *
+zfs_acl_ace_insert(zfs_acl_t *aclp, void *acep)
+{
+ zfs_acl_node_t *newnode;
+ zfs_acl_node_t *trailernode = NULL;
+ zfs_acl_node_t *currnode = zfs_acl_curr_node(aclp);
+ int curr_idx = aclp->z_curr_node->z_ace_idx;
+ int trailer_count;
+ size_t oldsize;
+
+ newnode = zfs_acl_node_alloc(aclp->z_ops.ace_size(acep));
+ newnode->z_ace_count = 1;
+
+ oldsize = currnode->z_size;
+
+ if (curr_idx != 1) {
+ trailernode = zfs_acl_node_alloc(0);
+ trailernode->z_acldata = acep;
+
+ trailer_count = currnode->z_ace_count - curr_idx + 1;
+ currnode->z_ace_count = curr_idx - 1;
+ currnode->z_size = (caddr_t)acep - (caddr_t)currnode->z_acldata;
+ trailernode->z_size = oldsize - currnode->z_size;
+ trailernode->z_ace_count = trailer_count;
}
-
- (void) memmove(&to[i + 1], &from[i],
- sizeof (ace_t) * (aclp->z_acl_count - i));
-
- if (oldaclp) {
- aclp->z_acl = to;
- oldslots = aclp->z_slots;
- aclp->z_slots = aclp->z_acl_count + OGE_PAD;
- if (need_free)
- kmem_free(oldaclp, ZFS_ACL_SIZE(oldslots));
+ aclp->z_acl_count += 1;
+ aclp->z_acl_bytes += aclp->z_ops.ace_size(acep);
+
+ if (curr_idx == 1)
+ list_insert_before(&aclp->z_acl, currnode, newnode);
+ else
+ list_insert_after(&aclp->z_acl, currnode, newnode);
+ if (trailernode) {
+ list_insert_after(&aclp->z_acl, newnode, trailernode);
+ aclp->z_curr_node = trailernode;
+ trailernode->z_ace_idx = 1;
}
+ return (newnode);
}
/*
* Prepend deny ACE
*/
-static void
-zfs_acl_prepend_deny(znode_t *zp, zfs_acl_t *aclp, int i,
+static void *
+zfs_acl_prepend_deny(znode_t *zp, zfs_acl_t *aclp, void *acep,
mode_t mode)
{
- ace_t *acep;
-
- zfs_acl_prepend(aclp, i);
-
- acep = aclp->z_acl;
- zfs_set_ace(&acep[i], 0, DENY, acep[i + 1].a_who,
- (acep[i + 1].a_flags & ACE_TYPE_FLAGS));
- zfs_acl_prepend_fixup(&acep[i], &acep[i+1], mode, zp->z_phys->zp_uid);
- aclp->z_acl_count++;
+ zfs_acl_node_t *aclnode;
+ void *newacep;
+ uint64_t fuid;
+ uint16_t flags;
+
+ aclnode = zfs_acl_ace_insert(aclp, acep);
+ newacep = aclnode->z_acldata;
+ fuid = aclp->z_ops.ace_who_get(acep);
+ flags = aclp->z_ops.ace_flags_get(acep);
+ zfs_set_ace(aclp, newacep, 0, DENY, fuid, (flags & ACE_TYPE_FLAGS));
+ zfs_acl_prepend_fixup(aclp, newacep, acep, mode, zp->z_phys->zp_uid);
+
+ return (newacep);
}
/*
@@ -677,41 +1356,74 @@ zfs_acl_prepend_deny(znode_t *zp, zfs_acl_t *aclp, int i,
* and original ACE with inheritance flags stripped off.
*/
static void
-zfs_acl_split_ace(zfs_acl_t *aclp, int i)
+zfs_acl_split_ace(zfs_acl_t *aclp, zfs_ace_hdr_t *acep)
{
- ace_t *acep = aclp->z_acl;
-
- zfs_acl_prepend(aclp, i);
- acep = aclp->z_acl;
- acep[i] = acep[i + 1];
- acep[i].a_flags |= ACE_INHERIT_ONLY_ACE;
- acep[i + 1].a_flags &= ~ALL_INHERIT;
- aclp->z_acl_count++;
+ zfs_acl_node_t *aclnode;
+ zfs_acl_node_t *currnode;
+ void *newacep;
+ uint16_t type, flags;
+ uint32_t mask;
+ uint64_t fuid;
+
+ type = aclp->z_ops.ace_type_get(acep);
+ flags = aclp->z_ops.ace_flags_get(acep);
+ mask = aclp->z_ops.ace_mask_get(acep);
+ fuid = aclp->z_ops.ace_who_get(acep);
+
+ aclnode = zfs_acl_ace_insert(aclp, acep);
+ newacep = aclnode->z_acldata;
+
+ aclp->z_ops.ace_type_set(newacep, type);
+ aclp->z_ops.ace_flags_set(newacep, flags | ACE_INHERIT_ONLY_ACE);
+ aclp->z_ops.ace_mask_set(newacep, mask);
+ aclp->z_ops.ace_type_set(newacep, type);
+ aclp->z_ops.ace_who_set(newacep, fuid);
+ aclp->z_next_ace = acep;
+ flags &= ~ALL_INHERIT;
+ aclp->z_ops.ace_flags_set(acep, flags);
+ currnode = zfs_acl_curr_node(aclp);
+ ASSERT(currnode->z_ace_idx >= 1);
+ currnode->z_ace_idx -= 1;
}
/*
* Are ACES started at index i, the canonical six ACES?
*/
static int
-zfs_have_canonical_six(zfs_acl_t *aclp, int i)
+zfs_have_canonical_six(zfs_acl_t *aclp)
{
- ace_t *acep = aclp->z_acl;
+ void *acep;
+ zfs_acl_node_t *aclnode = list_tail(&aclp->z_acl);
+ int i = 0;
+ size_t abstract_size = aclp->z_ops.ace_abstract_size();
- if ((zfs_acl_ace_match(&acep[i],
+ ASSERT(aclnode != NULL);
+
+ if (aclnode->z_ace_count < 6)
+ return (0);
+
+ acep = (void *)((caddr_t)aclnode->z_acldata +
+ aclnode->z_size - (aclp->z_ops.ace_abstract_size() * 6));
+
+ if ((zfs_acl_ace_match(aclp, (caddr_t)acep + (abstract_size * i++),
DENY, ACE_OWNER, 0) &&
- zfs_acl_ace_match(&acep[i + 1], ALLOW, ACE_OWNER,
- OWNER_ALLOW_MASK) && zfs_acl_ace_match(&acep[i + 2],
- DENY, OWNING_GROUP, 0) && zfs_acl_ace_match(&acep[i + 3],
- ALLOW, OWNING_GROUP, 0) && zfs_acl_ace_match(&acep[i + 4],
+ zfs_acl_ace_match(aclp, (caddr_t)acep + (abstract_size * i++),
+ ALLOW, ACE_OWNER, OWNER_ALLOW_MASK) &&
+ zfs_acl_ace_match(aclp, (caddr_t)acep + (abstract_size * i++), DENY,
+ OWNING_GROUP, 0) && zfs_acl_ace_match(aclp, (caddr_t)acep +
+ (abstract_size * i++),
+ ALLOW, OWNING_GROUP, 0) &&
+ zfs_acl_ace_match(aclp, (caddr_t)acep + (abstract_size * i++),
DENY, ACE_EVERYONE, EVERYONE_DENY_MASK) &&
- zfs_acl_ace_match(&acep[i + 5], ALLOW, ACE_EVERYONE,
- EVERYONE_ALLOW_MASK))) {
+ zfs_acl_ace_match(aclp, (caddr_t)acep + (abstract_size * i++),
+ ALLOW, ACE_EVERYONE, EVERYONE_ALLOW_MASK))) {
return (1);
} else {
return (0);
}
}
+
/*
* Apply step 1g, to group entries
*
@@ -721,73 +1433,89 @@ zfs_have_canonical_six(zfs_acl_t *aclp, int i)
* group has.
*/
static void
-zfs_fixup_group_entries(ace_t *acep, mode_t mode)
+zfs_fixup_group_entries(zfs_acl_t *aclp, void *acep, void *prevacep,
+ mode_t mode)
{
+ uint32_t prevmask = aclp->z_ops.ace_mask_get(prevacep);
+ uint32_t mask = aclp->z_ops.ace_mask_get(acep);
+ uint16_t prevflags = aclp->z_ops.ace_flags_get(prevacep);
mode_t extramode = (mode >> 3) & 07;
mode_t ownermode = (mode >> 6);
- if (acep[0].a_flags & ACE_IDENTIFIER_GROUP) {
+ if (prevflags & ACE_IDENTIFIER_GROUP) {
extramode &= ~ownermode;
if (extramode) {
- if (extramode & 04) {
- acep[0].a_access_mask &= ~ACE_READ_DATA;
- acep[1].a_access_mask &= ~ACE_READ_DATA;
+ if (extramode & S_IROTH) {
+ prevmask &= ~ACE_READ_DATA;
+ mask &= ~ACE_READ_DATA;
}
- if (extramode & 02) {
- acep[0].a_access_mask &=
- ~(ACE_WRITE_DATA|ACE_APPEND_DATA);
- acep[1].a_access_mask &=
- ~(ACE_WRITE_DATA|ACE_APPEND_DATA);
+ if (extramode & S_IWOTH) {
+ prevmask &= ~(ACE_WRITE_DATA|ACE_APPEND_DATA);
+ mask &= ~(ACE_WRITE_DATA|ACE_APPEND_DATA);
}
- if (extramode & 01) {
- acep[0].a_access_mask &= ~ACE_EXECUTE;
- acep[1].a_access_mask &= ~ACE_EXECUTE;
+ if (extramode & S_IXOTH) {
+ prevmask &= ~ACE_EXECUTE;
+ mask &= ~ACE_EXECUTE;
}
}
}
+ aclp->z_ops.ace_mask_set(acep, mask);
+ aclp->z_ops.ace_mask_set(prevacep, prevmask);
}
/*
* Apply the chmod algorithm as described
* in PSARC/2002/240
*/
-static int
-zfs_acl_chmod(znode_t *zp, uint64_t mode, zfs_acl_t *aclp,
- dmu_tx_t *tx)
+static void
+zfs_acl_chmod(znode_t *zp, uint64_t mode, zfs_acl_t *aclp)
{
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
- ace_t *acep;
+ void *acep = NULL, *prevacep = NULL;
+ uint64_t who;
int i;
- int error;
int entry_type;
int reuse_deny;
int need_canonical_six = 1;
- int inherit = 0;
- int iflags;
+ uint16_t iflags, type;
+ uint32_t access_mask;
ASSERT(MUTEX_HELD(&zp->z_acl_lock));
ASSERT(MUTEX_HELD(&zp->z_lock));
- i = 0;
- while (i < aclp->z_acl_count) {
- acep = aclp->z_acl;
- entry_type = (acep[i].a_flags & ACE_TYPE_FLAGS);
- iflags = (acep[i].a_flags & ALL_INHERIT);
+ aclp->z_hints = (zp->z_phys->zp_flags & V4_ACL_WIDE_FLAGS);
- if ((acep[i].a_type != ALLOW && acep[i].a_type != DENY) ||
- (iflags & ACE_INHERIT_ONLY_ACE)) {
- i++;
- if (iflags)
- inherit = 1;
- continue;
- }
+ /*
+ * If discard then just discard all ACL nodes which
+ * represent the ACEs.
+ *
+ * New owner@/group@/everone@ ACEs will be added
+ * later.
+ */
+ if (zfsvfs->z_acl_mode == ZFS_ACL_DISCARD)
+ zfs_acl_release_nodes(aclp);
+ while (acep = zfs_acl_next_ace(aclp, acep, &who, &access_mask,
+ &iflags, &type)) {
- if (zfsvfs->z_acl_mode == ZFS_ACL_DISCARD) {
- zfs_ace_remove(aclp, i);
- continue;
+ entry_type = (iflags & ACE_TYPE_FLAGS);
+ iflags = (iflags & ALL_INHERIT);
+
+ if ((type != ALLOW && type != DENY) ||
+ (iflags & ACE_INHERIT_ONLY_ACE)) {
+ if (iflags)
+ aclp->z_hints |= ZFS_INHERIT_ACE;
+ switch (type) {
+ case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
+ case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
+ case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
+ case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
+ aclp->z_hints |= ZFS_ACL_OBJ_ACE;
+ break;
+ }
+ goto nextace;
}
/*
@@ -796,20 +1524,19 @@ zfs_acl_chmod(znode_t *zp, uint64_t mode, zfs_acl_t *aclp,
if ((iflags & (ACE_FILE_INHERIT_ACE|
ACE_DIRECTORY_INHERIT_ACE)) &&
(!(iflags & ACE_INHERIT_ONLY_ACE))) {
- zfs_acl_split_ace(aclp, i);
- i++;
- inherit = 1;
- continue;
+ zfs_acl_split_ace(aclp, acep);
+ aclp->z_hints |= ZFS_INHERIT_ACE;
+ goto nextace;
}
if (entry_type == ACE_OWNER || entry_type == ACE_EVERYONE ||
(entry_type == OWNING_GROUP)) {
- acep[i].a_access_mask &= ~OGE_CLEAR;
- i++;
- continue;
-
+ access_mask &= ~OGE_CLEAR;
+ aclp->z_ops.ace_mask_set(acep, access_mask);
+ goto nextace;
} else {
- if (acep[i].a_type == ALLOW) {
+ reuse_deny = B_TRUE;
+ if (type == ALLOW) {
/*
* Check preceding ACE if any, to see
@@ -819,25 +1546,27 @@ zfs_acl_chmod(znode_t *zp, uint64_t mode, zfs_acl_t *aclp,
*/
if (zfsvfs->z_acl_mode == ZFS_ACL_GROUPMASK) {
- reuse_deny = zfs_reuse_deny(acep, i);
+ reuse_deny = zfs_reuse_deny(aclp, acep,
+ prevacep);
- if (reuse_deny == B_FALSE) {
- zfs_acl_prepend_deny(zp, aclp,
- i, mode);
- i++;
- acep = aclp->z_acl;
+ if (!reuse_deny) {
+ prevacep =
+ zfs_acl_prepend_deny(zp,
+ aclp, acep, mode);
} else {
zfs_acl_prepend_fixup(
- &acep[i - 1],
- &acep[i], mode,
+ aclp, prevacep,
+ acep, mode,
zp->z_phys->zp_uid);
}
- zfs_fixup_group_entries(&acep[i - 1],
- mode);
+ zfs_fixup_group_entries(aclp, acep,
+ prevacep, mode);
+
}
}
- i++;
}
+nextace:
+ prevacep = acep;
}
/*
@@ -845,51 +1574,56 @@ zfs_acl_chmod(znode_t *zp, uint64_t mode, zfs_acl_t *aclp,
*/
if (aclp->z_acl_count >= 6) {
- i = aclp->z_acl_count - 6;
-
- if (zfs_have_canonical_six(aclp, i)) {
+ if (zfs_have_canonical_six(aclp)) {
need_canonical_six = 0;
}
}
if (need_canonical_six) {
-
- zfs_acl_append(aclp, 6);
- i = aclp->z_acl_count;
- acep = aclp->z_acl;
- zfs_set_ace(&acep[i++], 0, DENY, -1, ACE_OWNER);
- zfs_set_ace(&acep[i++], OWNER_ALLOW_MASK, ALLOW, -1, ACE_OWNER);
- zfs_set_ace(&acep[i++], 0, DENY, -1, OWNING_GROUP);
- zfs_set_ace(&acep[i++], 0, ALLOW, -1, OWNING_GROUP);
- zfs_set_ace(&acep[i++], EVERYONE_DENY_MASK,
- DENY, -1, ACE_EVERYONE);
- zfs_set_ace(&acep[i++], EVERYONE_ALLOW_MASK,
- ALLOW, -1, ACE_EVERYONE);
+ size_t abstract_size = aclp->z_ops.ace_abstract_size();
+ void *zacep;
+ zfs_acl_node_t *aclnode =
+ zfs_acl_node_alloc(abstract_size * 6);
+
+ aclnode->z_size = abstract_size * 6;
+ aclnode->z_ace_count = 6;
+ aclp->z_acl_bytes += aclnode->z_size;
+ list_insert_tail(&aclp->z_acl, aclnode);
+
+ zacep = aclnode->z_acldata;
+
+ i = 0;
+ zfs_set_ace(aclp, (caddr_t)zacep + (abstract_size * i++),
+ 0, DENY, -1, ACE_OWNER);
+ zfs_set_ace(aclp, (caddr_t)zacep + (abstract_size * i++),
+ OWNER_ALLOW_MASK, ALLOW, -1, ACE_OWNER);
+ zfs_set_ace(aclp, (caddr_t)zacep + (abstract_size * i++), 0,
+ DENY, -1, OWNING_GROUP);
+ zfs_set_ace(aclp, (caddr_t)zacep + (abstract_size * i++), 0,
+ ALLOW, -1, OWNING_GROUP);
+ zfs_set_ace(aclp, (caddr_t)zacep + (abstract_size * i++),
+ EVERYONE_DENY_MASK, DENY, -1, ACE_EVERYONE);
+ zfs_set_ace(aclp, (caddr_t)zacep + (abstract_size * i++),
+ EVERYONE_ALLOW_MASK, ALLOW, -1, ACE_EVERYONE);
aclp->z_acl_count += 6;
}
zfs_acl_fixup_canonical_six(aclp, mode);
-
- zp->z_phys->zp_mode = mode;
- error = zfs_aclset_common(zp, aclp, tx, &inherit);
- return (error);
}
-
int
-zfs_acl_chmod_setattr(znode_t *zp, uint64_t mode, dmu_tx_t *tx)
+zfs_acl_chmod_setattr(znode_t *zp, zfs_acl_t **aclp, uint64_t mode)
{
- zfs_acl_t *aclp = NULL;
int error;
- ASSERT(MUTEX_HELD(&zp->z_lock));
+ mutex_enter(&zp->z_lock);
mutex_enter(&zp->z_acl_lock);
- error = zfs_acl_node_read(zp, &aclp);
+ *aclp = NULL;
+ error = zfs_acl_node_read(zp, aclp, B_TRUE);
if (error == 0)
- error = zfs_acl_chmod(zp, mode, aclp, tx);
+ zfs_acl_chmod(zp, mode, *aclp);
mutex_exit(&zp->z_acl_lock);
- if (aclp)
- zfs_acl_free(aclp);
+ mutex_exit(&zp->z_lock);
return (error);
}
@@ -897,104 +1631,159 @@ zfs_acl_chmod_setattr(znode_t *zp, uint64_t mode, dmu_tx_t *tx)
* strip off write_owner and write_acl
*/
static void
-zfs_securemode_update(zfsvfs_t *zfsvfs, ace_t *acep)
+zfs_restricted_update(zfsvfs_t *zfsvfs, zfs_acl_t *aclp, void *acep)
{
- if ((zfsvfs->z_acl_inherit == ZFS_ACL_SECURE) &&
- (acep->a_type == ALLOW))
- acep->a_access_mask &= ~SECURE_CLEAR;
+ uint32_t mask = aclp->z_ops.ace_mask_get(acep);
+
+ if ((zfsvfs->z_acl_inherit == ZFS_ACL_RESTRICTED) &&
+ (aclp->z_ops.ace_type_get(acep) == ALLOW)) {
+ mask &= ~RESTRICTED_CLEAR;
+ aclp->z_ops.ace_mask_set(acep, mask);
+ }
+}
+
+/*
+ * Should ACE be inherited?
+ */
+static int
+zfs_ace_can_use(znode_t *zp, uint16_t acep_flags)
+{
+ int vtype = ZTOV(zp)->v_type;
+ int iflags = (acep_flags & 0xf);
+
+ if ((vtype == VDIR) && (iflags & ACE_DIRECTORY_INHERIT_ACE))
+ return (1);
+ else if (iflags & ACE_FILE_INHERIT_ACE)
+ return (!((vtype == VDIR) &&
+ (iflags & ACE_NO_PROPAGATE_INHERIT_ACE)));
+ return (0);
}
/*
* inherit inheritable ACEs from parent
*/
static zfs_acl_t *
-zfs_acl_inherit(znode_t *zp, zfs_acl_t *paclp)
+zfs_acl_inherit(znode_t *zp, zfs_acl_t *paclp, boolean_t *need_chmod)
{
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
- ace_t *pacep;
- ace_t *acep;
- int ace_cnt = 0;
- int pace_cnt;
- int i, j;
+ void *pacep;
+ void *acep, *acep2;
+ zfs_acl_node_t *aclnode, *aclnode2;
zfs_acl_t *aclp = NULL;
-
- i = j = 0;
- pace_cnt = paclp->z_acl_count;
- pacep = paclp->z_acl;
+ uint64_t who;
+ uint32_t access_mask;
+ uint16_t iflags, newflags, type;
+ size_t ace_size;
+ void *data1, *data2;
+ size_t data1sz, data2sz;
+ enum vtype vntype = ZTOV(zp)->v_type;
+
+ *need_chmod = B_TRUE;
+ pacep = NULL;
+ aclp = zfs_acl_alloc(paclp->z_version);
if (zfsvfs->z_acl_inherit != ZFS_ACL_DISCARD) {
- for (i = 0; i != pace_cnt; i++) {
+ while (pacep = zfs_acl_next_ace(paclp, pacep, &who,
+ &access_mask, &iflags, &type)) {
- if (zfsvfs->z_acl_inherit == ZFS_ACL_NOALLOW &&
- pacep[i].a_type == ALLOW)
+ /*
+ * don't inherit bogus ACEs
+ */
+ if (!zfs_acl_valid_ace_type(type, iflags))
continue;
- if (zfs_ace_can_use(zp, &pacep[i])) {
- ace_cnt++;
- if (!(pacep[i].a_flags &
- ACE_NO_PROPAGATE_INHERIT_ACE))
- ace_cnt++;
- }
- }
- }
-
- aclp = zfs_acl_alloc(ace_cnt + OGE_PAD);
- if (ace_cnt && zfsvfs->z_acl_inherit != ZFS_ACL_DISCARD) {
- acep = aclp->z_acl;
- pacep = paclp->z_acl;
- for (i = 0; i != pace_cnt; i++) {
-
if (zfsvfs->z_acl_inherit == ZFS_ACL_NOALLOW &&
- pacep[i].a_type == ALLOW)
+ type == ALLOW)
continue;
- if (zfs_ace_can_use(zp, &pacep[i])) {
+ ace_size = aclp->z_ops.ace_size(pacep);
- /*
- * Now create entry for inherited ace
- */
-
- acep[j] = pacep[i];
+ if (!zfs_ace_can_use(zp, iflags))
+ continue;
- /*
- * When AUDIT/ALARM a_types are supported
- * they should be inherited here.
- */
+ /*
+ * If owner@, group@, or everyone@ inheritable
+ * then zfs_acl_chmod() isn't needed.
+ */
+ if (zfsvfs->z_acl_inherit ==
+ ZFS_ACL_PASSTHROUGH &&
+ ((iflags & (ACE_OWNER|ACE_EVERYONE)) ||
+ ((iflags & OWNING_GROUP) ==
+ OWNING_GROUP)) && (vntype == VREG ||
+ (vntype == VDIR &&
+ (iflags & ACE_DIRECTORY_INHERIT_ACE))))
+ *need_chmod = B_FALSE;
+
+ aclnode = zfs_acl_node_alloc(ace_size);
+ list_insert_tail(&aclp->z_acl, aclnode);
+ acep = aclnode->z_acldata;
+ zfs_set_ace(aclp, acep, access_mask, type,
+ who, iflags|ACE_INHERITED_ACE);
- if ((pacep[i].a_flags &
- ACE_NO_PROPAGATE_INHERIT_ACE) ||
- (ZTOV(zp)->v_type != VDIR)) {
- acep[j].a_flags &= ~ALL_INHERIT;
- zfs_securemode_update(zfsvfs, &acep[j]);
- j++;
- continue;
- }
+ /*
+ * Copy special opaque data if any
+ */
+ if ((data1sz = paclp->z_ops.ace_data(pacep,
+ &data1)) != 0) {
+ VERIFY((data2sz = aclp->z_ops.ace_data(acep,
+ &data2)) == data1sz);
+ bcopy(data1, data2, data2sz);
+ }
+ aclp->z_acl_count++;
+ aclnode->z_ace_count++;
+ aclp->z_acl_bytes += aclnode->z_size;
+ newflags = aclp->z_ops.ace_flags_get(acep);
+
+ if (vntype == VDIR)
+ aclp->z_hints |= ZFS_INHERIT_ACE;
+
+ if ((iflags & ACE_NO_PROPAGATE_INHERIT_ACE) ||
+ (vntype != VDIR)) {
+ newflags &= ~ALL_INHERIT;
+ aclp->z_ops.ace_flags_set(acep,
+ newflags|ACE_INHERITED_ACE);
+ zfs_restricted_update(zfsvfs, aclp, acep);
+ continue;
+ }
- ASSERT(ZTOV(zp)->v_type == VDIR);
+ ASSERT(vntype == VDIR);
+
+ newflags = aclp->z_ops.ace_flags_get(acep);
+ if ((iflags & (ACE_FILE_INHERIT_ACE |
+ ACE_DIRECTORY_INHERIT_ACE)) !=
+ ACE_FILE_INHERIT_ACE) {
+ aclnode2 = zfs_acl_node_alloc(ace_size);
+ list_insert_tail(&aclp->z_acl, aclnode2);
+ acep2 = aclnode2->z_acldata;
+ zfs_set_ace(aclp, acep2,
+ access_mask, type, who,
+ iflags|ACE_INHERITED_ACE);
+ newflags |= ACE_INHERIT_ONLY_ACE;
+ aclp->z_ops.ace_flags_set(acep, newflags);
+ newflags &= ~ALL_INHERIT;
+ aclp->z_ops.ace_flags_set(acep2,
+ newflags|ACE_INHERITED_ACE);
/*
- * If we are inheriting an ACE targeted for
- * only files, then make sure inherit_only
- * is on for future propagation.
+ * Copy special opaque data if any
*/
- if ((pacep[i].a_flags & (ACE_FILE_INHERIT_ACE |
- ACE_DIRECTORY_INHERIT_ACE)) !=
- ACE_FILE_INHERIT_ACE) {
- j++;
- acep[j] = acep[j-1];
- acep[j-1].a_flags |=
- ACE_INHERIT_ONLY_ACE;
- acep[j].a_flags &= ~ALL_INHERIT;
- } else {
- acep[j].a_flags |= ACE_INHERIT_ONLY_ACE;
+ if ((data1sz = aclp->z_ops.ace_data(acep,
+ &data1)) != 0) {
+ VERIFY((data2sz =
+ aclp->z_ops.ace_data(acep2,
+ &data2)) == data1sz);
+ bcopy(data1, data2, data1sz);
}
- zfs_securemode_update(zfsvfs, &acep[j]);
- j++;
+ aclp->z_acl_count++;
+ aclnode2->z_ace_count++;
+ aclp->z_acl_bytes += aclnode->z_size;
+ zfs_restricted_update(zfsvfs, aclp, acep2);
+ } else {
+ newflags |= ACE_INHERIT_ONLY_ACE;
+ aclp->z_ops.ace_flags_set(acep,
+ newflags|ACE_INHERITED_ACE);
}
}
}
- aclp->z_acl_count = j;
- ASSERT(aclp->z_slots >= aclp->z_acl_count);
-
return (aclp);
}
@@ -1004,14 +1793,20 @@ zfs_acl_inherit(znode_t *zp, zfs_acl_t *paclp)
*/
void
zfs_perm_init(znode_t *zp, znode_t *parent, int flag,
- vattr_t *vap, dmu_tx_t *tx, cred_t *cr)
+ vattr_t *vap, dmu_tx_t *tx, cred_t *cr,
+ zfs_acl_t *setaclp, zfs_fuid_info_t **fuidp)
{
- uint64_t mode;
- uid_t uid;
- gid_t gid;
+ uint64_t mode, fuid, fgid;
int error;
- int pull_down;
- zfs_acl_t *aclp, *paclp;
+ zfsvfs_t *zfsvfs = zp->z_zfsvfs;
+ zfs_acl_t *aclp = NULL;
+ zfs_acl_t *paclp;
+ xvattr_t *xvap = (xvattr_t *)vap;
+ gid_t gid;
+ boolean_t need_chmod = B_TRUE;
+
+ if (setaclp)
+ aclp = setaclp;
mode = MAKEIMODE(vap->va_type, vap->va_mode);
@@ -1020,22 +1815,38 @@ zfs_perm_init(znode_t *zp, znode_t *parent, int flag,
*/
if ((flag & (IS_ROOT_NODE | IS_REPLAY)) ||
((flag & IS_XATTR) && (vap->va_type == VDIR))) {
- uid = vap->va_uid;
+ fuid = zfs_fuid_create(zfsvfs, vap->va_uid, cr,
+ ZFS_OWNER, tx, fuidp);
+ fgid = zfs_fuid_create(zfsvfs, vap->va_gid, cr,
+ ZFS_GROUP, tx, fuidp);
gid = vap->va_gid;
} else {
- uid = crgetuid(cr);
- if ((vap->va_mask & AT_GID) &&
- ((vap->va_gid == parent->z_phys->zp_gid) ||
- groupmember(vap->va_gid, cr) ||
- secpolicy_vnode_create_gid(cr) == 0))
+ fuid = zfs_fuid_create_cred(zfsvfs, ZFS_OWNER, tx, cr, fuidp);
+ fgid = 0;
+ if (vap->va_mask & AT_GID) {
+ fgid = zfs_fuid_create(zfsvfs, vap->va_gid, cr,
+ ZFS_GROUP, tx, fuidp);
gid = vap->va_gid;
- else
+ if (fgid != parent->z_phys->zp_gid &&
+ !groupmember(vap->va_gid, cr) &&
+ secpolicy_vnode_create_gid(cr) != 0)
+ fgid = 0;
+ }
+ if (fgid == 0) {
+ if (parent->z_phys->zp_mode & S_ISGID) {
+ fgid = parent->z_phys->zp_gid;
+ gid = zfs_fuid_map_id(zfsvfs, fgid,
+ cr, ZFS_GROUP);
+ } else {
+ fgid = zfs_fuid_create_cred(zfsvfs,
+ ZFS_GROUP, tx, cr, fuidp);
#ifdef __FreeBSD__
- gid = parent->z_phys->zp_gid;
+ gid = parent->z_phys->zp_gid;
#else
- gid = (parent->z_phys->zp_mode & S_ISGID) ?
- parent->z_phys->zp_gid : crgetgid(cr);
+ gid = crgetgid(cr);
#endif
+ }
+ }
}
/*
@@ -1045,55 +1856,57 @@ zfs_perm_init(znode_t *zp, znode_t *parent, int flag,
* file's new group, clear the file's set-GID bit.
*/
- if ((parent->z_phys->zp_mode & S_ISGID) && (vap->va_type == VDIR))
+ if ((parent->z_phys->zp_mode & S_ISGID) && (vap->va_type == VDIR)) {
mode |= S_ISGID;
- else {
+ } else {
if ((mode & S_ISGID) &&
- secpolicy_vnode_setids_setgids(cr, gid) != 0)
+ secpolicy_vnode_setids_setgids(ZTOV(zp), cr, gid) != 0)
mode &= ~S_ISGID;
}
- zp->z_phys->zp_uid = uid;
- zp->z_phys->zp_gid = gid;
+ zp->z_phys->zp_uid = fuid;
+ zp->z_phys->zp_gid = fgid;
zp->z_phys->zp_mode = mode;
- mutex_enter(&parent->z_lock);
- pull_down = (parent->z_phys->zp_flags & ZFS_INHERIT_ACE);
- if (pull_down) {
- mutex_enter(&parent->z_acl_lock);
- VERIFY(0 == zfs_acl_node_read(parent, &paclp));
- mutex_exit(&parent->z_acl_lock);
- aclp = zfs_acl_inherit(zp, paclp);
- zfs_acl_free(paclp);
+ if (aclp == NULL) {
+ mutex_enter(&parent->z_lock);
+ if ((ZTOV(parent)->v_type == VDIR &&
+ (parent->z_phys->zp_flags & ZFS_INHERIT_ACE)) &&
+ !(zp->z_phys->zp_flags & ZFS_XATTR)) {
+ mutex_enter(&parent->z_acl_lock);
+ VERIFY(0 == zfs_acl_node_read(parent, &paclp, B_FALSE));
+ mutex_exit(&parent->z_acl_lock);
+ aclp = zfs_acl_inherit(zp, paclp, &need_chmod);
+ zfs_acl_free(paclp);
+ } else {
+ aclp = zfs_acl_alloc(zfs_acl_version_zp(zp));
+ }
+ mutex_exit(&parent->z_lock);
+ mutex_enter(&zp->z_lock);
+ mutex_enter(&zp->z_acl_lock);
+ if (need_chmod)
+ zfs_acl_chmod(zp, mode, aclp);
} else {
- aclp = zfs_acl_alloc(6);
+ mutex_enter(&zp->z_lock);
+ mutex_enter(&zp->z_acl_lock);
}
- mutex_exit(&parent->z_lock);
- mutex_enter(&zp->z_lock);
- mutex_enter(&zp->z_acl_lock);
- error = zfs_acl_chmod(zp, mode, aclp, tx);
+
+ /* Force auto_inherit on all new directory objects */
+ if (vap->va_type == VDIR)
+ aclp->z_hints |= ZFS_ACL_AUTO_INHERIT;
+
+ error = zfs_aclset_common(zp, aclp, cr, fuidp, tx);
+
+ /* Set optional attributes if any */
+ if (vap->va_mask & AT_XVATTR)
+ zfs_xvattr_set(zp, xvap);
+
mutex_exit(&zp->z_lock);
mutex_exit(&zp->z_acl_lock);
ASSERT3U(error, ==, 0);
- zfs_acl_free(aclp);
-}
-
-/*
- * Should ACE be inherited?
- */
-static int
-zfs_ace_can_use(znode_t *zp, ace_t *acep)
-{
- int vtype = ZTOV(zp)->v_type;
- int iflags = (acep->a_flags & 0xf);
-
- if ((vtype == VDIR) && (iflags & ACE_DIRECTORY_INHERIT_ACE))
- return (1);
- else if (iflags & ACE_FILE_INHERIT_ACE)
- return (!((vtype == VDIR) &&
- (iflags & ACE_NO_PROPAGATE_INHERIT_ACE)));
- return (0);
+ if (aclp != setaclp)
+ zfs_acl_free(aclp);
}
#ifdef TODO
@@ -1101,42 +1914,89 @@ zfs_ace_can_use(znode_t *zp, ace_t *acep)
* Retrieve a files ACL
*/
int
-zfs_getacl(znode_t *zp, vsecattr_t *vsecp, cred_t *cr)
+zfs_getacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr)
{
zfs_acl_t *aclp;
- ulong_t mask = vsecp->vsa_mask & (VSA_ACE | VSA_ACECNT);
+ ulong_t mask;
int error;
+ int count = 0;
+ int largeace = 0;
- if (error = zfs_zaccess(zp, ACE_READ_ACL, cr)) {
- /*
- * If owner of file then allow reading of the
- * ACL.
- */
- if (crgetuid(cr) != zp->z_phys->zp_uid)
- return (error);
- }
+ mask = vsecp->vsa_mask & (VSA_ACE | VSA_ACECNT |
+ VSA_ACE_ACLFLAGS | VSA_ACE_ALLTYPES);
+
+ if (error = zfs_zaccess(zp, ACE_READ_ACL, 0, skipaclchk, cr))
+ return (error);
if (mask == 0)
return (ENOSYS);
mutex_enter(&zp->z_acl_lock);
- error = zfs_acl_node_read(zp, &aclp);
+ error = zfs_acl_node_read(zp, &aclp, B_FALSE);
if (error != 0) {
mutex_exit(&zp->z_acl_lock);
return (error);
}
+ /*
+ * Scan ACL to determine number of ACEs
+ */
+ if ((zp->z_phys->zp_flags & ZFS_ACL_OBJ_ACE) &&
+ !(mask & VSA_ACE_ALLTYPES)) {
+ void *zacep = NULL;
+ uint64_t who;
+ uint32_t access_mask;
+ uint16_t type, iflags;
+
+ while (zacep = zfs_acl_next_ace(aclp, zacep,
+ &who, &access_mask, &iflags, &type)) {
+ switch (type) {
+ case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
+ case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
+ case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
+ case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
+ largeace++;
+ continue;
+ default:
+ count++;
+ }
+ }
+ vsecp->vsa_aclcnt = count;
+ } else
+ count = aclp->z_acl_count;
if (mask & VSA_ACECNT) {
- vsecp->vsa_aclcnt = aclp->z_acl_count;
+ vsecp->vsa_aclcnt = count;
}
if (mask & VSA_ACE) {
- vsecp->vsa_aclentp = kmem_alloc(aclp->z_acl_count *
- sizeof (ace_t), KM_SLEEP);
- bcopy(aclp->z_acl, vsecp->vsa_aclentp,
- aclp->z_acl_count * sizeof (ace_t));
+ size_t aclsz;
+
+ zfs_acl_node_t *aclnode = list_head(&aclp->z_acl);
+
+ aclsz = count * sizeof (ace_t) +
+ sizeof (ace_object_t) * largeace;
+
+ vsecp->vsa_aclentp = kmem_alloc(aclsz, KM_SLEEP);
+ vsecp->vsa_aclentsz = aclsz;
+
+ if (aclp->z_version == ZFS_ACL_VERSION_FUID)
+ zfs_copy_fuid_2_ace(zp->z_zfsvfs, aclp, cr,
+ vsecp->vsa_aclentp, !(mask & VSA_ACE_ALLTYPES));
+ else {
+ bcopy(aclnode->z_acldata, vsecp->vsa_aclentp,
+ count * sizeof (ace_t));
+ }
+ }
+ if (mask & VSA_ACE_ACLFLAGS) {
+ vsecp->vsa_aclflags = 0;
+ if (zp->z_phys->zp_flags & ZFS_ACL_DEFAULTED)
+ vsecp->vsa_aclflags |= ACL_DEFAULTED;
+ if (zp->z_phys->zp_flags & ZFS_ACL_PROTECTED)
+ vsecp->vsa_aclflags |= ACL_PROTECTED;
+ if (zp->z_phys->zp_flags & ZFS_ACL_AUTO_INHERIT)
+ vsecp->vsa_aclflags |= ACL_AUTO_INHERIT;
}
mutex_exit(&zp->z_acl_lock);
@@ -1147,37 +2007,100 @@ zfs_getacl(znode_t *zp, vsecattr_t *vsecp, cred_t *cr)
}
#endif /* TODO */
+int
+zfs_vsec_2_aclp(zfsvfs_t *zfsvfs, vtype_t obj_type,
+ vsecattr_t *vsecp, zfs_acl_t **zaclp)
+{
+ zfs_acl_t *aclp;
+ zfs_acl_node_t *aclnode;
+ int aclcnt = vsecp->vsa_aclcnt;
+ int error;
+
+ if (vsecp->vsa_aclcnt > MAX_ACL_ENTRIES || vsecp->vsa_aclcnt <= 0)
+ return (EINVAL);
+
+ aclp = zfs_acl_alloc(zfs_acl_version(zfsvfs->z_version));
+
+ aclp->z_hints = 0;
+ aclnode = zfs_acl_node_alloc(aclcnt * sizeof (zfs_object_ace_t));
+ if (aclp->z_version == ZFS_ACL_VERSION_INITIAL) {
+ if ((error = zfs_copy_ace_2_oldace(obj_type, aclp,
+ (ace_t *)vsecp->vsa_aclentp, aclnode->z_acldata,
+ aclcnt, &aclnode->z_size)) != 0) {
+ zfs_acl_free(aclp);
+ zfs_acl_node_free(aclnode);
+ return (error);
+ }
+ } else {
+ if ((error = zfs_copy_ace_2_fuid(obj_type, aclp,
+ vsecp->vsa_aclentp, aclnode->z_acldata, aclcnt,
+ &aclnode->z_size)) != 0) {
+ zfs_acl_free(aclp);
+ zfs_acl_node_free(aclnode);
+ return (error);
+ }
+ }
+ aclp->z_acl_bytes = aclnode->z_size;
+ aclnode->z_ace_count = aclcnt;
+ aclp->z_acl_count = aclcnt;
+ list_insert_head(&aclp->z_acl, aclnode);
+
+ /*
+ * If flags are being set then add them to z_hints
+ */
+ if (vsecp->vsa_mask & VSA_ACE_ACLFLAGS) {
+ if (vsecp->vsa_aclflags & ACL_PROTECTED)
+ aclp->z_hints |= ZFS_ACL_PROTECTED;
+ if (vsecp->vsa_aclflags & ACL_DEFAULTED)
+ aclp->z_hints |= ZFS_ACL_DEFAULTED;
+ if (vsecp->vsa_aclflags & ACL_AUTO_INHERIT)
+ aclp->z_hints |= ZFS_ACL_AUTO_INHERIT;
+ }
+
+ *zaclp = aclp;
+
+ return (0);
+}
+
#ifdef TODO
/*
* Set a files ACL
*/
int
-zfs_setacl(znode_t *zp, vsecattr_t *vsecp, cred_t *cr)
+zfs_setacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr)
{
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
zilog_t *zilog = zfsvfs->z_log;
- ace_t *acep = vsecp->vsa_aclentp;
- int aclcnt = vsecp->vsa_aclcnt;
ulong_t mask = vsecp->vsa_mask & (VSA_ACE | VSA_ACECNT);
dmu_tx_t *tx;
int error;
- int inherit;
zfs_acl_t *aclp;
+ zfs_fuid_info_t *fuidp = NULL;
if (mask == 0)
- return (EINVAL);
+ return (ENOSYS);
- if (!zfs_acl_valid(zp, acep, aclcnt, &inherit))
- return (EINVAL);
+ if (zp->z_phys->zp_flags & ZFS_IMMUTABLE)
+ return (EPERM);
+
+ if (error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr))
+ return (error);
+
+ error = zfs_vsec_2_aclp(zfsvfs, ZTOV(zp)->v_type, vsecp, &aclp);
+ if (error)
+ return (error);
+
+ /*
+ * If ACL wide flags aren't being set then preserve any
+ * existing flags.
+ */
+ if (!(vsecp->vsa_mask & VSA_ACE_ACLFLAGS)) {
+ aclp->z_hints |= (zp->z_phys->zp_flags & V4_ACL_WIDE_FLAGS);
+ }
top:
- error = zfs_zaccess_v4_perm(zp, ACE_WRITE_ACL, cr);
- if (error == EACCES || error == ACCESS_UNDETERMINED) {
- if ((error = secpolicy_vnode_setdac(cr,
- zp->z_phys->zp_uid)) != 0) {
- return (error);
- }
- } else if (error) {
- return (error == EROFS ? error : EPERM);
+ if (error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr)) {
+ zfs_acl_free(aclp);
+ return (error);
}
mutex_enter(&zp->z_lock);
@@ -1187,10 +2110,34 @@ top:
dmu_tx_hold_bonus(tx, zp->z_id);
if (zp->z_phys->zp_acl.z_acl_extern_obj) {
- dmu_tx_hold_write(tx, zp->z_phys->zp_acl.z_acl_extern_obj,
- 0, ZFS_ACL_SIZE(aclcnt));
- } else if (aclcnt > ACE_SLOT_CNT) {
- dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, ZFS_ACL_SIZE(aclcnt));
+ /* Are we upgrading ACL? */
+ if (zfsvfs->z_version <= ZPL_VERSION_FUID &&
+ zp->z_phys->zp_acl.z_acl_version ==
+ ZFS_ACL_VERSION_INITIAL) {
+ dmu_tx_hold_free(tx,
+ zp->z_phys->zp_acl.z_acl_extern_obj,
+ 0, DMU_OBJECT_END);
+ dmu_tx_hold_write(tx, DMU_NEW_OBJECT,
+ 0, aclp->z_acl_bytes);
+ } else {
+ dmu_tx_hold_write(tx,
+ zp->z_phys->zp_acl.z_acl_extern_obj,
+ 0, aclp->z_acl_bytes);
+ }
+ } else if (aclp->z_acl_bytes > ZFS_ACE_SPACE) {
+ dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, aclp->z_acl_bytes);
+ }
+ if (aclp->z_has_fuids) {
+ if (zfsvfs->z_fuid_obj == 0) {
+ dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT);
+ dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0,
+ FUID_SIZE_ESTIMATE(zfsvfs));
+ dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, FALSE, NULL);
+ } else {
+ dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj);
+ dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0,
+ FUID_SIZE_ESTIMATE(zfsvfs));
+ }
}
error = dmu_tx_assign(tx, zfsvfs->z_assign);
@@ -1204,17 +2151,18 @@ top:
goto top;
}
dmu_tx_abort(tx);
+ zfs_acl_free(aclp);
return (error);
}
- aclp = zfs_acl_alloc(aclcnt);
- bcopy(acep, aclp->z_acl, sizeof (ace_t) * aclcnt);
- aclp->z_acl_count = aclcnt;
- error = zfs_aclset_common(zp, aclp, tx, &inherit);
+ error = zfs_aclset_common(zp, aclp, cr, &fuidp, tx);
ASSERT(error == 0);
+ zfs_log_acl(zilog, tx, zp, vsecp, fuidp);
+
+ if (fuidp)
+ zfs_fuid_info_free(fuidp);
zfs_acl_free(aclp);
- zfs_log_acl(zilog, tx, TX_ACL, zp, aclcnt, acep);
dmu_tx_commit(tx);
done:
mutex_exit(&zp->z_acl_lock);
@@ -1224,46 +2172,34 @@ done:
}
#endif /* TODO */
+/*
+ * working_mode returns the permissions that were not granted
+ */
static int
-zfs_ace_access(ace_t *zacep, int *working_mode)
-{
- if (*working_mode == 0) {
- return (0);
- }
-
- if (zacep->a_access_mask & *working_mode) {
- if (zacep->a_type == ALLOW) {
- *working_mode &=
- ~(*working_mode & zacep->a_access_mask);
- if (*working_mode == 0)
- return (0);
- } else if (zacep->a_type == DENY) {
- return (EACCES);
- }
- }
-
- /*
- * haven't been specifcally denied at this point
- * so return UNDETERMINED.
- */
-
- return (ACCESS_UNDETERMINED);
-}
-
-
-static int
-zfs_zaccess_common(znode_t *zp, int v4_mode, int *working_mode, cred_t *cr)
+zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
+ boolean_t *check_privs, boolean_t skipaclchk, cred_t *cr)
{
zfs_acl_t *aclp;
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
- ace_t *zacep;
- gid_t gid;
- int cnt;
- int i;
int error;
- int access_deny = ACCESS_UNDETERMINED;
- uint_t entry_type;
uid_t uid = crgetuid(cr);
+ uint64_t who;
+ uint16_t type, iflags;
+ uint16_t entry_type;
+ uint32_t access_mask;
+ uint32_t deny_mask = 0;
+ zfs_ace_hdr_t *acep = NULL;
+ boolean_t checkit;
+ uid_t fowner;
+ uid_t gowner;
+
+ /*
+ * Short circuit empty requests
+ */
+ if (v4_mode == 0)
+ return (0);
+
+ *check_privs = B_TRUE;
if (zfsvfs->z_assign >= TXG_INITIAL) { /* ZIL replay */
*working_mode = 0;
@@ -1275,93 +2211,155 @@ zfs_zaccess_common(znode_t *zp, int v4_mode, int *working_mode, cred_t *cr)
if ((v4_mode & WRITE_MASK) &&
(zp->z_zfsvfs->z_vfs->vfs_flag & VFS_RDONLY) &&
(!IS_DEVVP(ZTOV(zp)))) {
+ *check_privs = B_FALSE;
return (EROFS);
}
+ /*
+ * Only check for READONLY on non-directories.
+ */
+ if ((v4_mode & WRITE_MASK_DATA) &&
+ (((ZTOV(zp)->v_type != VDIR) &&
+ (zp->z_phys->zp_flags & (ZFS_READONLY | ZFS_IMMUTABLE))) ||
+ (ZTOV(zp)->v_type == VDIR &&
+ (zp->z_phys->zp_flags & ZFS_IMMUTABLE)))) {
+ *check_privs = B_FALSE;
+ return (EPERM);
+ }
+
+ if ((v4_mode & (ACE_DELETE | ACE_DELETE_CHILD)) &&
+ (zp->z_phys->zp_flags & ZFS_NOUNLINK)) {
+ *check_privs = B_FALSE;
+ return (EPERM);
+ }
+
+ if (((v4_mode & (ACE_READ_DATA|ACE_EXECUTE)) &&
+ (zp->z_phys->zp_flags & ZFS_AV_QUARANTINED))) {
+ *check_privs = B_FALSE;
+ return (EACCES);
+ }
+
+ /*
+ * The caller requested that the ACL check be skipped. This
+ * would only happen if the caller checked VOP_ACCESS() with a
+ * 32 bit ACE mask and already had the appropriate permissions.
+ */
+ if (skipaclchk) {
+ *working_mode = 0;
+ return (0);
+ }
+
+ zfs_fuid_map_ids(zp, cr, &fowner, &gowner);
+
mutex_enter(&zp->z_acl_lock);
- error = zfs_acl_node_read(zp, &aclp);
+ error = zfs_acl_node_read(zp, &aclp, B_FALSE);
if (error != 0) {
mutex_exit(&zp->z_acl_lock);
return (error);
}
+ while (acep = zfs_acl_next_ace(aclp, acep, &who, &access_mask,
+ &iflags, &type)) {
- zacep = aclp->z_acl;
- cnt = aclp->z_acl_count;
+ if (!zfs_acl_valid_ace_type(type, iflags))
+ continue;
- for (i = 0; i != cnt; i++) {
+ if (ZTOV(zp)->v_type == VDIR && (iflags & ACE_INHERIT_ONLY_ACE))
+ continue;
- DTRACE_PROBE2(zfs__access__common,
- ace_t *, &zacep[i], int, *working_mode);
+ entry_type = (iflags & ACE_TYPE_FLAGS);
- if (zacep[i].a_flags & ACE_INHERIT_ONLY_ACE)
- continue;
+ checkit = B_FALSE;
- entry_type = (zacep[i].a_flags & ACE_TYPE_FLAGS);
switch (entry_type) {
case ACE_OWNER:
- if (uid == zp->z_phys->zp_uid) {
- access_deny = zfs_ace_access(&zacep[i],
- working_mode);
- }
+ if (uid == fowner)
+ checkit = B_TRUE;
break;
- case (ACE_IDENTIFIER_GROUP | ACE_GROUP):
+ case OWNING_GROUP:
+ who = gowner;
+ /*FALLTHROUGH*/
case ACE_IDENTIFIER_GROUP:
- /*
- * Owning group gid is in znode not ACL
- */
- if (entry_type == (ACE_IDENTIFIER_GROUP | ACE_GROUP))
- gid = zp->z_phys->zp_gid;
- else
- gid = zacep[i].a_who;
-
- if (groupmember(gid, cr)) {
- access_deny = zfs_ace_access(&zacep[i],
- working_mode);
- }
+ checkit = zfs_groupmember(zfsvfs, who, cr);
break;
case ACE_EVERYONE:
- access_deny = zfs_ace_access(&zacep[i], working_mode);
+ checkit = B_TRUE;
break;
/* USER Entry */
default:
if (entry_type == 0) {
- if (uid == zacep[i].a_who) {
- access_deny = zfs_ace_access(&zacep[i],
- working_mode);
- }
+ uid_t newid;
+
+ newid = zfs_fuid_map_id(zfsvfs, who, cr,
+ ZFS_ACE_USER);
+ if (newid != IDMAP_WK_CREATOR_OWNER_UID &&
+ uid == newid)
+ checkit = B_TRUE;
break;
+ } else {
+ zfs_acl_free(aclp);
+ mutex_exit(&zp->z_acl_lock);
+ return (EIO);
+ }
+ }
+
+ if (checkit) {
+ uint32_t mask_matched = (access_mask & *working_mode);
+
+ if (mask_matched) {
+ if (type == DENY)
+ deny_mask |= mask_matched;
+
+ *working_mode &= ~mask_matched;
}
- zfs_acl_free(aclp);
- mutex_exit(&zp->z_acl_lock);
- return (EIO);
}
- if (access_deny != ACCESS_UNDETERMINED)
+ /* Are we done? */
+ if (*working_mode == 0)
break;
}
mutex_exit(&zp->z_acl_lock);
zfs_acl_free(aclp);
- return (access_deny);
+ /* Put the found 'denies' back on the working mode */
+ if (deny_mask) {
+ *working_mode |= deny_mask;
+ return (EACCES);
+ } else if (*working_mode) {
+ return (-1);
+ }
+
+ return (0);
}
+static int
+zfs_zaccess_append(znode_t *zp, uint32_t *working_mode, boolean_t *check_privs,
+ cred_t *cr)
+{
+ if (*working_mode != ACE_WRITE_DATA)
+ return (EACCES);
+
+ return (zfs_zaccess_common(zp, ACE_APPEND_DATA, working_mode,
+ check_privs, B_FALSE, cr));
+}
/*
* Determine whether Access should be granted/denied, invoking least
* priv subsytem when a deny is determined.
*/
int
-zfs_zaccess(znode_t *zp, int mode, cred_t *cr)
+zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr)
{
- int working_mode;
- int error;
- int is_attr;
- znode_t *xzp;
- znode_t *check_zp = zp;
+ uint32_t working_mode;
+ int error;
+ int is_attr;
+ zfsvfs_t *zfsvfs = zp->z_zfsvfs;
+ boolean_t check_privs;
+ znode_t *xzp;
+ znode_t *check_zp = zp;
is_attr = ((zp->z_phys->zp_flags & ZFS_XATTR) &&
(ZTOV(zp)->v_type == VDIR));
@@ -1374,7 +2372,9 @@ zfs_zaccess(znode_t *zp, int mode, cred_t *cr)
zp->z_phys->zp_parent, &xzp)) != 0) {
return (error);
}
+
check_zp = xzp;
+
/*
* fixup mode to map to xattr perms
*/
@@ -1390,18 +2390,76 @@ zfs_zaccess(znode_t *zp, int mode, cred_t *cr)
}
}
- error = zfs_zaccess_common(check_zp, mode, &working_mode, cr);
+ if ((error = zfs_zaccess_common(check_zp, mode, &working_mode,
+ &check_privs, skipaclchk, cr)) == 0) {
+ if (is_attr)
+ VN_RELE(ZTOV(xzp));
+ return (0);
+ }
- if (error == EROFS) {
+ if (error && !check_privs) {
if (is_attr)
VN_RELE(ZTOV(xzp));
return (error);
}
- if (error || working_mode) {
- working_mode = (zfs_v4_to_unix(working_mode) << 6);
- error = secpolicy_vnode_access(cr, ZTOV(check_zp),
- check_zp->z_phys->zp_uid, working_mode);
+ if (error && (flags & V_APPEND)) {
+ error = zfs_zaccess_append(zp, &working_mode, &check_privs, cr);
+ }
+
+ if (error && check_privs) {
+ uid_t owner;
+ mode_t checkmode = 0;
+
+ owner = zfs_fuid_map_id(zfsvfs, check_zp->z_phys->zp_uid, cr,
+ ZFS_OWNER);
+
+ /*
+ * First check for implicit owner permission on
+ * read_acl/read_attributes
+ */
+
+ error = 0;
+ ASSERT(working_mode != 0);
+
+ if ((working_mode & (ACE_READ_ACL|ACE_READ_ATTRIBUTES) &&
+ owner == crgetuid(cr)))
+ working_mode &= ~(ACE_READ_ACL|ACE_READ_ATTRIBUTES);
+
+ if (working_mode & (ACE_READ_DATA|ACE_READ_NAMED_ATTRS|
+ ACE_READ_ACL|ACE_READ_ATTRIBUTES|ACE_SYNCHRONIZE))
+ checkmode |= VREAD;
+ if (working_mode & (ACE_WRITE_DATA|ACE_WRITE_NAMED_ATTRS|
+ ACE_APPEND_DATA|ACE_WRITE_ATTRIBUTES|ACE_SYNCHRONIZE))
+ checkmode |= VWRITE;
+ if (working_mode & ACE_EXECUTE)
+ checkmode |= VEXEC;
+
+ if (checkmode)
+ error = secpolicy_vnode_access(cr, ZTOV(check_zp),
+ owner, checkmode);
+
+ if (error == 0 && (working_mode & ACE_WRITE_OWNER))
+ error = secpolicy_vnode_chown(ZTOV(check_zp), cr, B_TRUE);
+ if (error == 0 && (working_mode & ACE_WRITE_ACL))
+ error = secpolicy_vnode_setdac(ZTOV(check_zp), cr, owner);
+
+ if (error == 0 && (working_mode &
+ (ACE_DELETE|ACE_DELETE_CHILD)))
+ error = secpolicy_vnode_remove(ZTOV(check_zp), cr);
+
+ if (error == 0 && (working_mode & ACE_SYNCHRONIZE)) {
+ error = secpolicy_vnode_chown(ZTOV(check_zp), cr, B_FALSE);
+ }
+ if (error == 0) {
+ /*
+ * See if any bits other than those already checked
+ * for are still present. If so then return EACCES
+ */
+ if (working_mode & ~(ZFS_CHECKED_MASKS)) {
+ error = EACCES;
+ }
+ }
}
if (is_attr)
@@ -1411,38 +2469,37 @@ zfs_zaccess(znode_t *zp, int mode, cred_t *cr)
}
/*
- * Special zaccess function to check for special nfsv4 perm.
- * doesn't call secpolicy_vnode_access() for failure, since that
- * would probably be the wrong policy function to call.
- * instead its up to the caller to handle that situation.
+ * Translate traditional unix VREAD/VWRITE/VEXEC mode into
+ * native ACL format and call zfs_zaccess()
*/
-
int
-zfs_zaccess_v4_perm(znode_t *zp, int mode, cred_t *cr)
+zfs_zaccess_rwx(znode_t *zp, mode_t mode, int flags, cred_t *cr)
{
- int working_mode = 0;
- return (zfs_zaccess_common(zp, mode, &working_mode, cr));
+ return (zfs_zaccess(zp, zfs_unix_to_v4(mode >> 6), flags, B_FALSE, cr));
}
/*
- * Translate tradition unix VREAD/VWRITE/VEXEC mode into
- * native ACL format and call zfs_zaccess()
+ * Access function for secpolicy_vnode_setattr
*/
int
-zfs_zaccess_rwx(znode_t *zp, mode_t mode, cred_t *cr)
+zfs_zaccess_unix(znode_t *zp, mode_t mode, cred_t *cr)
{
int v4_mode = zfs_unix_to_v4(mode >> 6);
- return (zfs_zaccess(zp, v4_mode, cr));
+ return (zfs_zaccess(zp, v4_mode, 0, B_FALSE, cr));
}
static int
-zfs_delete_final_check(znode_t *zp, znode_t *dzp, cred_t *cr)
+zfs_delete_final_check(znode_t *zp, znode_t *dzp,
+ mode_t missing_perms, cred_t *cr)
{
int error;
+ uid_t downer;
+ zfsvfs_t *zfsvfs = zp->z_zfsvfs;
- error = secpolicy_vnode_access(cr, ZTOV(zp),
- dzp->z_phys->zp_uid, S_IWRITE|S_IEXEC);
+ downer = zfs_fuid_map_id(zfsvfs, dzp->z_phys->zp_uid, cr, ZFS_OWNER);
+
+ error = secpolicy_vnode_access(cr, ZTOV(dzp), downer, missing_perms);
if (error == 0)
error = zfs_sticky_remove_access(dzp, zp, cr);
@@ -1488,83 +2545,88 @@ zfs_delete_final_check(znode_t *zp, znode_t *dzp, cred_t *cr)
int
zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr)
{
- int dzp_working_mode = 0;
- int zp_working_mode = 0;
+ uint32_t dzp_working_mode = 0;
+ uint32_t zp_working_mode = 0;
int dzp_error, zp_error;
+ mode_t missing_perms;
+ boolean_t dzpcheck_privs = B_TRUE;
+ boolean_t zpcheck_privs = B_TRUE;
/*
- * Arghh, this check is going to require a couple of questions
- * to be asked. We want specific DELETE permissions to
+ * We want specific DELETE permissions to
* take precedence over WRITE/EXECUTE. We don't
* want an ACL such as this to mess us up.
* user:joe:write_data:deny,user:joe:delete:allow
*
* However, deny permissions may ultimately be overridden
* by secpolicy_vnode_access().
+ *
+ * We will ask for all of the necessary permissions and then
+ * look at the working modes from the directory and target object
+ * to determine what was found.
*/
- dzp_error = zfs_zaccess_common(dzp, ACE_DELETE_CHILD,
- &dzp_working_mode, cr);
- zp_error = zfs_zaccess_common(zp, ACE_DELETE, &zp_working_mode, cr);
-
- if (dzp_error == EROFS || zp_error == EROFS)
- return (dzp_error);
+ if (zp->z_phys->zp_flags & (ZFS_IMMUTABLE | ZFS_NOUNLINK))
+ return (EPERM);
/*
- * First check the first row.
- * We only need to see if parent Allows delete_child
+ * First row
+ * If the directory permissions allow the delete, we are done.
*/
- if ((dzp_working_mode & ACE_DELETE_CHILD) == 0)
+ if ((dzp_error = zfs_zaccess_common(dzp, ACE_DELETE_CHILD,
+ &dzp_working_mode, &dzpcheck_privs, B_FALSE, cr)) == 0)
return (0);
/*
- * Second row
- * we already have the necessary information in
- * zp_working_mode, zp_error and dzp_error.
+ * If target object has delete permission then we are done
*/
-
- if ((zp_working_mode & ACE_DELETE) == 0)
+ if ((zp_error = zfs_zaccess_common(zp, ACE_DELETE, &zp_working_mode,
+ &zpcheck_privs, B_FALSE, cr)) == 0)
return (0);
+ ASSERT(dzp_error && zp_error);
+
+ if (!dzpcheck_privs)
+ return (dzp_error);
+ if (!zpcheck_privs)
+ return (zp_error);
+
/*
- * Now zp_error should either be EACCES which indicates
- * a "deny" delete entry or ACCESS_UNDETERMINED if the "delete"
- * entry exists on the target.
+ * Second row
*
- * dzp_error should be either EACCES which indicates a "deny"
- * entry for delete_child or ACCESS_UNDETERMINED if no delete_child
- * entry exists. If value is EACCES then we are done
- * and zfs_delete_final_check() will make the final decision
- * regarding to allow the delete.
+ * If directory returns EACCES then delete_child was denied
+ * due to deny delete_child. In this case send the request through
+ * secpolicy_vnode_remove(). We don't use zfs_delete_final_check()
+ * since that *could* allow the delete based on write/execute permission
+ * and we want delete permissions to override write/execute.
*/
- ASSERT(zp_error != 0 && dzp_error != 0);
if (dzp_error == EACCES)
- return (zfs_delete_final_check(zp, dzp, cr));
+ return (secpolicy_vnode_remove(ZTOV(dzp), cr)); /* XXXPJD: s/dzp/zp/ ? */
/*
* Third Row
- * Only need to check for write/execute on parent
+ * only need to see if we have write/execute on directory.
*/
- dzp_error = zfs_zaccess_common(dzp, ACE_WRITE_DATA|ACE_EXECUTE,
- &dzp_working_mode, cr);
+ if ((dzp_error = zfs_zaccess_common(dzp, ACE_EXECUTE|ACE_WRITE_DATA,
+ &dzp_working_mode, &dzpcheck_privs, B_FALSE, cr)) == 0)
+ return (zfs_sticky_remove_access(dzp, zp, cr));
- if (dzp_error == EROFS)
+ if (!dzpcheck_privs)
return (dzp_error);
- if ((dzp_working_mode & (ACE_WRITE_DATA|ACE_EXECUTE)) == 0)
- return (zfs_sticky_remove_access(dzp, zp, cr));
-
/*
- * Fourth Row
+ * Fourth row
*/
- if (((dzp_working_mode & (ACE_WRITE_DATA|ACE_EXECUTE)) != 0) &&
- ((zp_working_mode & ACE_DELETE) == 0))
- return (zfs_sticky_remove_access(dzp, zp, cr));
+ missing_perms = (dzp_working_mode & ACE_WRITE_DATA) ? VWRITE : 0;
+ missing_perms |= (dzp_working_mode & ACE_EXECUTE) ? VEXEC : 0;
+
+ ASSERT(missing_perms);
+
+ return (zfs_delete_final_check(zp, dzp, missing_perms, cr));
- return (zfs_delete_final_check(zp, dzp, cr));
}
int
@@ -1574,6 +2636,9 @@ zfs_zaccess_rename(znode_t *sdzp, znode_t *szp, znode_t *tdzp,
int add_perm;
int error;
+ if (szp->z_phys->zp_flags & ZFS_AV_QUARANTINED)
+ return (EACCES);
+
add_perm = (ZTOV(szp)->v_type == VDIR) ?
ACE_ADD_SUBDIRECTORY : ACE_ADD_FILE;
@@ -1586,7 +2651,7 @@ zfs_zaccess_rename(znode_t *sdzp, znode_t *szp, znode_t *tdzp,
* to another.
*/
if (ZTOV(szp)->v_type == VDIR && ZTOV(sdzp) != ZTOV(tdzp)) {
- if (error = zfs_zaccess(szp, ACE_WRITE_DATA, cr))
+ if (error = zfs_zaccess(szp, ACE_WRITE_DATA, 0, B_FALSE, cr))
return (error);
}
@@ -1610,7 +2675,7 @@ zfs_zaccess_rename(znode_t *sdzp, znode_t *szp, znode_t *tdzp,
/*
* Now check for add permissions
*/
- error = zfs_zaccess(tdzp, add_perm, cr);
+ error = zfs_zaccess(tdzp, add_perm, 0, B_FALSE, cr);
return (error);
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_byteswap.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_byteswap.c
index c8450d4..b6c43f4 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_byteswap.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_byteswap.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -33,7 +32,7 @@
#include <sys/zfs_acl.h>
void
-zfs_ace_byteswap(ace_t *ace, int ace_cnt)
+zfs_oldace_byteswap(ace_t *ace, int ace_cnt)
{
int i;
@@ -45,9 +44,78 @@ zfs_ace_byteswap(ace_t *ace, int ace_cnt)
}
}
+/*
+ * swap ace_t and ace_oject_t
+ */
+void
+zfs_ace_byteswap(void *buf, size_t size, boolean_t zfs_layout)
+{
+#ifdef TODO
+ caddr_t end;
+ caddr_t ptr;
+ zfs_ace_t *zacep;
+ ace_t *acep;
+ uint16_t entry_type;
+ size_t entry_size;
+ int ace_type;
+
+ end = (caddr_t)buf + size;
+ ptr = buf;
+
+ while (ptr < end) {
+ if (zfs_layout) {
+ zacep = (zfs_ace_t *)ptr;
+ zacep->z_hdr.z_access_mask =
+ BSWAP_32(zacep->z_hdr.z_access_mask);
+ zacep->z_hdr.z_flags = BSWAP_16(zacep->z_hdr.z_flags);
+ ace_type = zacep->z_hdr.z_type =
+ BSWAP_16(zacep->z_hdr.z_type);
+ entry_type = zacep->z_hdr.z_flags & ACE_TYPE_FLAGS;
+ } else {
+ acep = (ace_t *)ptr;
+ acep->a_access_mask = BSWAP_32(acep->a_access_mask);
+ acep->a_flags = BSWAP_16(acep->a_flags);
+ ace_type = acep->a_type = BSWAP_16(acep->a_type);
+ acep->a_who = BSWAP_32(acep->a_who);
+ entry_type = acep->a_flags & ACE_TYPE_FLAGS;
+ }
+ switch (entry_type) {
+ case ACE_OWNER:
+ case ACE_EVERYONE:
+ case (ACE_IDENTIFIER_GROUP | ACE_GROUP):
+ entry_size = zfs_layout ?
+ sizeof (zfs_ace_hdr_t) : sizeof (ace_t);
+ break;
+ case ACE_IDENTIFIER_GROUP:
+ default:
+ if (zfs_layout) {
+ zacep->z_fuid = BSWAP_64(zacep->z_fuid);
+ }
+ switch (ace_type) {
+ case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
+ case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
+ case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
+ case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
+ entry_size = zfs_layout ?
+ sizeof (zfs_object_ace_t) :
+ sizeof (ace_object_t);
+ break;
+ default:
+ entry_size = zfs_layout ? sizeof (zfs_ace_t) :
+ sizeof (ace_t);
+ break;
+ }
+ }
+ ptr = ptr + entry_size;
+ }
+#else /* TODO */
+ panic("%s:%u: TODO", __func__, __LINE__);
+#endif /* TODO */
+}
+
/* ARGSUSED */
void
-zfs_acl_byteswap(void *buf, size_t size)
+zfs_oldacl_byteswap(void *buf, size_t size)
{
int cnt;
@@ -58,7 +126,14 @@ zfs_acl_byteswap(void *buf, size_t size)
cnt = size / sizeof (ace_t);
- zfs_ace_byteswap((ace_t *)buf, cnt);
+ zfs_oldace_byteswap((ace_t *)buf, cnt);
+}
+
+/* ARGSUSED */
+void
+zfs_acl_byteswap(void *buf, size_t size)
+{
+ zfs_ace_byteswap(buf, size, B_TRUE);
}
void
@@ -86,14 +161,19 @@ zfs_znode_byteswap(void *buf, size_t size)
zp->zp_flags = BSWAP_64(zp->zp_flags);
zp->zp_uid = BSWAP_64(zp->zp_uid);
zp->zp_gid = BSWAP_64(zp->zp_gid);
+ zp->zp_zap = BSWAP_64(zp->zp_zap);
zp->zp_pad[0] = BSWAP_64(zp->zp_pad[0]);
zp->zp_pad[1] = BSWAP_64(zp->zp_pad[1]);
zp->zp_pad[2] = BSWAP_64(zp->zp_pad[2]);
- zp->zp_pad[3] = BSWAP_64(zp->zp_pad[3]);
zp->zp_acl.z_acl_extern_obj = BSWAP_64(zp->zp_acl.z_acl_extern_obj);
- zp->zp_acl.z_acl_count = BSWAP_32(zp->zp_acl.z_acl_count);
+ zp->zp_acl.z_acl_size = BSWAP_32(zp->zp_acl.z_acl_size);
zp->zp_acl.z_acl_version = BSWAP_16(zp->zp_acl.z_acl_version);
- zp->zp_acl.z_acl_pad = BSWAP_16(zp->zp_acl.z_acl_pad);
- zfs_ace_byteswap(&zp->zp_acl.z_ace_data[0], ACE_SLOT_CNT);
+ zp->zp_acl.z_acl_count = BSWAP_16(zp->zp_acl.z_acl_count);
+ if (zp->zp_acl.z_acl_version == ZFS_ACL_VERSION) {
+ zfs_acl_byteswap((void *)&zp->zp_acl.z_ace_data[0],
+ ZFS_ACE_SPACE);
+ } else
+ zfs_oldace_byteswap((ace_t *)&zp->zp_acl.z_ace_data[0],
+ ACE_SLOT_CNT);
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ctldir.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ctldir.c
index 286fe97..654d2f9 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ctldir.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ctldir.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -53,6 +53,17 @@
* reliable way to auto-unmount the filesystem when it's "no longer in use".
* When the user unmounts a filesystem, we call zfsctl_unmount(), which
* unmounts any snapshots within the snapshot directory.
+ *
+ * The '.zfs', '.zfs/snapshot', and all directories created under
+ * '.zfs/snapshot' (ie: '.zfs/snapshot/<snapname>') are all GFS nodes and
+ * share the same vfs_t as the head filesystem (what '.zfs' lives under).
+ *
+ * File systems mounted ontop of the GFS nodes '.zfs/snapshot/<snapname>'
+ * (ie: snapshots) are ZFS nodes and have their own unique vfs_t.
+ * However, vnodes within these mounted on file systems have their v_vfsp
+ * fields set to the head filesystem to make NFS happy (see
+ * zfsctl_snapdir_lookup()). We VFS_HOLD the head filesystem's vfs_t
+ * so that it cannot be freed until all snapshots have been unmounted.
*/
#include <sys/zfs_context.h>
@@ -63,7 +74,23 @@
#include <sys/gfs.h>
#include <sys/stat.h>
#include <sys/dmu.h>
+#include <sys/dsl_deleg.h>
#include <sys/mount.h>
+#include <sys/sunddi.h>
+
+#include "zfs_namecheck.h"
+
+typedef struct zfsctl_node {
+ gfs_dir_t zc_gfs_private;
+ uint64_t zc_id;
+ timestruc_t zc_cmtime; /* ctime and mtime, always the same */
+} zfsctl_node_t;
+
+typedef struct zfsctl_snapdir {
+ zfsctl_node_t sd_node;
+ kmutex_t sd_lock;
+ avl_tree_t sd_snaps;
+} zfsctl_snapdir_t;
typedef struct {
char *se_name;
@@ -92,18 +119,7 @@ static struct vop_vector zfsctl_ops_snapshot;
static vnode_t *zfsctl_mknode_snapdir(vnode_t *);
static vnode_t *zfsctl_snapshot_mknode(vnode_t *, uint64_t objset);
-
-typedef struct zfsctl_node {
- gfs_dir_t zc_gfs_private;
- uint64_t zc_id;
- timestruc_t zc_cmtime; /* ctime and mtime, always the same */
-} zfsctl_node_t;
-
-typedef struct zfsctl_snapdir {
- zfsctl_node_t sd_node;
- kmutex_t sd_lock;
- avl_tree_t sd_snaps;
-} zfsctl_snapdir_t;
+static int zfsctl_unmount_snap(zfs_snapentry_t *, int, cred_t *);
/*
* Root directory elements. We have only a single static entry, 'snapshot'.
@@ -237,14 +253,14 @@ static int
zfsctl_common_access(ap)
struct vop_access_args /* {
struct vnode *a_vp;
- accmode_t a_accmode;
+ int a_accmode;
struct ucred *a_cred;
struct thread *a_td;
} */ *ap;
{
- accmode_t accmode = ap->a_accmode;
+ int mode = ap->a_accmode;
- if (accmode & VWRITE)
+ if (mode & VWRITE)
return (EACCES);
return (0);
@@ -283,6 +299,7 @@ zfsctl_common_getattr(vnode_t *vp, vattr_t *vap)
vap->va_flags = 0;
}
+/*ARGSUSED*/
static int
zfsctl_common_fid(ap)
struct vop_fid_args /* {
@@ -360,6 +377,7 @@ zfsctl_root_getattr(ap)
struct vnode *a_vp;
struct vattr *a_vap;
struct ucred *a_cred;
+ struct thread *a_td;
} */ *ap;
{
struct vnode *vp = ap->a_vp;
@@ -382,11 +400,18 @@ zfsctl_root_getattr(ap)
/* ARGSUSED */
int
zfsctl_root_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp,
- int flags, vnode_t *rdir, cred_t *cr)
+ int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
+ int *direntflags, pathname_t *realpnp)
{
zfsvfs_t *zfsvfs = dvp->v_vfsp->vfs_data;
int err;
+ /*
+ * No extended attributes allowed under .zfs
+ */
+ if (flags & LOOKUP_XATTR)
+ return (EINVAL);
+
ZFS_ENTER(zfsvfs);
if (strcmp(nm, "..") == 0) {
@@ -394,7 +419,8 @@ zfsctl_root_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp,
if (err == 0)
VOP_UNLOCK(*vpp, 0);
} else {
- err = gfs_dir_lookup(dvp, nm, vpp);
+ err = gfs_vop_lookup(dvp, nm, vpp, pnp, flags, rdir,
+ cr, ct, direntflags, realpnp);
}
ZFS_EXIT(zfsvfs);
@@ -407,7 +433,7 @@ zfsctl_root_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp,
*/
/* ARGSUSED */
int
-zfsctl_root_lookup_vop(ap)
+zfsctl_freebsd_root_lookup(ap)
struct vop_lookup_args /* {
struct vnode *a_dvp;
struct vnode **a_vpp;
@@ -428,7 +454,7 @@ zfsctl_root_lookup_vop(ap)
ASSERT(ap->a_cnp->cn_namelen < sizeof(nm));
strlcpy(nm, ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen + 1);
- err = zfsctl_root_lookup(dvp, nm, vpp, NULL, 0, NULL, cr);
+ err = zfsctl_root_lookup(dvp, nm, vpp, NULL, 0, NULL, cr, NULL, NULL, NULL);
if (err == 0 && (nm[0] != '.' || nm[1] != '\0'))
vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY);
@@ -443,7 +469,7 @@ static struct vop_vector zfsctl_ops_root = {
.vop_getattr = zfsctl_root_getattr,
.vop_access = zfsctl_common_access,
.vop_readdir = gfs_vop_readdir,
- .vop_lookup = zfsctl_root_lookup_vop,
+ .vop_lookup = zfsctl_freebsd_root_lookup,
.vop_inactive = gfs_vop_inactive,
.vop_reclaim = zfsctl_common_reclaim,
.vop_fid = zfsctl_common_fid,
@@ -454,6 +480,8 @@ zfsctl_snapshot_zname(vnode_t *vp, const char *name, int len, char *zname)
{
objset_t *os = ((zfsvfs_t *)((vp)->v_vfsp->vfs_data))->z_os;
+ if (snapshot_namecheck(name, NULL, NULL) != 0)
+ return (EILSEQ);
dmu_objset_name(os, zname);
if (strlen(zname) + 1 + strlen(name) >= len)
return (ENAMETOOLONG);
@@ -463,38 +491,18 @@ zfsctl_snapshot_zname(vnode_t *vp, const char *name, int len, char *zname)
}
static int
-zfsctl_unmount_snap(vnode_t *dvp, const char *name, int force, cred_t *cr)
+zfsctl_unmount_snap(zfs_snapentry_t *sep, int fflags, cred_t *cr)
{
- zfsctl_snapdir_t *sdp = dvp->v_data;
- zfs_snapentry_t search, *sep;
- struct vop_inactive_args ap;
- avl_index_t where;
- int err;
-
- ASSERT(MUTEX_HELD(&sdp->sd_lock));
-
- search.se_name = (char *)name;
- if ((sep = avl_find(&sdp->sd_snaps, &search, &where)) == NULL)
- return (ENOENT);
+ vnode_t *svp = sep->se_root;
+ int error;
- ASSERT(vn_ismntpt(sep->se_root));
+ ASSERT(vn_ismntpt(svp));
/* this will be dropped by dounmount() */
- if ((err = vn_vfswlock(sep->se_root)) != 0)
- return (err);
-
- err = dounmount(vn_mountedvfs(sep->se_root), force, curthread);
- if (err)
- return (err);
- ASSERT(sep->se_root->v_count == 1);
- ap.a_vp = sep->se_root;
- gfs_vop_inactive(&ap);
-
- avl_remove(&sdp->sd_snaps, sep);
- kmem_free(sep->se_name, strlen(sep->se_name) + 1);
- kmem_free(sep, sizeof (zfs_snapentry_t));
+ if ((error = vn_vfswlock(svp)) != 0)
+ return (error);
- return (0);
+ return (dounmount(vn_mountedvfs(svp), fflags, curthread));
}
#if 0
@@ -553,20 +561,40 @@ zfsctl_rename_snap(zfsctl_snapdir_t *sdp, zfs_snapentry_t *sep, const char *nm)
#endif
#if 0
+/*ARGSUSED*/
static int
zfsctl_snapdir_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
- cred_t *cr)
+ cred_t *cr, caller_context_t *ct, int flags)
{
zfsctl_snapdir_t *sdp = sdvp->v_data;
zfs_snapentry_t search, *sep;
+ zfsvfs_t *zfsvfs;
avl_index_t where;
char from[MAXNAMELEN], to[MAXNAMELEN];
+ char real[MAXNAMELEN];
int err;
+ zfsvfs = sdvp->v_vfsp->vfs_data;
+ ZFS_ENTER(zfsvfs);
+
+ if ((flags & FIGNORECASE) || zfsvfs->z_case == ZFS_CASE_INSENSITIVE) {
+ err = dmu_snapshot_realname(zfsvfs->z_os, snm, real,
+ MAXNAMELEN, NULL);
+ if (err == 0) {
+ snm = real;
+ } else if (err != ENOTSUP) {
+ ZFS_EXIT(zfsvfs);
+ return (err);
+ }
+ }
+
+ ZFS_EXIT(zfsvfs);
+
err = zfsctl_snapshot_zname(sdvp, snm, MAXNAMELEN, from);
- if (err)
- return (err);
- err = zfs_secpolicy_write(from, cr);
+ if (!err)
+ err = zfsctl_snapshot_zname(tdvp, tnm, MAXNAMELEN, to);
+ if (!err)
+ err = zfs_secpolicy_rename_perms(from, to, cr);
if (err)
return (err);
@@ -579,10 +607,6 @@ zfsctl_snapdir_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
if (strcmp(snm, tnm) == 0)
return (0);
- err = zfsctl_snapshot_zname(tdvp, tnm, MAXNAMELEN, to);
- if (err)
- return (err);
-
mutex_enter(&sdp->sd_lock);
search.se_name = (char *)snm;
@@ -604,29 +628,55 @@ zfsctl_snapdir_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
#if 0
/* ARGSUSED */
static int
-zfsctl_snapdir_remove(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr)
+zfsctl_snapdir_remove(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr,
+ caller_context_t *ct, int flags)
{
zfsctl_snapdir_t *sdp = dvp->v_data;
+ zfs_snapentry_t *sep;
+ zfs_snapentry_t search;
+ zfsvfs_t *zfsvfs;
char snapname[MAXNAMELEN];
+ char real[MAXNAMELEN];
int err;
+ zfsvfs = dvp->v_vfsp->vfs_data;
+ ZFS_ENTER(zfsvfs);
+
+ if ((flags & FIGNORECASE) || zfsvfs->z_case == ZFS_CASE_INSENSITIVE) {
+
+ err = dmu_snapshot_realname(zfsvfs->z_os, name, real,
+ MAXNAMELEN, NULL);
+ if (err == 0) {
+ name = real;
+ } else if (err != ENOTSUP) {
+ ZFS_EXIT(zfsvfs);
+ return (err);
+ }
+ }
+
+ ZFS_EXIT(zfsvfs);
+
err = zfsctl_snapshot_zname(dvp, name, MAXNAMELEN, snapname);
- if (err)
- return (err);
- err = zfs_secpolicy_write(snapname, cr);
+ if (!err)
+ err = zfs_secpolicy_destroy_perms(snapname, cr);
if (err)
return (err);
mutex_enter(&sdp->sd_lock);
- err = zfsctl_unmount_snap(dvp, name, 0, cr);
- if (err) {
- mutex_exit(&sdp->sd_lock);
- return (err);
+ search.se_name = name;
+ sep = avl_find(&sdp->sd_snaps, &search, NULL);
+ if (sep) {
+ avl_remove(&sdp->sd_snaps, sep);
+ err = zfsctl_unmount_snap(sep, MS_FORCE, cr);
+ if (err)
+ avl_add(&sdp->sd_snaps, sep);
+ else
+ err = dmu_objset_destroy(snapname);
+ } else {
+ err = ENOENT;
}
- err = dmu_objset_destroy(snapname);
-
mutex_exit(&sdp->sd_lock);
return (err);
@@ -634,6 +684,57 @@ zfsctl_snapdir_remove(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr)
#endif
/*
+ * This creates a snapshot under '.zfs/snapshot'.
+ */
+/* ARGSUSED */
+static int
+zfsctl_snapdir_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp,
+ cred_t *cr, caller_context_t *cc, int flags, vsecattr_t *vsecp)
+{
+ zfsvfs_t *zfsvfs = dvp->v_vfsp->vfs_data;
+ char name[MAXNAMELEN];
+ int err;
+ static enum symfollow follow = NO_FOLLOW;
+ static enum uio_seg seg = UIO_SYSSPACE;
+
+ if (snapshot_namecheck(dirname, NULL, NULL) != 0)
+ return (EILSEQ);
+
+ dmu_objset_name(zfsvfs->z_os, name);
+
+ *vpp = NULL;
+
+ err = zfs_secpolicy_snapshot_perms(name, cr);
+ if (err)
+ return (err);
+
+ if (err == 0) {
+ err = dmu_objset_snapshot(name, dirname, B_FALSE);
+ if (err)
+ return (err);
+ err = lookupnameat(dirname, seg, follow, NULL, vpp, dvp);
+ }
+
+ return (err);
+}
+
+static int
+zfsctl_freebsd_snapdir_mkdir(ap)
+ struct vop_mkdir_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+
+ ASSERT(ap->a_cnp->cn_flags & SAVENAME);
+
+ return (zfsctl_snapdir_mkdir(ap->a_dvp, ap->a_cnp->cn_nameptr, NULL,
+ ap->a_vpp, ap->a_cnp->cn_cred, NULL, 0, NULL));
+}
+
+/*
* Lookup entry point for the 'snapshot' directory. Try to open the
* snapshot if it exist, creating the pseudo filesystem vnode as necessary.
* Perform a mount of the associated dataset on top of the vnode.
@@ -649,17 +750,25 @@ zfsctl_snapdir_lookup(ap)
{
vnode_t *dvp = ap->a_dvp;
vnode_t **vpp = ap->a_vpp;
+ struct componentname *cnp = ap->a_cnp;
char nm[NAME_MAX + 1];
zfsctl_snapdir_t *sdp = dvp->v_data;
objset_t *snap;
char snapname[MAXNAMELEN];
+ char real[MAXNAMELEN];
char *mountpoint;
zfs_snapentry_t *sep, search;
size_t mountpoint_len;
avl_index_t where;
zfsvfs_t *zfsvfs = dvp->v_vfsp->vfs_data;
int err;
+ int flags = 0;
+ /*
+ * No extended attributes allowed under .zfs
+ */
+ if (flags & LOOKUP_XATTR)
+ return (EINVAL);
ASSERT(ap->a_cnp->cn_namelen < sizeof(nm));
strlcpy(nm, ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen + 1);
@@ -681,6 +790,26 @@ zfsctl_snapdir_lookup(ap)
ZFS_ENTER(zfsvfs);
+ if (flags & FIGNORECASE) {
+ boolean_t conflict = B_FALSE;
+
+ err = dmu_snapshot_realname(zfsvfs->z_os, nm, real,
+ MAXNAMELEN, &conflict);
+ if (err == 0) {
+ strlcpy(nm, real, sizeof(nm));
+ } else if (err != ENOTSUP) {
+ ZFS_EXIT(zfsvfs);
+ return (err);
+ }
+#if 0
+ if (realpnp)
+ (void) strlcpy(realpnp->pn_buf, nm,
+ realpnp->pn_bufsize);
+ if (conflict && direntflags)
+ *direntflags = ED_CASE_CONFLICT;
+#endif
+ }
+
mutex_enter(&sdp->sd_lock);
search.se_name = (char *)nm;
if ((sep = avl_find(&sdp->sd_snaps, &search, &where)) != NULL) {
@@ -692,6 +821,13 @@ zfsctl_snapdir_lookup(ap)
* try to remount it.
*/
goto domount;
+ } else {
+ /*
+ * VROOT was set during the traverse call. We need
+ * to clear it since we're pretending to be part
+ * of our parent's vfs.
+ */
+ (*vpp)->v_flag &= ~VROOT;
}
vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY);
mutex_exit(&sdp->sd_lock);
@@ -706,13 +842,25 @@ zfsctl_snapdir_lookup(ap)
if (err) {
mutex_exit(&sdp->sd_lock);
ZFS_EXIT(zfsvfs);
- return (err);
+ /*
+ * handle "ls *" or "?" in a graceful manner,
+ * forcing EILSEQ to ENOENT.
+ * Since shell ultimately passes "*" or "?" as name to lookup
+ */
+ return (err == EILSEQ ? ENOENT : err);
}
if (dmu_objset_open(snapname, DMU_OST_ZFS,
- DS_MODE_STANDARD | DS_MODE_READONLY, &snap) != 0) {
+ DS_MODE_USER | DS_MODE_READONLY, &snap) != 0) {
mutex_exit(&sdp->sd_lock);
+ /* Translate errors and add SAVENAME when needed. */
+ if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) {
+ err = EJUSTRETURN;
+ cnp->cn_flags |= SAVENAME;
+ } else {
+ err = ENOENT;
+ }
ZFS_EXIT(zfsvfs);
- return (ENOENT);
+ return (err);
}
sep = kmem_alloc(sizeof (zfs_snapentry_t), KM_SLEEP);
@@ -735,7 +883,6 @@ domount:
if (err == 0)
vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY);
mutex_exit(&sdp->sd_lock);
-
/*
* If we had an error, drop our hold on the vnode and
* zfsctl_snapshot_inactive() will clean up.
@@ -750,25 +897,41 @@ domount:
/* ARGSUSED */
static int
-zfsctl_snapdir_readdir_cb(vnode_t *vp, struct dirent64 *dp, int *eofp,
- offset_t *offp, offset_t *nextp, void *data)
+zfsctl_snapdir_readdir_cb(vnode_t *vp, void *dp, int *eofp,
+ offset_t *offp, offset_t *nextp, void *data, int flags)
{
zfsvfs_t *zfsvfs = vp->v_vfsp->vfs_data;
char snapname[MAXNAMELEN];
uint64_t id, cookie;
+ boolean_t case_conflict;
+ int error;
ZFS_ENTER(zfsvfs);
cookie = *offp;
- if (dmu_snapshot_list_next(zfsvfs->z_os, MAXNAMELEN, snapname, &id,
- &cookie) == ENOENT) {
- *eofp = 1;
+ error = dmu_snapshot_list_next(zfsvfs->z_os, MAXNAMELEN, snapname, &id,
+ &cookie, &case_conflict);
+ if (error) {
ZFS_EXIT(zfsvfs);
- return (0);
+ if (error == ENOENT) {
+ *eofp = 1;
+ return (0);
+ }
+ return (error);
}
- (void) strcpy(dp->d_name, snapname);
- dp->d_ino = ZFSCTL_INO_SNAP(id);
+ if (flags & V_RDDIR_ENTFLAGS) {
+ edirent_t *eodp = dp;
+
+ (void) strcpy(eodp->ed_name, snapname);
+ eodp->ed_ino = ZFSCTL_INO_SNAP(id);
+ eodp->ed_eflags = case_conflict ? ED_CASE_CONFLICT : 0;
+ } else {
+ struct dirent64 *odp = dp;
+
+ (void) strcpy(odp->d_name, snapname);
+ odp->d_ino = ZFSCTL_INO_SNAP(id);
+ }
*nextp = cookie;
ZFS_EXIT(zfsvfs);
@@ -776,6 +939,13 @@ zfsctl_snapdir_readdir_cb(vnode_t *vp, struct dirent64 *dp, int *eofp,
return (0);
}
+/*
+ * pvp is the '.zfs' directory (zfsctl_node_t).
+ * Creates vp, which is '.zfs/snapshot' (zfsctl_snapdir_t).
+ *
+ * This function is the callback to create a GFS vnode for '.zfs/snapshot'
+ * when a lookup is performed on .zfs for "snapshot".
+ */
vnode_t *
zfsctl_mknode_snapdir(vnode_t *pvp)
{
@@ -802,6 +972,7 @@ zfsctl_snapdir_getattr(ap)
struct vnode *a_vp;
struct vattr *a_vap;
struct ucred *a_cred;
+ struct thread *a_td;
} */ *ap;
{
struct vnode *vp = ap->a_vp;
@@ -847,6 +1018,7 @@ static struct vop_vector zfsctl_ops_snapdir = {
.vop_ioctl = VOP_EINVAL,
.vop_getattr = zfsctl_snapdir_getattr,
.vop_access = zfsctl_common_access,
+ .vop_mkdir = zfsctl_freebsd_snapdir_mkdir,
.vop_readdir = gfs_vop_readdir,
.vop_lookup = zfsctl_snapdir_lookup,
.vop_inactive = zfsctl_snapdir_inactive,
@@ -854,6 +1026,13 @@ static struct vop_vector zfsctl_ops_snapdir = {
.vop_fid = zfsctl_common_fid,
};
+/*
+ * pvp is the GFS vnode '.zfs/snapshot'.
+ *
+ * This creates a GFS node under '.zfs/snapshot' representing each
+ * snapshot. This newly created GFS node is what we mount snapshot
+ * vfs_t's ontop of.
+ */
static vnode_t *
zfsctl_snapshot_mknode(vnode_t *pvp, uint64_t objset)
{
@@ -862,8 +1041,10 @@ zfsctl_snapshot_mknode(vnode_t *pvp, uint64_t objset)
vp = gfs_dir_create(sizeof (zfsctl_node_t), pvp, pvp->v_vfsp,
&zfsctl_ops_snapshot, NULL, NULL, MAXNAMELEN, NULL, NULL);
+ VN_HOLD(vp);
zcp = vp->v_data;
zcp->zc_id = objset;
+ VFS_HOLD(vp->v_vfsp);
VOP_UNLOCK(vp, 0);
return (vp);
@@ -877,13 +1058,14 @@ zfsctl_snapshot_inactive(ap)
} */ *ap;
{
vnode_t *vp = ap->a_vp;
+ cred_t *cr = ap->a_td->td_ucred;
struct vop_inactive_args iap;
zfsctl_snapdir_t *sdp;
zfs_snapentry_t *sep, *next;
int locked;
vnode_t *dvp;
- VERIFY(gfs_dir_lookup(vp, "..", &dvp) == 0);
+ VERIFY(gfs_dir_lookup(vp, "..", &dvp, cr, 0, NULL, NULL) == 0);
sdp = dvp->v_data;
VOP_UNLOCK(dvp, 0);
@@ -914,6 +1096,7 @@ zfsctl_snapshot_inactive(ap)
if (!locked)
mutex_exit(&sdp->sd_lock);
VN_RELE(dvp);
+ VFS_RELE(vp->v_vfsp);
/*
* Dispose of the vnode for the snapshot mount point.
@@ -931,7 +1114,6 @@ zfsctl_traverse_begin(vnode_t **vpp, int lktype)
{
VN_HOLD(*vpp);
-
/* Snapshot should be already mounted, but just in case. */
if (vn_mountedvfs(*vpp) == NULL)
return (ENOENT);
@@ -983,6 +1165,36 @@ zfsctl_snapshot_fid(ap)
return (err);
}
+static int
+zfsctl_snapshot_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ vnode_t *dvp = ap->a_dvp;
+ vnode_t **vpp = ap->a_vpp;
+ struct componentname *cnp = ap->a_cnp;
+ cred_t *cr = ap->a_cnp->cn_cred;
+ zfsvfs_t *zfsvfs = dvp->v_vfsp->vfs_data;
+ int error;
+
+ if (cnp->cn_namelen != 2 || cnp->cn_nameptr[0] != '.' ||
+ cnp->cn_nameptr[1] != '.') {
+ return (ENOENT);
+ }
+
+ ASSERT(dvp->v_type == VDIR);
+ ASSERT(zfsvfs->z_ctldir != NULL);
+
+ error = zfsctl_root_lookup(zfsvfs->z_ctldir, "snapshot", vpp,
+ NULL, 0, NULL, cr, NULL, NULL, NULL);
+ if (error == 0)
+ vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY);
+ return (error);
+}
+
/*
* These VP's should never see the light of day. They should always
* be covered.
@@ -990,6 +1202,7 @@ zfsctl_snapshot_fid(ap)
static struct vop_vector zfsctl_ops_snapshot = {
.vop_default = &default_vnodeops,
.vop_inactive = zfsctl_snapshot_inactive,
+ .vop_lookup = zfsctl_snapshot_lookup,
.vop_reclaim = zfsctl_common_reclaim,
.vop_getattr = zfsctl_snapshot_getattr,
.vop_fid = zfsctl_snapshot_fid,
@@ -1007,7 +1220,7 @@ zfsctl_lookup_objset(vfs_t *vfsp, uint64_t objsetid, zfsvfs_t **zfsvfsp)
ASSERT(zfsvfs->z_ctldir != NULL);
error = zfsctl_root_lookup(zfsvfs->z_ctldir, "snapshot", &dvp,
- NULL, 0, NULL, kcred);
+ NULL, 0, NULL, kcred, NULL, NULL, NULL);
if (error != 0)
return (error);
sdp = dvp->v_data;
@@ -1025,6 +1238,12 @@ zfsctl_lookup_objset(vfs_t *vfsp, uint64_t objsetid, zfsvfs_t **zfsvfsp)
if (sep != NULL) {
VN_HOLD(vp);
+ /*
+ * Return the mounted root rather than the covered mount point.
+ * Takes the GFS vnode at .zfs/snapshot/<snapshot objsetid>
+ * and returns the ZFS vnode mounted on top of the GFS node.
+ * This ZFS vnode is the root of the vfs for objset 'objsetid'.
+ */
error = traverse(&vp, LK_SHARED | LK_RETRY);
if (error == 0) {
if (vp == sep->se_root)
@@ -1055,16 +1274,15 @@ zfsctl_lookup_objset(vfs_t *vfsp, uint64_t objsetid, zfsvfs_t **zfsvfsp)
int
zfsctl_umount_snapshots(vfs_t *vfsp, int fflags, cred_t *cr)
{
- struct vop_inactive_args ap;
zfsvfs_t *zfsvfs = vfsp->vfs_data;
- vnode_t *dvp, *svp;
+ vnode_t *dvp;
zfsctl_snapdir_t *sdp;
zfs_snapentry_t *sep, *next;
int error;
ASSERT(zfsvfs->z_ctldir != NULL);
error = zfsctl_root_lookup(zfsvfs->z_ctldir, "snapshot", &dvp,
- NULL, 0, NULL, cr);
+ NULL, 0, NULL, cr, NULL, NULL, NULL);
if (error != 0)
return (error);
sdp = dvp->v_data;
@@ -1073,7 +1291,6 @@ zfsctl_umount_snapshots(vfs_t *vfsp, int fflags, cred_t *cr)
sep = avl_first(&sdp->sd_snaps);
while (sep != NULL) {
- svp = sep->se_root;
next = AVL_NEXT(&sdp->sd_snaps, sep);
/*
@@ -1081,40 +1298,16 @@ zfsctl_umount_snapshots(vfs_t *vfsp, int fflags, cred_t *cr)
* have just been unmounted by somebody else, and
* will be cleaned up by zfsctl_snapdir_inactive().
*/
- if (vn_ismntpt(svp)) {
- if ((error = vn_vfswlock(svp)) != 0)
- goto out;
-
- /*
- * Increase usecount, so dounmount() won't vrele() it
- * to 0 and call zfsctl_snapdir_inactive().
- */
- VN_HOLD(svp);
- vfsp = vn_mountedvfs(svp);
- mtx_lock(&Giant);
- error = dounmount(vfsp, fflags, curthread);
- mtx_unlock(&Giant);
- if (error != 0) {
- VN_RELE(svp);
- goto out;
+ if (vn_ismntpt(sep->se_root)) {
+ error = zfsctl_unmount_snap(sep, fflags, cr);
+ if (error) {
+ avl_add(&sdp->sd_snaps, sep);
+ break;
}
-
- avl_remove(&sdp->sd_snaps, sep);
- kmem_free(sep->se_name, strlen(sep->se_name) + 1);
- kmem_free(sep, sizeof (zfs_snapentry_t));
-
- /*
- * We can't use VN_RELE(), as that will try to
- * invoke zfsctl_snapdir_inactive(), and that
- * would lead to an attempt to re-grab the sd_lock.
- */
- ASSERT3U(svp->v_count, ==, 1);
- ap.a_vp = svp;
- gfs_vop_inactive(&ap);
}
sep = next;
}
-out:
+
mutex_exit(&sdp->sd_lock);
VN_RELE(dvp);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c
index f233b8f..45ec88b 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
@@ -40,6 +38,7 @@
#include <sys/errno.h>
#include <sys/stat.h>
#include <sys/unistd.h>
+#include <sys/sunddi.h>
#include <sys/random.h>
#include <sys/policy.h>
#include <sys/kcondvar.h>
@@ -52,7 +51,50 @@
#include <sys/dmu.h>
#include <sys/atomic.h>
#include <sys/zfs_ctldir.h>
+#include <sys/zfs_fuid.h>
#include <sys/dnlc.h>
+#include <sys/extdirent.h>
+
+/*
+ * zfs_match_find() is used by zfs_dirent_lock() to peform zap lookups
+ * of names after deciding which is the appropriate lookup interface.
+ */
+static int
+zfs_match_find(zfsvfs_t *zfsvfs, znode_t *dzp, char *name, boolean_t exact,
+ boolean_t update, int *deflags, pathname_t *rpnp, uint64_t *zoid)
+{
+ int error;
+
+ if (zfsvfs->z_norm) {
+ matchtype_t mt = MT_FIRST;
+ boolean_t conflict = B_FALSE;
+ size_t bufsz = 0;
+ char *buf = NULL;
+
+ if (rpnp) {
+ buf = rpnp->pn_buf;
+ bufsz = rpnp->pn_bufsize;
+ }
+ if (exact)
+ mt = MT_EXACT;
+ /*
+ * In the non-mixed case we only expect there would ever
+ * be one match, but we need to use the normalizing lookup.
+ */
+ error = zap_lookup_norm(zfsvfs->z_os, dzp->z_id, name, 8, 1,
+ zoid, mt, buf, bufsz, &conflict);
+ if (!error && deflags)
+ *deflags = conflict ? ED_CASE_CONFLICT : 0;
+ } else {
+ error = zap_lookup(zfsvfs->z_os, dzp->z_id, name, 8, 1, zoid);
+ }
+ *zoid = ZFS_DIRENT_OBJ(*zoid);
+
+ if (error == ENOENT && update)
+ dnlc_update(ZTOV(dzp), name, DNLC_NO_VNODE);
+
+ return (error);
+}
/*
* Lock a directory entry. A dirlock on <dzp, name> protects that name
@@ -67,24 +109,38 @@
* ZEXISTS: if the entry does not exist, fail with ENOENT.
* ZSHARED: allow concurrent access with other ZSHARED callers.
* ZXATTR: we want dzp's xattr directory
+ * ZCILOOK: On a mixed sensitivity file system,
+ * this lookup should be case-insensitive.
+ * ZCIEXACT: On a purely case-insensitive file system,
+ * this lookup should be case-sensitive.
+ * ZRENAMING: we are locking for renaming, force narrow locks
*
* Output arguments:
* zpp - pointer to the znode for the entry (NULL if there isn't one)
* dlpp - pointer to the dirlock for this entry (NULL on error)
+ * direntflags - (case-insensitive lookup only)
+ * flags if multiple case-sensitive matches exist in directory
+ * realpnp - (case-insensitive lookup only)
+ * actual name matched within the directory
*
* Return value: 0 on success or errno on failure.
*
* NOTE: Always checks for, and rejects, '.' and '..'.
+ * NOTE: For case-insensitive file systems we take wide locks (see below),
+ * but return znode pointers to a single match.
*/
int
zfs_dirent_lock(zfs_dirlock_t **dlpp, znode_t *dzp, char *name, znode_t **zpp,
- int flag)
+ int flag, int *direntflags, pathname_t *realpnp)
{
zfsvfs_t *zfsvfs = dzp->z_zfsvfs;
zfs_dirlock_t *dl;
+ boolean_t update;
+ boolean_t exact;
uint64_t zoid;
- int error;
- vnode_t *vp;
+ vnode_t *vp = NULL;
+ int error = 0;
+ int cmpflags;
*zpp = NULL;
*dlpp = NULL;
@@ -98,6 +154,59 @@ zfs_dirent_lock(zfs_dirlock_t **dlpp, znode_t *dzp, char *name, znode_t **zpp,
return (EEXIST);
/*
+ * Case sensitivity and normalization preferences are set when
+ * the file system is created. These are stored in the
+ * zfsvfs->z_case and zfsvfs->z_norm fields. These choices
+ * affect what vnodes can be cached in the DNLC, how we
+ * perform zap lookups, and the "width" of our dirlocks.
+ *
+ * A normal dirlock locks a single name. Note that with
+ * normalization a name can be composed multiple ways, but
+ * when normalized, these names all compare equal. A wide
+ * dirlock locks multiple names. We need these when the file
+ * system is supporting mixed-mode access. It is sometimes
+ * necessary to lock all case permutations of file name at
+ * once so that simultaneous case-insensitive/case-sensitive
+ * behaves as rationally as possible.
+ */
+
+ /*
+ * Decide if exact matches should be requested when performing
+ * a zap lookup on file systems supporting case-insensitive
+ * access.
+ */
+ exact =
+ ((zfsvfs->z_case == ZFS_CASE_INSENSITIVE) && (flag & ZCIEXACT)) ||
+ ((zfsvfs->z_case == ZFS_CASE_MIXED) && !(flag & ZCILOOK));
+
+ /*
+ * Only look in or update the DNLC if we are looking for the
+ * name on a file system that does not require normalization
+ * or case folding. We can also look there if we happen to be
+ * on a non-normalizing, mixed sensitivity file system IF we
+ * are looking for the exact name.
+ *
+ * Maybe can add TO-UPPERed version of name to dnlc in ci-only
+ * case for performance improvement?
+ */
+ update = !zfsvfs->z_norm ||
+ ((zfsvfs->z_case == ZFS_CASE_MIXED) &&
+ !(zfsvfs->z_norm & ~U8_TEXTPREP_TOUPPER) && !(flag & ZCILOOK));
+
+ /*
+ * ZRENAMING indicates we are in a situation where we should
+ * take narrow locks regardless of the file system's
+ * preferences for normalizing and case folding. This will
+ * prevent us deadlocking trying to grab the same wide lock
+ * twice if the two names happen to be case-insensitive
+ * matches.
+ */
+ if (flag & ZRENAMING)
+ cmpflags = 0;
+ else
+ cmpflags = zfsvfs->z_norm;
+
+ /*
* Wait until there are no locks on this name.
*/
rw_enter(&dzp->z_name_lock, RW_READER);
@@ -108,9 +217,16 @@ zfs_dirent_lock(zfs_dirlock_t **dlpp, znode_t *dzp, char *name, znode_t **zpp,
rw_exit(&dzp->z_name_lock);
return (ENOENT);
}
- for (dl = dzp->z_dirlocks; dl != NULL; dl = dl->dl_next)
- if (strcmp(name, dl->dl_name) == 0)
+ for (dl = dzp->z_dirlocks; dl != NULL; dl = dl->dl_next) {
+ if ((u8_strcmp(name, dl->dl_name, 0, cmpflags,
+ U8_UNICODE_LATEST, &error) == 0) || error != 0)
break;
+ }
+ if (error != 0) {
+ mutex_exit(&dzp->z_lock);
+ rw_exit(&dzp->z_name_lock);
+ return (ENOENT);
+ }
if (dl == NULL) {
/*
* Allocate a new dirlock and add it to the list.
@@ -156,7 +272,8 @@ zfs_dirent_lock(zfs_dirlock_t **dlpp, znode_t *dzp, char *name, znode_t **zpp,
zoid = dzp->z_phys->zp_xattr;
error = (zoid == 0 ? ENOENT : 0);
} else {
- vp = dnlc_lookup(ZTOV(dzp), name);
+ if (update)
+ vp = dnlc_lookup(ZTOV(dzp), name);
if (vp == DNLC_NO_VNODE) {
VN_RELE(vp);
error = ENOENT;
@@ -170,11 +287,8 @@ zfs_dirent_lock(zfs_dirlock_t **dlpp, znode_t *dzp, char *name, znode_t **zpp,
*zpp = VTOZ(vp);
return (0);
} else {
- error = zap_lookup(zfsvfs->z_os, dzp->z_id, name,
- 8, 1, &zoid);
- zoid = ZFS_DIRENT_OBJ(zoid);
- if (error == ENOENT)
- dnlc_update(ZTOV(dzp), name, DNLC_NO_VNODE);
+ error = zfs_match_find(zfsvfs, dzp, name, exact,
+ update, direntflags, realpnp, &zoid);
}
}
if (error) {
@@ -192,7 +306,7 @@ zfs_dirent_lock(zfs_dirlock_t **dlpp, znode_t *dzp, char *name, znode_t **zpp,
zfs_dirent_unlock(dl);
return (error);
}
- if (!(flag & ZXATTR))
+ if (!(flag & ZXATTR) && update)
dnlc_update(ZTOV(dzp), name, ZTOV(*zpp));
}
@@ -239,7 +353,8 @@ zfs_dirent_unlock(zfs_dirlock_t *dl)
* special pseudo-directory.
*/
int
-zfs_dirlook(znode_t *dzp, char *name, vnode_t **vpp)
+zfs_dirlook(znode_t *dzp, char *name, vnode_t **vpp, int flags,
+ int *deflg, pathname_t *rpnp)
{
zfs_dirlock_t *dl;
znode_t *zp;
@@ -257,7 +372,8 @@ zfs_dirlook(znode_t *dzp, char *name, vnode_t **vpp)
if (dzp->z_phys->zp_parent == dzp->z_id &&
zfsvfs->z_parent != zfsvfs) {
error = zfsctl_root_lookup(zfsvfs->z_parent->z_ctldir,
- "snapshot", vpp, NULL, 0, NULL, kcred);
+ "snapshot", vpp, NULL, 0, NULL, kcred,
+ NULL, NULL, NULL);
return (error);
}
rw_enter(&dzp->z_parent_lock, RW_READER);
@@ -268,30 +384,25 @@ zfs_dirlook(znode_t *dzp, char *name, vnode_t **vpp)
} else if (zfs_has_ctldir(dzp) && strcmp(name, ZFS_CTLDIR_NAME) == 0) {
*vpp = zfsctl_root(dzp);
} else {
- error = zfs_dirent_lock(&dl, dzp, name, &zp, ZEXISTS | ZSHARED);
+ int zf;
+
+ zf = ZEXISTS | ZSHARED;
+ if (flags & FIGNORECASE)
+ zf |= ZCILOOK;
+
+ error = zfs_dirent_lock(&dl, dzp, name, &zp, zf, deflg, rpnp);
if (error == 0) {
*vpp = ZTOV(zp);
zfs_dirent_unlock(dl);
dzp->z_zn_prefetch = B_TRUE; /* enable prefetching */
}
+ rpnp = NULL;
}
- return (error);
-}
-
-static char *
-zfs_unlinked_hexname(char namebuf[17], uint64_t x)
-{
- char *name = &namebuf[16];
- const char digits[16] = "0123456789abcdef";
-
- *name = '\0';
- do {
- *--name = digits[x & 0xf];
- x >>= 4;
- } while (x != 0);
+ if ((flags & FIGNORECASE) && rpnp && !error)
+ (void) strlcpy(rpnp->pn_buf, name, rpnp->pn_bufsize);
- return (name);
+ return (error);
}
/*
@@ -312,15 +423,12 @@ void
zfs_unlinked_add(znode_t *zp, dmu_tx_t *tx)
{
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
- char obj_name[17];
- int error;
ASSERT(zp->z_unlinked);
ASSERT3U(zp->z_phys->zp_links, ==, 0);
- error = zap_add(zfsvfs->z_os, zfsvfs->z_unlinkedobj,
- zfs_unlinked_hexname(obj_name, zp->z_id), 8, 1, &zp->z_id, tx);
- ASSERT3U(error, ==, 0);
+ VERIFY3U(0, ==,
+ zap_add_int(zfsvfs->z_os, zfsvfs->z_unlinkedobj, zp->z_id, tx));
}
/*
@@ -377,7 +485,9 @@ zfs_unlinked_drain(zfsvfs_t *zfsvfs)
/*
* Delete the entire contents of a directory. Return a count
- * of the number of entries that could not be deleted.
+ * of the number of entries that could not be deleted. If we encounter
+ * an error, return a count of at least one so that the directory stays
+ * in the unlinked set.
*
* NOTE: this function assumes that the directory is inactive,
* so there is no need to lock its entries before deletion.
@@ -401,7 +511,10 @@ zfs_purgedir(znode_t *dzp)
zap_cursor_advance(&zc)) {
error = zfs_zget(zfsvfs,
ZFS_DIRENT_OBJ(zap.za_first_integer), &xzp);
- ASSERT3U(error, ==, 0);
+ if (error) {
+ skipped += 1;
+ continue;
+ }
ASSERT((ZTOV(xzp)->v_type == VREG) ||
(ZTOV(xzp)->v_type == VLNK));
@@ -423,13 +536,15 @@ zfs_purgedir(znode_t *dzp)
dl.dl_name = zap.za_name;
error = zfs_link_destroy(&dl, xzp, tx, 0, NULL);
- ASSERT3U(error, ==, 0);
+ if (error)
+ skipped += 1;
dmu_tx_commit(tx);
VN_RELE(ZTOV(xzp));
}
zap_cursor_fini(&zc);
- ASSERT(error == ENOENT);
+ if (error != ENOENT)
+ skipped += 1;
return (skipped);
}
@@ -439,7 +554,6 @@ zfs_rmnode(znode_t *zp)
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
objset_t *os = zfsvfs->z_os;
znode_t *xzp = NULL;
- char obj_name[17];
dmu_tx_t *tx;
uint64_t acl_obj;
int error;
@@ -450,6 +564,24 @@ zfs_rmnode(znode_t *zp)
ASSERT(zp->z_phys->zp_links == 0);
/*
+ * If this is a ZIL replay then leave the object in the unlinked set.
+ * Otherwise we can get a deadlock, because the delete can be
+ * quite large and span multiple tx's and txgs, but each replay
+ * creates a tx to atomically run the replay function and mark the
+ * replay record as complete. We deadlock trying to start a tx in
+ * a new txg to further the deletion but can't because the replay
+ * tx hasn't finished.
+ *
+ * We actually delete the object if we get a failure to create an
+ * object in zil_replay_log_record(), or after calling zil_replay().
+ */
+ if (zfsvfs->z_assign >= TXG_INITIAL) {
+ zfs_znode_dmu_fini(zp);
+ zfs_znode_free(zp);
+ return;
+ }
+
+ /*
* If this is an attribute directory, purge its contents.
*/
if (ZTOV(zp) != NULL && ZTOV(zp)->v_type == VDIR &&
@@ -457,14 +589,29 @@ zfs_rmnode(znode_t *zp)
if (zfs_purgedir(zp) != 0) {
/*
* Not enough space to delete some xattrs.
- * Leave it on the unlinked set.
+ * Leave it in the unlinked set.
*/
+ zfs_znode_dmu_fini(zp);
+ zfs_znode_free(zp);
VFS_UNLOCK_GIANT(vfslocked);
return;
}
}
/*
+ * Free up all the data in the file.
+ */
+ error = dmu_free_long_range(os, zp->z_id, 0, DMU_OBJECT_END);
+ if (error) {
+ /*
+ * Not enough space. Leave the file in the unlinked set.
+ */
+ zfs_znode_dmu_fini(zp);
+ zfs_znode_free(zp);
+ return;
+ }
+
+ /*
* If the file has extended attributes, we're going to unlink
* the xattr dir.
*/
@@ -476,7 +623,7 @@ zfs_rmnode(znode_t *zp)
acl_obj = zp->z_phys->zp_acl.z_acl_extern_obj;
/*
- * Set up the transaction.
+ * Set up the final transaction.
*/
tx = dmu_tx_create(os);
dmu_tx_hold_free(tx, zp->z_id, 0, DMU_OBJECT_END);
@@ -495,8 +642,9 @@ zfs_rmnode(znode_t *zp)
* which point we'll call zfs_unlinked_drain() to process it).
*/
dmu_tx_abort(tx);
- VFS_UNLOCK_GIANT(vfslocked);
- return;
+ zfs_znode_dmu_fini(zp);
+ zfs_znode_free(zp);
+ goto out;
}
if (xzp) {
@@ -509,19 +657,27 @@ zfs_rmnode(znode_t *zp)
}
/* Remove this znode from the unlinked set */
- error = zap_remove(os, zfsvfs->z_unlinkedobj,
- zfs_unlinked_hexname(obj_name, zp->z_id), tx);
- ASSERT3U(error, ==, 0);
+ VERIFY3U(0, ==,
+ zap_remove_int(zfsvfs->z_os, zfsvfs->z_unlinkedobj, zp->z_id, tx));
zfs_znode_delete(zp, tx);
dmu_tx_commit(tx);
-
+out:
if (xzp)
VN_RELE(ZTOV(xzp));
VFS_UNLOCK_GIANT(vfslocked);
}
+static uint64_t
+zfs_dirent(znode_t *zp)
+{
+ uint64_t de = zp->z_id;
+ if (zp->z_zfsvfs->z_version >= ZPL_VERSION_DIRENT_TYPE)
+ de |= IFTODT((zp)->z_phys->zp_mode) << 60;
+ return (de);
+}
+
/*
* Link zp into dl. Can only fail if zp has been unlinked.
*/
@@ -558,10 +714,7 @@ zfs_link_create(zfs_dirlock_t *dl, znode_t *zp, dmu_tx_t *tx, int flag)
zfs_time_stamper_locked(dzp, CONTENT_MODIFIED, tx);
mutex_exit(&dzp->z_lock);
- /*
- * MacOS X will fill in the 4-bit object type here.
- */
- value = ZFS_DIRENT_MAKE(IFTODT(zp->z_phys->zp_mode), zp->z_id);
+ value = zfs_dirent(zp);
error = zap_add(zp->z_zfsvfs->z_os, dzp->z_id, dl->dl_name,
8, 1, &value, tx);
ASSERT(error == 0);
@@ -632,7 +785,20 @@ zfs_link_destroy(zfs_dirlock_t *dl, znode_t *zp, dmu_tx_t *tx, int flag,
zfs_time_stamper_locked(dzp, CONTENT_MODIFIED, tx);
mutex_exit(&dzp->z_lock);
- error = zap_remove(zp->z_zfsvfs->z_os, dzp->z_id, dl->dl_name, tx);
+ if (zp->z_zfsvfs->z_norm) {
+ if (((zp->z_zfsvfs->z_case == ZFS_CASE_INSENSITIVE) &&
+ (flag & ZCIEXACT)) ||
+ ((zp->z_zfsvfs->z_case == ZFS_CASE_MIXED) &&
+ !(flag & ZCILOOK)))
+ error = zap_remove_norm(zp->z_zfsvfs->z_os,
+ dzp->z_id, dl->dl_name, MT_EXACT, tx);
+ else
+ error = zap_remove_norm(zp->z_zfsvfs->z_os,
+ dzp->z_id, dl->dl_name, MT_FIRST, tx);
+ } else {
+ error = zap_remove(zp->z_zfsvfs->z_os,
+ dzp->z_id, dl->dl_name, tx);
+ }
ASSERT(error == 0);
if (unlinkedp != NULL)
@@ -660,17 +826,29 @@ zfs_make_xattrdir(znode_t *zp, vattr_t *vap, vnode_t **xvpp, cred_t *cr)
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
znode_t *xzp;
dmu_tx_t *tx;
- uint64_t xoid;
int error;
+ zfs_fuid_info_t *fuidp = NULL;
*xvpp = NULL;
- if (error = zfs_zaccess(zp, ACE_WRITE_NAMED_ATTRS, cr))
+ if (error = zfs_zaccess(zp, ACE_WRITE_NAMED_ATTRS, 0, B_FALSE, cr))
return (error);
tx = dmu_tx_create(zfsvfs->z_os);
dmu_tx_hold_bonus(tx, zp->z_id);
dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL);
+ if (IS_EPHEMERAL(crgetuid(cr)) || IS_EPHEMERAL(crgetgid(cr))) {
+ if (zfsvfs->z_fuid_obj == 0) {
+ dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT);
+ dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0,
+ FUID_SIZE_ESTIMATE(zfsvfs));
+ dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, FALSE, NULL);
+ } else {
+ dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj);
+ dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0,
+ FUID_SIZE_ESTIMATE(zfsvfs));
+ }
+ }
error = dmu_tx_assign(tx, zfsvfs->z_assign);
if (error) {
if (error == ERESTART && zfsvfs->z_assign == TXG_NOWAIT)
@@ -678,13 +856,15 @@ zfs_make_xattrdir(znode_t *zp, vattr_t *vap, vnode_t **xvpp, cred_t *cr)
dmu_tx_abort(tx);
return (error);
}
- zfs_mknode(zp, vap, &xoid, tx, cr, IS_XATTR, &xzp, 0);
- ASSERT(xzp->z_id == xoid);
+ zfs_mknode(zp, vap, tx, cr, IS_XATTR, &xzp, 0, NULL, &fuidp);
ASSERT(xzp->z_phys->zp_parent == zp->z_id);
dmu_buf_will_dirty(zp->z_dbuf, tx);
- zp->z_phys->zp_xattr = xoid;
+ zp->z_phys->zp_xattr = xzp->z_id;
- (void) zfs_log_create(zfsvfs->z_log, tx, TX_MKXATTR, zp, xzp, "");
+ (void) zfs_log_create(zfsvfs->z_log, tx, TX_MKXATTR, zp,
+ xzp, "", NULL, fuidp, vap);
+ if (fuidp)
+ zfs_fuid_info_free(fuidp);
dmu_tx_commit(tx);
*xvpp = ZTOV(xzp);
@@ -714,7 +894,7 @@ zfs_get_xattrdir(znode_t *zp, vnode_t **xvpp, cred_t *cr, int flags)
vattr_t va;
int error;
top:
- error = zfs_dirent_lock(&dl, zp, "", &xzp, ZXATTR);
+ error = zfs_dirent_lock(&dl, zp, "", &xzp, ZXATTR, NULL, NULL);
if (error)
return (error);
@@ -751,8 +931,7 @@ top:
va.va_mask = AT_TYPE | AT_MODE | AT_UID | AT_GID;
va.va_type = VDIR;
va.va_mode = S_IFDIR | S_ISVTX | 0777;
- va.va_uid = (uid_t)zp->z_phys->zp_uid;
- va.va_gid = (gid_t)zp->z_phys->zp_gid;
+ zfs_fuid_map_ids(zp, cr, &va.va_uid, &va.va_gid);
error = zfs_make_xattrdir(zp, &va, xvpp, cr);
zfs_dirent_unlock(dl);
@@ -782,16 +961,23 @@ int
zfs_sticky_remove_access(znode_t *zdp, znode_t *zp, cred_t *cr)
{
uid_t uid;
+ uid_t downer;
+ uid_t fowner;
+ zfsvfs_t *zfsvfs = zdp->z_zfsvfs;
if (zdp->z_zfsvfs->z_assign >= TXG_INITIAL) /* ZIL replay */
return (0);
- if ((zdp->z_phys->zp_mode & S_ISVTX) == 0 ||
- (uid = crgetuid(cr)) == zdp->z_phys->zp_uid ||
- uid == zp->z_phys->zp_uid ||
+ if ((zdp->z_phys->zp_mode & S_ISVTX) == 0)
+ return (0);
+
+ downer = zfs_fuid_map_id(zfsvfs, zdp->z_phys->zp_uid, cr, ZFS_OWNER);
+ fowner = zfs_fuid_map_id(zfsvfs, zp->z_phys->zp_uid, cr, ZFS_OWNER);
+
+ if ((uid = crgetuid(cr)) == downer || uid == fowner ||
(ZTOV(zp)->v_type == VREG &&
- zfs_zaccess(zp, ACE_WRITE_DATA, cr) == 0))
+ zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr) == 0))
return (0);
else
- return (secpolicy_vnode_remove(cr));
+ return (secpolicy_vnode_remove(ZTOV(zp), cr));
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_fm.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_fm.c
index e2385a0..17e4b0a 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_fm.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_fm.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/spa.h>
#include <sys/spa_impl.h>
#include <sys/vdev.h>
@@ -53,7 +51,7 @@ extern void devctl_notify(const char *__system, const char *__subsystem,
* pool X
*
* If we are in a loading state, all errors are chained together by the same
- * SPA-wide ENA.
+ * SPA-wide ENA (Error Numeric Association).
*
* For isolated I/O requests, we get the ENA from the zio_t. The propagation
* gets very complicated due to RAID-Z, gang blocks, and vdev caching. We want
@@ -90,11 +88,10 @@ extern void devctl_notify(const char *__system, const char *__subsystem,
* We keep track of the ENA for a ZIO chain through the 'io_logical' member.
* When a new logical I/O is issued, we set this to point to itself. Child I/Os
* then inherit this pointer, so that when it is first set subsequent failures
- * will use the same ENA. If a physical I/O is issued (by passing the
- * ZIO_FLAG_NOBOOKMARK flag), then this pointer is reset, guaranteeing that a
- * unique ENA will be generated. For an aggregate I/O, this pointer is set to
- * NULL, and no ereport will be generated (since it doesn't actually correspond
- * to any particular device or piece of data).
+ * will use the same ENA. For vdev cache fill and queue aggregation I/O,
+ * this pointer is set to NULL, and no ereport will be generated (since it
+ * doesn't actually correspond to any particular device or piece of data,
+ * and the caller will always retry without caching or queueing anyway).
*/
void
zfs_ereport_post(const char *subclass, spa_t *spa, vdev_t *vd, zio_t *zio,
@@ -104,6 +101,7 @@ zfs_ereport_post(const char *subclass, spa_t *spa, vdev_t *vd, zio_t *zio,
char buf[1024];
struct sbuf sb;
struct timespec ts;
+ int state;
/*
* If we are doing a spa_tryimport(), ignore errors.
@@ -120,21 +118,33 @@ zfs_ereport_post(const char *subclass, spa_t *spa, vdev_t *vd, zio_t *zio,
spa->spa_last_open_failed)
return;
- /*
- * Ignore any errors from I/Os that we are going to retry anyway - we
- * only generate errors from the final failure.
- */
- if (zio && zio_should_retry(zio))
- return;
+ if (zio != NULL) {
+ /*
+ * If this is not a read or write zio, ignore the error. This
+ * can occur if the DKIOCFLUSHWRITECACHE ioctl fails.
+ */
+ if (zio->io_type != ZIO_TYPE_READ &&
+ zio->io_type != ZIO_TYPE_WRITE)
+ return;
- /*
- * If this is not a read or write zio, ignore the error. This can occur
- * if the DKIOCFLUSHWRITECACHE ioctl fails.
- */
- if (zio && zio->io_type != ZIO_TYPE_READ &&
- zio->io_type != ZIO_TYPE_WRITE)
- return;
+ /*
+ * Ignore any errors from speculative I/Os, as failure is an
+ * expected result.
+ */
+ if (zio->io_flags & ZIO_FLAG_SPECULATIVE)
+ return;
+ /*
+ * If the vdev has already been marked as failing due to a
+ * failed probe, then ignore any subsequent I/O errors, as the
+ * DE will automatically fault the vdev on the first such
+ * failure.
+ */
+ if (vd != NULL &&
+ (!vdev_readable(vd) || !vdev_writeable(vd)) &&
+ strcmp(subclass, FM_EREPORT_ZFS_PROBE_FAILURE) != 0)
+ return;
+ }
nanotime(&ts);
sbuf_new(&sb, buf, sizeof(buf), SBUF_FIXEDLEN);
@@ -187,22 +197,28 @@ zfs_ereport_post(const char *subclass, spa_t *spa, vdev_t *vd, zio_t *zio,
*/
/*
+ * If we are importing a faulted pool, then we treat it like an open,
+ * not an import. Otherwise, the DE will ignore all faults during
+ * import, since the default behavior is to mark the devices as
+ * persistently unavailable, not leave them in the faulted state.
+ */
+ state = spa->spa_import_faulted ? SPA_LOAD_OPEN : spa->spa_load_state;
+
+ /*
* Generic payload members common to all ereports.
- *
- * The direct reference to spa_name is used rather than spa_name()
- * because of the asynchronous nature of the zio pipeline. spa_name()
- * asserts that the config lock is held in some form. This is always
- * the case in I/O context, but because the check for RW_WRITER compares
- * against 'curthread', we may be in an asynchronous context and blow
- * this assert. Rather than loosen this assert, we acknowledge that all
- * contexts in which this function is called (pool open, I/O) are safe,
- * and dereference the name directly.
*/
- sbuf_printf(&sb, " %s=%s", FM_EREPORT_PAYLOAD_ZFS_POOL, spa->spa_name);
+ sbuf_printf(&sb, " %s=%s", FM_EREPORT_PAYLOAD_ZFS_POOL, spa_name(spa));
sbuf_printf(&sb, " %s=%ju", FM_EREPORT_PAYLOAD_ZFS_POOL_GUID,
spa_guid(spa));
- sbuf_printf(&sb, " %s=%u", FM_EREPORT_PAYLOAD_ZFS_POOL_CONTEXT,
- spa->spa_load_state);
+ sbuf_printf(&sb, " %s=%d", FM_EREPORT_PAYLOAD_ZFS_POOL_CONTEXT, state);
+
+ if (spa != NULL) {
+ sbuf_printf(&sb, " %s=%s", FM_EREPORT_PAYLOAD_ZFS_POOL_FAILMODE,
+ spa_get_failmode(spa) == ZIO_FAILURE_MODE_WAIT ?
+ FM_EREPORT_FAILMODE_WAIT :
+ spa_get_failmode(spa) == ZIO_FAILURE_MODE_CONTINUE ?
+ FM_EREPORT_FAILMODE_CONTINUE : FM_EREPORT_FAILMODE_PANIC);
+ }
if (vd != NULL) {
vdev_t *pvd = vd->vdev_parent;
@@ -290,7 +306,6 @@ zfs_ereport_post(const char *subclass, spa_t *spa, vdev_t *vd, zio_t *zio,
mutex_exit(&spa->spa_errlist_lock);
sbuf_finish(&sb);
- ZFS_LOG(1, "%s", sbuf_data(&sb));
devctl_notify("ZFS", spa->spa_name, subclass, sbuf_data(&sb));
if (sbuf_overflowed(&sb))
printf("ZFS WARNING: sbuf overflowed\n");
@@ -298,13 +313,8 @@ zfs_ereport_post(const char *subclass, spa_t *spa, vdev_t *vd, zio_t *zio,
#endif
}
-/*
- * The 'resource.fs.zfs.ok' event is an internal signal that the associated
- * resource (pool or disk) has been identified by ZFS as healthy. This will
- * then trigger the DE to close the associated case, if any.
- */
-void
-zfs_post_ok(spa_t *spa, vdev_t *vd)
+static void
+zfs_post_common(spa_t *spa, vdev_t *vd, const char *name)
{
#ifdef _KERNEL
char buf[1024];
@@ -318,7 +328,7 @@ zfs_post_ok(spa_t *spa, vdev_t *vd)
sbuf_printf(&sb, "time=%ju.%ld", (uintmax_t)ts.tv_sec, ts.tv_nsec);
snprintf(class, sizeof(class), "%s.%s.%s", FM_RSRC_RESOURCE,
- ZFS_ERROR_CLASS, FM_RESOURCE_OK);
+ ZFS_ERROR_CLASS, name);
sbuf_printf(&sb, " %s=%hhu", FM_VERSION, FM_RSRC_VERSION);
sbuf_printf(&sb, " %s=%s", FM_CLASS, class);
sbuf_printf(&sb, " %s=%ju", FM_EREPORT_PAYLOAD_ZFS_POOL_GUID,
@@ -327,9 +337,33 @@ zfs_post_ok(spa_t *spa, vdev_t *vd)
sbuf_printf(&sb, " %s=%ju", FM_EREPORT_PAYLOAD_ZFS_VDEV_GUID,
vd->vdev_guid);
sbuf_finish(&sb);
+ ZFS_LOG(1, "%s", sbuf_data(&sb));
devctl_notify("ZFS", spa->spa_name, class, sbuf_data(&sb));
if (sbuf_overflowed(&sb))
printf("ZFS WARNING: sbuf overflowed\n");
sbuf_delete(&sb);
#endif
}
+
+/*
+ * The 'resource.fs.zfs.removed' event is an internal signal that the given vdev
+ * has been removed from the system. This will cause the DE to ignore any
+ * recent I/O errors, inferring that they are due to the asynchronous device
+ * removal.
+ */
+void
+zfs_post_remove(spa_t *spa, vdev_t *vd)
+{
+ zfs_post_common(spa, vd, FM_RESOURCE_REMOVED);
+}
+
+/*
+ * The 'resource.fs.zfs.autoreplace' event is an internal signal that the pool
+ * has the 'autoreplace' property set, and therefore any broken vdevs will be
+ * handled by higher level logic, and no vdev fault should be generated.
+ */
+void
+zfs_post_autoreplace(spa_t *spa, vdev_t *vd)
+{
+ zfs_post_common(spa, vd, FM_RESOURCE_AUTOREPLACE);
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_fuid.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_fuid.c
new file mode 100644
index 0000000..dfec3ed
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_fuid.c
@@ -0,0 +1,716 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL 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.
+ */
+
+#include <sys/zfs_context.h>
+#include <sys/sunddi.h>
+#include <sys/dmu.h>
+#include <sys/avl.h>
+#include <sys/zap.h>
+#include <sys/refcount.h>
+#include <sys/nvpair.h>
+#ifdef _KERNEL
+#include <sys/kidmap.h>
+#include <sys/sid.h>
+#include <sys/zfs_vfsops.h>
+#include <sys/zfs_znode.h>
+#endif
+#include <sys/zfs_fuid.h>
+
+/*
+ * FUID Domain table(s).
+ *
+ * The FUID table is stored as a packed nvlist of an array
+ * of nvlists which contain an index, domain string and offset
+ *
+ * During file system initialization the nvlist(s) are read and
+ * two AVL trees are created. One tree is keyed by the index number
+ * and the other by the domain string. Nodes are never removed from
+ * trees, but new entries may be added. If a new entry is added then the
+ * on-disk packed nvlist will also be updated.
+ */
+
+#define FUID_IDX "fuid_idx"
+#define FUID_DOMAIN "fuid_domain"
+#define FUID_OFFSET "fuid_offset"
+#define FUID_NVP_ARRAY "fuid_nvlist"
+
+typedef struct fuid_domain {
+ avl_node_t f_domnode;
+ avl_node_t f_idxnode;
+ ksiddomain_t *f_ksid;
+ uint64_t f_idx;
+} fuid_domain_t;
+
+static char *nulldomain = "";
+
+/*
+ * Compare two indexes.
+ */
+static int
+idx_compare(const void *arg1, const void *arg2)
+{
+ const fuid_domain_t *node1 = arg1;
+ const fuid_domain_t *node2 = arg2;
+
+ if (node1->f_idx < node2->f_idx)
+ return (-1);
+ else if (node1->f_idx > node2->f_idx)
+ return (1);
+ return (0);
+}
+
+/*
+ * Compare two domain strings.
+ */
+static int
+domain_compare(const void *arg1, const void *arg2)
+{
+ const fuid_domain_t *node1 = arg1;
+ const fuid_domain_t *node2 = arg2;
+ int val;
+
+ val = strcmp(node1->f_ksid->kd_name, node2->f_ksid->kd_name);
+ if (val == 0)
+ return (0);
+ return (val > 0 ? 1 : -1);
+}
+
+/*
+ * load initial fuid domain and idx trees. This function is used by
+ * both the kernel and zdb.
+ */
+uint64_t
+zfs_fuid_table_load(objset_t *os, uint64_t fuid_obj, avl_tree_t *idx_tree,
+ avl_tree_t *domain_tree)
+{
+ dmu_buf_t *db;
+ uint64_t fuid_size;
+
+ avl_create(idx_tree, idx_compare,
+ sizeof (fuid_domain_t), offsetof(fuid_domain_t, f_idxnode));
+ avl_create(domain_tree, domain_compare,
+ sizeof (fuid_domain_t), offsetof(fuid_domain_t, f_domnode));
+
+ VERIFY(0 == dmu_bonus_hold(os, fuid_obj, FTAG, &db));
+ fuid_size = *(uint64_t *)db->db_data;
+ dmu_buf_rele(db, FTAG);
+
+ if (fuid_size) {
+ nvlist_t **fuidnvp;
+ nvlist_t *nvp = NULL;
+ uint_t count;
+ char *packed;
+ int i;
+
+ packed = kmem_alloc(fuid_size, KM_SLEEP);
+ VERIFY(dmu_read(os, fuid_obj, 0, fuid_size, packed) == 0);
+ VERIFY(nvlist_unpack(packed, fuid_size,
+ &nvp, 0) == 0);
+ VERIFY(nvlist_lookup_nvlist_array(nvp, FUID_NVP_ARRAY,
+ &fuidnvp, &count) == 0);
+
+ for (i = 0; i != count; i++) {
+ fuid_domain_t *domnode;
+ char *domain;
+ uint64_t idx;
+
+ VERIFY(nvlist_lookup_string(fuidnvp[i], FUID_DOMAIN,
+ &domain) == 0);
+ VERIFY(nvlist_lookup_uint64(fuidnvp[i], FUID_IDX,
+ &idx) == 0);
+
+ domnode = kmem_alloc(sizeof (fuid_domain_t), KM_SLEEP);
+
+ domnode->f_idx = idx;
+ domnode->f_ksid = ksid_lookupdomain(domain);
+ avl_add(idx_tree, domnode);
+ avl_add(domain_tree, domnode);
+ }
+ nvlist_free(nvp);
+ kmem_free(packed, fuid_size);
+ }
+ return (fuid_size);
+}
+
+void
+zfs_fuid_table_destroy(avl_tree_t *idx_tree, avl_tree_t *domain_tree)
+{
+ fuid_domain_t *domnode;
+ void *cookie;
+
+ cookie = NULL;
+ while (domnode = avl_destroy_nodes(domain_tree, &cookie))
+ ksiddomain_rele(domnode->f_ksid);
+
+ avl_destroy(domain_tree);
+ cookie = NULL;
+ while (domnode = avl_destroy_nodes(idx_tree, &cookie))
+ kmem_free(domnode, sizeof (fuid_domain_t));
+ avl_destroy(idx_tree);
+}
+
+char *
+zfs_fuid_idx_domain(avl_tree_t *idx_tree, uint32_t idx)
+{
+ fuid_domain_t searchnode, *findnode;
+ avl_index_t loc;
+
+ searchnode.f_idx = idx;
+
+ findnode = avl_find(idx_tree, &searchnode, &loc);
+
+ return (findnode ? findnode->f_ksid->kd_name : nulldomain);
+}
+
+#ifdef _KERNEL
+/*
+ * Load the fuid table(s) into memory.
+ */
+static void
+zfs_fuid_init(zfsvfs_t *zfsvfs, dmu_tx_t *tx)
+{
+ int error = 0;
+
+ rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER);
+
+ if (zfsvfs->z_fuid_loaded) {
+ rw_exit(&zfsvfs->z_fuid_lock);
+ return;
+ }
+
+ if (zfsvfs->z_fuid_obj == 0) {
+
+ /* first make sure we need to allocate object */
+
+ error = zap_lookup(zfsvfs->z_os, MASTER_NODE_OBJ,
+ ZFS_FUID_TABLES, 8, 1, &zfsvfs->z_fuid_obj);
+ if (error == ENOENT && tx != NULL) {
+ zfsvfs->z_fuid_obj = dmu_object_alloc(zfsvfs->z_os,
+ DMU_OT_FUID, 1 << 14, DMU_OT_FUID_SIZE,
+ sizeof (uint64_t), tx);
+ VERIFY(zap_add(zfsvfs->z_os, MASTER_NODE_OBJ,
+ ZFS_FUID_TABLES, sizeof (uint64_t), 1,
+ &zfsvfs->z_fuid_obj, tx) == 0);
+ }
+ }
+
+ if (zfsvfs->z_fuid_obj != 0) {
+ zfsvfs->z_fuid_size = zfs_fuid_table_load(zfsvfs->z_os,
+ zfsvfs->z_fuid_obj, &zfsvfs->z_fuid_idx,
+ &zfsvfs->z_fuid_domain);
+ zfsvfs->z_fuid_loaded = B_TRUE;
+ }
+
+ rw_exit(&zfsvfs->z_fuid_lock);
+}
+
+/*
+ * Query domain table for a given domain.
+ *
+ * If domain isn't found it is added to AVL trees and
+ * the results are pushed out to disk.
+ */
+int
+zfs_fuid_find_by_domain(zfsvfs_t *zfsvfs, const char *domain, char **retdomain,
+ dmu_tx_t *tx)
+{
+ fuid_domain_t searchnode, *findnode;
+ avl_index_t loc;
+ krw_t rw = RW_READER;
+
+ /*
+ * If the dummy "nobody" domain then return an index of 0
+ * to cause the created FUID to be a standard POSIX id
+ * for the user nobody.
+ */
+ if (domain[0] == '\0') {
+ *retdomain = nulldomain;
+ return (0);
+ }
+
+ searchnode.f_ksid = ksid_lookupdomain(domain);
+ if (retdomain) {
+ *retdomain = searchnode.f_ksid->kd_name;
+ }
+ if (!zfsvfs->z_fuid_loaded)
+ zfs_fuid_init(zfsvfs, tx);
+
+retry:
+ rw_enter(&zfsvfs->z_fuid_lock, rw);
+ findnode = avl_find(&zfsvfs->z_fuid_domain, &searchnode, &loc);
+
+ if (findnode) {
+ rw_exit(&zfsvfs->z_fuid_lock);
+ ksiddomain_rele(searchnode.f_ksid);
+ return (findnode->f_idx);
+ } else {
+ fuid_domain_t *domnode;
+ nvlist_t *nvp;
+ nvlist_t **fuids;
+ uint64_t retidx;
+ size_t nvsize = 0;
+ char *packed;
+ dmu_buf_t *db;
+ int i = 0;
+
+ if (rw == RW_READER && !rw_tryupgrade(&zfsvfs->z_fuid_lock)) {
+ rw_exit(&zfsvfs->z_fuid_lock);
+ rw = RW_WRITER;
+ goto retry;
+ }
+
+ domnode = kmem_alloc(sizeof (fuid_domain_t), KM_SLEEP);
+ domnode->f_ksid = searchnode.f_ksid;
+
+ retidx = domnode->f_idx = avl_numnodes(&zfsvfs->z_fuid_idx) + 1;
+
+ avl_add(&zfsvfs->z_fuid_domain, domnode);
+ avl_add(&zfsvfs->z_fuid_idx, domnode);
+ /*
+ * Now resync the on-disk nvlist.
+ */
+ VERIFY(nvlist_alloc(&nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
+
+ domnode = avl_first(&zfsvfs->z_fuid_domain);
+ fuids = kmem_alloc(retidx * sizeof (void *), KM_SLEEP);
+ while (domnode) {
+ VERIFY(nvlist_alloc(&fuids[i],
+ NV_UNIQUE_NAME, KM_SLEEP) == 0);
+ VERIFY(nvlist_add_uint64(fuids[i], FUID_IDX,
+ domnode->f_idx) == 0);
+ VERIFY(nvlist_add_uint64(fuids[i],
+ FUID_OFFSET, 0) == 0);
+ VERIFY(nvlist_add_string(fuids[i++], FUID_DOMAIN,
+ domnode->f_ksid->kd_name) == 0);
+ domnode = AVL_NEXT(&zfsvfs->z_fuid_domain, domnode);
+ }
+ VERIFY(nvlist_add_nvlist_array(nvp, FUID_NVP_ARRAY,
+ fuids, retidx) == 0);
+ for (i = 0; i != retidx; i++)
+ nvlist_free(fuids[i]);
+ kmem_free(fuids, retidx * sizeof (void *));
+ VERIFY(nvlist_size(nvp, &nvsize, NV_ENCODE_XDR) == 0);
+ packed = kmem_alloc(nvsize, KM_SLEEP);
+ VERIFY(nvlist_pack(nvp, &packed, &nvsize,
+ NV_ENCODE_XDR, KM_SLEEP) == 0);
+ nvlist_free(nvp);
+ zfsvfs->z_fuid_size = nvsize;
+ dmu_write(zfsvfs->z_os, zfsvfs->z_fuid_obj, 0,
+ zfsvfs->z_fuid_size, packed, tx);
+ kmem_free(packed, zfsvfs->z_fuid_size);
+ VERIFY(0 == dmu_bonus_hold(zfsvfs->z_os, zfsvfs->z_fuid_obj,
+ FTAG, &db));
+ dmu_buf_will_dirty(db, tx);
+ *(uint64_t *)db->db_data = zfsvfs->z_fuid_size;
+ dmu_buf_rele(db, FTAG);
+
+ rw_exit(&zfsvfs->z_fuid_lock);
+ return (retidx);
+ }
+}
+
+/*
+ * Query domain table by index, returning domain string
+ *
+ * Returns a pointer from an avl node of the domain string.
+ *
+ */
+static char *
+zfs_fuid_find_by_idx(zfsvfs_t *zfsvfs, uint32_t idx)
+{
+ char *domain;
+
+ if (idx == 0 || !zfsvfs->z_use_fuids)
+ return (NULL);
+
+ if (!zfsvfs->z_fuid_loaded)
+ zfs_fuid_init(zfsvfs, NULL);
+
+ rw_enter(&zfsvfs->z_fuid_lock, RW_READER);
+
+ if (zfsvfs->z_fuid_obj)
+ domain = zfs_fuid_idx_domain(&zfsvfs->z_fuid_idx, idx);
+ else
+ domain = nulldomain;
+ rw_exit(&zfsvfs->z_fuid_lock);
+
+ ASSERT(domain);
+ return (domain);
+}
+
+void
+zfs_fuid_map_ids(znode_t *zp, cred_t *cr, uid_t *uidp, uid_t *gidp)
+{
+ *uidp = zfs_fuid_map_id(zp->z_zfsvfs, zp->z_phys->zp_uid,
+ cr, ZFS_OWNER);
+ *gidp = zfs_fuid_map_id(zp->z_zfsvfs, zp->z_phys->zp_gid,
+ cr, ZFS_GROUP);
+}
+
+uid_t
+zfs_fuid_map_id(zfsvfs_t *zfsvfs, uint64_t fuid,
+ cred_t *cr, zfs_fuid_type_t type)
+{
+ uint32_t index = FUID_INDEX(fuid);
+ char *domain;
+ uid_t id;
+
+ if (index == 0)
+ return (fuid);
+
+ domain = zfs_fuid_find_by_idx(zfsvfs, index);
+ ASSERT(domain != NULL);
+
+#ifdef TODO
+ if (type == ZFS_OWNER || type == ZFS_ACE_USER) {
+ (void) kidmap_getuidbysid(crgetzone(cr), domain,
+ FUID_RID(fuid), &id);
+ } else {
+ (void) kidmap_getgidbysid(crgetzone(cr), domain,
+ FUID_RID(fuid), &id);
+ }
+#else
+ panic(__func__);
+#endif
+ return (id);
+}
+
+/*
+ * Add a FUID node to the list of fuid's being created for this
+ * ACL
+ *
+ * If ACL has multiple domains, then keep only one copy of each unique
+ * domain.
+ */
+static void
+zfs_fuid_node_add(zfs_fuid_info_t **fuidpp, const char *domain, uint32_t rid,
+ uint64_t idx, uint64_t id, zfs_fuid_type_t type)
+{
+ zfs_fuid_t *fuid;
+ zfs_fuid_domain_t *fuid_domain;
+ zfs_fuid_info_t *fuidp;
+ uint64_t fuididx;
+ boolean_t found = B_FALSE;
+
+ if (*fuidpp == NULL)
+ *fuidpp = zfs_fuid_info_alloc();
+
+ fuidp = *fuidpp;
+ /*
+ * First find fuid domain index in linked list
+ *
+ * If one isn't found then create an entry.
+ */
+
+ for (fuididx = 1, fuid_domain = list_head(&fuidp->z_domains);
+ fuid_domain; fuid_domain = list_next(&fuidp->z_domains,
+ fuid_domain), fuididx++) {
+ if (idx == fuid_domain->z_domidx) {
+ found = B_TRUE;
+ break;
+ }
+ }
+
+ if (!found) {
+ fuid_domain = kmem_alloc(sizeof (zfs_fuid_domain_t), KM_SLEEP);
+ fuid_domain->z_domain = domain;
+ fuid_domain->z_domidx = idx;
+ list_insert_tail(&fuidp->z_domains, fuid_domain);
+ fuidp->z_domain_str_sz += strlen(domain) + 1;
+ fuidp->z_domain_cnt++;
+ }
+
+ if (type == ZFS_ACE_USER || type == ZFS_ACE_GROUP) {
+ /*
+ * Now allocate fuid entry and add it on the end of the list
+ */
+
+ fuid = kmem_alloc(sizeof (zfs_fuid_t), KM_SLEEP);
+ fuid->z_id = id;
+ fuid->z_domidx = idx;
+ fuid->z_logfuid = FUID_ENCODE(fuididx, rid);
+
+ list_insert_tail(&fuidp->z_fuids, fuid);
+ fuidp->z_fuid_cnt++;
+ } else {
+ if (type == ZFS_OWNER)
+ fuidp->z_fuid_owner = FUID_ENCODE(fuididx, rid);
+ else
+ fuidp->z_fuid_group = FUID_ENCODE(fuididx, rid);
+ }
+}
+
+/*
+ * Create a file system FUID, based on information in the users cred
+ */
+uint64_t
+zfs_fuid_create_cred(zfsvfs_t *zfsvfs, zfs_fuid_type_t type,
+ dmu_tx_t *tx, cred_t *cr, zfs_fuid_info_t **fuidp)
+{
+ uint64_t idx;
+ ksid_t *ksid;
+ uint32_t rid;
+ char *kdomain;
+ const char *domain;
+ uid_t id;
+
+ VERIFY(type == ZFS_OWNER || type == ZFS_GROUP);
+
+ if (type == ZFS_OWNER)
+ id = crgetuid(cr);
+ else
+ id = crgetgid(cr);
+
+ if (!zfsvfs->z_use_fuids || !IS_EPHEMERAL(id))
+ return ((uint64_t)id);
+
+#ifdef TODO
+ ksid = crgetsid(cr, (type == ZFS_OWNER) ? KSID_OWNER : KSID_GROUP);
+
+ VERIFY(ksid != NULL);
+ rid = ksid_getrid(ksid);
+ domain = ksid_getdomain(ksid);
+
+ idx = zfs_fuid_find_by_domain(zfsvfs, domain, &kdomain, tx);
+
+ zfs_fuid_node_add(fuidp, kdomain, rid, idx, id, type);
+
+ return (FUID_ENCODE(idx, rid));
+#else
+ panic(__func__);
+#endif
+}
+
+/*
+ * Create a file system FUID for an ACL ace
+ * or a chown/chgrp of the file.
+ * This is similar to zfs_fuid_create_cred, except that
+ * we can't find the domain + rid information in the
+ * cred. Instead we have to query Winchester for the
+ * domain and rid.
+ *
+ * During replay operations the domain+rid information is
+ * found in the zfs_fuid_info_t that the replay code has
+ * attached to the zfsvfs of the file system.
+ */
+uint64_t
+zfs_fuid_create(zfsvfs_t *zfsvfs, uint64_t id, cred_t *cr,
+ zfs_fuid_type_t type, dmu_tx_t *tx, zfs_fuid_info_t **fuidpp)
+{
+ const char *domain;
+ char *kdomain;
+ uint32_t fuid_idx = FUID_INDEX(id);
+ uint32_t rid;
+ idmap_stat status;
+ uint64_t idx;
+ boolean_t is_replay = (zfsvfs->z_assign >= TXG_INITIAL);
+ zfs_fuid_t *zfuid = NULL;
+ zfs_fuid_info_t *fuidp;
+
+ /*
+ * If POSIX ID, or entry is already a FUID then
+ * just return the id
+ *
+ * We may also be handed an already FUID'ized id via
+ * chmod.
+ */
+
+ if (!zfsvfs->z_use_fuids || !IS_EPHEMERAL(id) || fuid_idx != 0)
+ return (id);
+
+ if (is_replay) {
+ fuidp = zfsvfs->z_fuid_replay;
+
+ /*
+ * If we are passed an ephemeral id, but no
+ * fuid_info was logged then return NOBODY.
+ * This is most likely a result of idmap service
+ * not being available.
+ */
+ if (fuidp == NULL)
+ return (UID_NOBODY);
+
+ switch (type) {
+ case ZFS_ACE_USER:
+ case ZFS_ACE_GROUP:
+ zfuid = list_head(&fuidp->z_fuids);
+ rid = FUID_RID(zfuid->z_logfuid);
+ idx = FUID_INDEX(zfuid->z_logfuid);
+ break;
+ case ZFS_OWNER:
+ rid = FUID_RID(fuidp->z_fuid_owner);
+ idx = FUID_INDEX(fuidp->z_fuid_owner);
+ break;
+ case ZFS_GROUP:
+ rid = FUID_RID(fuidp->z_fuid_group);
+ idx = FUID_INDEX(fuidp->z_fuid_group);
+ break;
+ };
+ domain = fuidp->z_domain_table[idx -1];
+ } else {
+#ifdef TODO
+ if (type == ZFS_OWNER || type == ZFS_ACE_USER)
+ status = kidmap_getsidbyuid(crgetzone(cr), id,
+ &domain, &rid);
+ else
+ status = kidmap_getsidbygid(crgetzone(cr), id,
+ &domain, &rid);
+
+ if (status != 0) {
+ /*
+ * When returning nobody we will need to
+ * make a dummy fuid table entry for logging
+ * purposes.
+ */
+ rid = UID_NOBODY;
+ domain = nulldomain;
+ }
+#else
+ panic(__func__);
+#endif
+ }
+
+ idx = zfs_fuid_find_by_domain(zfsvfs, domain, &kdomain, tx);
+
+ if (!is_replay)
+ zfs_fuid_node_add(fuidpp, kdomain, rid, idx, id, type);
+ else if (zfuid != NULL) {
+ list_remove(&fuidp->z_fuids, zfuid);
+ kmem_free(zfuid, sizeof (zfs_fuid_t));
+ }
+ return (FUID_ENCODE(idx, rid));
+}
+
+void
+zfs_fuid_destroy(zfsvfs_t *zfsvfs)
+{
+ rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER);
+ if (!zfsvfs->z_fuid_loaded) {
+ rw_exit(&zfsvfs->z_fuid_lock);
+ return;
+ }
+ zfs_fuid_table_destroy(&zfsvfs->z_fuid_idx, &zfsvfs->z_fuid_domain);
+ rw_exit(&zfsvfs->z_fuid_lock);
+}
+
+/*
+ * Allocate zfs_fuid_info for tracking FUIDs created during
+ * zfs_mknode, VOP_SETATTR() or VOP_SETSECATTR()
+ */
+zfs_fuid_info_t *
+zfs_fuid_info_alloc(void)
+{
+ zfs_fuid_info_t *fuidp;
+
+ fuidp = kmem_zalloc(sizeof (zfs_fuid_info_t), KM_SLEEP);
+ list_create(&fuidp->z_domains, sizeof (zfs_fuid_domain_t),
+ offsetof(zfs_fuid_domain_t, z_next));
+ list_create(&fuidp->z_fuids, sizeof (zfs_fuid_t),
+ offsetof(zfs_fuid_t, z_next));
+ return (fuidp);
+}
+
+/*
+ * Release all memory associated with zfs_fuid_info_t
+ */
+void
+zfs_fuid_info_free(zfs_fuid_info_t *fuidp)
+{
+ zfs_fuid_t *zfuid;
+ zfs_fuid_domain_t *zdomain;
+
+ while ((zfuid = list_head(&fuidp->z_fuids)) != NULL) {
+ list_remove(&fuidp->z_fuids, zfuid);
+ kmem_free(zfuid, sizeof (zfs_fuid_t));
+ }
+
+ if (fuidp->z_domain_table != NULL)
+ kmem_free(fuidp->z_domain_table,
+ (sizeof (char **)) * fuidp->z_domain_cnt);
+
+ while ((zdomain = list_head(&fuidp->z_domains)) != NULL) {
+ list_remove(&fuidp->z_domains, zdomain);
+ kmem_free(zdomain, sizeof (zfs_fuid_domain_t));
+ }
+
+ kmem_free(fuidp, sizeof (zfs_fuid_info_t));
+}
+
+/*
+ * Check to see if id is a groupmember. If cred
+ * has ksid info then sidlist is checked first
+ * and if still not found then POSIX groups are checked
+ *
+ * Will use a straight FUID compare when possible.
+ */
+boolean_t
+zfs_groupmember(zfsvfs_t *zfsvfs, uint64_t id, cred_t *cr)
+{
+ ksid_t *ksid = crgetsid(cr, KSID_GROUP);
+ uid_t gid;
+
+#ifdef TODO
+ if (ksid) {
+ int i;
+ ksid_t *ksid_groups;
+ ksidlist_t *ksidlist = crgetsidlist(cr);
+ uint32_t idx = FUID_INDEX(id);
+ uint32_t rid = FUID_RID(id);
+
+ ASSERT(ksidlist);
+ ksid_groups = ksidlist->ksl_sids;
+
+ for (i = 0; i != ksidlist->ksl_nsid; i++) {
+ if (idx == 0) {
+ if (id != IDMAP_WK_CREATOR_GROUP_GID &&
+ id == ksid_groups[i].ks_id) {
+ return (B_TRUE);
+ }
+ } else {
+ char *domain;
+
+ domain = zfs_fuid_find_by_idx(zfsvfs, idx);
+ ASSERT(domain != NULL);
+
+ if (strcmp(domain,
+ IDMAP_WK_CREATOR_SID_AUTHORITY) == 0)
+ return (B_FALSE);
+
+ if ((strcmp(domain,
+ ksid_groups[i].ks_domain->kd_name) == 0) &&
+ rid == ksid_groups[i].ks_rid)
+ return (B_TRUE);
+ }
+ }
+ }
+#endif
+
+ /*
+ * Not found in ksidlist, check posix groups
+ */
+ gid = zfs_fuid_map_id(zfsvfs, id, cr, ZFS_GROUP);
+ return (groupmember(gid, cr));
+}
+#endif
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
index 8699922..a6829eb 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
@@ -43,6 +41,7 @@
#include <sys/cmn_err.h>
#include <sys/stat.h>
#include <sys/zfs_ioctl.h>
+#include <sys/zfs_znode.h>
#include <sys/zap.h>
#include <sys/spa.h>
#include <sys/spa_impl.h>
@@ -52,6 +51,8 @@
#include <sys/dsl_dir.h>
#include <sys/dsl_dataset.h>
#include <sys/dsl_prop.h>
+#include <sys/dsl_deleg.h>
+#include <sys/dmu_objset.h>
#include <sys/sunddi.h>
#include <sys/policy.h>
#include <sys/zone.h>
@@ -62,10 +63,13 @@
#include <sys/varargs.h>
#include <sys/fs/zfs.h>
#include <sys/zfs_ctldir.h>
+#include <sys/zfs_dir.h>
#include <sys/zvol.h>
+#include <sys/dmu_objset.h>
#include "zfs_namecheck.h"
#include "zfs_prop.h"
+#include "zfs_deleg.h"
CTASSERT(sizeof(zfs_cmd_t) <= PAGE_SIZE);
@@ -75,18 +79,24 @@ extern void zfs_init(void);
extern void zfs_fini(void);
typedef int zfs_ioc_func_t(zfs_cmd_t *);
-typedef int zfs_secpolicy_func_t(const char *, cred_t *);
+typedef int zfs_secpolicy_func_t(zfs_cmd_t *, cred_t *);
typedef struct zfs_ioc_vec {
zfs_ioc_func_t *zvec_func;
zfs_secpolicy_func_t *zvec_secpolicy;
enum {
- no_name,
- pool_name,
- dataset_name
- } zvec_namecheck;
+ NO_NAME,
+ POOL_NAME,
+ DATASET_NAME
+ } zvec_namecheck;
+ boolean_t zvec_his_log;
} zfs_ioc_vec_t;
+static void clear_props(char *dataset, nvlist_t *props);
+static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *,
+ boolean_t *);
+int zfs_set_prop_nvlist(const char *, nvlist_t *);
+
/* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */
void
__dprintf(const char *file, const char *func, int line, const char *fmt, ...)
@@ -123,13 +133,122 @@ __dprintf(const char *file, const char *func, int line, const char *fmt, ...)
char *, newfile, char *, func, int, line, char *, buf);
}
+static void
+history_str_free(char *buf)
+{
+ kmem_free(buf, HIS_MAX_RECORD_LEN);
+}
+
+static char *
+history_str_get(zfs_cmd_t *zc)
+{
+ char *buf;
+
+ if (zc->zc_history == 0)
+ return (NULL);
+
+ buf = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP);
+ if (copyinstr((void *)(uintptr_t)zc->zc_history,
+ buf, HIS_MAX_RECORD_LEN, NULL) != 0) {
+ history_str_free(buf);
+ return (NULL);
+ }
+
+ buf[HIS_MAX_RECORD_LEN -1] = '\0';
+
+ return (buf);
+}
+
+/*
+ * Check to see if the named dataset is currently defined as bootable
+ */
+static boolean_t
+zfs_is_bootfs(const char *name)
+{
+ spa_t *spa;
+ boolean_t ret = B_FALSE;
+
+ if (spa_open(name, &spa, FTAG) == 0) {
+ if (spa->spa_bootfs) {
+ objset_t *os;
+
+ if (dmu_objset_open(name, DMU_OST_ZFS,
+ DS_MODE_USER | DS_MODE_READONLY, &os) == 0) {
+ ret = (dmu_objset_id(os) == spa->spa_bootfs);
+ dmu_objset_close(os);
+ }
+ }
+ spa_close(spa, FTAG);
+ }
+ return (ret);
+}
+
+/*
+ * zfs_earlier_version
+ *
+ * Return non-zero if the spa version is less than requested version.
+ */
+static int
+zfs_earlier_version(const char *name, int version)
+{
+ spa_t *spa;
+
+ if (spa_open(name, &spa, FTAG) == 0) {
+ if (spa_version(spa) < version) {
+ spa_close(spa, FTAG);
+ return (1);
+ }
+ spa_close(spa, FTAG);
+ }
+ return (0);
+}
+
+/*
+ * zpl_earlier_version
+ *
+ * Return TRUE if the ZPL version is less than requested version.
+ */
+static boolean_t
+zpl_earlier_version(const char *name, int version)
+{
+ objset_t *os;
+ boolean_t rc = B_TRUE;
+
+ if (dmu_objset_open(name, DMU_OST_ANY,
+ DS_MODE_USER | DS_MODE_READONLY, &os) == 0) {
+ uint64_t zplversion;
+
+ if (zfs_get_zplprop(os, ZFS_PROP_VERSION, &zplversion) == 0)
+ rc = zplversion < version;
+ dmu_objset_close(os);
+ }
+ return (rc);
+}
+
+static void
+zfs_log_history(zfs_cmd_t *zc)
+{
+ spa_t *spa;
+ char *buf;
+
+ if ((buf = history_str_get(zc)) == NULL)
+ return;
+
+ if (spa_open(zc->zc_name, &spa, FTAG) == 0) {
+ if (spa_version(spa) >= SPA_VERSION_ZPOOL_HISTORY)
+ (void) spa_history_log(spa, buf, LOG_CMD_NORMAL);
+ spa_close(spa, FTAG);
+ }
+ history_str_free(buf);
+}
+
/*
* Policy for top-level read operations (list pools). Requires no privileges,
* and can be used in the local zone, as there is no associated dataset.
*/
/* ARGSUSED */
static int
-zfs_secpolicy_none(const char *unused1, cred_t *cr)
+zfs_secpolicy_none(zfs_cmd_t *zc, cred_t *cr)
{
return (0);
}
@@ -140,10 +259,10 @@ zfs_secpolicy_none(const char *unused1, cred_t *cr)
*/
/* ARGSUSED */
static int
-zfs_secpolicy_read(const char *dataset, cred_t *cr)
+zfs_secpolicy_read(zfs_cmd_t *zc, cred_t *cr)
{
- if (INGLOBALZONE(curproc) ||
- zone_dataset_visible(dataset, NULL))
+ if (INGLOBALZONE(curthread) ||
+ zone_dataset_visible(zc->zc_name, NULL))
return (0);
return (ENOENT);
@@ -159,14 +278,14 @@ zfs_dozonecheck(const char *dataset, cred_t *cr)
* The dataset must be visible by this zone -- check this first
* so they don't see EPERM on something they shouldn't know about.
*/
- if (!INGLOBALZONE(curproc) &&
+ if (!INGLOBALZONE(curthread) &&
!zone_dataset_visible(dataset, &writable))
return (ENOENT);
if (dsl_prop_get_integer(dataset, "jailed", &zoned, NULL))
return (ENOENT);
- if (INGLOBALZONE(curproc)) {
+ if (INGLOBALZONE(curthread)) {
/*
* If the fs is zoned, only root can access it from the
* global zone.
@@ -187,47 +306,324 @@ zfs_dozonecheck(const char *dataset, cred_t *cr)
return (0);
}
-/*
- * Policy for dataset write operations (create children, set properties, etc).
- * Requires SYS_MOUNT privilege, and must be writable in the local zone.
- */
int
-zfs_secpolicy_write(const char *dataset, cred_t *cr)
+zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr)
+{
+ int error;
+
+ error = zfs_dozonecheck(name, cr);
+ if (error == 0) {
+ error = secpolicy_zfs(cr);
+ if (error)
+ error = dsl_deleg_access(name, perm, cr);
+ }
+ return (error);
+}
+
+static int
+zfs_secpolicy_setprop(const char *name, zfs_prop_t prop, cred_t *cr)
+{
+ /*
+ * Check permissions for special properties.
+ */
+ switch (prop) {
+ case ZFS_PROP_ZONED:
+ /*
+ * Disallow setting of 'zoned' from within a local zone.
+ */
+ if (!INGLOBALZONE(curthread))
+ return (EPERM);
+ break;
+
+ case ZFS_PROP_QUOTA:
+ if (!INGLOBALZONE(curthread)) {
+ uint64_t zoned;
+ char setpoint[MAXNAMELEN];
+ /*
+ * Unprivileged users are allowed to modify the
+ * quota on things *under* (ie. contained by)
+ * the thing they own.
+ */
+ if (dsl_prop_get_integer(name, "zoned", &zoned,
+ setpoint))
+ return (EPERM);
+ if (!zoned || strlen(name) <= strlen(setpoint))
+ return (EPERM);
+ }
+ break;
+ }
+
+ return (zfs_secpolicy_write_perms(name, zfs_prop_to_name(prop), cr));
+}
+
+int
+zfs_secpolicy_fsacl(zfs_cmd_t *zc, cred_t *cr)
{
int error;
- if (error = zfs_dozonecheck(dataset, cr))
+ error = zfs_dozonecheck(zc->zc_name, cr);
+ if (error)
return (error);
- return (secpolicy_zfs(cr));
+ /*
+ * permission to set permissions will be evaluated later in
+ * dsl_deleg_can_allow()
+ */
+ return (0);
+}
+
+int
+zfs_secpolicy_rollback(zfs_cmd_t *zc, cred_t *cr)
+{
+ int error;
+ error = zfs_secpolicy_write_perms(zc->zc_name,
+ ZFS_DELEG_PERM_ROLLBACK, cr);
+ if (error == 0)
+ error = zfs_secpolicy_write_perms(zc->zc_name,
+ ZFS_DELEG_PERM_MOUNT, cr);
+ return (error);
+}
+
+int
+zfs_secpolicy_send(zfs_cmd_t *zc, cred_t *cr)
+{
+ return (zfs_secpolicy_write_perms(zc->zc_name,
+ ZFS_DELEG_PERM_SEND, cr));
+}
+
+int
+zfs_secpolicy_share(zfs_cmd_t *zc, cred_t *cr)
+{
+ if (!INGLOBALZONE(curthread))
+ return (EPERM);
+
+ if (secpolicy_nfs(cr) == 0) {
+ return (0);
+ } else {
+ vnode_t *vp;
+ int error;
+
+ if ((error = lookupname(zc->zc_value, UIO_SYSSPACE,
+ NO_FOLLOW, NULL, &vp)) != 0)
+ return (error);
+
+ /* Now make sure mntpnt and dataset are ZFS */
+
+ if (strcmp(vp->v_vfsp->mnt_stat.f_fstypename, "zfs") != 0 ||
+ (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource),
+ zc->zc_name) != 0)) {
+ VN_RELE(vp);
+ return (EPERM);
+ }
+
+ VN_RELE(vp);
+ return (dsl_deleg_access(zc->zc_name,
+ ZFS_DELEG_PERM_SHARE, cr));
+ }
}
-/*
- * Policy for operations that want to write a dataset's parent:
- * create, destroy, snapshot, clone, restore.
- */
static int
-zfs_secpolicy_parent(const char *dataset, cred_t *cr)
+zfs_get_parent(const char *datasetname, char *parent, int parentsize)
{
- char parentname[MAXNAMELEN];
char *cp;
/*
* Remove the @bla or /bla from the end of the name to get the parent.
*/
- (void) strncpy(parentname, dataset, sizeof (parentname));
- cp = strrchr(parentname, '@');
+ (void) strncpy(parent, datasetname, parentsize);
+ cp = strrchr(parent, '@');
if (cp != NULL) {
cp[0] = '\0';
} else {
- cp = strrchr(parentname, '/');
+ cp = strrchr(parent, '/');
if (cp == NULL)
return (ENOENT);
cp[0] = '\0';
+ }
+
+ return (0);
+}
+
+int
+zfs_secpolicy_destroy_perms(const char *name, cred_t *cr)
+{
+ int error;
+
+ if ((error = zfs_secpolicy_write_perms(name,
+ ZFS_DELEG_PERM_MOUNT, cr)) != 0)
+ return (error);
+
+ return (zfs_secpolicy_write_perms(name, ZFS_DELEG_PERM_DESTROY, cr));
+}
+
+static int
+zfs_secpolicy_destroy(zfs_cmd_t *zc, cred_t *cr)
+{
+ return (zfs_secpolicy_destroy_perms(zc->zc_name, cr));
+}
+
+/*
+ * Must have sys_config privilege to check the iscsi permission
+ */
+/* ARGSUSED */
+static int
+zfs_secpolicy_iscsi(zfs_cmd_t *zc, cred_t *cr)
+{
+ return (secpolicy_zfs(cr));
+}
+
+int
+zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr)
+{
+ char parentname[MAXNAMELEN];
+ int error;
+
+ if ((error = zfs_secpolicy_write_perms(from,
+ ZFS_DELEG_PERM_RENAME, cr)) != 0)
+ return (error);
+
+ if ((error = zfs_secpolicy_write_perms(from,
+ ZFS_DELEG_PERM_MOUNT, cr)) != 0)
+ return (error);
+
+ if ((error = zfs_get_parent(to, parentname,
+ sizeof (parentname))) != 0)
+ return (error);
+
+ if ((error = zfs_secpolicy_write_perms(parentname,
+ ZFS_DELEG_PERM_CREATE, cr)) != 0)
+ return (error);
+
+ if ((error = zfs_secpolicy_write_perms(parentname,
+ ZFS_DELEG_PERM_MOUNT, cr)) != 0)
+ return (error);
+
+ return (error);
+}
+
+static int
+zfs_secpolicy_rename(zfs_cmd_t *zc, cred_t *cr)
+{
+ return (zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr));
+}
+static int
+zfs_secpolicy_promote(zfs_cmd_t *zc, cred_t *cr)
+{
+ char parentname[MAXNAMELEN];
+ objset_t *clone;
+ int error;
+
+ error = zfs_secpolicy_write_perms(zc->zc_name,
+ ZFS_DELEG_PERM_PROMOTE, cr);
+ if (error)
+ return (error);
+
+ error = dmu_objset_open(zc->zc_name, DMU_OST_ANY,
+ DS_MODE_USER | DS_MODE_READONLY, &clone);
+
+ if (error == 0) {
+ dsl_dataset_t *pclone = NULL;
+ dsl_dir_t *dd;
+ dd = clone->os->os_dsl_dataset->ds_dir;
+
+ rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
+ error = dsl_dataset_hold_obj(dd->dd_pool,
+ dd->dd_phys->dd_origin_obj, FTAG, &pclone);
+ rw_exit(&dd->dd_pool->dp_config_rwlock);
+ if (error) {
+ dmu_objset_close(clone);
+ return (error);
+ }
+
+ error = zfs_secpolicy_write_perms(zc->zc_name,
+ ZFS_DELEG_PERM_MOUNT, cr);
+
+ dsl_dataset_name(pclone, parentname);
+ dmu_objset_close(clone);
+ dsl_dataset_rele(pclone, FTAG);
+ if (error == 0)
+ error = zfs_secpolicy_write_perms(parentname,
+ ZFS_DELEG_PERM_PROMOTE, cr);
}
+ return (error);
+}
+
+static int
+zfs_secpolicy_receive(zfs_cmd_t *zc, cred_t *cr)
+{
+ int error;
+
+ if ((error = zfs_secpolicy_write_perms(zc->zc_name,
+ ZFS_DELEG_PERM_RECEIVE, cr)) != 0)
+ return (error);
+
+ if ((error = zfs_secpolicy_write_perms(zc->zc_name,
+ ZFS_DELEG_PERM_MOUNT, cr)) != 0)
+ return (error);
- return (zfs_secpolicy_write(parentname, cr));
+ return (zfs_secpolicy_write_perms(zc->zc_name,
+ ZFS_DELEG_PERM_CREATE, cr));
+}
+
+int
+zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr)
+{
+ int error;
+
+ if ((error = zfs_secpolicy_write_perms(name,
+ ZFS_DELEG_PERM_SNAPSHOT, cr)) != 0)
+ return (error);
+
+ error = zfs_secpolicy_write_perms(name,
+ ZFS_DELEG_PERM_MOUNT, cr);
+
+ return (error);
+}
+
+static int
+zfs_secpolicy_snapshot(zfs_cmd_t *zc, cred_t *cr)
+{
+
+ return (zfs_secpolicy_snapshot_perms(zc->zc_name, cr));
+}
+
+static int
+zfs_secpolicy_create(zfs_cmd_t *zc, cred_t *cr)
+{
+ char parentname[MAXNAMELEN];
+ int error;
+
+ if ((error = zfs_get_parent(zc->zc_name, parentname,
+ sizeof (parentname))) != 0)
+ return (error);
+
+ if (zc->zc_value[0] != '\0') {
+ if ((error = zfs_secpolicy_write_perms(zc->zc_value,
+ ZFS_DELEG_PERM_CLONE, cr)) != 0)
+ return (error);
+ }
+
+ if ((error = zfs_secpolicy_write_perms(parentname,
+ ZFS_DELEG_PERM_CREATE, cr)) != 0)
+ return (error);
+
+ error = zfs_secpolicy_write_perms(parentname,
+ ZFS_DELEG_PERM_MOUNT, cr);
+
+ return (error);
+}
+
+static int
+zfs_secpolicy_umount(zfs_cmd_t *zc, cred_t *cr)
+{
+ int error;
+
+ error = secpolicy_fs_unmount(cr, NULL);
+ if (error) {
+ error = dsl_deleg_access(zc->zc_name, ZFS_DELEG_PERM_MOUNT, cr);
+ }
+ return (error);
}
/*
@@ -236,7 +632,7 @@ zfs_secpolicy_parent(const char *dataset, cred_t *cr)
*/
/* ARGSUSED */
static int
-zfs_secpolicy_config(const char *unused, cred_t *cr)
+zfs_secpolicy_config(zfs_cmd_t *zc, cred_t *cr)
{
if (secpolicy_sys_config(cr, B_FALSE) != 0)
return (EPERM);
@@ -245,15 +641,48 @@ zfs_secpolicy_config(const char *unused, cred_t *cr)
}
/*
+ * Just like zfs_secpolicy_config, except that we will check for
+ * mount permission on the dataset for permission to create/remove
+ * the minor nodes.
+ */
+static int
+zfs_secpolicy_minor(zfs_cmd_t *zc, cred_t *cr)
+{
+ if (secpolicy_sys_config(cr, B_FALSE) != 0) {
+ return (dsl_deleg_access(zc->zc_name,
+ ZFS_DELEG_PERM_MOUNT, cr));
+ }
+
+ return (0);
+}
+
+/*
* Policy for fault injection. Requires all privileges.
*/
/* ARGSUSED */
static int
-zfs_secpolicy_inject(const char *unused, cred_t *cr)
+zfs_secpolicy_inject(zfs_cmd_t *zc, cred_t *cr)
{
return (secpolicy_zinject(cr));
}
+static int
+zfs_secpolicy_inherit(zfs_cmd_t *zc, cred_t *cr)
+{
+ zfs_prop_t prop = zfs_name_to_prop(zc->zc_value);
+
+ if (prop == ZPROP_INVAL) {
+ if (!zfs_prop_user(zc->zc_value))
+ return (EINVAL);
+ return (zfs_secpolicy_write_perms(zc->zc_name,
+ ZFS_DELEG_PERM_USERPROP, cr));
+ } else {
+ if (!zfs_prop_inheritable(prop))
+ return (EINVAL);
+ return (zfs_secpolicy_setprop(zc->zc_name, prop, cr));
+ }
+}
+
/*
* Policy for dataset backup operations (sendbackup).
* Requires SYS_MOUNT privilege, and must be writable in the local zone.
@@ -263,7 +692,7 @@ zfs_secpolicy_operator(const char *dataset, cred_t *cr)
{
int writable = 1;
- if (!INGLOBALZONE(curproc) && !zone_dataset_visible(dataset, &writable))
+ if (!INGLOBALZONE(curthread) && !zone_dataset_visible(dataset, &writable))
return (ENOENT);
if (secpolicy_zfs(cr) != 0 && !groupmember(GID_OPERATOR, cr))
return (EPERM);
@@ -274,35 +703,33 @@ zfs_secpolicy_operator(const char *dataset, cred_t *cr)
* Returns the nvlist as specified by the user in the zfs_cmd_t.
*/
static int
-get_nvlist(zfs_cmd_t *zc, nvlist_t **nvp)
+get_nvlist(uint64_t nvl, uint64_t size, nvlist_t **nvp)
{
char *packed;
- size_t size;
int error;
- nvlist_t *config = NULL;
+ nvlist_t *list = NULL;
/*
* Read in and unpack the user-supplied nvlist.
*/
- if ((size = zc->zc_nvlist_src_size) == 0)
+ if (size == 0)
return (EINVAL);
packed = kmem_alloc(size, KM_SLEEP);
- if ((error = xcopyin((void *)(uintptr_t)zc->zc_nvlist_src, packed,
- size)) != 0) {
+ if ((error = xcopyin((void *)(uintptr_t)nvl, packed, size)) != 0) {
kmem_free(packed, size);
return (error);
}
- if ((error = nvlist_unpack(packed, size, &config, 0)) != 0) {
+ if ((error = nvlist_unpack(packed, size, &list, 0)) != 0) {
kmem_free(packed, size);
return (error);
}
kmem_free(packed, size);
- *nvp = config;
+ *nvp = list;
return (0);
}
@@ -326,6 +753,7 @@ put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl)
*/
error = 0;
} else {
+ packed = kmem_alloc(size, KM_SLEEP);
VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
KM_SLEEP) == 0);
error = xcopyout(packed, (void *)(uintptr_t)zc->zc_nvlist_dst,
@@ -341,15 +769,67 @@ static int
zfs_ioc_pool_create(zfs_cmd_t *zc)
{
int error;
- nvlist_t *config;
+ nvlist_t *config, *props = NULL;
+ nvlist_t *rootprops = NULL;
+ nvlist_t *zplprops = NULL;
+ char *buf;
+
+ if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
+ &config))
+ return (error);
- if ((error = get_nvlist(zc, &config)) != 0)
+ if (zc->zc_nvlist_src_size != 0 && (error =
+ get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, &props))) {
+ nvlist_free(config);
return (error);
+ }
+
+ if (props) {
+ nvlist_t *nvl = NULL;
+ uint64_t version = SPA_VERSION;
+
+ (void) nvlist_lookup_uint64(props,
+ zpool_prop_to_name(ZPOOL_PROP_VERSION), &version);
+ if (version < SPA_VERSION_INITIAL || version > SPA_VERSION) {
+ error = EINVAL;
+ goto pool_props_bad;
+ }
+ (void) nvlist_lookup_nvlist(props, ZPOOL_ROOTFS_PROPS, &nvl);
+ if (nvl) {
+ error = nvlist_dup(nvl, &rootprops, KM_SLEEP);
+ if (error != 0) {
+ nvlist_free(config);
+ nvlist_free(props);
+ return (error);
+ }
+ (void) nvlist_remove_all(props, ZPOOL_ROOTFS_PROPS);
+ }
+ VERIFY(nvlist_alloc(&zplprops, NV_UNIQUE_NAME, KM_SLEEP) == 0);
+ error = zfs_fill_zplprops_root(version, rootprops,
+ zplprops, NULL);
+ if (error)
+ goto pool_props_bad;
+ }
+
+ buf = history_str_get(zc);
- error = spa_create(zc->zc_name, config, zc->zc_value[0] == '\0' ?
- NULL : zc->zc_value);
+ error = spa_create(zc->zc_name, config, props, buf, zplprops);
+ /*
+ * Set the remaining root properties
+ */
+ if (!error &&
+ (error = zfs_set_prop_nvlist(zc->zc_name, rootprops)) != 0)
+ (void) spa_destroy(zc->zc_name);
+
+ if (buf != NULL)
+ history_str_free(buf);
+
+pool_props_bad:
+ nvlist_free(rootprops);
+ nvlist_free(zplprops);
nvlist_free(config);
+ nvlist_free(props);
return (error);
}
@@ -357,35 +837,55 @@ zfs_ioc_pool_create(zfs_cmd_t *zc)
static int
zfs_ioc_pool_destroy(zfs_cmd_t *zc)
{
- return (spa_destroy(zc->zc_name));
+ int error;
+ zfs_log_history(zc);
+ error = spa_destroy(zc->zc_name);
+ return (error);
}
static int
zfs_ioc_pool_import(zfs_cmd_t *zc)
{
int error;
- nvlist_t *config;
+ nvlist_t *config, *props = NULL;
uint64_t guid;
- if ((error = get_nvlist(zc, &config)) != 0)
+ if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
+ &config)) != 0)
return (error);
+ if (zc->zc_nvlist_src_size != 0 && (error =
+ get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, &props))) {
+ nvlist_free(config);
+ return (error);
+ }
+
if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 ||
guid != zc->zc_guid)
error = EINVAL;
+ else if (zc->zc_cookie)
+ error = spa_import_faulted(zc->zc_name, config,
+ props);
else
- error = spa_import(zc->zc_name, config,
- zc->zc_value[0] == '\0' ? NULL : zc->zc_value);
+ error = spa_import(zc->zc_name, config, props);
nvlist_free(config);
+ if (props)
+ nvlist_free(props);
+
return (error);
}
static int
zfs_ioc_pool_export(zfs_cmd_t *zc)
{
- return (spa_export(zc->zc_name, NULL));
+ int error;
+ boolean_t force = (boolean_t)zc->zc_cookie;
+
+ zfs_log_history(zc);
+ error = spa_export(zc->zc_name, NULL, force);
+ return (error);
}
static int
@@ -441,7 +941,8 @@ zfs_ioc_pool_tryimport(zfs_cmd_t *zc)
nvlist_t *tryconfig, *config;
int error;
- if ((error = get_nvlist(zc, &tryconfig)) != 0)
+ if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
+ &tryconfig)) != 0)
return (error);
config = spa_tryimport(tryconfig);
@@ -466,7 +967,7 @@ zfs_ioc_pool_scrub(zfs_cmd_t *zc)
if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
return (error);
- error = spa_scrub(spa, zc->zc_cookie, B_FALSE);
+ error = spa_scrub(spa, zc->zc_cookie);
spa_close(spa, FTAG);
@@ -496,8 +997,12 @@ zfs_ioc_pool_upgrade(zfs_cmd_t *zc)
if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
return (error);
- spa_upgrade(spa);
+ if (zc->zc_cookie < spa_version(spa) || zc->zc_cookie > SPA_VERSION) {
+ spa_close(spa, FTAG);
+ return (EINVAL);
+ }
+ spa_upgrade(spa, zc->zc_cookie);
spa_close(spa, FTAG);
return (error);
@@ -517,7 +1022,7 @@ zfs_ioc_pool_get_history(zfs_cmd_t *zc)
if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
return (error);
- if (spa_version(spa) < ZFS_VERSION_ZPOOL_HISTORY) {
+ if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) {
spa_close(spa, FTAG);
return (ENOTSUP);
}
@@ -525,7 +1030,8 @@ zfs_ioc_pool_get_history(zfs_cmd_t *zc)
hist_buf = kmem_alloc(size, KM_SLEEP);
if ((error = spa_history_get(spa, &zc->zc_history_offset,
&zc->zc_history_len, hist_buf)) == 0) {
- error = xcopyout(hist_buf, (char *)(uintptr_t)zc->zc_history,
+ error = xcopyout(hist_buf,
+ (char *)(uintptr_t)zc->zc_history,
zc->zc_history_len);
}
@@ -535,45 +1041,6 @@ zfs_ioc_pool_get_history(zfs_cmd_t *zc)
}
static int
-zfs_ioc_pool_log_history(zfs_cmd_t *zc)
-{
- spa_t *spa;
- char *history_str = NULL;
- size_t size;
- int error;
-
- size = zc->zc_history_len;
- if (size == 0 || size > HIS_MAX_RECORD_LEN)
- return (EINVAL);
-
- if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
- return (error);
-
- if (spa_version(spa) < ZFS_VERSION_ZPOOL_HISTORY) {
- spa_close(spa, FTAG);
- return (ENOTSUP);
- }
-
- /* add one for the NULL delimiter */
- size++;
- history_str = kmem_alloc(size, KM_SLEEP);
- if ((error = xcopyin((void *)(uintptr_t)zc->zc_history, history_str,
- size)) != 0) {
- spa_close(spa, FTAG);
- kmem_free(history_str, size);
- return (error);
- }
- history_str[size - 1] = '\0';
-
- error = spa_history_log(spa, history_str, zc->zc_history_offset);
-
- spa_close(spa, FTAG);
- kmem_free(history_str, size);
-
- return (error);
-}
-
-static int
zfs_ioc_dsobj_to_dsname(zfs_cmd_t *zc)
{
int error;
@@ -591,9 +1058,8 @@ zfs_ioc_obj_to_path(zfs_cmd_t *zc)
int error;
if ((error = dmu_objset_open(zc->zc_name, DMU_OST_ZFS,
- DS_MODE_NONE | DS_MODE_READONLY, &osp)) != 0)
+ DS_MODE_USER | DS_MODE_READONLY, &osp)) != 0)
return (error);
-
error = zfs_obj_to_path(osp, zc->zc_obj, zc->zc_value,
sizeof (zc->zc_value));
dmu_objset_close(osp);
@@ -606,26 +1072,40 @@ zfs_ioc_vdev_add(zfs_cmd_t *zc)
{
spa_t *spa;
int error;
- nvlist_t *config;
+ nvlist_t *config, **l2cache, **spares;
+ uint_t nl2cache = 0, nspares = 0;
error = spa_open(zc->zc_name, &spa, FTAG);
if (error != 0)
return (error);
+ error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
+ &config);
+ (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_L2CACHE,
+ &l2cache, &nl2cache);
+
+ (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_SPARES,
+ &spares, &nspares);
+
/*
* A root pool with concatenated devices is not supported.
- * Thus, can not add a device to a root pool with one device.
+ * Thus, can not add a device to a root pool.
+ *
+ * Intent log device can not be added to a rootpool because
+ * during mountroot, zil is replayed, a seperated log device
+ * can not be accessed during the mountroot time.
+ *
+ * l2cache and spare devices are ok to be added to a rootpool.
*/
- if (spa->spa_root_vdev->vdev_children == 1 && spa->spa_bootfs != 0) {
+ if (spa->spa_bootfs != 0 && nl2cache == 0 && nspares == 0) {
spa_close(spa, FTAG);
return (EDOM);
}
- if ((error = get_nvlist(zc, &config)) == 0) {
+ if (error == 0) {
error = spa_vdev_add(spa, config);
nvlist_free(config);
}
-
spa_close(spa, FTAG);
return (error);
}
@@ -645,28 +1125,35 @@ zfs_ioc_vdev_remove(zfs_cmd_t *zc)
}
static int
-zfs_ioc_vdev_online(zfs_cmd_t *zc)
+zfs_ioc_vdev_set_state(zfs_cmd_t *zc)
{
spa_t *spa;
int error;
+ vdev_state_t newstate = VDEV_STATE_UNKNOWN;
if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
return (error);
- error = vdev_online(spa, zc->zc_guid);
- spa_close(spa, FTAG);
- return (error);
-}
+ switch (zc->zc_cookie) {
+ case VDEV_STATE_ONLINE:
+ error = vdev_online(spa, zc->zc_guid, zc->zc_obj, &newstate);
+ break;
-static int
-zfs_ioc_vdev_offline(zfs_cmd_t *zc)
-{
- spa_t *spa;
- int istmp = zc->zc_cookie;
- int error;
+ case VDEV_STATE_OFFLINE:
+ error = vdev_offline(spa, zc->zc_guid, zc->zc_obj);
+ break;
- if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
- return (error);
- error = vdev_offline(spa, zc->zc_guid, istmp);
+ case VDEV_STATE_FAULTED:
+ error = vdev_fault(spa, zc->zc_guid);
+ break;
+
+ case VDEV_STATE_DEGRADED:
+ error = vdev_degrade(spa, zc->zc_guid);
+ break;
+
+ default:
+ error = EINVAL;
+ }
+ zc->zc_cookie = newstate;
spa_close(spa, FTAG);
return (error);
}
@@ -682,7 +1169,8 @@ zfs_ioc_vdev_attach(zfs_cmd_t *zc)
if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
return (error);
- if ((error = get_nvlist(zc, &config)) == 0) {
+ if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
+ &config)) == 0) {
error = spa_vdev_attach(spa, zc->zc_guid, config, replacing);
nvlist_free(config);
}
@@ -723,6 +1211,16 @@ zfs_ioc_vdev_setpath(zfs_cmd_t *zc)
return (error);
}
+/*
+ * inputs:
+ * zc_name name of filesystem
+ * zc_nvlist_dst_size size of buffer for property nvlist
+ *
+ * outputs:
+ * zc_objset_stats stats
+ * zc_nvlist_dst property nvlist
+ * zc_nvlist_dst_size size of property nvlist
+ */
static int
zfs_ioc_objset_stats(zfs_cmd_t *zc)
{
@@ -730,44 +1228,29 @@ zfs_ioc_objset_stats(zfs_cmd_t *zc)
int error;
nvlist_t *nv;
-retry:
- error = dmu_objset_open(zc->zc_name, DMU_OST_ANY,
- DS_MODE_STANDARD | DS_MODE_READONLY, &os);
- if (error != 0) {
- /*
- * This is ugly: dmu_objset_open() can return EBUSY if
- * the objset is held exclusively. Fortunately this hold is
- * only for a short while, so we retry here.
- * This avoids user code having to handle EBUSY,
- * for example for a "zfs list".
- */
- if (error == EBUSY) {
- delay(1);
- goto retry;
- }
+ if (error = dmu_objset_open(zc->zc_name,
+ DMU_OST_ANY, DS_MODE_USER | DS_MODE_READONLY, &os))
return (error);
- }
dmu_objset_fast_stat(os, &zc->zc_objset_stats);
if (zc->zc_nvlist_dst != 0 &&
- (error = dsl_prop_get_all(os, &nv)) == 0) {
+ (error = dsl_prop_get_all(os, &nv, FALSE)) == 0) {
dmu_objset_stats(os, nv);
/*
* NB: zvol_get_stats() will read the objset contents,
* which we aren't supposed to do with a
- * DS_MODE_STANDARD open, because it could be
+ * DS_MODE_USER hold, because it could be
* inconsistent. So this is a bit of a workaround...
*/
- if (!zc->zc_objset_stats.dds_inconsistent &&
- dmu_objset_type(os) == DMU_OST_ZVOL)
- VERIFY(zvol_get_stats(os, nv) == 0);
+ if (!zc->zc_objset_stats.dds_inconsistent) {
+ if (dmu_objset_type(os) == DMU_OST_ZVOL)
+ VERIFY(zvol_get_stats(os, nv) == 0);
+ }
error = put_nvlist(zc, nv);
nvlist_free(nv);
}
- spa_altroot(dmu_objset_spa(os), zc->zc_value, sizeof (zc->zc_value));
-
dmu_objset_close(os);
if (error == ENOMEM)
error = 0;
@@ -775,27 +1258,87 @@ retry:
}
static int
+nvl_add_zplprop(objset_t *os, nvlist_t *props, zfs_prop_t prop)
+{
+ uint64_t value;
+ int error;
+
+ /*
+ * zfs_get_zplprop() will either find a value or give us
+ * the default value (if there is one).
+ */
+ if ((error = zfs_get_zplprop(os, prop, &value)) != 0)
+ return (error);
+ VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(prop), value) == 0);
+ return (0);
+}
+
+/*
+ * inputs:
+ * zc_name name of filesystem
+ * zc_nvlist_dst_size size of buffer for zpl property nvlist
+ *
+ * outputs:
+ * zc_nvlist_dst zpl property nvlist
+ * zc_nvlist_dst_size size of zpl property nvlist
+ */
+static int
+zfs_ioc_objset_zplprops(zfs_cmd_t *zc)
+{
+ objset_t *os;
+ int err;
+
+ if (err = dmu_objset_open(zc->zc_name,
+ DMU_OST_ANY, DS_MODE_USER | DS_MODE_READONLY, &os))
+ return (err);
+
+ dmu_objset_fast_stat(os, &zc->zc_objset_stats);
+
+ /*
+ * NB: nvl_add_zplprop() will read the objset contents,
+ * which we aren't supposed to do with a DS_MODE_USER
+ * hold, because it could be inconsistent.
+ */
+ if (zc->zc_nvlist_dst != 0 &&
+ !zc->zc_objset_stats.dds_inconsistent &&
+ dmu_objset_type(os) == DMU_OST_ZFS) {
+ nvlist_t *nv;
+
+ VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0);
+ if ((err = nvl_add_zplprop(os, nv, ZFS_PROP_VERSION)) == 0 &&
+ (err = nvl_add_zplprop(os, nv, ZFS_PROP_NORMALIZE)) == 0 &&
+ (err = nvl_add_zplprop(os, nv, ZFS_PROP_UTF8ONLY)) == 0 &&
+ (err = nvl_add_zplprop(os, nv, ZFS_PROP_CASE)) == 0)
+ err = put_nvlist(zc, nv);
+ nvlist_free(nv);
+ } else {
+ err = ENOENT;
+ }
+ dmu_objset_close(os);
+ return (err);
+}
+
+/*
+ * inputs:
+ * zc_name name of filesystem
+ * zc_cookie zap cursor
+ * zc_nvlist_dst_size size of buffer for property nvlist
+ *
+ * outputs:
+ * zc_name name of next filesystem
+ * zc_objset_stats stats
+ * zc_nvlist_dst property nvlist
+ * zc_nvlist_dst_size size of property nvlist
+ */
+static int
zfs_ioc_dataset_list_next(zfs_cmd_t *zc)
{
objset_t *os;
int error;
char *p;
-retry:
- error = dmu_objset_open(zc->zc_name, DMU_OST_ANY,
- DS_MODE_STANDARD | DS_MODE_READONLY, &os);
- if (error != 0) {
- /*
- * This is ugly: dmu_objset_open() can return EBUSY if
- * the objset is held exclusively. Fortunately this hold is
- * only for a short while, so we retry here.
- * This avoids user code having to handle EBUSY,
- * for example for a "zfs list".
- */
- if (error == EBUSY) {
- delay(1);
- goto retry;
- }
+ if (error = dmu_objset_open(zc->zc_name,
+ DMU_OST_ANY, DS_MODE_USER | DS_MODE_READONLY, &os)) {
if (error == ENOENT)
error = ESRCH;
return (error);
@@ -812,8 +1355,9 @@ retry:
NULL, &zc->zc_cookie);
if (error == ENOENT)
error = ESRCH;
- } while (error == 0 && !INGLOBALZONE(curproc) &&
+ } while (error == 0 && !INGLOBALZONE(curthread) &&
!zone_dataset_visible(zc->zc_name, NULL));
+ dmu_objset_close(os);
/*
* If it's a hidden dataset (ie. with a '$' in its name), don't
@@ -822,35 +1366,31 @@ retry:
if (error == 0 && strchr(zc->zc_name, '$') == NULL)
error = zfs_ioc_objset_stats(zc); /* fill in the stats */
- dmu_objset_close(os);
return (error);
}
+/*
+ * inputs:
+ * zc_name name of filesystem
+ * zc_cookie zap cursor
+ * zc_nvlist_dst_size size of buffer for property nvlist
+ *
+ * outputs:
+ * zc_name name of next snapshot
+ * zc_objset_stats stats
+ * zc_nvlist_dst property nvlist
+ * zc_nvlist_dst_size size of property nvlist
+ */
static int
zfs_ioc_snapshot_list_next(zfs_cmd_t *zc)
{
objset_t *os;
int error;
-retry:
- error = dmu_objset_open(zc->zc_name, DMU_OST_ANY,
- DS_MODE_STANDARD | DS_MODE_READONLY, &os);
- if (error != 0) {
- /*
- * This is ugly: dmu_objset_open() can return EBUSY if
- * the objset is held exclusively. Fortunately this hold is
- * only for a short while, so we retry here.
- * This avoids user code having to handle EBUSY,
- * for example for a "zfs list".
- */
- if (error == EBUSY) {
- delay(1);
- goto retry;
- }
- if (error == ENOENT)
- error = ESRCH;
- return (error);
- }
+ error = dmu_objset_open(zc->zc_name,
+ DMU_OST_ANY, DS_MODE_USER | DS_MODE_READONLY, &os);
+ if (error)
+ return (error == ENOENT ? ESRCH : error);
/*
* A dataset name of maximum length cannot have any snapshots,
@@ -863,36 +1403,36 @@ retry:
error = dmu_snapshot_list_next(os,
sizeof (zc->zc_name) - strlen(zc->zc_name),
- zc->zc_name + strlen(zc->zc_name), NULL, &zc->zc_cookie);
- if (error == ENOENT)
- error = ESRCH;
-
+ zc->zc_name + strlen(zc->zc_name), NULL, &zc->zc_cookie, NULL);
+ dmu_objset_close(os);
if (error == 0)
error = zfs_ioc_objset_stats(zc); /* fill in the stats */
+ else if (error == ENOENT)
+ error = ESRCH;
- dmu_objset_close(os);
+ /* if we failed, undo the @ that we tacked on to zc_name */
+ if (error)
+ *strchr(zc->zc_name, '@') = '\0';
return (error);
}
-static int
-zfs_set_prop_nvlist(const char *name, dev_t dev, cred_t *cr, nvlist_t *nvl)
+int
+zfs_set_prop_nvlist(const char *name, nvlist_t *nvl)
{
nvpair_t *elem;
int error;
- const char *propname;
- zfs_prop_t prop;
uint64_t intval;
char *strval;
- char buf[MAXNAMELEN];
- const char *p;
- spa_t *spa;
+ /*
+ * First validate permission to set all of the properties
+ */
elem = NULL;
while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
- propname = nvpair_name(elem);
+ const char *propname = nvpair_name(elem);
+ zfs_prop_t prop = zfs_name_to_prop(propname);
- if ((prop = zfs_name_to_prop(propname)) ==
- ZFS_PROP_INVAL) {
+ if (prop == ZPROP_INVAL) {
/*
* If this is a user-defined property, it must be a
* string, and there is no further validation to do.
@@ -901,51 +1441,19 @@ zfs_set_prop_nvlist(const char *name, dev_t dev, cred_t *cr, nvlist_t *nvl)
nvpair_type(elem) != DATA_TYPE_STRING)
return (EINVAL);
- VERIFY(nvpair_value_string(elem, &strval) == 0);
- error = dsl_prop_set(name, propname, 1,
- strlen(strval) + 1, strval);
- if (error == 0)
- continue;
- else
+ if (error = zfs_secpolicy_write_perms(name,
+ ZFS_DELEG_PERM_USERPROP, CRED()))
return (error);
+ continue;
}
+ if ((error = zfs_secpolicy_setprop(name, prop, CRED())) != 0)
+ return (error);
+
/*
- * Check permissions for special properties.
+ * Check that this value is valid for this pool version
*/
switch (prop) {
- case ZFS_PROP_ZONED:
- /*
- * Disallow setting of 'zoned' from within a local zone.
- */
- if (!INGLOBALZONE(curproc))
- return (EPERM);
- break;
-
- case ZFS_PROP_QUOTA:
- if (error = zfs_dozonecheck(name, cr))
- return (error);
-
- if (!INGLOBALZONE(curproc)) {
- uint64_t zoned;
- char setpoint[MAXNAMELEN];
- int dslen;
- /*
- * Unprivileged users are allowed to modify the
- * quota on things *under* (ie. contained by)
- * the thing they own.
- */
- if (dsl_prop_get_integer(name, "jailed", &zoned,
- setpoint))
- return (EPERM);
- if (!zoned) /* this shouldn't happen */
- return (EPERM);
- dslen = strlen(name);
- if (dslen <= strlen(setpoint))
- return (EPERM);
- }
- break;
-
case ZFS_PROP_COMPRESSION:
/*
* If the user specified gzip compression, make sure
@@ -953,35 +1461,64 @@ zfs_set_prop_nvlist(const char *name, dev_t dev, cred_t *cr, nvlist_t *nvl)
* we'll catch them later.
*/
if (nvpair_type(elem) == DATA_TYPE_UINT64 &&
- nvpair_value_uint64(elem, &intval) == 0 &&
- intval >= ZIO_COMPRESS_GZIP_1 &&
- intval <= ZIO_COMPRESS_GZIP_9) {
- if ((p = strchr(name, '/')) == NULL) {
- p = name;
- } else {
- bcopy(name, buf, p - name);
- buf[p - name] = '\0';
- p = buf;
- }
-
- if (spa_open(p, &spa, FTAG) == 0) {
- if (spa_version(spa) <
- ZFS_VERSION_GZIP_COMPRESSION) {
- spa_close(spa, FTAG);
- return (ENOTSUP);
- }
+ nvpair_value_uint64(elem, &intval) == 0) {
+ if (intval >= ZIO_COMPRESS_GZIP_1 &&
+ intval <= ZIO_COMPRESS_GZIP_9 &&
+ zfs_earlier_version(name,
+ SPA_VERSION_GZIP_COMPRESSION))
+ return (ENOTSUP);
- spa_close(spa, FTAG);
- }
+ /*
+ * If this is a bootable dataset then
+ * verify that the compression algorithm
+ * is supported for booting. We must return
+ * something other than ENOTSUP since it
+ * implies a downrev pool version.
+ */
+ if (zfs_is_bootfs(name) &&
+ !BOOTFS_COMPRESS_VALID(intval))
+ return (ERANGE);
}
break;
+
+ case ZFS_PROP_COPIES:
+ if (zfs_earlier_version(name,
+ SPA_VERSION_DITTO_BLOCKS))
+ return (ENOTSUP);
+ break;
+
+ case ZFS_PROP_SHARESMB:
+ if (zpl_earlier_version(name, ZPL_VERSION_FUID))
+ return (ENOTSUP);
+ break;
+ }
+ }
+
+ elem = NULL;
+ while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
+ const char *propname = nvpair_name(elem);
+ zfs_prop_t prop = zfs_name_to_prop(propname);
+
+ if (prop == ZPROP_INVAL) {
+ VERIFY(nvpair_value_string(elem, &strval) == 0);
+ error = dsl_prop_set(name, propname, 1,
+ strlen(strval) + 1, strval);
+ if (error == 0)
+ continue;
+ else
+ return (error);
}
switch (prop) {
case ZFS_PROP_QUOTA:
if ((error = nvpair_value_uint64(elem, &intval)) != 0 ||
- (error = dsl_dir_set_quota(name,
- intval)) != 0)
+ (error = dsl_dir_set_quota(name, intval)) != 0)
+ return (error);
+ break;
+
+ case ZFS_PROP_REFQUOTA:
+ if ((error = nvpair_value_uint64(elem, &intval)) != 0 ||
+ (error = dsl_dataset_set_quota(name, intval)) != 0)
return (error);
break;
@@ -992,24 +1529,36 @@ zfs_set_prop_nvlist(const char *name, dev_t dev, cred_t *cr, nvlist_t *nvl)
return (error);
break;
- case ZFS_PROP_VOLSIZE:
+ case ZFS_PROP_REFRESERVATION:
if ((error = nvpair_value_uint64(elem, &intval)) != 0 ||
- (error = zvol_set_volsize(name, dev,
+ (error = dsl_dataset_set_reservation(name,
intval)) != 0)
return (error);
break;
+ case ZFS_PROP_VOLSIZE:
+ if ((error = nvpair_value_uint64(elem, &intval)) != 0 ||
+ (error = zvol_set_volsize(name,
+ ddi_driver_major(zfs_dip), intval)) != 0)
+ return (error);
+ break;
+
case ZFS_PROP_VOLBLOCKSIZE:
if ((error = nvpair_value_uint64(elem, &intval)) != 0 ||
- (error = zvol_set_volblocksize(name,
- intval)) != 0)
+ (error = zvol_set_volblocksize(name, intval)) != 0)
+ return (error);
+ break;
+
+ case ZFS_PROP_VERSION:
+ if ((error = nvpair_value_uint64(elem, &intval)) != 0 ||
+ (error = zfs_set_version(name, intval)) != 0)
return (error);
break;
default:
if (nvpair_type(elem) == DATA_TYPE_STRING) {
if (zfs_prop_get_type(prop) !=
- prop_type_string)
+ PROP_TYPE_STRING)
return (EINVAL);
VERIFY(nvpair_value_string(elem, &strval) == 0);
if ((error = dsl_prop_set(name,
@@ -1022,22 +1571,18 @@ zfs_set_prop_nvlist(const char *name, dev_t dev, cred_t *cr, nvlist_t *nvl)
VERIFY(nvpair_value_uint64(elem, &intval) == 0);
switch (zfs_prop_get_type(prop)) {
- case prop_type_number:
+ case PROP_TYPE_NUMBER:
break;
- case prop_type_boolean:
- if (intval > 1)
- return (EINVAL);
- break;
- case prop_type_string:
+ case PROP_TYPE_STRING:
return (EINVAL);
- case prop_type_index:
+ case PROP_TYPE_INDEX:
if (zfs_prop_index_to_string(prop,
intval, &unused) != 0)
return (EINVAL);
break;
default:
- cmn_err(CE_PANIC, "unknown property "
- "type");
+ cmn_err(CE_PANIC,
+ "unknown property type");
break;
}
@@ -1054,127 +1599,79 @@ zfs_set_prop_nvlist(const char *name, dev_t dev, cred_t *cr, nvlist_t *nvl)
return (0);
}
+/*
+ * inputs:
+ * zc_name name of filesystem
+ * zc_value name of property to inherit
+ * zc_nvlist_src{_size} nvlist of properties to apply
+ * zc_cookie clear existing local props?
+ *
+ * outputs: none
+ */
static int
zfs_ioc_set_prop(zfs_cmd_t *zc)
{
nvlist_t *nvl;
int error;
- zfs_prop_t prop;
- /*
- * If zc_value is set, then this is an attempt to inherit a value.
- * Otherwise, zc_nvlist refers to a list of properties to set.
- */
- if (zc->zc_value[0] != '\0') {
- if (!zfs_prop_user(zc->zc_value) &&
- ((prop = zfs_name_to_prop(zc->zc_value)) ==
- ZFS_PROP_INVAL ||
- !zfs_prop_inheritable(prop)))
- return (EINVAL);
+ if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+ &nvl)) != 0)
+ return (error);
+
+ if (zc->zc_cookie) {
+ nvlist_t *origprops;
+ objset_t *os;
+
+ if (dmu_objset_open(zc->zc_name, DMU_OST_ANY,
+ DS_MODE_USER | DS_MODE_READONLY, &os) == 0) {
+ if (dsl_prop_get_all(os, &origprops, TRUE) == 0) {
+ clear_props(zc->zc_name, origprops);
+ nvlist_free(origprops);
+ }
+ dmu_objset_close(os);
+ }
- return (dsl_prop_set(zc->zc_name, zc->zc_value, 0, 0, NULL));
}
- if ((error = get_nvlist(zc, &nvl)) != 0)
- return (error);
+ error = zfs_set_prop_nvlist(zc->zc_name, nvl);
- error = zfs_set_prop_nvlist(zc->zc_name, zc->zc_dev,
- (cred_t *)(uintptr_t)zc->zc_cred, nvl);
nvlist_free(nvl);
return (error);
}
+/*
+ * inputs:
+ * zc_name name of filesystem
+ * zc_value name of property to inherit
+ *
+ * outputs: none
+ */
+static int
+zfs_ioc_inherit_prop(zfs_cmd_t *zc)
+{
+ /* the property name has been validated by zfs_secpolicy_inherit() */
+ return (dsl_prop_set(zc->zc_name, zc->zc_value, 0, 0, NULL));
+}
+
static int
zfs_ioc_pool_set_props(zfs_cmd_t *zc)
{
- nvlist_t *nvl;
- int error, reset_bootfs = 0;
- uint64_t objnum;
- zpool_prop_t prop;
- nvpair_t *elem;
- char *propname, *strval;
+ nvlist_t *props;
spa_t *spa;
- vdev_t *rvdev;
- char *vdev_type;
- objset_t *os;
+ int error;
- if ((error = get_nvlist(zc, &nvl)) != 0)
+ if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+ &props)))
return (error);
if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
- nvlist_free(nvl);
+ nvlist_free(props);
return (error);
}
- if (spa_version(spa) < ZFS_VERSION_BOOTFS) {
- nvlist_free(nvl);
- spa_close(spa, FTAG);
- return (ENOTSUP);
- }
-
- elem = NULL;
- while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
-
- propname = nvpair_name(elem);
-
- if ((prop = zpool_name_to_prop(propname)) ==
- ZFS_PROP_INVAL) {
- nvlist_free(nvl);
- spa_close(spa, FTAG);
- return (EINVAL);
- }
-
- switch (prop) {
- case ZFS_PROP_BOOTFS:
- /*
- * A bootable filesystem can not be on a RAIDZ pool
- * nor a striped pool with more than 1 device.
- */
- rvdev = spa->spa_root_vdev;
- vdev_type =
- rvdev->vdev_child[0]->vdev_ops->vdev_op_type;
- if (strcmp(vdev_type, VDEV_TYPE_RAIDZ) == 0 ||
- (strcmp(vdev_type, VDEV_TYPE_MIRROR) != 0 &&
- rvdev->vdev_children > 1)) {
- error = ENOTSUP;
- break;
- }
-
- reset_bootfs = 1;
-
- VERIFY(nvpair_value_string(elem, &strval) == 0);
- if (strval == NULL || strval[0] == '\0') {
- objnum =
- zfs_prop_default_numeric(ZFS_PROP_BOOTFS);
- break;
- }
-
- if (error = dmu_objset_open(strval, DMU_OST_ZFS,
- DS_MODE_STANDARD | DS_MODE_READONLY, &os))
- break;
- objnum = dmu_objset_id(os);
- dmu_objset_close(os);
- break;
+ error = spa_prop_set(spa, props);
- default:
- error = EINVAL;
- }
-
- if (error)
- break;
- }
- if (error == 0) {
- if (reset_bootfs) {
- VERIFY(nvlist_remove(nvl,
- zpool_prop_to_name(ZFS_PROP_BOOTFS),
- DATA_TYPE_STRING) == 0);
- VERIFY(nvlist_add_uint64(nvl,
- zpool_prop_to_name(ZFS_PROP_BOOTFS), objnum) == 0);
- }
- error = spa_set_props(spa, nvl);
- }
-
- nvlist_free(nvl);
+ nvlist_free(props);
spa_close(spa, FTAG);
return (error);
@@ -1190,7 +1687,7 @@ zfs_ioc_pool_get_props(zfs_cmd_t *zc)
if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
return (error);
- error = spa_get_props(spa, &nvp);
+ error = spa_prop_get(spa, &nvp);
if (error == 0 && zc->zc_nvlist_dst != 0)
error = put_nvlist(zc, nvp);
@@ -1205,11 +1702,145 @@ zfs_ioc_pool_get_props(zfs_cmd_t *zc)
}
static int
+zfs_ioc_iscsi_perm_check(zfs_cmd_t *zc)
+{
+#ifdef TODO
+ nvlist_t *nvp;
+ int error;
+ uint32_t uid;
+ uint32_t gid;
+ uint32_t *groups;
+ uint_t group_cnt;
+ cred_t *usercred;
+
+ if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+ &nvp)) != 0) {
+ return (error);
+ }
+
+ if ((error = nvlist_lookup_uint32(nvp,
+ ZFS_DELEG_PERM_UID, &uid)) != 0) {
+ nvlist_free(nvp);
+ return (EPERM);
+ }
+
+ if ((error = nvlist_lookup_uint32(nvp,
+ ZFS_DELEG_PERM_GID, &gid)) != 0) {
+ nvlist_free(nvp);
+ return (EPERM);
+ }
+
+ if ((error = nvlist_lookup_uint32_array(nvp, ZFS_DELEG_PERM_GROUPS,
+ &groups, &group_cnt)) != 0) {
+ nvlist_free(nvp);
+ return (EPERM);
+ }
+ usercred = cralloc();
+ if ((crsetugid(usercred, uid, gid) != 0) ||
+ (crsetgroups(usercred, group_cnt, (gid_t *)groups) != 0)) {
+ nvlist_free(nvp);
+ crfree(usercred);
+ return (EPERM);
+ }
+ nvlist_free(nvp);
+ error = dsl_deleg_access(zc->zc_name,
+ zfs_prop_to_name(ZFS_PROP_SHAREISCSI), usercred);
+ crfree(usercred);
+ return (error);
+#else
+ return (EPERM);
+#endif
+}
+
+/*
+ * inputs:
+ * zc_name name of filesystem
+ * zc_nvlist_src{_size} nvlist of delegated permissions
+ * zc_perm_action allow/unallow flag
+ *
+ * outputs: none
+ */
+static int
+zfs_ioc_set_fsacl(zfs_cmd_t *zc)
+{
+ int error;
+ nvlist_t *fsaclnv = NULL;
+
+ if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+ &fsaclnv)) != 0)
+ return (error);
+
+ /*
+ * Verify nvlist is constructed correctly
+ */
+ if ((error = zfs_deleg_verify_nvlist(fsaclnv)) != 0) {
+ nvlist_free(fsaclnv);
+ return (EINVAL);
+ }
+
+ /*
+ * If we don't have PRIV_SYS_MOUNT, then validate
+ * that user is allowed to hand out each permission in
+ * the nvlist(s)
+ */
+
+ error = secpolicy_zfs(CRED());
+ if (error) {
+ if (zc->zc_perm_action == B_FALSE) {
+ error = dsl_deleg_can_allow(zc->zc_name,
+ fsaclnv, CRED());
+ } else {
+ error = dsl_deleg_can_unallow(zc->zc_name,
+ fsaclnv, CRED());
+ }
+ }
+
+ if (error == 0)
+ error = dsl_deleg_set(zc->zc_name, fsaclnv, zc->zc_perm_action);
+
+ nvlist_free(fsaclnv);
+ return (error);
+}
+
+/*
+ * inputs:
+ * zc_name name of filesystem
+ *
+ * outputs:
+ * zc_nvlist_src{_size} nvlist of delegated permissions
+ */
+static int
+zfs_ioc_get_fsacl(zfs_cmd_t *zc)
+{
+ nvlist_t *nvp;
+ int error;
+
+ if ((error = dsl_deleg_get(zc->zc_name, &nvp)) == 0) {
+ error = put_nvlist(zc, nvp);
+ nvlist_free(nvp);
+ }
+
+ return (error);
+}
+
+/*
+ * inputs:
+ * zc_name name of volume
+ *
+ * outputs: none
+ */
+static int
zfs_ioc_create_minor(zfs_cmd_t *zc)
{
- return (zvol_create_minor(zc->zc_name, zc->zc_dev));
+ return (zvol_create_minor(zc->zc_name, ddi_driver_major(zfs_dip)));
}
+/*
+ * inputs:
+ * zc_name name of volume
+ *
+ * outputs: none
+ */
static int
zfs_ioc_remove_minor(zfs_cmd_t *zc)
{
@@ -1228,7 +1859,7 @@ zfs_get_vfs(const char *resource)
mtx_lock(&mountlist_mtx);
TAILQ_FOREACH(vfsp, &mountlist, mnt_list) {
- if (strcmp(vfsp->mnt_stat.f_mntfromname, resource) == 0) {
+ if (strcmp(refstr_value(vfsp->vfs_resource), resource) == 0) {
VFS_HOLD(vfsp);
break;
}
@@ -1237,21 +1868,183 @@ zfs_get_vfs(const char *resource)
return (vfsp);
}
+/* ARGSUSED */
static void
-zfs_create_cb(objset_t *os, void *arg, dmu_tx_t *tx)
+zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx)
+{
+ zfs_creat_t *zct = arg;
+
+ zfs_create_fs(os, cr, zct->zct_zplprops, tx);
+}
+
+#define ZFS_PROP_UNDEFINED ((uint64_t)-1)
+
+/*
+ * inputs:
+ * createprops list of properties requested by creator
+ * default_zplver zpl version to use if unspecified in createprops
+ * fuids_ok fuids allowed in this version of the spa?
+ * os parent objset pointer (NULL if root fs)
+ *
+ * outputs:
+ * zplprops values for the zplprops we attach to the master node object
+ * is_ci true if requested file system will be purely case-insensitive
+ *
+ * Determine the settings for utf8only, normalization and
+ * casesensitivity. Specific values may have been requested by the
+ * creator and/or we can inherit values from the parent dataset. If
+ * the file system is of too early a vintage, a creator can not
+ * request settings for these properties, even if the requested
+ * setting is the default value. We don't actually want to create dsl
+ * properties for these, so remove them from the source nvlist after
+ * processing.
+ */
+static int
+zfs_fill_zplprops_impl(objset_t *os, uint64_t default_zplver,
+ boolean_t fuids_ok, nvlist_t *createprops, nvlist_t *zplprops,
+ boolean_t *is_ci)
+{
+ uint64_t zplver = default_zplver;
+ uint64_t sense = ZFS_PROP_UNDEFINED;
+ uint64_t norm = ZFS_PROP_UNDEFINED;
+ uint64_t u8 = ZFS_PROP_UNDEFINED;
+
+ ASSERT(zplprops != NULL);
+
+ /*
+ * Pull out creator prop choices, if any.
+ */
+ if (createprops) {
+ (void) nvlist_lookup_uint64(createprops,
+ zfs_prop_to_name(ZFS_PROP_VERSION), &zplver);
+ (void) nvlist_lookup_uint64(createprops,
+ zfs_prop_to_name(ZFS_PROP_NORMALIZE), &norm);
+ (void) nvlist_remove_all(createprops,
+ zfs_prop_to_name(ZFS_PROP_NORMALIZE));
+ (void) nvlist_lookup_uint64(createprops,
+ zfs_prop_to_name(ZFS_PROP_UTF8ONLY), &u8);
+ (void) nvlist_remove_all(createprops,
+ zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
+ (void) nvlist_lookup_uint64(createprops,
+ zfs_prop_to_name(ZFS_PROP_CASE), &sense);
+ (void) nvlist_remove_all(createprops,
+ zfs_prop_to_name(ZFS_PROP_CASE));
+ }
+
+ /*
+ * If the zpl version requested is whacky or the file system
+ * or pool is version is too "young" to support normalization
+ * and the creator tried to set a value for one of the props,
+ * error out.
+ */
+ if ((zplver < ZPL_VERSION_INITIAL || zplver > ZPL_VERSION) ||
+ (zplver >= ZPL_VERSION_FUID && !fuids_ok) ||
+ (zplver < ZPL_VERSION_NORMALIZATION &&
+ (norm != ZFS_PROP_UNDEFINED || u8 != ZFS_PROP_UNDEFINED ||
+ sense != ZFS_PROP_UNDEFINED)))
+ return (ENOTSUP);
+
+ /*
+ * Put the version in the zplprops
+ */
+ VERIFY(nvlist_add_uint64(zplprops,
+ zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0);
+
+ if (norm == ZFS_PROP_UNDEFINED)
+ VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0);
+ VERIFY(nvlist_add_uint64(zplprops,
+ zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0);
+
+ /*
+ * If we're normalizing, names must always be valid UTF-8 strings.
+ */
+ if (norm)
+ u8 = 1;
+ if (u8 == ZFS_PROP_UNDEFINED)
+ VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0);
+ VERIFY(nvlist_add_uint64(zplprops,
+ zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0);
+
+ if (sense == ZFS_PROP_UNDEFINED)
+ VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0);
+ VERIFY(nvlist_add_uint64(zplprops,
+ zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0);
+
+ if (is_ci)
+ *is_ci = (sense == ZFS_CASE_INSENSITIVE);
+
+ return (0);
+}
+
+static int
+zfs_fill_zplprops(const char *dataset, nvlist_t *createprops,
+ nvlist_t *zplprops, boolean_t *is_ci)
+{
+ boolean_t fuids_ok = B_TRUE;
+ uint64_t zplver = ZPL_VERSION;
+ objset_t *os = NULL;
+ char parentname[MAXNAMELEN];
+ char *cp;
+ int error;
+
+ (void) strlcpy(parentname, dataset, sizeof (parentname));
+ cp = strrchr(parentname, '/');
+ ASSERT(cp != NULL);
+ cp[0] = '\0';
+
+ if (zfs_earlier_version(dataset, SPA_VERSION_FUID)) {
+ zplver = ZPL_VERSION_FUID - 1;
+ fuids_ok = B_FALSE;
+ }
+
+ /*
+ * Open parent object set so we can inherit zplprop values.
+ */
+ if ((error = dmu_objset_open(parentname, DMU_OST_ANY,
+ DS_MODE_USER | DS_MODE_READONLY, &os)) != 0)
+ return (error);
+
+ error = zfs_fill_zplprops_impl(os, zplver, fuids_ok, createprops,
+ zplprops, is_ci);
+ dmu_objset_close(os);
+ return (error);
+}
+
+static int
+zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops,
+ nvlist_t *zplprops, boolean_t *is_ci)
{
- zfs_create_data_t *zc = arg;
+ boolean_t fuids_ok = B_TRUE;
+ uint64_t zplver = ZPL_VERSION;
+ int error;
- zfs_create_fs(os, (cred_t *)(uintptr_t)zc->zc_cred, tx);
+ if (spa_vers < SPA_VERSION_FUID) {
+ zplver = ZPL_VERSION_FUID - 1;
+ fuids_ok = B_FALSE;
+ }
+
+ error = zfs_fill_zplprops_impl(NULL, zplver, fuids_ok, createprops,
+ zplprops, is_ci);
+ return (error);
}
+/*
+ * inputs:
+ * zc_objset_type type of objset to create (fs vs zvol)
+ * zc_name name of new objset
+ * zc_value name of snapshot to clone from (may be empty)
+ * zc_nvlist_src{_size} nvlist of properties to apply
+ *
+ * outputs: none
+ */
static int
zfs_ioc_create(zfs_cmd_t *zc)
{
objset_t *clone;
int error = 0;
- zfs_create_data_t cbdata = { 0 };
- void (*cbfunc)(objset_t *os, void *arg, dmu_tx_t *tx);
+ zfs_creat_t zct;
+ nvlist_t *nvprops = NULL;
+ void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx);
dmu_objset_type_t type = zc->zc_objset_type;
switch (type) {
@@ -1266,16 +2059,19 @@ zfs_ioc_create(zfs_cmd_t *zc)
default:
cbfunc = NULL;
+ break;
}
- if (strchr(zc->zc_name, '@'))
+ if (strchr(zc->zc_name, '@') ||
+ strchr(zc->zc_name, '%'))
return (EINVAL);
if (zc->zc_nvlist_src != 0 &&
- (error = get_nvlist(zc, &cbdata.zc_props)) != 0)
+ (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+ &nvprops)) != 0)
return (error);
- cbdata.zc_cred = (cred_t *)(uintptr_t)zc->zc_cred;
- cbdata.zc_dev = (dev_t)zc->zc_dev;
+ zct.zct_zplprops = NULL;
+ zct.zct_props = nvprops;
if (zc->zc_value[0] != '\0') {
/*
@@ -1283,39 +2079,48 @@ zfs_ioc_create(zfs_cmd_t *zc)
*/
zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0) {
- nvlist_free(cbdata.zc_props);
+ nvlist_free(nvprops);
return (EINVAL);
}
error = dmu_objset_open(zc->zc_value, type,
- DS_MODE_STANDARD | DS_MODE_READONLY, &clone);
+ DS_MODE_USER | DS_MODE_READONLY, &clone);
+ if (error) {
+ nvlist_free(nvprops);
+ return (error);
+ }
+
+ error = dmu_objset_create(zc->zc_name, type, clone, 0,
+ NULL, NULL);
if (error) {
- nvlist_free(cbdata.zc_props);
+ dmu_objset_close(clone);
+ nvlist_free(nvprops);
return (error);
}
- error = dmu_objset_create(zc->zc_name, type, clone, NULL, NULL);
dmu_objset_close(clone);
} else {
+ boolean_t is_insensitive = B_FALSE;
+
if (cbfunc == NULL) {
- nvlist_free(cbdata.zc_props);
+ nvlist_free(nvprops);
return (EINVAL);
}
if (type == DMU_OST_ZVOL) {
uint64_t volsize, volblocksize;
- if (cbdata.zc_props == NULL ||
- nvlist_lookup_uint64(cbdata.zc_props,
+ if (nvprops == NULL ||
+ nvlist_lookup_uint64(nvprops,
zfs_prop_to_name(ZFS_PROP_VOLSIZE),
&volsize) != 0) {
- nvlist_free(cbdata.zc_props);
+ nvlist_free(nvprops);
return (EINVAL);
}
- if ((error = nvlist_lookup_uint64(cbdata.zc_props,
+ if ((error = nvlist_lookup_uint64(nvprops,
zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
&volblocksize)) != 0 && error != ENOENT) {
- nvlist_free(cbdata.zc_props);
+ nvlist_free(nvprops);
return (EINVAL);
}
@@ -1327,56 +2132,127 @@ zfs_ioc_create(zfs_cmd_t *zc)
volblocksize)) != 0 ||
(error = zvol_check_volsize(volsize,
volblocksize)) != 0) {
- nvlist_free(cbdata.zc_props);
+ nvlist_free(nvprops);
return (error);
}
- }
+ } else if (type == DMU_OST_ZFS) {
+ int error;
- error = dmu_objset_create(zc->zc_name, type, NULL, cbfunc,
- &cbdata);
+ /*
+ * We have to have normalization and
+ * case-folding flags correct when we do the
+ * file system creation, so go figure them out
+ * now.
+ */
+ VERIFY(nvlist_alloc(&zct.zct_zplprops,
+ NV_UNIQUE_NAME, KM_SLEEP) == 0);
+ error = zfs_fill_zplprops(zc->zc_name, nvprops,
+ zct.zct_zplprops, &is_insensitive);
+ if (error != 0) {
+ nvlist_free(nvprops);
+ nvlist_free(zct.zct_zplprops);
+ return (error);
+ }
+ }
+ error = dmu_objset_create(zc->zc_name, type, NULL,
+ is_insensitive ? DS_FLAG_CI_DATASET : 0, cbfunc, &zct);
+ nvlist_free(zct.zct_zplprops);
}
/*
* It would be nice to do this atomically.
*/
if (error == 0) {
- if ((error = zfs_set_prop_nvlist(zc->zc_name,
- zc->zc_dev, (cred_t *)(uintptr_t)zc->zc_cred,
- cbdata.zc_props)) != 0)
+ if ((error = zfs_set_prop_nvlist(zc->zc_name, nvprops)) != 0)
(void) dmu_objset_destroy(zc->zc_name);
}
-
- nvlist_free(cbdata.zc_props);
+ nvlist_free(nvprops);
return (error);
}
+struct snap_prop_arg {
+ nvlist_t *nvprops;
+ const char *snapname;
+};
+
+static int
+set_snap_props(char *name, void *arg)
+{
+ struct snap_prop_arg *snpa = arg;
+ int len = strlen(name) + strlen(snpa->snapname) + 2;
+ char *buf = kmem_alloc(len, KM_SLEEP);
+ int err;
+
+ (void) snprintf(buf, len, "%s@%s", name, snpa->snapname);
+ err = zfs_set_prop_nvlist(buf, snpa->nvprops);
+ if (err)
+ (void) dmu_objset_destroy(buf);
+ kmem_free(buf, len);
+ return (err);
+}
+
+/*
+ * inputs:
+ * zc_name name of filesystem
+ * zc_value short name of snapshot
+ * zc_cookie recursive flag
+ *
+ * outputs: none
+ */
static int
zfs_ioc_snapshot(zfs_cmd_t *zc)
{
+ nvlist_t *nvprops = NULL;
+ int error;
+ boolean_t recursive = zc->zc_cookie;
+
if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
return (EINVAL);
- return (dmu_objset_snapshot(zc->zc_name,
- zc->zc_value, zc->zc_cookie));
+
+ if (zc->zc_nvlist_src != 0 &&
+ (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+ &nvprops)) != 0)
+ return (error);
+
+ error = dmu_objset_snapshot(zc->zc_name, zc->zc_value, recursive);
+
+ /*
+ * It would be nice to do this atomically.
+ */
+ if (error == 0) {
+ struct snap_prop_arg snpa;
+ snpa.nvprops = nvprops;
+ snpa.snapname = zc->zc_value;
+ if (recursive) {
+ error = dmu_objset_find(zc->zc_name,
+ set_snap_props, &snpa, DS_FIND_CHILDREN);
+ if (error) {
+ (void) dmu_snapshots_destroy(zc->zc_name,
+ zc->zc_value);
+ }
+ } else {
+ error = set_snap_props(zc->zc_name, &snpa);
+ }
+ }
+ nvlist_free(nvprops);
+ return (error);
}
int
zfs_unmount_snap(char *name, void *arg)
{
- char *snapname = arg;
- char *cp;
vfs_t *vfsp = NULL;
- /*
- * Snapshots (which are under .zfs control) must be unmounted
- * before they can be destroyed.
- */
+ if (arg) {
+ char *snapname = arg;
+ int len = strlen(name) + strlen(snapname) + 2;
+ char *buf = kmem_alloc(len, KM_SLEEP);
- if (snapname) {
- (void) strcat(name, "@");
- (void) strcat(name, snapname);
- vfsp = zfs_get_vfs(name);
- cp = strchr(name, '@');
- *cp = '\0';
+ (void) strcpy(buf, name);
+ (void) strcat(buf, "@");
+ (void) strcat(buf, snapname);
+ vfsp = zfs_get_vfs(buf);
+ kmem_free(buf, len);
} else if (strchr(name, '@')) {
vfsp = zfs_get_vfs(name);
}
@@ -1400,6 +2276,13 @@ zfs_unmount_snap(char *name, void *arg)
return (0);
}
+/*
+ * inputs:
+ * zc_name name of filesystem
+ * zc_value short name of snapshot
+ *
+ * outputs: none
+ */
static int
zfs_ioc_destroy_snaps(zfs_cmd_t *zc)
{
@@ -1414,6 +2297,13 @@ zfs_ioc_destroy_snaps(zfs_cmd_t *zc)
return (dmu_snapshots_destroy(zc->zc_name, zc->zc_value));
}
+/*
+ * inputs:
+ * zc_name name of dataset to destroy
+ * zc_objset_type type of objset
+ *
+ * outputs: none
+ */
static int
zfs_ioc_destroy(zfs_cmd_t *zc)
{
@@ -1426,19 +2316,76 @@ zfs_ioc_destroy(zfs_cmd_t *zc)
return (dmu_objset_destroy(zc->zc_name));
}
+/*
+ * inputs:
+ * zc_name name of dataset to rollback (to most recent snapshot)
+ *
+ * outputs: none
+ */
static int
zfs_ioc_rollback(zfs_cmd_t *zc)
{
- return (dmu_objset_rollback(zc->zc_name));
+ objset_t *os;
+ int error;
+ zfsvfs_t *zfsvfs = NULL;
+
+ /*
+ * Get the zfsvfs for the receiving objset. There
+ * won't be one if we're operating on a zvol, if the
+ * objset doesn't exist yet, or is not mounted.
+ */
+ error = dmu_objset_open(zc->zc_name, DMU_OST_ANY, DS_MODE_USER, &os);
+ if (error)
+ return (error);
+
+ if (dmu_objset_type(os) == DMU_OST_ZFS) {
+ mutex_enter(&os->os->os_user_ptr_lock);
+ zfsvfs = dmu_objset_get_user(os);
+ if (zfsvfs != NULL)
+ VFS_HOLD(zfsvfs->z_vfs);
+ mutex_exit(&os->os->os_user_ptr_lock);
+ }
+
+ if (zfsvfs != NULL) {
+ char osname[MAXNAMELEN];
+ int mode;
+
+ error = zfs_suspend_fs(zfsvfs, osname, &mode);
+ if (error == 0) {
+ int resume_err;
+
+ ASSERT(strcmp(osname, zc->zc_name) == 0);
+ error = dmu_objset_rollback(os);
+ resume_err = zfs_resume_fs(zfsvfs, osname, mode);
+ error = error ? error : resume_err;
+ } else {
+ dmu_objset_close(os);
+ }
+ VFS_RELE(zfsvfs->z_vfs);
+ } else {
+ error = dmu_objset_rollback(os);
+ }
+ /* Note, the dmu_objset_rollback() releases the objset for us. */
+
+ return (error);
}
+/*
+ * inputs:
+ * zc_name old name of dataset
+ * zc_value new name of dataset
+ * zc_cookie recursive flag (only valid for snapshots)
+ *
+ * outputs: none
+ */
static int
zfs_ioc_rename(zfs_cmd_t *zc)
{
- int recursive = zc->zc_cookie & 1;
+ boolean_t recursive = zc->zc_cookie & 1;
zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
- if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0)
+ if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
+ strchr(zc->zc_value, '%'))
return (EINVAL);
/*
@@ -1452,48 +2399,199 @@ zfs_ioc_rename(zfs_cmd_t *zc)
if (err)
return (err);
}
-
return (dmu_objset_rename(zc->zc_name, zc->zc_value, recursive));
}
+static void
+clear_props(char *dataset, nvlist_t *props)
+{
+ zfs_cmd_t *zc;
+ nvpair_t *prop;
+
+ if (props == NULL)
+ return;
+ zc = kmem_alloc(sizeof (zfs_cmd_t), KM_SLEEP);
+ (void) strcpy(zc->zc_name, dataset);
+ for (prop = nvlist_next_nvpair(props, NULL); prop;
+ prop = nvlist_next_nvpair(props, prop)) {
+ (void) strcpy(zc->zc_value, nvpair_name(prop));
+ if (zfs_secpolicy_inherit(zc, CRED()) == 0)
+ (void) zfs_ioc_inherit_prop(zc);
+ }
+ kmem_free(zc, sizeof (zfs_cmd_t));
+}
+
+/*
+ * inputs:
+ * zc_name name of containing filesystem
+ * zc_nvlist_src{_size} nvlist of properties to apply
+ * zc_value name of snapshot to create
+ * zc_string name of clone origin (if DRR_FLAG_CLONE)
+ * zc_cookie file descriptor to recv from
+ * zc_begin_record the BEGIN record of the stream (not byteswapped)
+ * zc_guid force flag
+ *
+ * outputs:
+ * zc_cookie number of bytes read
+ */
static int
-zfs_ioc_recvbackup(zfs_cmd_t *zc)
+zfs_ioc_recv(zfs_cmd_t *zc)
{
- kthread_t *td = curthread;
- struct file *fp;
- int error;
- offset_t new_off;
+ file_t *fp;
+ objset_t *os;
+ dmu_recv_cookie_t drc;
+ zfsvfs_t *zfsvfs = NULL;
+ boolean_t force = (boolean_t)zc->zc_guid;
+ int error, fd;
+ offset_t off;
+ nvlist_t *props = NULL;
+ nvlist_t *origprops = NULL;
+ objset_t *origin = NULL;
+ char *tosnap;
+ char tofs[ZFS_MAXNAMELEN];
if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
- strchr(zc->zc_value, '@') == NULL)
+ strchr(zc->zc_value, '@') == NULL ||
+ strchr(zc->zc_value, '%'))
return (EINVAL);
- error = fget_read(td, zc->zc_cookie, &fp);
- if (error)
+ (void) strcpy(tofs, zc->zc_value);
+ tosnap = strchr(tofs, '@');
+ *tosnap = '\0';
+ tosnap++;
+
+ if (zc->zc_nvlist_src != 0 &&
+ (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+ &props)) != 0)
return (error);
- error = dmu_recvbackup(zc->zc_value, &zc->zc_begin_record,
- &zc->zc_cookie, (boolean_t)zc->zc_guid, fp,
- fp->f_offset);
+ fd = zc->zc_cookie;
+ fp = getf(fd, 0);
+ if (fp == NULL) {
+ nvlist_free(props);
+ return (EBADF);
+ }
- new_off = fp->f_offset + zc->zc_cookie;
- fp->f_offset = new_off;
+ if (dmu_objset_open(tofs, DMU_OST_ANY,
+ DS_MODE_USER | DS_MODE_READONLY, &os) == 0) {
+ /*
+ * Try to get the zfsvfs for the receiving objset.
+ * There won't be one if we're operating on a zvol,
+ * if the objset doesn't exist yet, or is not mounted.
+ */
+ mutex_enter(&os->os->os_user_ptr_lock);
+ if (zfsvfs = dmu_objset_get_user(os)) {
+ if (!mutex_tryenter(&zfsvfs->z_online_recv_lock)) {
+ mutex_exit(&os->os->os_user_ptr_lock);
+ dmu_objset_close(os);
+ zfsvfs = NULL;
+ error = EBUSY;
+ goto out;
+ }
+ VFS_HOLD(zfsvfs->z_vfs);
+ }
+ mutex_exit(&os->os->os_user_ptr_lock);
+
+ /*
+ * If new properties are supplied, they are to completely
+ * replace the existing ones, so stash away the existing ones.
+ */
+ if (props)
+ (void) dsl_prop_get_all(os, &origprops, TRUE);
+
+ dmu_objset_close(os);
+ }
+
+ if (zc->zc_string[0]) {
+ error = dmu_objset_open(zc->zc_string, DMU_OST_ANY,
+ DS_MODE_USER | DS_MODE_READONLY, &origin);
+ if (error)
+ goto out;
+ }
+
+ error = dmu_recv_begin(tofs, tosnap, &zc->zc_begin_record,
+ force, origin, zfsvfs != NULL, &drc);
+ if (origin)
+ dmu_objset_close(origin);
+ if (error)
+ goto out;
+
+ /*
+ * Reset properties. We do this before we receive the stream
+ * so that the properties are applied to the new data.
+ */
+ if (props) {
+ clear_props(tofs, origprops);
+ /*
+ * XXX - Note, this is all-or-nothing; should be best-effort.
+ */
+ (void) zfs_set_prop_nvlist(tofs, props);
+ }
+
+ off = fp->f_offset;
+ error = dmu_recv_stream(&drc, fp, &off);
+
+ if (error == 0 && zfsvfs) {
+ char osname[MAXNAMELEN];
+ int mode;
+
+ /* online recv */
+ error = zfs_suspend_fs(zfsvfs, osname, &mode);
+ if (error == 0) {
+ int resume_err;
+
+ error = dmu_recv_end(&drc);
+ resume_err = zfs_resume_fs(zfsvfs, osname, mode);
+ error = error ? error : resume_err;
+ } else {
+ dmu_recv_abort_cleanup(&drc);
+ }
+ } else if (error == 0) {
+ error = dmu_recv_end(&drc);
+ }
- fdrop(fp, td);
+ zc->zc_cookie = off - fp->f_offset;
+ if (off >= 0 && off <= MAXOFFSET_T)
+ fp->f_offset = off;
+
+ /*
+ * On error, restore the original props.
+ */
+ if (error && props) {
+ clear_props(tofs, props);
+ (void) zfs_set_prop_nvlist(tofs, origprops);
+ }
+out:
+ if (zfsvfs) {
+ mutex_exit(&zfsvfs->z_online_recv_lock);
+ VFS_RELE(zfsvfs->z_vfs);
+ }
+ nvlist_free(props);
+ nvlist_free(origprops);
+ releasef(fp);
return (error);
}
+/*
+ * inputs:
+ * zc_name name of snapshot to send
+ * zc_value short name of incremental fromsnap (may be empty)
+ * zc_cookie file descriptor to send stream to
+ * zc_obj fromorigin flag (mutually exclusive with zc_value)
+ *
+ * outputs: none
+ */
static int
-zfs_ioc_sendbackup(zfs_cmd_t *zc)
+zfs_ioc_send(zfs_cmd_t *zc)
{
- kthread_t *td = curthread;
- struct file *fp;
objset_t *fromsnap = NULL;
objset_t *tosnap;
- int error, fd;
+ file_t *fp;
+ int error;
+ offset_t off;
error = dmu_objset_open(zc->zc_name, DMU_OST_ANY,
- DS_MODE_STANDARD | DS_MODE_READONLY, &tosnap);
+ DS_MODE_USER | DS_MODE_READONLY, &tosnap);
if (error)
return (error);
@@ -1507,25 +2605,27 @@ zfs_ioc_sendbackup(zfs_cmd_t *zc)
*(cp+1) = 0;
(void) strlcat(buf, zc->zc_value, sizeof (buf));
error = dmu_objset_open(buf, DMU_OST_ANY,
- DS_MODE_STANDARD | DS_MODE_READONLY, &fromsnap);
+ DS_MODE_USER | DS_MODE_READONLY, &fromsnap);
if (error) {
dmu_objset_close(tosnap);
return (error);
}
}
- fd = zc->zc_cookie;
- error = fget_write(td, fd, &fp);
- if (error) {
+ fp = getf(zc->zc_cookie, 1);
+ if (fp == NULL) {
dmu_objset_close(tosnap);
if (fromsnap)
dmu_objset_close(fromsnap);
- return (error);
+ return (EBADF);
}
- error = dmu_sendbackup(tosnap, fromsnap, fp);
+ off = fp->f_offset;
+ error = dmu_sendbackup(tosnap, fromsnap, zc->zc_obj, fp, &off);
- fdrop(fp, td);
+ if (off >= 0 && off <= MAXOFFSET_T)
+ fp->f_offset = off;
+ releasef(fp);
if (fromsnap)
dmu_objset_close(fromsnap);
dmu_objset_close(tosnap);
@@ -1595,28 +2695,58 @@ zfs_ioc_clear(zfs_cmd_t *zc)
vdev_t *vd;
int error;
+ /*
+ * On zpool clear we also fix up missing slogs
+ */
+ mutex_enter(&spa_namespace_lock);
+ spa = spa_lookup(zc->zc_name);
+ if (spa == NULL) {
+ mutex_exit(&spa_namespace_lock);
+ return (EIO);
+ }
+ if (spa->spa_log_state == SPA_LOG_MISSING) {
+ /* we need to let spa_open/spa_load clear the chains */
+ spa->spa_log_state = SPA_LOG_CLEAR;
+ }
+ mutex_exit(&spa_namespace_lock);
+
if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
return (error);
- spa_config_enter(spa, RW_WRITER, FTAG);
+ spa_vdev_state_enter(spa);
if (zc->zc_guid == 0) {
vd = NULL;
- } else if ((vd = spa_lookup_by_guid(spa, zc->zc_guid)) == NULL) {
- spa_config_exit(spa, FTAG);
- spa_close(spa, FTAG);
- return (ENODEV);
+ } else {
+ vd = spa_lookup_by_guid(spa, zc->zc_guid, B_TRUE);
+ if (vd == NULL) {
+ (void) spa_vdev_state_exit(spa, NULL, ENODEV);
+ spa_close(spa, FTAG);
+ return (ENODEV);
+ }
}
vdev_clear(spa, vd);
- spa_config_exit(spa, FTAG);
+ (void) spa_vdev_state_exit(spa, NULL, 0);
+
+ /*
+ * Resume any suspended I/Os.
+ */
+ zio_resume(spa);
spa_close(spa, FTAG);
return (0);
}
+/*
+ * inputs:
+ * zc_name name of filesystem
+ * zc_value name of origin snapshot
+ *
+ * outputs: none
+ */
static int
zfs_ioc_promote(zfs_cmd_t *zc)
{
@@ -1634,68 +2764,221 @@ zfs_ioc_promote(zfs_cmd_t *zc)
return (dsl_dataset_promote(zc->zc_name));
}
+#ifdef TODO
+/*
+ * We don't want to have a hard dependency
+ * against some special symbols in sharefs
+ * nfs, and smbsrv. Determine them if needed when
+ * the first file system is shared.
+ * Neither sharefs, nfs or smbsrv are unloadable modules.
+ */
+int (*znfsexport_fs)(void *arg);
+int (*zshare_fs)(enum sharefs_sys_op, share_t *, uint32_t);
+int (*zsmbexport_fs)(void *arg, boolean_t add_share);
+
+int zfs_nfsshare_inited;
+int zfs_smbshare_inited;
+
+ddi_modhandle_t nfs_mod;
+ddi_modhandle_t sharefs_mod;
+ddi_modhandle_t smbsrv_mod;
+#endif
+kmutex_t zfs_share_lock;
+
+#ifdef TODO
+static int
+zfs_init_sharefs()
+{
+ int error;
+
+ ASSERT(MUTEX_HELD(&zfs_share_lock));
+ /* Both NFS and SMB shares also require sharetab support. */
+ if (sharefs_mod == NULL && ((sharefs_mod =
+ ddi_modopen("fs/sharefs",
+ KRTLD_MODE_FIRST, &error)) == NULL)) {
+ return (ENOSYS);
+ }
+ if (zshare_fs == NULL && ((zshare_fs =
+ (int (*)(enum sharefs_sys_op, share_t *, uint32_t))
+ ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) {
+ return (ENOSYS);
+ }
+ return (0);
+}
+#endif
+
+static int
+zfs_ioc_share(zfs_cmd_t *zc)
+{
+#ifdef TODO
+ int error;
+ int opcode;
+
+ switch (zc->zc_share.z_sharetype) {
+ case ZFS_SHARE_NFS:
+ case ZFS_UNSHARE_NFS:
+ if (zfs_nfsshare_inited == 0) {
+ mutex_enter(&zfs_share_lock);
+ if (nfs_mod == NULL && ((nfs_mod = ddi_modopen("fs/nfs",
+ KRTLD_MODE_FIRST, &error)) == NULL)) {
+ mutex_exit(&zfs_share_lock);
+ return (ENOSYS);
+ }
+ if (znfsexport_fs == NULL &&
+ ((znfsexport_fs = (int (*)(void *))
+ ddi_modsym(nfs_mod,
+ "nfs_export", &error)) == NULL)) {
+ mutex_exit(&zfs_share_lock);
+ return (ENOSYS);
+ }
+ error = zfs_init_sharefs();
+ if (error) {
+ mutex_exit(&zfs_share_lock);
+ return (ENOSYS);
+ }
+ zfs_nfsshare_inited = 1;
+ mutex_exit(&zfs_share_lock);
+ }
+ break;
+ case ZFS_SHARE_SMB:
+ case ZFS_UNSHARE_SMB:
+ if (zfs_smbshare_inited == 0) {
+ mutex_enter(&zfs_share_lock);
+ if (smbsrv_mod == NULL && ((smbsrv_mod =
+ ddi_modopen("drv/smbsrv",
+ KRTLD_MODE_FIRST, &error)) == NULL)) {
+ mutex_exit(&zfs_share_lock);
+ return (ENOSYS);
+ }
+ if (zsmbexport_fs == NULL && ((zsmbexport_fs =
+ (int (*)(void *, boolean_t))ddi_modsym(smbsrv_mod,
+ "smb_server_share", &error)) == NULL)) {
+ mutex_exit(&zfs_share_lock);
+ return (ENOSYS);
+ }
+ error = zfs_init_sharefs();
+ if (error) {
+ mutex_exit(&zfs_share_lock);
+ return (ENOSYS);
+ }
+ zfs_smbshare_inited = 1;
+ mutex_exit(&zfs_share_lock);
+ }
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ switch (zc->zc_share.z_sharetype) {
+ case ZFS_SHARE_NFS:
+ case ZFS_UNSHARE_NFS:
+ if (error =
+ znfsexport_fs((void *)
+ (uintptr_t)zc->zc_share.z_exportdata))
+ return (error);
+ break;
+ case ZFS_SHARE_SMB:
+ case ZFS_UNSHARE_SMB:
+ if (error = zsmbexport_fs((void *)
+ (uintptr_t)zc->zc_share.z_exportdata,
+ zc->zc_share.z_sharetype == ZFS_SHARE_SMB ?
+ B_TRUE : B_FALSE)) {
+ return (error);
+ }
+ break;
+ }
+
+ opcode = (zc->zc_share.z_sharetype == ZFS_SHARE_NFS ||
+ zc->zc_share.z_sharetype == ZFS_SHARE_SMB) ?
+ SHAREFS_ADD : SHAREFS_REMOVE;
+
+ /*
+ * Add or remove share from sharetab
+ */
+ error = zshare_fs(opcode,
+ (void *)(uintptr_t)zc->zc_share.z_sharedata,
+ zc->zc_share.z_sharemax);
+
+ return (error);
+#else
+ return (ENOSYS);
+#endif
+}
+
+/*
+ * pool create, destroy, and export don't log the history as part of
+ * zfsdev_ioctl, but rather zfs_ioc_pool_create, and zfs_ioc_pool_export
+ * do the logging of those commands.
+ */
static int
zfs_ioc_jail(zfs_cmd_t *zc)
{
- return (zone_dataset_attach((cred_t *)(uintptr_t)zc->zc_cred,
- zc->zc_name, (int)zc->zc_jailid));
+ return (zone_dataset_attach(curthread->td_ucred, zc->zc_name,
+ (int)zc->zc_jailid));
}
static int
zfs_ioc_unjail(zfs_cmd_t *zc)
{
- return (zone_dataset_detach((cred_t *)(uintptr_t)zc->zc_cred,
- zc->zc_name, (int)zc->zc_jailid));
+ return (zone_dataset_detach(curthread->td_ucred, zc->zc_name,
+ (int)zc->zc_jailid));
}
static zfs_ioc_vec_t zfs_ioc_vec[] = {
- { zfs_ioc_pool_create, zfs_secpolicy_config, pool_name },
- { zfs_ioc_pool_destroy, zfs_secpolicy_config, pool_name },
- { zfs_ioc_pool_import, zfs_secpolicy_config, pool_name },
- { zfs_ioc_pool_export, zfs_secpolicy_config, pool_name },
- { zfs_ioc_pool_configs, zfs_secpolicy_none, no_name },
- { zfs_ioc_pool_stats, zfs_secpolicy_read, pool_name },
- { zfs_ioc_pool_tryimport, zfs_secpolicy_config, no_name },
- { zfs_ioc_pool_scrub, zfs_secpolicy_config, pool_name },
- { zfs_ioc_pool_freeze, zfs_secpolicy_config, no_name },
- { zfs_ioc_pool_upgrade, zfs_secpolicy_config, pool_name },
- { zfs_ioc_pool_get_history, zfs_secpolicy_config, pool_name },
- { zfs_ioc_pool_log_history, zfs_secpolicy_config, pool_name },
- { zfs_ioc_vdev_add, zfs_secpolicy_config, pool_name },
- { zfs_ioc_vdev_remove, zfs_secpolicy_config, pool_name },
- { zfs_ioc_vdev_online, zfs_secpolicy_config, pool_name },
- { zfs_ioc_vdev_offline, zfs_secpolicy_config, pool_name },
- { zfs_ioc_vdev_attach, zfs_secpolicy_config, pool_name },
- { zfs_ioc_vdev_detach, zfs_secpolicy_config, pool_name },
- { zfs_ioc_vdev_setpath, zfs_secpolicy_config, pool_name },
- { zfs_ioc_objset_stats, zfs_secpolicy_read, dataset_name },
- { zfs_ioc_dataset_list_next, zfs_secpolicy_read, dataset_name },
- { zfs_ioc_snapshot_list_next, zfs_secpolicy_read, dataset_name },
- { zfs_ioc_set_prop, zfs_secpolicy_write, dataset_name },
- { zfs_ioc_create_minor, zfs_secpolicy_config, dataset_name },
- { zfs_ioc_remove_minor, zfs_secpolicy_config, dataset_name },
- { zfs_ioc_create, zfs_secpolicy_parent, dataset_name },
- { zfs_ioc_destroy, zfs_secpolicy_parent, dataset_name },
- { zfs_ioc_rollback, zfs_secpolicy_write, dataset_name },
- { zfs_ioc_rename, zfs_secpolicy_write, dataset_name },
- { zfs_ioc_recvbackup, zfs_secpolicy_write, dataset_name },
- { zfs_ioc_sendbackup, zfs_secpolicy_operator, dataset_name },
- { zfs_ioc_inject_fault, zfs_secpolicy_inject, no_name },
- { zfs_ioc_clear_fault, zfs_secpolicy_inject, no_name },
- { zfs_ioc_inject_list_next, zfs_secpolicy_inject, no_name },
- { zfs_ioc_error_log, zfs_secpolicy_inject, pool_name },
- { zfs_ioc_clear, zfs_secpolicy_config, pool_name },
- { zfs_ioc_promote, zfs_secpolicy_write, dataset_name },
- { zfs_ioc_destroy_snaps, zfs_secpolicy_write, dataset_name },
- { zfs_ioc_snapshot, zfs_secpolicy_operator, dataset_name },
- { zfs_ioc_dsobj_to_dsname, zfs_secpolicy_config, pool_name },
- { zfs_ioc_obj_to_path, zfs_secpolicy_config, no_name },
- { zfs_ioc_pool_set_props, zfs_secpolicy_config, pool_name },
- { zfs_ioc_pool_get_props, zfs_secpolicy_read, pool_name },
- { zfs_ioc_jail, zfs_secpolicy_config, dataset_name },
- { zfs_ioc_unjail, zfs_secpolicy_config, dataset_name }
+ { zfs_ioc_pool_create, zfs_secpolicy_config, POOL_NAME, B_FALSE },
+ { zfs_ioc_pool_destroy, zfs_secpolicy_config, POOL_NAME, B_FALSE },
+ { zfs_ioc_pool_import, zfs_secpolicy_config, POOL_NAME, B_TRUE },
+ { zfs_ioc_pool_export, zfs_secpolicy_config, POOL_NAME, B_FALSE },
+ { zfs_ioc_pool_configs, zfs_secpolicy_none, NO_NAME, B_FALSE },
+ { zfs_ioc_pool_stats, zfs_secpolicy_read, POOL_NAME, B_FALSE },
+ { zfs_ioc_pool_tryimport, zfs_secpolicy_config, NO_NAME, B_FALSE },
+ { zfs_ioc_pool_scrub, zfs_secpolicy_config, POOL_NAME, B_TRUE },
+ { zfs_ioc_pool_freeze, zfs_secpolicy_config, NO_NAME, B_FALSE },
+ { zfs_ioc_pool_upgrade, zfs_secpolicy_config, POOL_NAME, B_TRUE },
+ { zfs_ioc_pool_get_history, zfs_secpolicy_config, POOL_NAME, B_FALSE },
+ { zfs_ioc_vdev_add, zfs_secpolicy_config, POOL_NAME, B_TRUE },
+ { zfs_ioc_vdev_remove, zfs_secpolicy_config, POOL_NAME, B_TRUE },
+ { zfs_ioc_vdev_set_state, zfs_secpolicy_config, POOL_NAME, B_TRUE },
+ { zfs_ioc_vdev_attach, zfs_secpolicy_config, POOL_NAME, B_TRUE },
+ { zfs_ioc_vdev_detach, zfs_secpolicy_config, POOL_NAME, B_TRUE },
+ { zfs_ioc_vdev_setpath, zfs_secpolicy_config, POOL_NAME, B_FALSE },
+ { zfs_ioc_objset_stats, zfs_secpolicy_read, DATASET_NAME, B_FALSE },
+ { zfs_ioc_objset_zplprops, zfs_secpolicy_read, DATASET_NAME, B_FALSE },
+ { zfs_ioc_dataset_list_next, zfs_secpolicy_read,
+ DATASET_NAME, B_FALSE },
+ { zfs_ioc_snapshot_list_next, zfs_secpolicy_read,
+ DATASET_NAME, B_FALSE },
+ { zfs_ioc_set_prop, zfs_secpolicy_none, DATASET_NAME, B_TRUE },
+ { zfs_ioc_create_minor, zfs_secpolicy_minor, DATASET_NAME, B_FALSE },
+ { zfs_ioc_remove_minor, zfs_secpolicy_minor, DATASET_NAME, B_FALSE },
+ { zfs_ioc_create, zfs_secpolicy_create, DATASET_NAME, B_TRUE },
+ { zfs_ioc_destroy, zfs_secpolicy_destroy, DATASET_NAME, B_TRUE },
+ { zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME, B_TRUE },
+ { zfs_ioc_rename, zfs_secpolicy_rename, DATASET_NAME, B_TRUE },
+ { zfs_ioc_recv, zfs_secpolicy_receive, DATASET_NAME, B_TRUE },
+ { zfs_ioc_send, zfs_secpolicy_send, DATASET_NAME, B_TRUE },
+ { zfs_ioc_inject_fault, zfs_secpolicy_inject, NO_NAME, B_FALSE },
+ { zfs_ioc_clear_fault, zfs_secpolicy_inject, NO_NAME, B_FALSE },
+ { zfs_ioc_inject_list_next, zfs_secpolicy_inject, NO_NAME, B_FALSE },
+ { zfs_ioc_error_log, zfs_secpolicy_inject, POOL_NAME, B_FALSE },
+ { zfs_ioc_clear, zfs_secpolicy_config, POOL_NAME, B_TRUE },
+ { zfs_ioc_promote, zfs_secpolicy_promote, DATASET_NAME, B_TRUE },
+ { zfs_ioc_destroy_snaps, zfs_secpolicy_destroy, DATASET_NAME, B_TRUE },
+ { zfs_ioc_snapshot, zfs_secpolicy_snapshot, DATASET_NAME, B_TRUE },
+ { zfs_ioc_dsobj_to_dsname, zfs_secpolicy_config, POOL_NAME, B_FALSE },
+ { zfs_ioc_obj_to_path, zfs_secpolicy_config, NO_NAME, B_FALSE },
+ { zfs_ioc_pool_set_props, zfs_secpolicy_config, POOL_NAME, B_TRUE },
+ { zfs_ioc_pool_get_props, zfs_secpolicy_read, POOL_NAME, B_FALSE },
+ { zfs_ioc_set_fsacl, zfs_secpolicy_fsacl, DATASET_NAME, B_TRUE },
+ { zfs_ioc_get_fsacl, zfs_secpolicy_read, DATASET_NAME, B_FALSE },
+ { zfs_ioc_iscsi_perm_check, zfs_secpolicy_iscsi,
+ DATASET_NAME, B_FALSE },
+ { zfs_ioc_share, zfs_secpolicy_share, DATASET_NAME, B_FALSE },
+ { zfs_ioc_inherit_prop, zfs_secpolicy_inherit, DATASET_NAME, B_TRUE },
+ { zfs_ioc_jail, zfs_secpolicy_config, DATASET_NAME, B_TRUE },
+ { zfs_ioc_unjail, zfs_secpolicy_config, DATASET_NAME, B_TRUE }
};
static int
@@ -1711,9 +2994,7 @@ zfsdev_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
if (vec >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0]))
return (EINVAL);
- zc->zc_cred = (uintptr_t)td->td_ucred;
- zc->zc_dev = (uintptr_t)dev;
- error = zfs_ioc_vec[vec].zvec_secpolicy(zc->zc_name, td->td_ucred);
+ error = zfs_ioc_vec[vec].zvec_secpolicy(zc, td->td_ucred);
/*
* Ensure that all pool/dataset names are valid before we pass down to
@@ -1722,17 +3003,17 @@ zfsdev_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
if (error == 0) {
zc->zc_name[sizeof (zc->zc_name) - 1] = '\0';
switch (zfs_ioc_vec[vec].zvec_namecheck) {
- case pool_name:
+ case POOL_NAME:
if (pool_namecheck(zc->zc_name, NULL, NULL) != 0)
error = EINVAL;
break;
- case dataset_name:
+ case DATASET_NAME:
if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0)
error = EINVAL;
break;
- case no_name:
+ case NO_NAME:
break;
}
}
@@ -1740,6 +3021,9 @@ zfsdev_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
if (error == 0)
error = zfs_ioc_vec[vec].zvec_func(zc);
+ if (zfs_ioc_vec[vec].zvec_his_log == B_TRUE)
+ zfs_log_history(zc);
+
return (error);
}
@@ -1761,7 +3045,7 @@ static struct cdevsw zfs_cdevsw = {
static void
zfsdev_init(void)
{
- zfsdev = make_dev(&zfs_cdevsw, 0x0, UID_ROOT, GID_OPERATOR, 0660,
+ zfsdev = make_dev(&zfs_cdevsw, 0x0, UID_ROOT, GID_OPERATOR, 0666,
ZFS_DEV_NAME);
}
@@ -1775,6 +3059,10 @@ zfsdev_fini(void)
static struct task zfs_start_task;
static struct root_hold_token *zfs_root_token;
+
+uint_t zfs_fsyncer_key;
+extern uint_t rrw_tsd_key;
+
static void
zfs_start(void *context __unused, int pending __unused)
{
@@ -1783,7 +3071,11 @@ zfs_start(void *context __unused, int pending __unused)
spa_init(FREAD | FWRITE);
zfs_init();
zvol_init();
- printf("ZFS storage pool version " ZFS_VERSION_STRING "\n");
+
+ tsd_create(&zfs_fsyncer_key, NULL);
+ tsd_create(&rrw_tsd_key, NULL);
+
+ printf("ZFS storage pool version " SPA_VERSION_STRING "\n");
root_mount_rel(zfs_root_token);
}
@@ -1800,6 +3092,7 @@ zfs_modevent(module_t mod, int type, void *unused __unused)
"feature in FreeBSD.\n");
TASK_INIT(&zfs_start_task, 0, zfs_start, NULL);
taskqueue_enqueue(taskqueue_thread, &zfs_start_task);
+ mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL);
error = 0;
break;
case MOD_UNLOAD:
@@ -1812,6 +3105,9 @@ zfs_modevent(module_t mod, int type, void *unused __unused)
zfs_fini();
spa_fini();
zfsdev_fini();
+ tsd_destroy(&zfs_fsyncer_key);
+ tsd_destroy(&rrw_tsd_key);
+ mutex_destroy(&zfs_share_lock);
error = 0;
break;
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_log.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_log.c
index dde9ec1..5f99780 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_log.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_log.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
@@ -36,49 +34,282 @@
#include <sys/zfs_znode.h>
#include <sys/zfs_dir.h>
#include <sys/zil.h>
+#include <sys/zil_impl.h>
#include <sys/byteorder.h>
#include <sys/policy.h>
#include <sys/stat.h>
#include <sys/acl.h>
#include <sys/dmu.h>
#include <sys/spa.h>
+#include <sys/zfs_fuid.h>
/*
* All the functions in this file are used to construct the log entries
- * to record transactions. They allocate * a intent log transaction
+ * to record transactions. They allocate * an intent log transaction
* structure (itx_t) and save within it all the information necessary to
* possibly replay the transaction. The itx is then assigned a sequence
* number and inserted in the in-memory list anchored in the zilog.
*/
+int
+zfs_log_create_txtype(zil_create_t type, vsecattr_t *vsecp, vattr_t *vap)
+{
+ int isxvattr = (vap->va_mask & AT_XVATTR);
+ switch (type) {
+ case Z_FILE:
+ if (vsecp == NULL && !isxvattr)
+ return (TX_CREATE);
+ if (vsecp && isxvattr)
+ return (TX_CREATE_ACL_ATTR);
+ if (vsecp)
+ return (TX_CREATE_ACL);
+ else
+ return (TX_CREATE_ATTR);
+ /*NOTREACHED*/
+ case Z_DIR:
+ if (vsecp == NULL && !isxvattr)
+ return (TX_MKDIR);
+ if (vsecp && isxvattr)
+ return (TX_MKDIR_ACL_ATTR);
+ if (vsecp)
+ return (TX_MKDIR_ACL);
+ else
+ return (TX_MKDIR_ATTR);
+ case Z_XATTRDIR:
+ return (TX_MKXATTR);
+ }
+ ASSERT(0);
+ return (TX_MAX_TYPE);
+}
+
+/*
+ * build up the log data necessary for logging xvattr_t
+ * First lr_attr_t is initialized. following the lr_attr_t
+ * is the mapsize and attribute bitmap copied from the xvattr_t.
+ * Following the bitmap and bitmapsize two 64 bit words are reserved
+ * for the create time which may be set. Following the create time
+ * records a single 64 bit integer which has the bits to set on
+ * replay for the xvattr.
+ */
+static void
+zfs_log_xvattr(lr_attr_t *lrattr, xvattr_t *xvap)
+{
+ uint32_t *bitmap;
+ uint64_t *attrs;
+ uint64_t *crtime;
+ xoptattr_t *xoap;
+ void *scanstamp;
+ int i;
+
+ xoap = xva_getxoptattr(xvap);
+ ASSERT(xoap);
+
+ lrattr->lr_attr_masksize = xvap->xva_mapsize;
+ bitmap = &lrattr->lr_attr_bitmap;
+ for (i = 0; i != xvap->xva_mapsize; i++, bitmap++) {
+ *bitmap = xvap->xva_reqattrmap[i];
+ }
+
+ /* Now pack the attributes up in a single uint64_t */
+ attrs = (uint64_t *)bitmap;
+ crtime = attrs + 1;
+ scanstamp = (caddr_t)(crtime + 2);
+ *attrs = 0;
+ if (XVA_ISSET_REQ(xvap, XAT_READONLY))
+ *attrs |= (xoap->xoa_readonly == 0) ? 0 :
+ XAT0_READONLY;
+ if (XVA_ISSET_REQ(xvap, XAT_HIDDEN))
+ *attrs |= (xoap->xoa_hidden == 0) ? 0 :
+ XAT0_HIDDEN;
+ if (XVA_ISSET_REQ(xvap, XAT_SYSTEM))
+ *attrs |= (xoap->xoa_system == 0) ? 0 :
+ XAT0_SYSTEM;
+ if (XVA_ISSET_REQ(xvap, XAT_ARCHIVE))
+ *attrs |= (xoap->xoa_archive == 0) ? 0 :
+ XAT0_ARCHIVE;
+ if (XVA_ISSET_REQ(xvap, XAT_IMMUTABLE))
+ *attrs |= (xoap->xoa_immutable == 0) ? 0 :
+ XAT0_IMMUTABLE;
+ if (XVA_ISSET_REQ(xvap, XAT_NOUNLINK))
+ *attrs |= (xoap->xoa_nounlink == 0) ? 0 :
+ XAT0_NOUNLINK;
+ if (XVA_ISSET_REQ(xvap, XAT_APPENDONLY))
+ *attrs |= (xoap->xoa_appendonly == 0) ? 0 :
+ XAT0_APPENDONLY;
+ if (XVA_ISSET_REQ(xvap, XAT_OPAQUE))
+ *attrs |= (xoap->xoa_opaque == 0) ? 0 :
+ XAT0_APPENDONLY;
+ if (XVA_ISSET_REQ(xvap, XAT_NODUMP))
+ *attrs |= (xoap->xoa_nodump == 0) ? 0 :
+ XAT0_NODUMP;
+ if (XVA_ISSET_REQ(xvap, XAT_AV_QUARANTINED))
+ *attrs |= (xoap->xoa_av_quarantined == 0) ? 0 :
+ XAT0_AV_QUARANTINED;
+ if (XVA_ISSET_REQ(xvap, XAT_AV_MODIFIED))
+ *attrs |= (xoap->xoa_av_modified == 0) ? 0 :
+ XAT0_AV_MODIFIED;
+ if (XVA_ISSET_REQ(xvap, XAT_CREATETIME))
+ ZFS_TIME_ENCODE(&xoap->xoa_createtime, crtime);
+ if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP))
+ bcopy(xoap->xoa_av_scanstamp, scanstamp, AV_SCANSTAMP_SZ);
+}
+
+static void *
+zfs_log_fuid_ids(zfs_fuid_info_t *fuidp, void *start)
+{
+ zfs_fuid_t *zfuid;
+ uint64_t *fuidloc = start;
+
+ /* First copy in the ACE FUIDs */
+ for (zfuid = list_head(&fuidp->z_fuids); zfuid;
+ zfuid = list_next(&fuidp->z_fuids, zfuid)) {
+ *fuidloc++ = zfuid->z_logfuid;
+ }
+ return (fuidloc);
+}
+
+
+static void *
+zfs_log_fuid_domains(zfs_fuid_info_t *fuidp, void *start)
+{
+ zfs_fuid_domain_t *zdomain;
+
+ /* now copy in the domain info, if any */
+ if (fuidp->z_domain_str_sz != 0) {
+ for (zdomain = list_head(&fuidp->z_domains); zdomain;
+ zdomain = list_next(&fuidp->z_domains, zdomain)) {
+ bcopy((void *)zdomain->z_domain, start,
+ strlen(zdomain->z_domain) + 1);
+ start = (caddr_t)start +
+ strlen(zdomain->z_domain) + 1;
+ }
+ }
+ return (start);
+}
+
/*
- * zfs_log_create() is used to handle TX_CREATE, TX_MKDIR and TX_MKXATTR
+ * zfs_log_create() is used to handle TX_CREATE, TX_CREATE_ATTR, TX_MKDIR,
+ * TX_MKDIR_ATTR and TX_MKXATTR
* transactions.
+ *
+ * TX_CREATE and TX_MKDIR are standard creates, but they may have FUID
+ * domain information appended prior to the name. In this case the
+ * uid/gid in the log record will be a log centric FUID.
+ *
+ * TX_CREATE_ACL_ATTR and TX_MKDIR_ACL_ATTR handle special creates that
+ * may contain attributes, ACL and optional fuid information.
+ *
+ * TX_CREATE_ACL and TX_MKDIR_ACL handle special creates that specify
+ * and ACL and normal users/groups in the ACEs.
+ *
+ * There may be an optional xvattr attribute information similar
+ * to zfs_log_setattr.
+ *
+ * Also, after the file name "domain" strings may be appended.
*/
void
-zfs_log_create(zilog_t *zilog, dmu_tx_t *tx, int txtype,
- znode_t *dzp, znode_t *zp, char *name)
+zfs_log_create(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype,
+ znode_t *dzp, znode_t *zp, char *name, vsecattr_t *vsecp,
+ zfs_fuid_info_t *fuidp, vattr_t *vap)
{
itx_t *itx;
uint64_t seq;
lr_create_t *lr;
+ lr_acl_create_t *lracl;
+ size_t aclsize;
+ size_t xvatsize = 0;
+ size_t txsize;
+ xvattr_t *xvap = (xvattr_t *)vap;
+ void *end;
+ size_t lrsize;
size_t namesize = strlen(name) + 1;
+ size_t fuidsz = 0;
if (zilog == NULL)
return;
- itx = zil_itx_create(txtype, sizeof (*lr) + namesize);
+ /*
+ * If we have FUIDs present then add in space for
+ * domains and ACE fuid's if any.
+ */
+ if (fuidp) {
+ fuidsz += fuidp->z_domain_str_sz;
+ fuidsz += fuidp->z_fuid_cnt * sizeof (uint64_t);
+ }
+
+ if (vap->va_mask & AT_XVATTR)
+ xvatsize = ZIL_XVAT_SIZE(xvap->xva_mapsize);
+
+ if ((int)txtype == TX_CREATE_ATTR || (int)txtype == TX_MKDIR_ATTR ||
+ (int)txtype == TX_CREATE || (int)txtype == TX_MKDIR ||
+ (int)txtype == TX_MKXATTR) {
+ txsize = sizeof (*lr) + namesize + fuidsz + xvatsize;
+ lrsize = sizeof (*lr);
+ } else {
+ aclsize = (vsecp) ? vsecp->vsa_aclentsz : 0;
+ txsize =
+ sizeof (lr_acl_create_t) + namesize + fuidsz +
+ ZIL_ACE_LENGTH(aclsize) + xvatsize;
+ lrsize = sizeof (lr_acl_create_t);
+ }
+
+ itx = zil_itx_create(txtype, txsize);
+
lr = (lr_create_t *)&itx->itx_lr;
lr->lr_doid = dzp->z_id;
lr->lr_foid = zp->z_id;
lr->lr_mode = zp->z_phys->zp_mode;
- lr->lr_uid = zp->z_phys->zp_uid;
- lr->lr_gid = zp->z_phys->zp_gid;
+ if (!IS_EPHEMERAL(zp->z_phys->zp_uid)) {
+ lr->lr_uid = (uint64_t)zp->z_phys->zp_uid;
+ } else {
+ lr->lr_uid = fuidp->z_fuid_owner;
+ }
+ if (!IS_EPHEMERAL(zp->z_phys->zp_gid)) {
+ lr->lr_gid = (uint64_t)zp->z_phys->zp_gid;
+ } else {
+ lr->lr_gid = fuidp->z_fuid_group;
+ }
lr->lr_gen = zp->z_phys->zp_gen;
lr->lr_crtime[0] = zp->z_phys->zp_crtime[0];
lr->lr_crtime[1] = zp->z_phys->zp_crtime[1];
lr->lr_rdev = zp->z_phys->zp_rdev;
- bcopy(name, (char *)(lr + 1), namesize);
+
+ /*
+ * Fill in xvattr info if any
+ */
+ if (vap->va_mask & AT_XVATTR) {
+ zfs_log_xvattr((lr_attr_t *)((caddr_t)lr + lrsize), xvap);
+ end = (caddr_t)lr + lrsize + xvatsize;
+ } else {
+ end = (caddr_t)lr + lrsize;
+ }
+
+ /* Now fill in any ACL info */
+
+ if (vsecp) {
+ lracl = (lr_acl_create_t *)&itx->itx_lr;
+ lracl->lr_aclcnt = vsecp->vsa_aclcnt;
+ lracl->lr_acl_bytes = aclsize;
+ lracl->lr_domcnt = fuidp ? fuidp->z_domain_cnt : 0;
+ lracl->lr_fuidcnt = fuidp ? fuidp->z_fuid_cnt : 0;
+ if (vsecp->vsa_aclflags & VSA_ACE_ACLFLAGS)
+ lracl->lr_acl_flags = (uint64_t)vsecp->vsa_aclflags;
+ else
+ lracl->lr_acl_flags = 0;
+
+ bcopy(vsecp->vsa_aclentp, end, aclsize);
+ end = (caddr_t)end + ZIL_ACE_LENGTH(aclsize);
+ }
+
+ /* drop in FUID info */
+ if (fuidp) {
+ end = zfs_log_fuid_ids(fuidp, end);
+ end = zfs_log_fuid_domains(fuidp, end);
+ }
+ /*
+ * Now place file name in log record
+ */
+ bcopy(name, end, namesize);
seq = zil_itx_assign(zilog, itx, tx);
dzp->z_last_itx = seq;
@@ -89,7 +320,7 @@ zfs_log_create(zilog_t *zilog, dmu_tx_t *tx, int txtype,
* zfs_log_remove() handles both TX_REMOVE and TX_RMDIR transactions.
*/
void
-zfs_log_remove(zilog_t *zilog, dmu_tx_t *tx, int txtype,
+zfs_log_remove(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype,
znode_t *dzp, char *name)
{
itx_t *itx;
@@ -113,7 +344,7 @@ zfs_log_remove(zilog_t *zilog, dmu_tx_t *tx, int txtype,
* zfs_log_link() handles TX_LINK transactions.
*/
void
-zfs_log_link(zilog_t *zilog, dmu_tx_t *tx, int txtype,
+zfs_log_link(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype,
znode_t *dzp, znode_t *zp, char *name)
{
itx_t *itx;
@@ -139,8 +370,8 @@ zfs_log_link(zilog_t *zilog, dmu_tx_t *tx, int txtype,
* zfs_log_symlink() handles TX_SYMLINK transactions.
*/
void
-zfs_log_symlink(zilog_t *zilog, dmu_tx_t *tx, int txtype,
- znode_t *dzp, znode_t *zp, char *name, char *link)
+zfs_log_symlink(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype,
+ znode_t *dzp, znode_t *zp, char *name, char *link)
{
itx_t *itx;
uint64_t seq;
@@ -173,7 +404,7 @@ zfs_log_symlink(zilog_t *zilog, dmu_tx_t *tx, int txtype,
* zfs_log_rename() handles TX_RENAME transactions.
*/
void
-zfs_log_rename(zilog_t *zilog, dmu_tx_t *tx, int txtype,
+zfs_log_rename(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype,
znode_t *sdzp, char *sname, znode_t *tdzp, char *dname, znode_t *szp)
{
itx_t *itx;
@@ -203,15 +434,16 @@ zfs_log_rename(zilog_t *zilog, dmu_tx_t *tx, int txtype,
*/
ssize_t zfs_immediate_write_sz = 32768;
+#define ZIL_MAX_LOG_DATA (SPA_MAXBLOCKSIZE - sizeof (zil_trailer_t) - \
+ sizeof (lr_write_t))
+
void
zfs_log_write(zilog_t *zilog, dmu_tx_t *tx, int txtype,
- znode_t *zp, offset_t off, ssize_t len, int ioflag)
+ znode_t *zp, offset_t off, ssize_t resid, int ioflag)
{
- itx_t *itx;
- uint64_t seq;
- lr_write_t *lr;
itx_wr_state_t write_state;
- int err;
+ boolean_t slogging;
+ uintptr_t fsync_cnt;
if (zilog == NULL || zp->z_unlinked)
return;
@@ -220,52 +452,84 @@ zfs_log_write(zilog_t *zilog, dmu_tx_t *tx, int txtype,
* Writes are handled in three different ways:
*
* WR_INDIRECT:
- * If the write is greater than zfs_immediate_write_sz then
- * later *if* we need to log the write then dmu_sync() is used
- * to immediately write the block and it's block pointer is put
- * in the log record.
+ * In this mode, if we need to commit the write later, then the block
+ * is immediately written into the file system (using dmu_sync),
+ * and a pointer to the block is put into the log record.
+ * When the txg commits the block is linked in.
+ * This saves additionally writing the data into the log record.
+ * There are a few requirements for this to occur:
+ * - write is greater than zfs_immediate_write_sz
+ * - not using slogs (as slogs are assumed to always be faster
+ * than writing into the main pool)
+ * - the write occupies only one block
* WR_COPIED:
* If we know we'll immediately be committing the
- * transaction (FDSYNC (O_DSYNC)), the we allocate a larger
+ * transaction (FSYNC or FDSYNC), the we allocate a larger
* log record here for the data and copy the data in.
* WR_NEED_COPY:
* Otherwise we don't allocate a buffer, and *if* we need to
* flush the write later then a buffer is allocated and
* we retrieve the data using the dmu.
*/
- if (len > zfs_immediate_write_sz)
+ slogging = spa_has_slogs(zilog->zl_spa);
+ if (resid > zfs_immediate_write_sz && !slogging && resid <= zp->z_blksz)
write_state = WR_INDIRECT;
- else if (ioflag & FDSYNC)
+ else if (ioflag & (FSYNC | FDSYNC))
write_state = WR_COPIED;
else
write_state = WR_NEED_COPY;
- itx = zil_itx_create(txtype, sizeof (*lr) +
- (write_state == WR_COPIED ? len : 0));
- lr = (lr_write_t *)&itx->itx_lr;
- if (write_state == WR_COPIED) {
- err = dmu_read(zp->z_zfsvfs->z_os, zp->z_id, off, len, lr + 1);
- if (err) {
+ if ((fsync_cnt = (uintptr_t)tsd_get(zfs_fsyncer_key)) != 0) {
+ (void) tsd_set(zfs_fsyncer_key, (void *)(fsync_cnt - 1));
+ }
+
+ while (resid) {
+ itx_t *itx;
+ lr_write_t *lr;
+ ssize_t len;
+
+ /*
+ * If the write would overflow the largest block then split it.
+ */
+ if (write_state != WR_INDIRECT && resid > ZIL_MAX_LOG_DATA)
+ len = SPA_MAXBLOCKSIZE >> 1;
+ else
+ len = resid;
+
+ itx = zil_itx_create(txtype, sizeof (*lr) +
+ (write_state == WR_COPIED ? len : 0));
+ lr = (lr_write_t *)&itx->itx_lr;
+ if (write_state == WR_COPIED && dmu_read(zp->z_zfsvfs->z_os,
+ zp->z_id, off, len, lr + 1) != 0) {
kmem_free(itx, offsetof(itx_t, itx_lr) +
itx->itx_lr.lrc_reclen);
itx = zil_itx_create(txtype, sizeof (*lr));
lr = (lr_write_t *)&itx->itx_lr;
write_state = WR_NEED_COPY;
}
- }
- itx->itx_wr_state = write_state;
- lr->lr_foid = zp->z_id;
- lr->lr_offset = off;
- lr->lr_length = len;
- lr->lr_blkoff = 0;
- BP_ZERO(&lr->lr_blkptr);
+ itx->itx_wr_state = write_state;
+ if (write_state == WR_NEED_COPY)
+ itx->itx_sod += len;
+ lr->lr_foid = zp->z_id;
+ lr->lr_offset = off;
+ lr->lr_length = len;
+ lr->lr_blkoff = 0;
+ BP_ZERO(&lr->lr_blkptr);
- itx->itx_private = zp->z_zfsvfs;
+ itx->itx_private = zp->z_zfsvfs;
- itx->itx_sync = (zp->z_sync_cnt != 0);
- seq = zil_itx_assign(zilog, itx, tx);
- zp->z_last_itx = seq;
+ if ((zp->z_sync_cnt != 0) || (fsync_cnt != 0) ||
+ (ioflag & (FSYNC | FDSYNC)))
+ itx->itx_sync = B_TRUE;
+ else
+ itx->itx_sync = B_FALSE;
+
+ zp->z_last_itx = zil_itx_assign(zilog, itx, tx);
+
+ off += len;
+ resid -= len;
+ }
}
/*
@@ -298,25 +562,60 @@ zfs_log_truncate(zilog_t *zilog, dmu_tx_t *tx, int txtype,
*/
void
zfs_log_setattr(zilog_t *zilog, dmu_tx_t *tx, int txtype,
- znode_t *zp, vattr_t *vap, uint_t mask_applied)
+ znode_t *zp, vattr_t *vap, uint_t mask_applied, zfs_fuid_info_t *fuidp)
{
- itx_t *itx;
- uint64_t seq;
- lr_setattr_t *lr;
+ itx_t *itx;
+ uint64_t seq;
+ lr_setattr_t *lr;
+ xvattr_t *xvap = (xvattr_t *)vap;
+ size_t recsize = sizeof (lr_setattr_t);
+ void *start;
+
if (zilog == NULL || zp->z_unlinked)
return;
- itx = zil_itx_create(txtype, sizeof (*lr));
+ /*
+ * If XVATTR set, then log record size needs to allow
+ * for lr_attr_t + xvattr mask, mapsize and create time
+ * plus actual attribute values
+ */
+ if (vap->va_mask & AT_XVATTR)
+ recsize = sizeof (*lr) + ZIL_XVAT_SIZE(xvap->xva_mapsize);
+
+ if (fuidp)
+ recsize += fuidp->z_domain_str_sz;
+
+ itx = zil_itx_create(txtype, recsize);
lr = (lr_setattr_t *)&itx->itx_lr;
lr->lr_foid = zp->z_id;
lr->lr_mask = (uint64_t)mask_applied;
lr->lr_mode = (uint64_t)vap->va_mode;
- lr->lr_uid = (uint64_t)vap->va_uid;
- lr->lr_gid = (uint64_t)vap->va_gid;
+ if ((mask_applied & AT_UID) && IS_EPHEMERAL(vap->va_uid))
+ lr->lr_uid = fuidp->z_fuid_owner;
+ else
+ lr->lr_uid = (uint64_t)vap->va_uid;
+
+ if ((mask_applied & AT_GID) && IS_EPHEMERAL(vap->va_gid))
+ lr->lr_gid = fuidp->z_fuid_group;
+ else
+ lr->lr_gid = (uint64_t)vap->va_gid;
+
lr->lr_size = (uint64_t)vap->va_size;
ZFS_TIME_ENCODE(&vap->va_atime, lr->lr_atime);
ZFS_TIME_ENCODE(&vap->va_mtime, lr->lr_mtime);
+ start = (lr_setattr_t *)(lr + 1);
+ if (vap->va_mask & AT_XVATTR) {
+ zfs_log_xvattr((lr_attr_t *)start, xvap);
+ start = (caddr_t)start + ZIL_XVAT_SIZE(xvap->xva_mapsize);
+ }
+
+ /*
+ * Now stick on domain information if any on end
+ */
+
+ if (fuidp)
+ (void) zfs_log_fuid_domains(fuidp, start);
itx->itx_sync = (zp->z_sync_cnt != 0);
seq = zil_itx_assign(zilog, itx, tx);
@@ -327,21 +626,64 @@ zfs_log_setattr(zilog_t *zilog, dmu_tx_t *tx, int txtype,
* zfs_log_acl() handles TX_ACL transactions.
*/
void
-zfs_log_acl(zilog_t *zilog, dmu_tx_t *tx, int txtype,
- znode_t *zp, int aclcnt, ace_t *z_ace)
+zfs_log_acl(zilog_t *zilog, dmu_tx_t *tx, znode_t *zp,
+ vsecattr_t *vsecp, zfs_fuid_info_t *fuidp)
{
itx_t *itx;
uint64_t seq;
+ lr_acl_v0_t *lrv0;
lr_acl_t *lr;
+ int txtype;
+ int lrsize;
+ size_t txsize;
+ size_t aclbytes = vsecp->vsa_aclentsz;
if (zilog == NULL || zp->z_unlinked)
return;
- itx = zil_itx_create(txtype, sizeof (*lr) + aclcnt * sizeof (ace_t));
+ txtype = (zp->z_zfsvfs->z_version < ZPL_VERSION_FUID) ?
+ TX_ACL_V0 : TX_ACL;
+
+ if (txtype == TX_ACL)
+ lrsize = sizeof (*lr);
+ else
+ lrsize = sizeof (*lrv0);
+
+ txsize = lrsize +
+ ((txtype == TX_ACL) ? ZIL_ACE_LENGTH(aclbytes) : aclbytes) +
+ (fuidp ? fuidp->z_domain_str_sz : 0) +
+ sizeof (uint64_t) * (fuidp ? fuidp->z_fuid_cnt : 0);
+
+ itx = zil_itx_create(txtype, txsize);
+
lr = (lr_acl_t *)&itx->itx_lr;
lr->lr_foid = zp->z_id;
- lr->lr_aclcnt = (uint64_t)aclcnt;
- bcopy(z_ace, (ace_t *)(lr + 1), aclcnt * sizeof (ace_t));
+ if (txtype == TX_ACL) {
+ lr->lr_acl_bytes = aclbytes;
+ lr->lr_domcnt = fuidp ? fuidp->z_domain_cnt : 0;
+ lr->lr_fuidcnt = fuidp ? fuidp->z_fuid_cnt : 0;
+ if (vsecp->vsa_mask & VSA_ACE_ACLFLAGS)
+ lr->lr_acl_flags = (uint64_t)vsecp->vsa_aclflags;
+ else
+ lr->lr_acl_flags = 0;
+ }
+ lr->lr_aclcnt = (uint64_t)vsecp->vsa_aclcnt;
+
+ if (txtype == TX_ACL_V0) {
+ lrv0 = (lr_acl_v0_t *)lr;
+ bcopy(vsecp->vsa_aclentp, (ace_t *)(lrv0 + 1), aclbytes);
+ } else {
+ void *start = (ace_t *)(lr + 1);
+
+ bcopy(vsecp->vsa_aclentp, start, aclbytes);
+
+ start = (caddr_t)start + ZIL_ACE_LENGTH(aclbytes);
+
+ if (fuidp) {
+ start = zfs_log_fuid_ids(fuidp, start);
+ (void) zfs_log_fuid_domains(fuidp, start);
+ }
+ }
itx->itx_sync = (zp->z_sync_cnt != 0);
seq = zil_itx_assign(zilog, itx, tx);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_replay.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_replay.c
index eb3215d..573a82c 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_replay.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_replay.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -38,6 +38,7 @@
#include <sys/zfs_znode.h>
#include <sys/zfs_dir.h>
#include <sys/zfs_acl.h>
+#include <sys/zfs_fuid.h>
#include <sys/spa.h>
#include <sys/zil.h>
#include <sys/byteorder.h>
@@ -61,8 +62,8 @@ zfs_init_vattr(vattr_t *vap, uint64_t mask, uint64_t mode,
vap->va_mask = (uint_t)mask;
vap->va_type = IFTOVT(mode);
vap->va_mode = mode & MODEMASK;
- vap->va_uid = (uid_t)uid;
- vap->va_gid = (gid_t)gid;
+ vap->va_uid = (uid_t)(IS_EPHEMERAL(uid)) ? -1 : uid;
+ vap->va_gid = (gid_t)(IS_EPHEMERAL(gid)) ? -1 : gid;
vap->va_rdev = zfs_cmpldev(rdev);
vap->va_nodeid = nodeid;
}
@@ -74,24 +75,365 @@ zfs_replay_error(zfsvfs_t *zfsvfs, lr_t *lr, boolean_t byteswap)
return (ENOTSUP);
}
+static void
+zfs_replay_xvattr(lr_attr_t *lrattr, xvattr_t *xvap)
+{
+ xoptattr_t *xoap = NULL;
+ uint64_t *attrs;
+ uint64_t *crtime;
+ uint32_t *bitmap;
+ void *scanstamp;
+ int i;
+
+ xvap->xva_vattr.va_mask |= AT_XVATTR;
+ if ((xoap = xva_getxoptattr(xvap)) == NULL) {
+ xvap->xva_vattr.va_mask &= ~AT_XVATTR; /* shouldn't happen */
+ return;
+ }
+
+ ASSERT(lrattr->lr_attr_masksize == xvap->xva_mapsize);
+
+ bitmap = &lrattr->lr_attr_bitmap;
+ for (i = 0; i != lrattr->lr_attr_masksize; i++, bitmap++)
+ xvap->xva_reqattrmap[i] = *bitmap;
+
+ attrs = (uint64_t *)(lrattr + lrattr->lr_attr_masksize - 1);
+ crtime = attrs + 1;
+ scanstamp = (caddr_t)(crtime + 2);
+
+ if (XVA_ISSET_REQ(xvap, XAT_HIDDEN))
+ xoap->xoa_hidden = ((*attrs & XAT0_HIDDEN) != 0);
+ if (XVA_ISSET_REQ(xvap, XAT_SYSTEM))
+ xoap->xoa_system = ((*attrs & XAT0_SYSTEM) != 0);
+ if (XVA_ISSET_REQ(xvap, XAT_ARCHIVE))
+ xoap->xoa_archive = ((*attrs & XAT0_ARCHIVE) != 0);
+ if (XVA_ISSET_REQ(xvap, XAT_READONLY))
+ xoap->xoa_readonly = ((*attrs & XAT0_READONLY) != 0);
+ if (XVA_ISSET_REQ(xvap, XAT_IMMUTABLE))
+ xoap->xoa_immutable = ((*attrs & XAT0_IMMUTABLE) != 0);
+ if (XVA_ISSET_REQ(xvap, XAT_NOUNLINK))
+ xoap->xoa_nounlink = ((*attrs & XAT0_NOUNLINK) != 0);
+ if (XVA_ISSET_REQ(xvap, XAT_APPENDONLY))
+ xoap->xoa_appendonly = ((*attrs & XAT0_APPENDONLY) != 0);
+ if (XVA_ISSET_REQ(xvap, XAT_NODUMP))
+ xoap->xoa_nodump = ((*attrs & XAT0_NODUMP) != 0);
+ if (XVA_ISSET_REQ(xvap, XAT_OPAQUE))
+ xoap->xoa_opaque = ((*attrs & XAT0_OPAQUE) != 0);
+ if (XVA_ISSET_REQ(xvap, XAT_AV_MODIFIED))
+ xoap->xoa_av_modified = ((*attrs & XAT0_AV_MODIFIED) != 0);
+ if (XVA_ISSET_REQ(xvap, XAT_AV_QUARANTINED))
+ xoap->xoa_av_quarantined =
+ ((*attrs & XAT0_AV_QUARANTINED) != 0);
+ if (XVA_ISSET_REQ(xvap, XAT_CREATETIME))
+ ZFS_TIME_DECODE(&xoap->xoa_createtime, crtime);
+ if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP))
+ bcopy(scanstamp, xoap->xoa_av_scanstamp, AV_SCANSTAMP_SZ);
+}
+
+static int
+zfs_replay_domain_cnt(uint64_t uid, uint64_t gid)
+{
+ uint64_t uid_idx;
+ uint64_t gid_idx;
+ int domcnt = 0;
+
+ uid_idx = FUID_INDEX(uid);
+ gid_idx = FUID_INDEX(gid);
+ if (uid_idx)
+ domcnt++;
+ if (gid_idx > 0 && gid_idx != uid_idx)
+ domcnt++;
+
+ return (domcnt);
+}
+
+static void *
+zfs_replay_fuid_domain_common(zfs_fuid_info_t *fuid_infop, void *start,
+ int domcnt)
+{
+ int i;
+
+ for (i = 0; i != domcnt; i++) {
+ fuid_infop->z_domain_table[i] = start;
+ start = (caddr_t)start + strlen(start) + 1;
+ }
+
+ return (start);
+}
+
+/*
+ * Set the uid/gid in the fuid_info structure.
+ */
+static void
+zfs_replay_fuid_ugid(zfs_fuid_info_t *fuid_infop, uint64_t uid, uint64_t gid)
+{
+ /*
+ * If owner or group are log specific FUIDs then slurp up
+ * domain information and build zfs_fuid_info_t
+ */
+ if (IS_EPHEMERAL(uid))
+ fuid_infop->z_fuid_owner = uid;
+
+ if (IS_EPHEMERAL(gid))
+ fuid_infop->z_fuid_group = gid;
+}
+
+/*
+ * Load fuid domains into fuid_info_t
+ */
+static zfs_fuid_info_t *
+zfs_replay_fuid_domain(void *buf, void **end, uint64_t uid, uint64_t gid)
+{
+ int domcnt;
+
+ zfs_fuid_info_t *fuid_infop;
+
+ fuid_infop = zfs_fuid_info_alloc();
+
+ domcnt = zfs_replay_domain_cnt(uid, gid);
+
+ if (domcnt == 0)
+ return (fuid_infop);
+
+ fuid_infop->z_domain_table =
+ kmem_zalloc(domcnt * sizeof (char **), KM_SLEEP);
+
+ zfs_replay_fuid_ugid(fuid_infop, uid, gid);
+
+ fuid_infop->z_domain_cnt = domcnt;
+ *end = zfs_replay_fuid_domain_common(fuid_infop, buf, domcnt);
+ return (fuid_infop);
+}
+
+/*
+ * load zfs_fuid_t's and fuid_domains into fuid_info_t
+ */
+static zfs_fuid_info_t *
+zfs_replay_fuids(void *start, void **end, int idcnt, int domcnt, uint64_t uid,
+ uint64_t gid)
+{
+ uint64_t *log_fuid = (uint64_t *)start;
+ zfs_fuid_info_t *fuid_infop;
+ int i;
+
+ fuid_infop = zfs_fuid_info_alloc();
+ fuid_infop->z_domain_cnt = domcnt;
+
+ fuid_infop->z_domain_table =
+ kmem_zalloc(domcnt * sizeof (char **), KM_SLEEP);
+
+ for (i = 0; i != idcnt; i++) {
+ zfs_fuid_t *zfuid;
+
+ zfuid = kmem_alloc(sizeof (zfs_fuid_t), KM_SLEEP);
+ zfuid->z_logfuid = *log_fuid;
+ zfuid->z_id = -1;
+ zfuid->z_domidx = 0;
+ list_insert_tail(&fuid_infop->z_fuids, zfuid);
+ log_fuid++;
+ }
+
+ zfs_replay_fuid_ugid(fuid_infop, uid, gid);
+
+ *end = zfs_replay_fuid_domain_common(fuid_infop, log_fuid, domcnt);
+ return (fuid_infop);
+}
+
+static void
+zfs_replay_swap_attrs(lr_attr_t *lrattr)
+{
+ /* swap the lr_attr structure */
+ byteswap_uint32_array(lrattr, sizeof (*lrattr));
+ /* swap the bitmap */
+ byteswap_uint32_array(lrattr + 1, (lrattr->lr_attr_masksize - 1) *
+ sizeof (uint32_t));
+ /* swap the attributes, create time + 64 bit word for attributes */
+ byteswap_uint64_array((caddr_t)(lrattr + 1) + (sizeof (uint32_t) *
+ (lrattr->lr_attr_masksize - 1)), 3 * sizeof (uint64_t));
+}
+
+/*
+ * Replay file create with optional ACL, xvattr information as well
+ * as option FUID information.
+ */
+static int
+zfs_replay_create_acl(zfsvfs_t *zfsvfs,
+ lr_acl_create_t *lracl, boolean_t byteswap)
+{
+ char *name = NULL; /* location determined later */
+ lr_create_t *lr = (lr_create_t *)lracl;
+ znode_t *dzp;
+ vnode_t *vp = NULL;
+ xvattr_t xva;
+ int vflg = 0;
+ vsecattr_t vsec = { 0 };
+ lr_attr_t *lrattr;
+ void *aclstart;
+ void *fuidstart;
+ size_t xvatlen = 0;
+ uint64_t txtype;
+ int error;
+
+ if (byteswap) {
+ byteswap_uint64_array(lracl, sizeof (*lracl));
+ txtype = (int)lr->lr_common.lrc_txtype;
+ if (txtype == TX_CREATE_ACL_ATTR ||
+ txtype == TX_MKDIR_ACL_ATTR) {
+ lrattr = (lr_attr_t *)(caddr_t)(lracl + 1);
+ zfs_replay_swap_attrs(lrattr);
+ xvatlen = ZIL_XVAT_SIZE(lrattr->lr_attr_masksize);
+ }
+
+ aclstart = (caddr_t)(lracl + 1) + xvatlen;
+ zfs_ace_byteswap(aclstart, lracl->lr_acl_bytes, B_FALSE);
+ /* swap fuids */
+ if (lracl->lr_fuidcnt) {
+ byteswap_uint64_array((caddr_t)aclstart +
+ ZIL_ACE_LENGTH(lracl->lr_acl_bytes),
+ lracl->lr_fuidcnt * sizeof (uint64_t));
+ }
+ }
+
+ if ((error = zfs_zget(zfsvfs, lr->lr_doid, &dzp)) != 0)
+ return (error);
+
+ xva_init(&xva);
+ zfs_init_vattr(&xva.xva_vattr, AT_TYPE | AT_MODE | AT_UID | AT_GID,
+ lr->lr_mode, lr->lr_uid, lr->lr_gid, lr->lr_rdev, lr->lr_foid);
+
+ /*
+ * All forms of zfs create (create, mkdir, mkxattrdir, symlink)
+ * eventually end up in zfs_mknode(), which assigns the object's
+ * creation time and generation number. The generic VOP_CREATE()
+ * doesn't have either concept, so we smuggle the values inside
+ * the vattr's otherwise unused va_ctime and va_nblocks fields.
+ */
+ ZFS_TIME_DECODE(&xva.xva_vattr.va_ctime, lr->lr_crtime);
+ xva.xva_vattr.va_nblocks = lr->lr_gen;
+
+ error = dmu_object_info(zfsvfs->z_os, lr->lr_foid, NULL);
+ if (error != ENOENT)
+ goto bail;
+
+ if (lr->lr_common.lrc_txtype & TX_CI)
+ vflg |= FIGNORECASE;
+ switch ((int)lr->lr_common.lrc_txtype) {
+ case TX_CREATE_ACL:
+ aclstart = (caddr_t)(lracl + 1);
+ fuidstart = (caddr_t)aclstart +
+ ZIL_ACE_LENGTH(lracl->lr_acl_bytes);
+ zfsvfs->z_fuid_replay = zfs_replay_fuids(fuidstart,
+ (void *)&name, lracl->lr_fuidcnt, lracl->lr_domcnt,
+ lr->lr_uid, lr->lr_gid);
+ /*FALLTHROUGH*/
+ case TX_CREATE_ACL_ATTR:
+ if (name == NULL) {
+ lrattr = (lr_attr_t *)(caddr_t)(lracl + 1);
+ xvatlen = ZIL_XVAT_SIZE(lrattr->lr_attr_masksize);
+ xva.xva_vattr.va_mask |= AT_XVATTR;
+ zfs_replay_xvattr(lrattr, &xva);
+ }
+ vsec.vsa_mask = VSA_ACE | VSA_ACE_ACLFLAGS;
+ vsec.vsa_aclentp = (caddr_t)(lracl + 1) + xvatlen;
+ vsec.vsa_aclcnt = lracl->lr_aclcnt;
+ vsec.vsa_aclentsz = lracl->lr_acl_bytes;
+ vsec.vsa_aclflags = lracl->lr_acl_flags;
+ if (zfsvfs->z_fuid_replay == NULL) {
+ fuidstart = (caddr_t)(lracl + 1) + xvatlen +
+ ZIL_ACE_LENGTH(lracl->lr_acl_bytes);
+ zfsvfs->z_fuid_replay =
+ zfs_replay_fuids(fuidstart,
+ (void *)&name, lracl->lr_fuidcnt, lracl->lr_domcnt,
+ lr->lr_uid, lr->lr_gid);
+ }
+
+#ifdef TODO
+ error = VOP_CREATE(ZTOV(dzp), name, &xva.xva_vattr,
+ 0, 0, &vp, kcred, vflg, NULL, &vsec);
+#else
+ panic("%s:%u: unsupported condition", __func__, __LINE__);
+#endif
+ break;
+ case TX_MKDIR_ACL:
+ aclstart = (caddr_t)(lracl + 1);
+ fuidstart = (caddr_t)aclstart +
+ ZIL_ACE_LENGTH(lracl->lr_acl_bytes);
+ zfsvfs->z_fuid_replay = zfs_replay_fuids(fuidstart,
+ (void *)&name, lracl->lr_fuidcnt, lracl->lr_domcnt,
+ lr->lr_uid, lr->lr_gid);
+ /*FALLTHROUGH*/
+ case TX_MKDIR_ACL_ATTR:
+ if (name == NULL) {
+ lrattr = (lr_attr_t *)(caddr_t)(lracl + 1);
+ xvatlen = ZIL_XVAT_SIZE(lrattr->lr_attr_masksize);
+ zfs_replay_xvattr(lrattr, &xva);
+ }
+ vsec.vsa_mask = VSA_ACE | VSA_ACE_ACLFLAGS;
+ vsec.vsa_aclentp = (caddr_t)(lracl + 1) + xvatlen;
+ vsec.vsa_aclcnt = lracl->lr_aclcnt;
+ vsec.vsa_aclentsz = lracl->lr_acl_bytes;
+ vsec.vsa_aclflags = lracl->lr_acl_flags;
+ if (zfsvfs->z_fuid_replay == NULL) {
+ fuidstart = (caddr_t)(lracl + 1) + xvatlen +
+ ZIL_ACE_LENGTH(lracl->lr_acl_bytes);
+ zfsvfs->z_fuid_replay =
+ zfs_replay_fuids(fuidstart,
+ (void *)&name, lracl->lr_fuidcnt, lracl->lr_domcnt,
+ lr->lr_uid, lr->lr_gid);
+ }
+#ifdef TODO
+ error = VOP_MKDIR(ZTOV(dzp), name, &xva.xva_vattr,
+ &vp, kcred, NULL, vflg, &vsec);
+#else
+ panic("%s:%u: unsupported condition", __func__, __LINE__);
+#endif
+ break;
+ default:
+ error = ENOTSUP;
+ }
+
+bail:
+ if (error == 0 && vp != NULL)
+ VN_RELE(vp);
+
+ VN_RELE(ZTOV(dzp));
+
+ zfs_fuid_info_free(zfsvfs->z_fuid_replay);
+ zfsvfs->z_fuid_replay = NULL;
+
+ return (error);
+}
+
static int
zfs_replay_create(zfsvfs_t *zfsvfs, lr_create_t *lr, boolean_t byteswap)
{
- char *name = (char *)(lr + 1); /* name follows lr_create_t */
+ char *name = NULL; /* location determined later */
char *link; /* symlink content follows name */
znode_t *dzp;
vnode_t *vp = NULL;
- vattr_t va;
+ xvattr_t xva;
+ int vflg = 0;
+ size_t lrsize = sizeof (lr_create_t);
+ lr_attr_t *lrattr;
+ void *start;
+ size_t xvatlen;
+ uint64_t txtype;
struct componentname cn;
int error;
- if (byteswap)
+ if (byteswap) {
byteswap_uint64_array(lr, sizeof (*lr));
+ txtype = (int)lr->lr_common.lrc_txtype;
+ if (txtype == TX_CREATE_ATTR || txtype == TX_MKDIR_ATTR)
+ zfs_replay_swap_attrs((lr_attr_t *)(lr + 1));
+ }
+
if ((error = zfs_zget(zfsvfs, lr->lr_doid, &dzp)) != 0)
return (error);
- zfs_init_vattr(&va, AT_TYPE | AT_MODE | AT_UID | AT_GID,
+ xva_init(&xva);
+ zfs_init_vattr(&xva.xva_vattr, AT_TYPE | AT_MODE | AT_UID | AT_GID,
lr->lr_mode, lr->lr_uid, lr->lr_gid, lr->lr_rdev, lr->lr_foid);
/*
@@ -101,34 +443,89 @@ zfs_replay_create(zfsvfs_t *zfsvfs, lr_create_t *lr, boolean_t byteswap)
* doesn't have either concept, so we smuggle the values inside
* the vattr's otherwise unused va_ctime and va_nblocks fields.
*/
- ZFS_TIME_DECODE(&va.va_ctime, lr->lr_crtime);
- va.va_nblocks = lr->lr_gen;
+ ZFS_TIME_DECODE(&xva.xva_vattr.va_ctime, lr->lr_crtime);
+ xva.xva_vattr.va_nblocks = lr->lr_gen;
+
+ error = dmu_object_info(zfsvfs->z_os, lr->lr_foid, NULL);
+ if (error != ENOENT)
+ goto out;
+
+ if (lr->lr_common.lrc_txtype & TX_CI)
+ vflg |= FIGNORECASE;
+
+ /*
+ * Symlinks don't have fuid info, and CIFS never creates
+ * symlinks.
+ *
+ * The _ATTR versions will grab the fuid info in their subcases.
+ */
+ if ((int)lr->lr_common.lrc_txtype != TX_SYMLINK &&
+ (int)lr->lr_common.lrc_txtype != TX_MKDIR_ATTR &&
+ (int)lr->lr_common.lrc_txtype != TX_CREATE_ATTR) {
+ start = (lr + 1);
+ zfsvfs->z_fuid_replay =
+ zfs_replay_fuid_domain(start, &start,
+ lr->lr_uid, lr->lr_gid);
+ }
- cn.cn_nameptr = name;
cn.cn_cred = kcred;
cn.cn_thread = curthread;
cn.cn_flags = SAVENAME;
vn_lock(ZTOV(dzp), LK_EXCLUSIVE | LK_RETRY);
switch ((int)lr->lr_common.lrc_txtype) {
+ case TX_CREATE_ATTR:
+ lrattr = (lr_attr_t *)(caddr_t)(lr + 1);
+ xvatlen = ZIL_XVAT_SIZE(lrattr->lr_attr_masksize);
+ zfs_replay_xvattr((lr_attr_t *)((caddr_t)lr + lrsize), &xva);
+ start = (caddr_t)(lr + 1) + xvatlen;
+ zfsvfs->z_fuid_replay =
+ zfs_replay_fuid_domain(start, &start,
+ lr->lr_uid, lr->lr_gid);
+ name = (char *)start;
+
+ /*FALLTHROUGH*/
case TX_CREATE:
- error = VOP_CREATE(ZTOV(dzp), &vp, &cn, &va);
+ if (name == NULL)
+ name = (char *)start;
+
+ cn.cn_nameptr = name;
+ error = VOP_CREATE(ZTOV(dzp), &vp, &cn, &xva.xva_vattr /*,vflg*/);
break;
+ case TX_MKDIR_ATTR:
+ lrattr = (lr_attr_t *)(caddr_t)(lr + 1);
+ xvatlen = ZIL_XVAT_SIZE(lrattr->lr_attr_masksize);
+ zfs_replay_xvattr((lr_attr_t *)((caddr_t)lr + lrsize), &xva);
+ start = (caddr_t)(lr + 1) + xvatlen;
+ zfsvfs->z_fuid_replay =
+ zfs_replay_fuid_domain(start, &start,
+ lr->lr_uid, lr->lr_gid);
+ name = (char *)start;
+
+ /*FALLTHROUGH*/
case TX_MKDIR:
- error = VOP_MKDIR(ZTOV(dzp), &vp, &cn, &va);
+ if (name == NULL)
+ name = (char *)(lr + 1);
+
+ cn.cn_nameptr = name;
+ error = VOP_MKDIR(ZTOV(dzp), &vp, &cn, &xva.xva_vattr /*,vflg*/);
break;
case TX_MKXATTR:
- error = zfs_make_xattrdir(dzp, &va, &vp, kcred);
+ name = (char *)(lr + 1);
+ error = zfs_make_xattrdir(dzp, &xva.xva_vattr, &vp, kcred);
break;
case TX_SYMLINK:
+ name = (char *)(lr + 1);
link = name + strlen(name) + 1;
- error = VOP_SYMLINK(ZTOV(dzp), &vp, &cn, &va, link);
+ cn.cn_nameptr = name;
+ error = VOP_SYMLINK(ZTOV(dzp), &vp, &cn, &xva.xva_vattr, link /*,vflg*/);
break;
default:
error = ENOTSUP;
}
VOP_UNLOCK(ZTOV(dzp), 0);
+out:
if (error == 0 && vp != NULL) {
VOP_UNLOCK(vp, 0);
VN_RELE(vp);
@@ -136,6 +533,9 @@ zfs_replay_create(zfsvfs_t *zfsvfs, lr_create_t *lr, boolean_t byteswap)
VN_RELE(ZTOV(dzp));
+ if (zfsvfs->z_fuid_replay)
+ zfs_fuid_info_free(zfsvfs->z_fuid_replay);
+ zfsvfs->z_fuid_replay = NULL;
return (error);
}
@@ -147,6 +547,7 @@ zfs_replay_remove(zfsvfs_t *zfsvfs, lr_remove_t *lr, boolean_t byteswap)
struct componentname cn;
vnode_t *vp;
int error;
+ int vflg = 0;
if (byteswap)
byteswap_uint64_array(lr, sizeof (*lr));
@@ -154,7 +555,8 @@ zfs_replay_remove(zfsvfs_t *zfsvfs, lr_remove_t *lr, boolean_t byteswap)
if ((error = zfs_zget(zfsvfs, lr->lr_doid, &dzp)) != 0)
return (error);
- bzero(&cn, sizeof(cn));
+ if (lr->lr_common.lrc_txtype & TX_CI)
+ vflg |= FIGNORECASE;
cn.cn_nameptr = name;
cn.cn_namelen = strlen(name);
cn.cn_nameiop = DELETE;
@@ -171,10 +573,10 @@ zfs_replay_remove(zfsvfs_t *zfsvfs, lr_remove_t *lr, boolean_t byteswap)
switch ((int)lr->lr_common.lrc_txtype) {
case TX_REMOVE:
- error = VOP_REMOVE(ZTOV(dzp), vp, &cn);
+ error = VOP_REMOVE(ZTOV(dzp), vp, &cn /*,vflg*/);
break;
case TX_RMDIR:
- error = VOP_RMDIR(ZTOV(dzp), vp, &cn);
+ error = VOP_RMDIR(ZTOV(dzp), vp, &cn /*,vflg*/);
break;
default:
error = ENOTSUP;
@@ -194,6 +596,7 @@ zfs_replay_link(zfsvfs_t *zfsvfs, lr_link_t *lr, boolean_t byteswap)
znode_t *dzp, *zp;
struct componentname cn;
int error;
+ int vflg = 0;
if (byteswap)
byteswap_uint64_array(lr, sizeof (*lr));
@@ -206,6 +609,8 @@ zfs_replay_link(zfsvfs_t *zfsvfs, lr_link_t *lr, boolean_t byteswap)
return (error);
}
+ if (lr->lr_common.lrc_txtype & TX_CI)
+ vflg |= FIGNORECASE;
cn.cn_nameptr = name;
cn.cn_cred = kcred;
cn.cn_thread = curthread;
@@ -213,7 +618,7 @@ zfs_replay_link(zfsvfs_t *zfsvfs, lr_link_t *lr, boolean_t byteswap)
vn_lock(ZTOV(dzp), LK_EXCLUSIVE | LK_RETRY);
vn_lock(ZTOV(zp), LK_EXCLUSIVE | LK_RETRY);
- error = VOP_LINK(ZTOV(dzp), ZTOV(zp), &cn);
+ error = VOP_LINK(ZTOV(dzp), ZTOV(zp), &cn /*,vflg*/);
VOP_UNLOCK(ZTOV(zp), 0);
VOP_UNLOCK(ZTOV(dzp), 0);
@@ -233,6 +638,7 @@ zfs_replay_rename(zfsvfs_t *zfsvfs, lr_rename_t *lr, boolean_t byteswap)
vnode_t *svp, *tvp;
kthread_t *td = curthread;
int error;
+ int vflg = 0;
if (byteswap)
byteswap_uint64_array(lr, sizeof (*lr));
@@ -245,9 +651,10 @@ zfs_replay_rename(zfsvfs_t *zfsvfs, lr_rename_t *lr, boolean_t byteswap)
return (error);
}
+ if (lr->lr_common.lrc_txtype & TX_CI)
+ vflg |= FIGNORECASE;
svp = tvp = NULL;
- bzero(&scn, sizeof(scn));
scn.cn_nameptr = sname;
scn.cn_namelen = strlen(sname);
scn.cn_nameiop = DELETE;
@@ -262,7 +669,6 @@ zfs_replay_rename(zfsvfs_t *zfsvfs, lr_rename_t *lr, boolean_t byteswap)
goto fail;
VOP_UNLOCK(svp, 0);
- bzero(&tcn, sizeof(tcn));
tcn.cn_nameptr = tname;
tcn.cn_namelen = strlen(tname);
tcn.cn_nameiop = RENAME;
@@ -279,7 +685,7 @@ zfs_replay_rename(zfsvfs_t *zfsvfs, lr_rename_t *lr, boolean_t byteswap)
goto fail;
}
- error = VOP_RENAME(ZTOV(sdzp), svp, &scn, ZTOV(tdzp), tvp, &tcn);
+ error = VOP_RENAME(ZTOV(sdzp), svp, &scn, ZTOV(tdzp), tvp, &tcn /*,vflg*/);
return (error);
fail:
if (svp != NULL)
@@ -334,13 +740,21 @@ static int
zfs_replay_setattr(zfsvfs_t *zfsvfs, lr_setattr_t *lr, boolean_t byteswap)
{
znode_t *zp;
- vattr_t va;
+ xvattr_t xva;
+ vattr_t *vap = &xva.xva_vattr;
vnode_t *vp;
int error;
+ void *start;
- if (byteswap)
+ xva_init(&xva);
+ if (byteswap) {
byteswap_uint64_array(lr, sizeof (*lr));
+ if ((lr->lr_mask & AT_XVATTR) &&
+ zfsvfs->z_version >= ZPL_VERSION_INITIAL)
+ zfs_replay_swap_attrs((lr_attr_t *)(lr + 1));
+ }
+
if ((error = zfs_zget(zfsvfs, lr->lr_foid, &zp)) != 0) {
/*
* As we can log setattrs out of order, it's possible the
@@ -352,35 +766,112 @@ zfs_replay_setattr(zfsvfs_t *zfsvfs, lr_setattr_t *lr, boolean_t byteswap)
return (error);
}
- zfs_init_vattr(&va, lr->lr_mask, lr->lr_mode,
+ zfs_init_vattr(vap, lr->lr_mask, lr->lr_mode,
lr->lr_uid, lr->lr_gid, 0, lr->lr_foid);
- va.va_size = lr->lr_size;
- ZFS_TIME_DECODE(&va.va_atime, lr->lr_atime);
- ZFS_TIME_DECODE(&va.va_mtime, lr->lr_mtime);
+ vap->va_size = lr->lr_size;
+ ZFS_TIME_DECODE(&vap->va_atime, lr->lr_atime);
+ ZFS_TIME_DECODE(&vap->va_mtime, lr->lr_mtime);
+
+ /*
+ * Fill in xvattr_t portions if necessary.
+ */
+
+ start = (lr_setattr_t *)(lr + 1);
+ if (vap->va_mask & AT_XVATTR) {
+ zfs_replay_xvattr((lr_attr_t *)start, &xva);
+ start = (caddr_t)start +
+ ZIL_XVAT_SIZE(((lr_attr_t *)start)->lr_attr_masksize);
+ } else
+ xva.xva_vattr.va_mask &= ~AT_XVATTR;
+
+ zfsvfs->z_fuid_replay = zfs_replay_fuid_domain(start, &start,
+ lr->lr_uid, lr->lr_gid);
vp = ZTOV(zp);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
- error = VOP_SETATTR(vp, &va, kcred);
+ error = VOP_SETATTR(vp, vap, kcred);
VOP_UNLOCK(vp, 0);
+
+ zfs_fuid_info_free(zfsvfs->z_fuid_replay);
+ zfsvfs->z_fuid_replay = NULL;
VN_RELE(vp);
return (error);
}
static int
-zfs_replay_acl(zfsvfs_t *zfsvfs, lr_acl_t *lr, boolean_t byteswap)
+zfs_replay_acl_v0(zfsvfs_t *zfsvfs, lr_acl_v0_t *lr, boolean_t byteswap)
{
ace_t *ace = (ace_t *)(lr + 1); /* ace array follows lr_acl_t */
-#ifdef TODO
vsecattr_t vsa;
+ znode_t *zp;
+ int error;
+
+ if (byteswap) {
+ byteswap_uint64_array(lr, sizeof (*lr));
+ zfs_oldace_byteswap(ace, lr->lr_aclcnt);
+ }
+
+ if ((error = zfs_zget(zfsvfs, lr->lr_foid, &zp)) != 0) {
+ /*
+ * As we can log acls out of order, it's possible the
+ * file has been removed. In this case just drop the acl
+ * and return success.
+ */
+ if (error == ENOENT)
+ error = 0;
+ return (error);
+ }
+
+ bzero(&vsa, sizeof (vsa));
+ vsa.vsa_mask = VSA_ACE | VSA_ACECNT;
+ vsa.vsa_aclcnt = lr->lr_aclcnt;
+ vsa.vsa_aclentsz = sizeof (ace_t) * vsa.vsa_aclcnt;
+ vsa.vsa_aclflags = 0;
+ vsa.vsa_aclentp = ace;
+
+#ifdef TODO
+ error = VOP_SETSECATTR(ZTOV(zp), &vsa, 0, kcred, NULL);
+#else
+ panic("%s:%u: unsupported condition", __func__, __LINE__);
#endif
+
+ VN_RELE(ZTOV(zp));
+
+ return (error);
+}
+
+/*
+ * Replaying ACLs is complicated by FUID support.
+ * The log record may contain some optional data
+ * to be used for replaying FUID's. These pieces
+ * are the actual FUIDs that were created initially.
+ * The FUID table index may no longer be valid and
+ * during zfs_create() a new index may be assigned.
+ * Because of this the log will contain the original
+ * doman+rid in order to create a new FUID.
+ *
+ * The individual ACEs may contain an ephemeral uid/gid which is no
+ * longer valid and will need to be replaced with an actual FUID.
+ *
+ */
+static int
+zfs_replay_acl(zfsvfs_t *zfsvfs, lr_acl_t *lr, boolean_t byteswap)
+{
+ ace_t *ace = (ace_t *)(lr + 1);
+ vsecattr_t vsa;
znode_t *zp;
int error;
if (byteswap) {
byteswap_uint64_array(lr, sizeof (*lr));
- zfs_ace_byteswap(ace, lr->lr_aclcnt);
+ zfs_ace_byteswap(ace, lr->lr_acl_bytes, B_FALSE);
+ if (lr->lr_fuidcnt) {
+ byteswap_uint64_array((caddr_t)ace +
+ ZIL_ACE_LENGTH(lr->lr_acl_bytes),
+ lr->lr_fuidcnt * sizeof (uint64_t));
+ }
}
if ((error = zfs_zget(zfsvfs, lr->lr_foid, &zp)) != 0) {
@@ -396,15 +887,30 @@ zfs_replay_acl(zfsvfs_t *zfsvfs, lr_acl_t *lr, boolean_t byteswap)
#ifdef TODO
bzero(&vsa, sizeof (vsa));
- vsa.vsa_mask = VSA_ACE | VSA_ACECNT;
+ vsa.vsa_mask = VSA_ACE | VSA_ACECNT | VSA_ACE_ACLFLAGS;
vsa.vsa_aclcnt = lr->lr_aclcnt;
vsa.vsa_aclentp = ace;
+ vsa.vsa_aclentsz = lr->lr_acl_bytes;
+ vsa.vsa_aclflags = lr->lr_acl_flags;
+
+ if (lr->lr_fuidcnt) {
+ void *fuidstart = (caddr_t)ace +
+ ZIL_ACE_LENGTH(lr->lr_acl_bytes);
+
+ zfsvfs->z_fuid_replay =
+ zfs_replay_fuids(fuidstart, &fuidstart,
+ lr->lr_fuidcnt, lr->lr_domcnt, 0, 0);
+ }
+
+ error = VOP_SETSECATTR(ZTOV(zp), &vsa, 0, kcred, NULL);
- error = VOP_SETSECATTR(ZTOV(zp), &vsa, 0, kcred);
+ if (zfsvfs->z_fuid_replay)
+ zfs_fuid_info_free(zfsvfs->z_fuid_replay);
#else
error = EOPNOTSUPP;
#endif
+ zfsvfs->z_fuid_replay = NULL;
VN_RELE(ZTOV(zp));
return (error);
@@ -426,5 +932,12 @@ zil_replay_func_t *zfs_replay_vector[TX_MAX_TYPE] = {
zfs_replay_write, /* TX_WRITE */
zfs_replay_truncate, /* TX_TRUNCATE */
zfs_replay_setattr, /* TX_SETATTR */
+ zfs_replay_acl_v0, /* TX_ACL_V0 */
zfs_replay_acl, /* TX_ACL */
+ zfs_replay_create_acl, /* TX_CREATE_ACL */
+ zfs_replay_create, /* TX_CREATE_ATTR */
+ zfs_replay_create_acl, /* TX_CREATE_ACL_ATTR */
+ zfs_replay_create_acl, /* TX_MKDIR_ACL */
+ zfs_replay_create, /* TX_MKDIR_ATTR */
+ zfs_replay_create_acl, /* TX_MKDIR_ACL_ATTR */
};
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_rlock.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_rlock.c
index 07ec0f6..f0a75b5 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_rlock.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_rlock.c
@@ -472,10 +472,14 @@ zfs_range_unlock_reader(znode_t *zp, rl_t *remove)
*/
if (remove->r_cnt == 1) {
avl_remove(tree, remove);
- if (remove->r_write_wanted)
+ if (remove->r_write_wanted) {
cv_broadcast(&remove->r_wr_cv);
- if (remove->r_read_wanted)
+ cv_destroy(&remove->r_wr_cv);
+ }
+ if (remove->r_read_wanted) {
cv_broadcast(&remove->r_rd_cv);
+ cv_destroy(&remove->r_rd_cv);
+ }
} else {
ASSERT3U(remove->r_cnt, ==, 0);
ASSERT3U(remove->r_write_wanted, ==, 0);
@@ -501,10 +505,14 @@ zfs_range_unlock_reader(znode_t *zp, rl_t *remove)
rl->r_cnt--;
if (rl->r_cnt == 0) {
avl_remove(tree, rl);
- if (rl->r_write_wanted)
+ if (rl->r_write_wanted) {
cv_broadcast(&rl->r_wr_cv);
- if (rl->r_read_wanted)
+ cv_destroy(&rl->r_wr_cv);
+ }
+ if (rl->r_read_wanted) {
cv_broadcast(&rl->r_rd_cv);
+ cv_destroy(&rl->r_rd_cv);
+ }
kmem_free(rl, sizeof (rl_t));
}
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c
index 28f3293..5becdb4 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
@@ -44,6 +42,7 @@
#include <sys/dmu.h>
#include <sys/dsl_prop.h>
#include <sys/dsl_dataset.h>
+#include <sys/dsl_deleg.h>
#include <sys/spa.h>
#include <sys/zap.h>
#include <sys/varargs.h>
@@ -51,17 +50,47 @@
#include <sys/atomic.h>
#include <sys/zfs_ioctl.h>
#include <sys/zfs_ctldir.h>
+#include <sys/zfs_fuid.h>
#include <sys/sunddi.h>
#include <sys/dnlc.h>
+#include <sys/dmu_objset.h>
+#include <sys/spa_boot.h>
+#include <sys/vdev_impl.h> /* VDEV_BOOT_VERSION */
struct mtx zfs_debug_mtx;
MTX_SYSINIT(zfs_debug_mtx, &zfs_debug_mtx, "zfs_debug", MTX_DEF);
+
SYSCTL_NODE(_vfs, OID_AUTO, zfs, CTLFLAG_RW, 0, "ZFS file system");
+
+int zfs_super_owner = 0;
+SYSCTL_INT(_vfs_zfs, OID_AUTO, super_owner, CTLFLAG_RW, &zfs_super_owner, 0,
+ "File system owner can perform privileged operation on his file systems");
+
int zfs_debug_level = 0;
TUNABLE_INT("vfs.zfs.debug", &zfs_debug_level);
SYSCTL_INT(_vfs_zfs, OID_AUTO, debug, CTLFLAG_RW, &zfs_debug_level, 0,
"Debug level");
+SYSCTL_NODE(_vfs_zfs, OID_AUTO, version, CTLFLAG_RD, 0, "ZFS versions");
+static int zfs_version_acl = ZFS_ACL_VERSION;
+SYSCTL_INT(_vfs_zfs_version, OID_AUTO, acl, CTLFLAG_RD, &zfs_version_acl, 0,
+ "ZFS_ACL_VERSION");
+static int zfs_version_dmu_backup_header = DMU_BACKUP_HEADER_VERSION;
+SYSCTL_INT(_vfs_zfs_version, OID_AUTO, dmu_backup_header, CTLFLAG_RD,
+ &zfs_version_dmu_backup_header, 0, "DMU_BACKUP_HEADER_VERSION");
+static int zfs_version_dmu_backup_stream = DMU_BACKUP_STREAM_VERSION;
+SYSCTL_INT(_vfs_zfs_version, OID_AUTO, dmu_backup_stream, CTLFLAG_RD,
+ &zfs_version_dmu_backup_stream, 0, "DMU_BACKUP_STREAM_VERSION");
+static int zfs_version_spa = SPA_VERSION;
+SYSCTL_INT(_vfs_zfs_version, OID_AUTO, spa, CTLFLAG_RD, &zfs_version_spa, 0,
+ "SPA_VERSION");
+static int zfs_version_vdev_boot = VDEV_BOOT_VERSION;
+SYSCTL_INT(_vfs_zfs_version, OID_AUTO, vdev_boot, CTLFLAG_RD,
+ &zfs_version_vdev_boot, 0, "VDEV_BOOT_VERSION");
+static int zfs_version_zpl = ZPL_VERSION;
+SYSCTL_INT(_vfs_zfs_version, OID_AUTO, zpl, CTLFLAG_RD, &zfs_version_zpl, 0,
+ "ZPL_VERSION");
+
static int zfs_mount(vfs_t *vfsp, kthread_t *td);
static int zfs_umount(vfs_t *vfsp, int fflag, kthread_t *td);
static int zfs_root(vfs_t *vfsp, int flags, vnode_t **vpp, kthread_t *td);
@@ -82,7 +111,7 @@ static struct vfsops zfs_vfsops = {
.vfs_fhtovp = zfs_fhtovp,
};
-VFS_SET(zfs_vfsops, zfs, VFCF_JAIL);
+VFS_SET(zfs_vfsops, zfs, VFCF_JAIL | VFCF_DELEGADMIN);
/*
* We need to keep a count of active fs's.
@@ -235,6 +264,27 @@ exec_changed_cb(void *arg, uint64_t newval)
}
}
+/*
+ * The nbmand mount option can be changed at mount time.
+ * We can't allow it to be toggled on live file systems or incorrect
+ * behavior may be seen from cifs clients
+ *
+ * This property isn't registered via dsl_prop_register(), but this callback
+ * will be called when a file system is first mounted
+ */
+static void
+nbmand_changed_cb(void *arg, uint64_t newval)
+{
+ zfsvfs_t *zfsvfs = arg;
+ if (newval == FALSE) {
+ vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NBMAND);
+ vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NONBMAND, NULL, 0);
+ } else {
+ vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NONBMAND);
+ vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NBMAND, NULL, 0);
+ }
+}
+
static void
snapdir_changed_cb(void *arg, uint64_t newval)
{
@@ -244,64 +294,27 @@ snapdir_changed_cb(void *arg, uint64_t newval)
}
static void
-acl_mode_changed_cb(void *arg, uint64_t newval)
+vscan_changed_cb(void *arg, uint64_t newval)
{
zfsvfs_t *zfsvfs = arg;
- zfsvfs->z_acl_mode = newval;
+ zfsvfs->z_vscan = newval;
}
static void
-acl_inherit_changed_cb(void *arg, uint64_t newval)
+acl_mode_changed_cb(void *arg, uint64_t newval)
{
zfsvfs_t *zfsvfs = arg;
- zfsvfs->z_acl_inherit = newval;
+ zfsvfs->z_acl_mode = newval;
}
-static int
-zfs_refresh_properties(vfs_t *vfsp)
+static void
+acl_inherit_changed_cb(void *arg, uint64_t newval)
{
- zfsvfs_t *zfsvfs = vfsp->vfs_data;
-
- /*
- * Remount operations default to "rw" unless "ro" is explicitly
- * specified.
- */
- if (vfs_optionisset(vfsp, MNTOPT_RO, NULL)) {
- readonly_changed_cb(zfsvfs, B_TRUE);
- } else {
- if (!dmu_objset_is_snapshot(zfsvfs->z_os))
- readonly_changed_cb(zfsvfs, B_FALSE);
- else if (vfs_optionisset(vfsp, MNTOPT_RW, NULL))
- return (EROFS);
- }
-
- if (vfs_optionisset(vfsp, MNTOPT_NOSUID, NULL)) {
- setuid_changed_cb(zfsvfs, B_FALSE);
- } else {
- if (vfs_optionisset(vfsp, MNTOPT_NOSETUID, NULL))
- setuid_changed_cb(zfsvfs, B_FALSE);
- else if (vfs_optionisset(vfsp, MNTOPT_SETUID, NULL))
- setuid_changed_cb(zfsvfs, B_TRUE);
- }
-
- if (vfs_optionisset(vfsp, MNTOPT_NOEXEC, NULL))
- exec_changed_cb(zfsvfs, B_FALSE);
- else if (vfs_optionisset(vfsp, MNTOPT_EXEC, NULL))
- exec_changed_cb(zfsvfs, B_TRUE);
-
- if (vfs_optionisset(vfsp, MNTOPT_ATIME, NULL))
- atime_changed_cb(zfsvfs, B_TRUE);
- else if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL))
- atime_changed_cb(zfsvfs, B_FALSE);
-
- if (vfs_optionisset(vfsp, MNTOPT_XATTR, NULL))
- xattr_changed_cb(zfsvfs, B_TRUE);
- else if (vfs_optionisset(vfsp, MNTOPT_NOXATTR, NULL))
- xattr_changed_cb(zfsvfs, B_FALSE);
+ zfsvfs_t *zfsvfs = arg;
- return (0);
+ zfsvfs->z_acl_inherit = newval;
}
static int
@@ -310,10 +323,12 @@ zfs_register_callbacks(vfs_t *vfsp)
struct dsl_dataset *ds = NULL;
objset_t *os = NULL;
zfsvfs_t *zfsvfs = NULL;
+ uint64_t nbmand;
int readonly, do_readonly = FALSE;
int setuid, do_setuid = FALSE;
int exec, do_exec = FALSE;
int xattr, do_xattr = FALSE;
+ int atime, do_atime = FALSE;
int error = 0;
ASSERT(vfsp);
@@ -360,6 +375,34 @@ zfs_register_callbacks(vfs_t *vfsp)
xattr = B_TRUE;
do_xattr = B_TRUE;
}
+ if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL)) {
+ atime = B_FALSE;
+ do_atime = B_TRUE;
+ } else if (vfs_optionisset(vfsp, MNTOPT_ATIME, NULL)) {
+ atime = B_TRUE;
+ do_atime = B_TRUE;
+ }
+
+ /*
+ * nbmand is a special property. It can only be changed at
+ * mount time.
+ *
+ * This is weird, but it is documented to only be changeable
+ * at mount time.
+ */
+ if (vfs_optionisset(vfsp, MNTOPT_NONBMAND, NULL)) {
+ nbmand = B_FALSE;
+ } else if (vfs_optionisset(vfsp, MNTOPT_NBMAND, NULL)) {
+ nbmand = B_TRUE;
+ } else {
+ char osname[MAXNAMELEN];
+
+ dmu_objset_name(os, osname);
+ if (error = dsl_prop_get_integer(osname, "nbmand", &nbmand,
+ NULL)) {
+ return (error);
+ }
+ }
/*
* Register property callbacks.
@@ -386,6 +429,8 @@ zfs_register_callbacks(vfs_t *vfsp)
"aclmode", acl_mode_changed_cb, zfsvfs);
error = error ? error : dsl_prop_register(ds,
"aclinherit", acl_inherit_changed_cb, zfsvfs);
+ error = error ? error : dsl_prop_register(ds,
+ "vscan", vscan_changed_cb, zfsvfs);
if (error)
goto unregister;
@@ -400,6 +445,10 @@ zfs_register_callbacks(vfs_t *vfsp)
exec_changed_cb(zfsvfs, exec);
if (do_xattr)
xattr_changed_cb(zfsvfs, xattr);
+ if (do_atime)
+ atime_changed_cb(zfsvfs, atime);
+
+ nbmand_changed_cb(zfsvfs, nbmand);
return (0);
@@ -419,14 +468,73 @@ unregister:
(void) dsl_prop_unregister(ds, "aclmode", acl_mode_changed_cb, zfsvfs);
(void) dsl_prop_unregister(ds, "aclinherit", acl_inherit_changed_cb,
zfsvfs);
+ (void) dsl_prop_unregister(ds, "vscan", vscan_changed_cb, zfsvfs);
return (error);
}
static int
-zfs_domount(vfs_t *vfsp, char *osname, kthread_t *td)
+zfsvfs_setup(zfsvfs_t *zfsvfs, boolean_t mounting)
+{
+ int error;
+
+ error = zfs_register_callbacks(zfsvfs->z_vfs);
+ if (error)
+ return (error);
+
+ /*
+ * Set the objset user_ptr to track its zfsvfs.
+ */
+ mutex_enter(&zfsvfs->z_os->os->os_user_ptr_lock);
+ dmu_objset_set_user(zfsvfs->z_os, zfsvfs);
+ mutex_exit(&zfsvfs->z_os->os->os_user_ptr_lock);
+
+ /*
+ * If we are not mounting (ie: online recv), then we don't
+ * have to worry about replaying the log as we blocked all
+ * operations out since we closed the ZIL.
+ */
+ if (mounting) {
+ boolean_t readonly;
+
+ /*
+ * During replay we remove the read only flag to
+ * allow replays to succeed.
+ */
+ readonly = zfsvfs->z_vfs->vfs_flag & VFS_RDONLY;
+ zfsvfs->z_vfs->vfs_flag &= ~VFS_RDONLY;
+
+ /*
+ * Parse and replay the intent log.
+ */
+ zil_replay(zfsvfs->z_os, zfsvfs, &zfsvfs->z_assign,
+ zfs_replay_vector, zfs_unlinked_drain);
+
+ zfs_unlinked_drain(zfsvfs);
+ zfsvfs->z_vfs->vfs_flag |= readonly; /* restore readonly bit */
+ }
+
+ if (!zil_disable)
+ zfsvfs->z_log = zil_open(zfsvfs->z_os, zfs_get_data);
+
+ return (0);
+}
+
+static void
+zfs_freezfsvfs(zfsvfs_t *zfsvfs)
+{
+ mutex_destroy(&zfsvfs->z_znodes_lock);
+ mutex_destroy(&zfsvfs->z_online_recv_lock);
+ list_destroy(&zfsvfs->z_all_znodes);
+ rrw_destroy(&zfsvfs->z_teardown_lock);
+ rw_destroy(&zfsvfs->z_teardown_inactive_lock);
+ rw_destroy(&zfsvfs->z_fuid_lock);
+ kmem_free(zfsvfs, sizeof (zfsvfs_t));
+}
+
+static int
+zfs_domount(vfs_t *vfsp, char *osname)
{
- cred_t *cr = td->td_ucred;
uint64_t recordsize, readonly;
int error = 0;
int mode;
@@ -449,9 +557,12 @@ zfs_domount(vfs_t *vfsp, char *osname, kthread_t *td)
zfsvfs->z_show_ctldir = ZFS_SNAPDIR_VISIBLE;
mutex_init(&zfsvfs->z_znodes_lock, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&zfsvfs->z_online_recv_lock, NULL, MUTEX_DEFAULT, NULL);
list_create(&zfsvfs->z_all_znodes, sizeof (znode_t),
offsetof(znode_t, z_link_node));
- rw_init(&zfsvfs->z_um_lock, NULL, RW_DEFAULT, NULL);
+ rrw_init(&zfsvfs->z_teardown_lock);
+ rw_init(&zfsvfs->z_teardown_inactive_lock, NULL, RW_DEFAULT, NULL);
+ rw_init(&zfsvfs->z_fuid_lock, NULL, RW_DEFAULT, NULL);
if (error = dsl_prop_get_integer(osname, "recordsize", &recordsize,
NULL))
@@ -466,14 +577,13 @@ zfs_domount(vfs_t *vfsp, char *osname, kthread_t *td)
if (error = dsl_prop_get_integer(osname, "readonly", &readonly, NULL))
goto out;
+ mode = DS_MODE_OWNER;
if (readonly)
- mode = DS_MODE_PRIMARY | DS_MODE_READONLY;
- else
- mode = DS_MODE_PRIMARY;
+ mode |= DS_MODE_READONLY;
error = dmu_objset_open(osname, DMU_OST_ZFS, mode, &zfsvfs->z_os);
if (error == EROFS) {
- mode = DS_MODE_PRIMARY | DS_MODE_READONLY;
+ mode = DS_MODE_OWNER | DS_MODE_READONLY;
error = dmu_objset_open(osname, DMU_OST_ZFS, mode,
&zfsvfs->z_os);
}
@@ -481,34 +591,40 @@ zfs_domount(vfs_t *vfsp, char *osname, kthread_t *td)
if (error)
goto out;
- if (error = zfs_init_fs(zfsvfs, &zp, cr))
+ if (error = zfs_init_fs(zfsvfs, &zp))
goto out;
+ /*
+ * Set features for file system.
+ */
+ zfsvfs->z_use_fuids = USE_FUIDS(zfsvfs->z_version, zfsvfs->z_os);
+ if (zfsvfs->z_use_fuids) {
+ vfs_set_feature(vfsp, VFSFT_XVATTR);
+ vfs_set_feature(vfsp, VFSFT_SYSATTR_VIEWS);
+ vfs_set_feature(vfsp, VFSFT_ACEMASKONACCESS);
+ vfs_set_feature(vfsp, VFSFT_ACLONCREATE);
+ }
+ if (zfsvfs->z_case == ZFS_CASE_INSENSITIVE) {
+ vfs_set_feature(vfsp, VFSFT_DIRENTFLAGS);
+ vfs_set_feature(vfsp, VFSFT_CASEINSENSITIVE);
+ vfs_set_feature(vfsp, VFSFT_NOCASESENSITIVE);
+ } else if (zfsvfs->z_case == ZFS_CASE_MIXED) {
+ vfs_set_feature(vfsp, VFSFT_DIRENTFLAGS);
+ vfs_set_feature(vfsp, VFSFT_CASEINSENSITIVE);
+ }
+
if (dmu_objset_is_snapshot(zfsvfs->z_os)) {
- uint64_t xattr;
+ uint64_t pval;
ASSERT(mode & DS_MODE_READONLY);
atime_changed_cb(zfsvfs, B_FALSE);
readonly_changed_cb(zfsvfs, B_TRUE);
- if (error = dsl_prop_get_integer(osname, "xattr", &xattr, NULL))
+ if (error = dsl_prop_get_integer(osname, "xattr", &pval, NULL))
goto out;
- xattr_changed_cb(zfsvfs, xattr);
+ xattr_changed_cb(zfsvfs, pval);
zfsvfs->z_issnap = B_TRUE;
} else {
- error = zfs_register_callbacks(vfsp);
- if (error)
- goto out;
-
- zfs_unlinked_drain(zfsvfs);
-
- /*
- * Parse and replay the intent log.
- */
- zil_replay(zfsvfs->z_os, zfsvfs, &zfsvfs->z_assign,
- zfs_replay_vector);
-
- if (!zil_disable)
- zfsvfs->z_log = zil_open(zfsvfs->z_os, zfs_get_data);
+ error = zfsvfs_setup(zfsvfs, B_TRUE);
}
vfs_mountedfrom(vfsp, osname);
@@ -519,15 +635,12 @@ out:
if (error) {
if (zfsvfs->z_os)
dmu_objset_close(zfsvfs->z_os);
- rw_destroy(&zfsvfs->z_um_lock);
- mutex_destroy(&zfsvfs->z_znodes_lock);
- kmem_free(zfsvfs, sizeof (zfsvfs_t));
+ zfs_freezfsvfs(zfsvfs);
} else {
atomic_add_32(&zfs_active_fs_count, 1);
}
return (error);
-
}
void
@@ -567,6 +680,9 @@ zfs_unregister_callbacks(zfsvfs_t *zfsvfs)
VERIFY(dsl_prop_unregister(ds, "aclinherit",
acl_inherit_changed_cb, zfsvfs) == 0);
+
+ VERIFY(dsl_prop_unregister(ds, "vscan",
+ vscan_changed_cb, zfsvfs) == 0);
}
}
@@ -574,22 +690,94 @@ zfs_unregister_callbacks(zfsvfs_t *zfsvfs)
static int
zfs_mount(vfs_t *vfsp, kthread_t *td)
{
- char *from;
- int error;
+ vnode_t *mvp = vfsp->mnt_vnodecovered;
+ cred_t *cr = td->td_ucred;
+ char *osname;
+ int error = 0;
+ int canwrite;
+
+ if (vfs_getopt(vfsp->mnt_optnew, "from", (void **)&osname, NULL))
+ return (EINVAL);
+
+ /*
+ * If full-owner-access is enabled and delegated administration is
+ * turned on, we must set nosuid.
+ */
+ if (zfs_super_owner &&
+ dsl_deleg_access(osname, ZFS_DELEG_PERM_MOUNT, cr) != ECANCELED) {
+ secpolicy_fs_mount_clearopts(cr, vfsp);
+ }
+
+ /*
+ * Check for mount privilege?
+ *
+ * If we don't have privilege then see if
+ * we have local permission to allow it
+ */
+ error = secpolicy_fs_mount(cr, mvp, vfsp);
+ if (error) {
+ error = dsl_deleg_access(osname, ZFS_DELEG_PERM_MOUNT, cr);
+ if (error == 0) {
+ vattr_t vattr;
+
+ /*
+ * Make sure user is the owner of the mount point
+ * or has sufficient privileges.
+ */
+
+ vattr.va_mask = AT_UID;
+
+ if (error = VOP_GETATTR(mvp, &vattr, cr)) {
+ goto out;
+ }
+
+#if 0 /* CHECK THIS! Is probably needed for zfs_suser. */
+ if (secpolicy_vnode_owner(mvp, cr, vattr.va_uid) != 0 &&
+ VOP_ACCESS(mvp, VWRITE, cr, td) != 0) {
+ error = EPERM;
+ goto out;
+ }
+#else
+ if (error = secpolicy_vnode_owner(mvp, cr, vattr.va_uid)) {
+ goto out;
+ }
+
+ if (error = VOP_ACCESS(mvp, VWRITE, cr, td)) {
+ goto out;
+ }
+#endif
+
+ secpolicy_fs_mount_clearopts(cr, vfsp);
+ } else {
+ goto out;
+ }
+ }
+
+ /*
+ * Refuse to mount a filesystem if we are in a local zone and the
+ * dataset is not visible.
+ */
+ if (!INGLOBALZONE(curthread) &&
+ (!zone_dataset_visible(osname, &canwrite) || !canwrite)) {
+ error = EPERM;
+ goto out;
+ }
/*
* When doing a remount, we simply refresh our temporary properties
* according to those options set in the current VFS options.
*/
- if (vfsp->vfs_flag & MS_REMOUNT)
- return (zfs_refresh_properties(vfsp));
-
- if (vfs_getopt(vfsp->mnt_optnew, "from", (void **)&from, NULL))
- return (EINVAL);
+ if (vfsp->vfs_flag & MS_REMOUNT) {
+ /* refresh mount options */
+ zfs_unregister_callbacks(vfsp->vfs_data);
+ error = zfs_register_callbacks(vfsp);
+ goto out;
+ }
DROP_GIANT();
- error = zfs_domount(vfsp, from, td);
+ error = zfs_domount(vfsp, osname);
PICKUP_GIANT();
+out:
return (error);
}
@@ -671,18 +859,131 @@ zfs_root(vfs_t *vfsp, int flags, vnode_t **vpp, kthread_t *td)
return (error);
}
+/*
+ * Teardown the zfsvfs::z_os.
+ *
+ * Note, if 'unmounting' if FALSE, we return with the 'z_teardown_lock'
+ * and 'z_teardown_inactive_lock' held.
+ */
+static int
+zfsvfs_teardown(zfsvfs_t *zfsvfs, boolean_t unmounting)
+{
+ znode_t *zp;
+
+ rrw_enter(&zfsvfs->z_teardown_lock, RW_WRITER, FTAG);
+
+ if (!unmounting) {
+ /*
+ * We purge the parent filesystem's vfsp as the parent
+ * filesystem and all of its snapshots have their vnode's
+ * v_vfsp set to the parent's filesystem's vfsp. Note,
+ * 'z_parent' is self referential for non-snapshots.
+ */
+ (void) dnlc_purge_vfsp(zfsvfs->z_parent->z_vfs, 0);
+ }
+
+ /*
+ * Close the zil. NB: Can't close the zil while zfs_inactive
+ * threads are blocked as zil_close can call zfs_inactive.
+ */
+ if (zfsvfs->z_log) {
+ zil_close(zfsvfs->z_log);
+ zfsvfs->z_log = NULL;
+ }
+
+ rw_enter(&zfsvfs->z_teardown_inactive_lock, RW_WRITER);
+
+ /*
+ * If we are not unmounting (ie: online recv) and someone already
+ * unmounted this file system while we were doing the switcheroo,
+ * or a reopen of z_os failed then just bail out now.
+ */
+ if (!unmounting && (zfsvfs->z_unmounted || zfsvfs->z_os == NULL)) {
+ rw_exit(&zfsvfs->z_teardown_inactive_lock);
+ rrw_exit(&zfsvfs->z_teardown_lock, FTAG);
+ return (EIO);
+ }
+
+ /*
+ * At this point there are no vops active, and any new vops will
+ * fail with EIO since we have z_teardown_lock for writer (only
+ * relavent for forced unmount).
+ *
+ * Release all holds on dbufs.
+ */
+ mutex_enter(&zfsvfs->z_znodes_lock);
+ for (zp = list_head(&zfsvfs->z_all_znodes); zp != NULL;
+ zp = list_next(&zfsvfs->z_all_znodes, zp))
+ if (zp->z_dbuf) {
+ ASSERT(ZTOV(zp)->v_count > 0);
+ zfs_znode_dmu_fini(zp);
+ }
+ mutex_exit(&zfsvfs->z_znodes_lock);
+
+ /*
+ * If we are unmounting, set the unmounted flag and let new vops
+ * unblock. zfs_inactive will have the unmounted behavior, and all
+ * other vops will fail with EIO.
+ */
+ if (unmounting) {
+ zfsvfs->z_unmounted = B_TRUE;
+ rrw_exit(&zfsvfs->z_teardown_lock, FTAG);
+ rw_exit(&zfsvfs->z_teardown_inactive_lock);
+ }
+
+ /*
+ * z_os will be NULL if there was an error in attempting to reopen
+ * zfsvfs, so just return as the properties had already been
+ * unregistered and cached data had been evicted before.
+ */
+ if (zfsvfs->z_os == NULL)
+ return (0);
+
+ /*
+ * Unregister properties.
+ */
+ zfs_unregister_callbacks(zfsvfs);
+
+ /*
+ * Evict cached data
+ */
+ if (dmu_objset_evict_dbufs(zfsvfs->z_os)) {
+ txg_wait_synced(dmu_objset_pool(zfsvfs->z_os), 0);
+ (void) dmu_objset_evict_dbufs(zfsvfs->z_os);
+ }
+
+ return (0);
+}
+
/*ARGSUSED*/
static int
zfs_umount(vfs_t *vfsp, int fflag, kthread_t *td)
{
zfsvfs_t *zfsvfs = vfsp->vfs_data;
+ objset_t *os;
cred_t *cr = td->td_ucred;
int ret;
- if ((ret = secpolicy_fs_unmount(cr, vfsp)) != 0)
- return (ret);
+ if (fflag & MS_FORCE) {
+ /* TODO: Force unmount is not well implemented yet, so deny it. */
+ ZFS_LOG(0, "Force unmount is not supported, removing FORCE flag.");
+ fflag &= ~MS_FORCE;
+ }
- (void) dnlc_purge_vfsp(vfsp, 0);
+ ret = secpolicy_fs_unmount(cr, vfsp);
+ if (ret) {
+ ret = dsl_deleg_access((char *)refstr_value(vfsp->vfs_resource),
+ ZFS_DELEG_PERM_MOUNT, cr);
+ if (ret)
+ return (ret);
+ }
+ /*
+ * We purge the parent filesystem's vfsp as the parent filesystem
+ * and all of its snapshots have their vnode's v_vfsp set to the
+ * parent's filesystem's vfsp. Note, 'z_parent' is self
+ * referential for non-snapshots.
+ */
+ (void) dnlc_purge_vfsp(zfsvfs->z_parent->z_vfs, 0);
/*
* Unmount any snapshots mounted under .zfs before unmounting the
@@ -714,33 +1015,63 @@ zfs_umount(vfs_t *vfsp, int fflag, kthread_t *td)
return (ret);
}
- if (fflag & MS_FORCE) {
+ if (!(fflag & MS_FORCE)) {
+ /*
+ * Check the number of active vnodes in the file system.
+ * Our count is maintained in the vfs structure, but the
+ * number is off by 1 to indicate a hold on the vfs
+ * structure itself.
+ *
+ * The '.zfs' directory maintains a reference of its
+ * own, and any active references underneath are
+ * reflected in the vnode count.
+ */
+ if (zfsvfs->z_ctldir == NULL) {
+ if (vfsp->vfs_count > 1)
+ return (EBUSY);
+ } else {
+ if (vfsp->vfs_count > 2 ||
+ zfsvfs->z_ctldir->v_count > 1)
+ return (EBUSY);
+ }
+ } else {
MNT_ILOCK(vfsp);
vfsp->mnt_kern_flag |= MNTK_UNMOUNTF;
MNT_IUNLOCK(vfsp);
- zfsvfs->z_unmounted1 = B_TRUE;
+ }
+
+ VERIFY(zfsvfs_teardown(zfsvfs, B_TRUE) == 0);
+ os = zfsvfs->z_os;
+
+ /*
+ * z_os will be NULL if there was an error in
+ * attempting to reopen zfsvfs.
+ */
+ if (os != NULL) {
+ /*
+ * Unset the objset user_ptr.
+ */
+ mutex_enter(&os->os->os_user_ptr_lock);
+ dmu_objset_set_user(os, NULL);
+ mutex_exit(&os->os->os_user_ptr_lock);
/*
- * Wait for all zfs threads to leave zfs.
- * Grabbing a rwlock as reader in all vops and
- * as writer here doesn't work because it too easy to get
- * multiple reader enters as zfs can re-enter itself.
- * This can lead to deadlock if there is an intervening
- * rw_enter as writer.
- * So a file system threads ref count (z_op_cnt) is used.
- * A polling loop on z_op_cnt may seem inefficient, but
- * - this saves all threads on exit from having to grab a
- * mutex in order to cv_signal
- * - only occurs on forced unmount in the rare case when
- * there are outstanding threads within the file system.
+ * Finally release the objset
*/
- while (zfsvfs->z_op_cnt) {
- delay(1);
- }
+ dmu_objset_close(os);
}
- zfs_objset_close(zfsvfs);
- VFS_RELE(vfsp);
+ /*
+ * We can now safely destroy the '.zfs' directory node.
+ */
+ if (zfsvfs->z_ctldir != NULL)
+ zfsctl_destroy(zfsvfs);
+ if (zfsvfs->z_issnap) {
+ vnode_t *svp = vfsp->mnt_vnodecovered;
+
+ ASSERT(svp->v_count == 2);
+ VN_RELE(svp);
+ }
zfs_freevfs(vfsp);
return (0);
@@ -772,7 +1103,6 @@ zfs_vget(vfs_t *vfsp, ino_t ino, int flags, vnode_t **vpp)
static int
zfs_fhtovp(vfs_t *vfsp, fid_t *fidp, vnode_t **vpp)
{
- kthread_t *td = curthread;
zfsvfs_t *zfsvfs = vfsp->vfs_data;
znode_t *zp;
uint64_t object = 0;
@@ -824,7 +1154,7 @@ zfs_fhtovp(vfs_t *vfsp, fid_t *fidp, vnode_t **vpp)
ASSERT(*vpp != NULL);
if (object == ZFSCTL_INO_SNAPDIR) {
VERIFY(zfsctl_root_lookup(*vpp, "snapshot", vpp, NULL,
- 0, NULL, NULL) == 0);
+ 0, NULL, NULL, NULL, NULL, NULL) == 0);
} else {
VN_HOLD(*vpp);
}
@@ -854,84 +1184,79 @@ zfs_fhtovp(vfs_t *vfsp, fid_t *fidp, vnode_t **vpp)
*vpp = ZTOV(zp);
/* XXX: LK_RETRY? */
vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY);
- vnode_create_vobject(*vpp, zp->z_phys->zp_size, td);
+ vnode_create_vobject(*vpp, zp->z_phys->zp_size, curthread);
ZFS_EXIT(zfsvfs);
return (0);
}
-static void
-zfs_objset_close(zfsvfs_t *zfsvfs)
+/*
+ * Block out VOPs and close zfsvfs_t::z_os
+ *
+ * Note, if successful, then we return with the 'z_teardown_lock' and
+ * 'z_teardown_inactive_lock' write held.
+ */
+int
+zfs_suspend_fs(zfsvfs_t *zfsvfs, char *name, int *mode)
{
- znode_t *zp, *nextzp;
- objset_t *os = zfsvfs->z_os;
+ int error;
- /*
- * For forced unmount, at this point all vops except zfs_inactive
- * are erroring EIO. We need to now suspend zfs_inactive threads
- * while we are freeing dbufs before switching zfs_inactive
- * to use behaviour without a objset.
- */
- rw_enter(&zfsvfs->z_um_lock, RW_WRITER);
+ if ((error = zfsvfs_teardown(zfsvfs, B_FALSE)) != 0)
+ return (error);
- /*
- * Release all holds on dbufs
- * Note, although we have stopped all other vop threads and
- * zfs_inactive(), the dmu can callback via znode_pageout_func()
- * which can zfs_znode_free() the znode.
- * So we lock z_all_znodes; search the list for a held
- * dbuf; drop the lock (we know zp can't disappear if we hold
- * a dbuf lock; then regrab the lock and restart.
- */
- mutex_enter(&zfsvfs->z_znodes_lock);
- for (zp = list_head(&zfsvfs->z_all_znodes); zp; zp = nextzp) {
- nextzp = list_next(&zfsvfs->z_all_znodes, zp);
- if (zp->z_dbuf_held) {
- /* dbufs should only be held when force unmounting */
- zp->z_dbuf_held = 0;
- mutex_exit(&zfsvfs->z_znodes_lock);
- dmu_buf_rele(zp->z_dbuf, NULL);
- /* Start again */
- mutex_enter(&zfsvfs->z_znodes_lock);
- nextzp = list_head(&zfsvfs->z_all_znodes);
- }
- }
- mutex_exit(&zfsvfs->z_znodes_lock);
+ *mode = zfsvfs->z_os->os_mode;
+ dmu_objset_name(zfsvfs->z_os, name);
+ dmu_objset_close(zfsvfs->z_os);
- /*
- * Unregister properties.
- */
- if (!dmu_objset_is_snapshot(os))
- zfs_unregister_callbacks(zfsvfs);
+ return (0);
+}
- /*
- * Switch zfs_inactive to behaviour without an objset.
- * It just tosses cached pages and frees the znode & vnode.
- * Then re-enable zfs_inactive threads in that new behaviour.
- */
- zfsvfs->z_unmounted2 = B_TRUE;
- rw_exit(&zfsvfs->z_um_lock); /* re-enable any zfs_inactive threads */
+/*
+ * Reopen zfsvfs_t::z_os and release VOPs.
+ */
+int
+zfs_resume_fs(zfsvfs_t *zfsvfs, const char *osname, int mode)
+{
+ int err;
- /*
- * Close the zil. Can't close the zil while zfs_inactive
- * threads are blocked as zil_close can call zfs_inactive.
- */
- if (zfsvfs->z_log) {
- zil_close(zfsvfs->z_log);
- zfsvfs->z_log = NULL;
- }
+ ASSERT(RRW_WRITE_HELD(&zfsvfs->z_teardown_lock));
+ ASSERT(RW_WRITE_HELD(&zfsvfs->z_teardown_inactive_lock));
+
+ err = dmu_objset_open(osname, DMU_OST_ZFS, mode, &zfsvfs->z_os);
+ if (err) {
+ zfsvfs->z_os = NULL;
+ } else {
+ znode_t *zp;
+
+ VERIFY(zfsvfs_setup(zfsvfs, B_FALSE) == 0);
+
+ /*
+ * Attempt to re-establish all the active znodes with
+ * their dbufs. If a zfs_rezget() fails, then we'll let
+ * any potential callers discover that via ZFS_ENTER_VERIFY_VP
+ * when they try to use their znode.
+ */
+ mutex_enter(&zfsvfs->z_znodes_lock);
+ for (zp = list_head(&zfsvfs->z_all_znodes); zp;
+ zp = list_next(&zfsvfs->z_all_znodes, zp)) {
+ (void) zfs_rezget(zp);
+ }
+ mutex_exit(&zfsvfs->z_znodes_lock);
- /*
- * Evict all dbufs so that cached znodes will be freed
- */
- if (dmu_objset_evict_dbufs(os, 1)) {
- txg_wait_synced(dmu_objset_pool(zfsvfs->z_os), 0);
- (void) dmu_objset_evict_dbufs(os, 0);
}
- /*
- * Finally close the objset
- */
- dmu_objset_close(os);
+ /* release the VOPs */
+ rw_exit(&zfsvfs->z_teardown_inactive_lock);
+ rrw_exit(&zfsvfs->z_teardown_lock, FTAG);
+
+ if (err) {
+ /*
+ * Since we couldn't reopen zfsvfs::z_os, force
+ * unmount this file system.
+ */
+ if (vn_vfswlock(zfsvfs->z_vfs->vfs_vnodecovered) == 0)
+ (void) dounmount(zfsvfs->z_vfs, MS_FORCE, curthread);
+ }
+ return (err);
}
static void
@@ -942,9 +1267,9 @@ zfs_freevfs(vfs_t *vfsp)
for (i = 0; i != ZFS_OBJ_MTX_SZ; i++)
mutex_destroy(&zfsvfs->z_hold_mtx[i]);
- rw_destroy(&zfsvfs->z_um_lock);
- mutex_destroy(&zfsvfs->z_znodes_lock);
- kmem_free(zfsvfs, sizeof (zfsvfs_t));
+
+ zfs_fuid_destroy(zfsvfs);
+ zfs_freezfsvfs(zfsvfs);
atomic_add_32(&zfs_active_fs_count, -1);
}
@@ -957,7 +1282,7 @@ static void
zfs_vnodes_adjust(void)
{
#ifdef __i386__
- int val;
+ int newdesiredvnodes;
desiredvnodes_backup = desiredvnodes;
@@ -966,10 +1291,11 @@ zfs_vnodes_adjust(void)
* vntblinit(). If it is equal to desiredvnodes, it means that
* it wasn't tuned by the administrator and we can tune it down.
*/
- val = min(maxproc + cnt.v_page_count / 4, 2 * vm_kmem_size /
- (5 * (sizeof(struct vm_object) + sizeof(struct vnode))));
- if (desiredvnodes == val)
- desiredvnodes = (3 * desiredvnodes) / 4;
+ newdesiredvnodes = min(maxproc + cnt.v_page_count / 4, 2 *
+ vm_kmem_size / (5 * (sizeof(struct vm_object) +
+ sizeof(struct vnode))));
+ if (newdesiredvnodes == desiredvnodes)
+ desiredvnodes = (3 * newdesiredvnodes) / 4;
#endif
}
@@ -986,20 +1312,20 @@ void
zfs_init(void)
{
- printf("ZFS filesystem version " ZFS_VERSION_STRING "\n");
+ printf("ZFS filesystem version " SPA_VERSION_STRING "\n");
/*
- * Initialize .zfs directory structures
+ * Initialize znode cache, vnode ops, etc...
*/
- zfsctl_init();
+ zfs_znode_init();
/*
- * Initialize znode cache, vnode ops, etc...
+ * Initialize .zfs directory structures
*/
- zfs_znode_init();
+ zfsctl_init();
/*
- * Reduce number of vnodes. Originally number of vnodes is calculated
+ * Reduce number of vnode. Originally number of vnodes is calculated
* with UFS inode in mind. We reduce it here, because it's too big for
* ZFS/i386.
*/
@@ -1019,3 +1345,95 @@ zfs_busy(void)
{
return (zfs_active_fs_count != 0);
}
+
+int
+zfs_set_version(const char *name, uint64_t newvers)
+{
+ int error;
+ objset_t *os;
+ dmu_tx_t *tx;
+ uint64_t curvers;
+
+ /*
+ * XXX for now, require that the filesystem be unmounted. Would
+ * be nice to find the zfsvfs_t and just update that if
+ * possible.
+ */
+
+ if (newvers < ZPL_VERSION_INITIAL || newvers > ZPL_VERSION)
+ return (EINVAL);
+
+ error = dmu_objset_open(name, DMU_OST_ZFS, DS_MODE_OWNER, &os);
+ if (error)
+ return (error);
+
+ error = zap_lookup(os, MASTER_NODE_OBJ, ZPL_VERSION_STR,
+ 8, 1, &curvers);
+ if (error)
+ goto out;
+ if (newvers < curvers) {
+ error = EINVAL;
+ goto out;
+ }
+
+ tx = dmu_tx_create(os);
+ dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, 0, ZPL_VERSION_STR);
+ error = dmu_tx_assign(tx, TXG_WAIT);
+ if (error) {
+ dmu_tx_abort(tx);
+ goto out;
+ }
+ error = zap_update(os, MASTER_NODE_OBJ, ZPL_VERSION_STR, 8, 1,
+ &newvers, tx);
+
+ spa_history_internal_log(LOG_DS_UPGRADE,
+ dmu_objset_spa(os), tx, CRED(),
+ "oldver=%llu newver=%llu dataset = %llu", curvers, newvers,
+ dmu_objset_id(os));
+ dmu_tx_commit(tx);
+
+out:
+ dmu_objset_close(os);
+ return (error);
+}
+/*
+ * Read a property stored within the master node.
+ */
+int
+zfs_get_zplprop(objset_t *os, zfs_prop_t prop, uint64_t *value)
+{
+ const char *pname;
+ int error = ENOENT;
+
+ /*
+ * Look up the file system's value for the property. For the
+ * version property, we look up a slightly different string.
+ */
+ if (prop == ZFS_PROP_VERSION)
+ pname = ZPL_VERSION_STR;
+ else
+ pname = zfs_prop_to_name(prop);
+
+ if (os != NULL)
+ error = zap_lookup(os, MASTER_NODE_OBJ, pname, 8, 1, value);
+
+ if (error == ENOENT) {
+ /* No value set, use the default value */
+ switch (prop) {
+ case ZFS_PROP_VERSION:
+ *value = ZPL_VERSION;
+ break;
+ case ZFS_PROP_NORMALIZE:
+ case ZFS_PROP_UTF8ONLY:
+ *value = 0;
+ break;
+ case ZFS_PROP_CASE:
+ *value = ZFS_CASE_SENSITIVE;
+ break;
+ default:
+ return (error);
+ }
+ error = 0;
+ }
+ return (error);
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c
index 49ea690..d37c90e 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c
@@ -19,14 +19,12 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Portions Copyright 2007 Jeremy Teo */
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
@@ -46,7 +44,6 @@
#include <sys/cmn_err.h>
#include <sys/errno.h>
#include <sys/unistd.h>
-#include <sys/zfs_vfsops.h>
#include <sys/zfs_dir.h>
#include <sys/zfs_acl.h>
#include <sys/zfs_ioctl.h>
@@ -61,8 +58,11 @@
#include <sys/sunddi.h>
#include <sys/filio.h>
#include <sys/zfs_ctldir.h>
+#include <sys/zfs_fuid.h>
#include <sys/dnlc.h>
#include <sys/zfs_rlock.h>
+#include <sys/extdirent.h>
+#include <sys/kidmap.h>
#include <sys/bio.h>
#include <sys/buf.h>
#include <sys/sf_buf.h>
@@ -74,14 +74,16 @@
* Each vnode op performs some logical unit of work. To do this, the ZPL must
* properly lock its in-core state, create a DMU transaction, do the work,
* record this work in the intent log (ZIL), commit the DMU transaction,
- * and wait the the intent log to commit if it's is a synchronous operation.
- * Morover, the vnode ops must work in both normal and log replay context.
+ * and wait for the intent log to commit if it is a synchronous operation.
+ * Moreover, the vnode ops must work in both normal and log replay context.
* The ordering of events is important to avoid deadlocks and references
* to freed memory. The example below illustrates the following Big Rules:
*
* (1) A check must be made in each zfs thread for a mounted file system.
* This is done avoiding races using ZFS_ENTER(zfsvfs).
- * A ZFS_EXIT(zfsvfs) is needed before all returns.
+ * A ZFS_EXIT(zfsvfs) is needed before all returns. Any znodes
+ * must be checked with ZFS_VERIFY_ZP(zp). Both of these macros
+ * can return EIO from the calling function.
*
* (2) VN_RELE() should always be the last thing except for zil_commit()
* (if necessary) and ZFS_EXIT(). This is for 3 reasons:
@@ -154,26 +156,41 @@
* ZFS_EXIT(zfsvfs); // finished in zfs
* return (error); // done, report error
*/
+
/* ARGSUSED */
static int
-zfs_open(vnode_t **vpp, int flag, cred_t *cr)
+zfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
{
znode_t *zp = VTOZ(*vpp);
+ if ((flag & FWRITE) && (zp->z_phys->zp_flags & ZFS_APPENDONLY) &&
+ ((flag & FAPPEND) == 0)) {
+ return (EPERM);
+ }
+
+ if (!zfs_has_ctldir(zp) && zp->z_zfsvfs->z_vscan &&
+ ZTOV(zp)->v_type == VREG &&
+ !(zp->z_phys->zp_flags & ZFS_AV_QUARANTINED) &&
+ zp->z_phys->zp_size > 0)
+ if (fs_vscan(*vpp, cr, 0) != 0)
+ return (EACCES);
+
/* Keep a count of the synchronous opens in the znode */
if (flag & (FSYNC | FDSYNC))
atomic_inc_32(&zp->z_sync_cnt);
+
return (0);
}
/* ARGSUSED */
static int
-zfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr)
+zfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
+ caller_context_t *ct)
{
znode_t *zp = VTOZ(vp);
/* Decrement the synchronous opens in the znode */
- if (flag & (FSYNC | FDSYNC))
+ if ((flag & (FSYNC | FDSYNC)) && (count == 1))
atomic_dec_32(&zp->z_sync_cnt);
/*
@@ -182,6 +199,12 @@ zfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr)
cleanlocks(vp, ddi_get_pid(), 0);
cleanshares(vp, ddi_get_pid());
+ if (!zfs_has_ctldir(zp) && zp->z_zfsvfs->z_vscan &&
+ ZTOV(zp)->v_type == VREG &&
+ !(zp->z_phys->zp_flags & ZFS_AV_QUARANTINED) &&
+ zp->z_phys->zp_size > 0)
+ VERIFY(fs_vscan(vp, cr, 1) == 0);
+
return (0);
}
@@ -231,31 +254,34 @@ zfs_holey(vnode_t *vp, u_long cmd, offset_t *off)
/* ARGSUSED */
static int
zfs_ioctl(vnode_t *vp, u_long com, intptr_t data, int flag, cred_t *cred,
- int *rvalp)
+ int *rvalp, caller_context_t *ct)
{
offset_t off;
int error;
zfsvfs_t *zfsvfs;
+ znode_t *zp;
switch (com) {
- case _FIOFFS:
+ case _FIOFFS:
return (0);
/*
* The following two ioctls are used by bfu. Faking out,
* necessary to avoid bfu errors.
*/
- case _FIOGDIO:
- case _FIOSDIO:
+ case _FIOGDIO:
+ case _FIOSDIO:
return (0);
- case _FIO_SEEK_DATA:
- case _FIO_SEEK_HOLE:
+ case _FIO_SEEK_DATA:
+ case _FIO_SEEK_HOLE:
if (ddi_copyin((void *)data, &off, sizeof (off), flag))
return (EFAULT);
- zfsvfs = VTOZ(vp)->z_zfsvfs;
+ zp = VTOZ(vp);
+ zfsvfs = zp->z_zfsvfs;
ZFS_ENTER(zfsvfs);
+ ZFS_VERIFY_ZP(zp);
/* offset parameter is in/out */
error = zfs_holey(vp, com, &off);
@@ -474,6 +500,7 @@ offset_t zfs_read_chunk_size = 1024 * 1024; /* Tunable */
* and return buffer.
* ioflag - SYNC flags; used to provide FRSYNC semantics.
* cr - credentials of caller.
+ * ct - caller context
*
* OUT: uio - updated offset and range, buffer filled.
*
@@ -489,12 +516,19 @@ zfs_read(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
{
znode_t *zp = VTOZ(vp);
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
- objset_t *os = zfsvfs->z_os;
+ objset_t *os;
ssize_t n, nbytes;
int error;
rl_t *rl;
ZFS_ENTER(zfsvfs);
+ ZFS_VERIFY_ZP(zp);
+ os = zfsvfs->z_os;
+
+ if (zp->z_phys->zp_flags & ZFS_AV_QUARANTINED) {
+ ZFS_EXIT(zfsvfs);
+ return (EACCES);
+ }
/*
* Validate file offset
@@ -554,8 +588,12 @@ zfs_read(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
error = mappedread(vp, nbytes, uio);
else
error = dmu_read_uio(os, zp->z_id, uio, nbytes);
- if (error)
+ if (error) {
+ /* convert checksum errors into IO errors */
+ if (error == ECKSUM)
+ error = EIO;
break;
+ }
n -= nbytes;
}
@@ -623,6 +661,7 @@ zfs_prefault_write(ssize_t n, struct uio *uio)
* and data buffer.
* ioflag - IO_APPEND flag set if in append mode.
* cr - credentials of caller.
+ * ct - caller context (NFS/CIFS fem monitor only)
*
* OUT: uio - updated offset and range.
*
@@ -643,11 +682,12 @@ zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
uint64_t end_size;
dmu_tx_t *tx;
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
- zilog_t *zilog = zfsvfs->z_log;
+ zilog_t *zilog;
offset_t woff;
ssize_t n, nbytes;
rl_t *rl;
int max_blksz = zfsvfs->z_max_blksz;
+ uint64_t pflags;
int error;
/*
@@ -661,6 +701,20 @@ zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
limit = MAXOFFSET_T;
ZFS_ENTER(zfsvfs);
+ ZFS_VERIFY_ZP(zp);
+
+ /*
+ * If immutable or not appending then return EPERM
+ */
+ pflags = zp->z_phys->zp_flags;
+ if ((pflags & (ZFS_IMMUTABLE | ZFS_READONLY)) ||
+ ((pflags & ZFS_APPENDONLY) && !(ioflag & FAPPEND) &&
+ (uio->uio_loffset < zp->z_phys->zp_size))) {
+ ZFS_EXIT(zfsvfs);
+ return (EPERM);
+ }
+
+ zilog = zfsvfs->z_log;
/*
* Pre-fault the pages to ensure slow (eg NFS) pages
@@ -808,15 +862,18 @@ zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
* It would be nice to to this after all writes have
* been done, but that would still expose the ISUID/ISGID
* to another app after the partial write is committed.
+ *
+ * Note: we don't call zfs_fuid_map_id() here because
+ * user 0 is not an ephemeral uid.
*/
mutex_enter(&zp->z_acl_lock);
if ((zp->z_phys->zp_mode & (S_IXUSR | (S_IXUSR >> 3) |
(S_IXUSR >> 6))) != 0 &&
(zp->z_phys->zp_mode & (S_ISUID | S_ISGID)) != 0 &&
- secpolicy_vnode_setid_retain(cr,
+ secpolicy_vnode_setid_retain(vp, cr,
(zp->z_phys->zp_mode & S_ISUID) != 0 &&
zp->z_phys->zp_uid == 0) != 0) {
- zp->z_phys->zp_mode &= ~(S_ISUID | S_ISGID);
+ zp->z_phys->zp_mode &= ~(S_ISUID | S_ISGID);
}
mutex_exit(&zp->z_acl_lock);
@@ -872,7 +929,7 @@ zfs_get_done(dmu_buf_t *db, void *vzgd)
dmu_buf_rele(db, vzgd);
zfs_range_unlock(rl);
VN_RELE(vp);
- zil_add_vdev(zgd->zgd_zilog, DVA_GET_VDEV(BP_IDENTITY(zgd->zgd_bp)));
+ zil_add_block(zgd->zgd_zilog, zgd->zgd_bp);
kmem_free(zgd, sizeof (zgd_t));
VFS_UNLOCK_GIANT(vfslocked);
}
@@ -957,11 +1014,10 @@ zfs_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio)
lr->lr_blkoff = off - boff;
error = dmu_sync(zio, db, &lr->lr_blkptr,
lr->lr_common.lrc_txg, zfs_get_done, zgd);
- ASSERT(error == EEXIST || lr->lr_length <= zp->z_blksz);
- if (error == 0) {
- zil_add_vdev(zfsvfs->z_log,
- DVA_GET_VDEV(BP_IDENTITY(&lr->lr_blkptr)));
- }
+ ASSERT((error && error != EINPROGRESS) ||
+ lr->lr_length <= zp->z_blksz);
+ if (error == 0)
+ zil_add_block(zfsvfs->z_log, &lr->lr_blkptr);
/*
* If we get EINPROGRESS, then we need to wait for a
* write IO initiated by dmu_sync() to complete before
@@ -981,14 +1037,21 @@ out:
/*ARGSUSED*/
static int
-zfs_access(vnode_t *vp, int mode, int flags, cred_t *cr)
+zfs_access(vnode_t *vp, int mode, int flag, cred_t *cr,
+ caller_context_t *ct)
{
znode_t *zp = VTOZ(vp);
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
int error;
ZFS_ENTER(zfsvfs);
- error = zfs_zaccess_rwx(zp, mode, cr);
+ ZFS_VERIFY_ZP(zp);
+
+ if (flag & V_ACE_MASK)
+ error = zfs_zaccess(zp, mode, flag, B_FALSE, cr);
+ else
+ error = zfs_zaccess_rwx(zp, mode, flag, cr);
+
ZFS_EXIT(zfsvfs);
return (error);
}
@@ -1003,6 +1066,9 @@ zfs_access(vnode_t *vp, int mode, int flags, cred_t *cr)
* flags - LOOKUP_XATTR set if looking for an attribute.
* rdir - root directory vnode [UNUSED].
* cr - credentials of caller.
+ * ct - caller context
+ * direntflags - directory lookup flags
+ * realpnp - returned pathname.
*
* OUT: vpp - vnode of located entry, NULL if not found.
*
@@ -1015,19 +1081,21 @@ zfs_access(vnode_t *vp, int mode, int flags, cred_t *cr)
/* ARGSUSED */
static int
zfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct componentname *cnp,
- int nameiop, cred_t *cr, kthread_t *td)
+ int nameiop, cred_t *cr, kthread_t *td, int flags)
{
-
znode_t *zdp = VTOZ(dvp);
zfsvfs_t *zfsvfs = zdp->z_zfsvfs;
int error;
+ int *direntflags = NULL;
+ void *realpnp = NULL;
ZFS_ENTER(zfsvfs);
+ ZFS_VERIFY_ZP(zdp);
*vpp = NULL;
-#ifdef TODO
if (flags & LOOKUP_XATTR) {
+#ifdef TODO
/*
* If the xattr property is off, refuse the lookup request.
*/
@@ -1035,6 +1103,7 @@ zfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct componentname *cnp,
ZFS_EXIT(zfsvfs);
return (EINVAL);
}
+#endif
/*
* We don't allow recursive attributes..
@@ -1054,14 +1123,15 @@ zfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct componentname *cnp,
* Do we have permission to get into attribute directory?
*/
- if (error = zfs_zaccess(VTOZ(*vpp), ACE_EXECUTE, cr)) {
+ if (error = zfs_zaccess(VTOZ(*vpp), ACE_EXECUTE, 0,
+ B_FALSE, cr)) {
VN_RELE(*vpp);
+ *vpp = NULL;
}
ZFS_EXIT(zfsvfs);
return (error);
}
-#endif /* TODO */
if (dvp->v_type != VDIR) {
ZFS_EXIT(zfsvfs);
@@ -1072,13 +1142,19 @@ zfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct componentname *cnp,
* Check accessibility of directory.
*/
- if (error = zfs_zaccess(zdp, ACE_EXECUTE, cr)) {
+ if (error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr)) {
ZFS_EXIT(zfsvfs);
return (error);
}
- if ((error = zfs_dirlook(zdp, nm, vpp)) == 0) {
+ if (zfsvfs->z_utf8 && u8_validate(nm, strlen(nm),
+ NULL, U8_VALIDATE_ENTIRE, &error) < 0) {
+ ZFS_EXIT(zfsvfs);
+ return (EILSEQ);
+ }
+ error = zfs_dirlook(zdp, nm, vpp, flags, direntflags, realpnp);
+ if (error == 0) {
/*
* Convert device special files
*/
@@ -1162,6 +1238,8 @@ zfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct componentname *cnp,
* mode - mode to open file with.
* cr - credentials of caller.
* flag - large file flag [UNUSED].
+ * ct - caller context
+ * vsecp - ACL to be set
*
* OUT: vpp - vnode of created or trunc'd entry.
*
@@ -1172,22 +1250,52 @@ zfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct componentname *cnp,
* dvp - ctime|mtime updated if new entry created
* vp - ctime|mtime always, atime if new
*/
+
/* ARGSUSED */
static int
zfs_create(vnode_t *dvp, char *name, vattr_t *vap, int excl, int mode,
- vnode_t **vpp, cred_t *cr)
+ vnode_t **vpp, cred_t *cr, kthread_t *td)
{
znode_t *zp, *dzp = VTOZ(dvp);
zfsvfs_t *zfsvfs = dzp->z_zfsvfs;
- zilog_t *zilog = zfsvfs->z_log;
- objset_t *os = zfsvfs->z_os;
+ zilog_t *zilog;
+ objset_t *os;
zfs_dirlock_t *dl;
dmu_tx_t *tx;
int error;
- uint64_t zoid;
+ zfs_acl_t *aclp = NULL;
+ zfs_fuid_info_t *fuidp = NULL;
+ void *vsecp = NULL;
+ int flag = 0;
+
+ /*
+ * If we have an ephemeral id, ACL, or XVATTR then
+ * make sure file system is at proper version
+ */
+
+ if (zfsvfs->z_use_fuids == B_FALSE &&
+ (vsecp || (vap->va_mask & AT_XVATTR) ||
+ IS_EPHEMERAL(crgetuid(cr)) || IS_EPHEMERAL(crgetgid(cr))))
+ return (EINVAL);
ZFS_ENTER(zfsvfs);
+ ZFS_VERIFY_ZP(dzp);
+ os = zfsvfs->z_os;
+ zilog = zfsvfs->z_log;
+ if (zfsvfs->z_utf8 && u8_validate(name, strlen(name),
+ NULL, U8_VALIDATE_ENTIRE, &error) < 0) {
+ ZFS_EXIT(zfsvfs);
+ return (EILSEQ);
+ }
+
+ if (vap->va_mask & AT_XVATTR) {
+ if ((error = secpolicy_xvattr((xvattr_t *)vap,
+ crgetuid(cr), cr, vap->va_type)) != 0) {
+ ZFS_EXIT(zfsvfs);
+ return (error);
+ }
+ }
top:
*vpp = NULL;
@@ -1204,22 +1312,40 @@ top:
error = 0;
} else {
/* possible VN_HOLD(zp) */
- if (error = zfs_dirent_lock(&dl, dzp, name, &zp, 0)) {
+ int zflg = 0;
+
+ if (flag & FIGNORECASE)
+ zflg |= ZCILOOK;
+
+ error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg,
+ NULL, NULL);
+ if (error) {
if (strcmp(name, "..") == 0)
error = EISDIR;
ZFS_EXIT(zfsvfs);
+ if (aclp)
+ zfs_acl_free(aclp);
+ return (error);
+ }
+ }
+ if (vsecp && aclp == NULL) {
+ error = zfs_vsec_2_aclp(zfsvfs, vap->va_type, vsecp, &aclp);
+ if (error) {
+ ZFS_EXIT(zfsvfs);
+ if (dl)
+ zfs_dirent_unlock(dl);
return (error);
}
}
-
- zoid = zp ? zp->z_id : -1ULL;
if (zp == NULL) {
+ uint64_t txtype;
+
/*
* Create a new file object and update the directory
* to reference it.
*/
- if (error = zfs_zaccess(dzp, ACE_ADD_FILE, cr)) {
+ if (error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr)) {
goto out;
}
@@ -1235,11 +1361,26 @@ top:
tx = dmu_tx_create(os);
dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT);
+ if ((aclp && aclp->z_has_fuids) || IS_EPHEMERAL(crgetuid(cr)) ||
+ IS_EPHEMERAL(crgetgid(cr))) {
+ if (zfsvfs->z_fuid_obj == 0) {
+ dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT);
+ dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0,
+ FUID_SIZE_ESTIMATE(zfsvfs));
+ dmu_tx_hold_zap(tx, MASTER_NODE_OBJ,
+ FALSE, NULL);
+ } else {
+ dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj);
+ dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0,
+ FUID_SIZE_ESTIMATE(zfsvfs));
+ }
+ }
dmu_tx_hold_bonus(tx, dzp->z_id);
dmu_tx_hold_zap(tx, dzp->z_id, TRUE, name);
- if (dzp->z_phys->zp_flags & ZFS_INHERIT_ACE)
+ if ((dzp->z_phys->zp_flags & ZFS_INHERIT_ACE) || aclp) {
dmu_tx_hold_write(tx, DMU_NEW_OBJECT,
0, SPA_MAXBLOCKSIZE);
+ }
error = dmu_tx_assign(tx, zfsvfs->z_assign);
if (error) {
zfs_dirent_unlock(dl);
@@ -1251,14 +1392,23 @@ top:
}
dmu_tx_abort(tx);
ZFS_EXIT(zfsvfs);
+ if (aclp)
+ zfs_acl_free(aclp);
return (error);
}
- zfs_mknode(dzp, vap, &zoid, tx, cr, 0, &zp, 0);
- ASSERT(zp->z_id == zoid);
+ zfs_mknode(dzp, vap, tx, cr, 0, &zp, 0, aclp, &fuidp);
(void) zfs_link_create(dl, zp, tx, ZNEW);
- zfs_log_create(zilog, tx, TX_CREATE, dzp, zp, name);
+ txtype = zfs_log_create_txtype(Z_FILE, vsecp, vap);
+ if (flag & FIGNORECASE)
+ txtype |= TX_CI;
+ zfs_log_create(zilog, tx, txtype, dzp, zp, name,
+ vsecp, fuidp, vap);
+ if (fuidp)
+ zfs_fuid_info_free(fuidp);
dmu_tx_commit(tx);
} else {
+ int aflags = (flag & FAPPEND) ? V_APPEND : 0;
+
/*
* A directory entry already exists for this name.
*/
@@ -1279,7 +1429,7 @@ top:
/*
* Verify requested access to file.
*/
- if (mode && (error = zfs_zaccess_rwx(zp, mode, cr))) {
+ if (mode && (error = zfs_zaccess_rwx(zp, mode, aflags, cr))) {
goto out;
}
@@ -1292,13 +1442,12 @@ top:
*/
if ((ZTOV(zp)->v_type == VREG) &&
(vap->va_mask & AT_SIZE) && (vap->va_size == 0)) {
+ /* we can't hold any locks when calling zfs_freesp() */
+ zfs_dirent_unlock(dl);
+ dl = NULL;
error = zfs_freesp(zp, 0, 0, mode, TRUE);
- if (error == ERESTART &&
- zfsvfs->z_assign == TXG_NOWAIT) {
- /* NB: we already did dmu_tx_wait() */
- zfs_dirent_unlock(dl);
- VN_RELE(ZTOV(zp));
- goto top;
+ if (error == 0) {
+ vnevent_create(ZTOV(zp), ct);
}
}
}
@@ -1325,6 +1474,8 @@ out:
*vpp = svp;
}
}
+ if (aclp)
+ zfs_acl_free(aclp);
ZFS_EXIT(zfsvfs);
return (error);
@@ -1336,6 +1487,8 @@ out:
* IN: dvp - vnode of directory to remove entry from.
* name - name of entry to remove.
* cr - credentials of caller.
+ * ct - caller context
+ * flags - case flags
*
* RETURN: 0 if success
* error code if failure
@@ -1344,28 +1497,45 @@ out:
* dvp - ctime|mtime
* vp - ctime (if nlink > 0)
*/
+/*ARGSUSED*/
static int
-zfs_remove(vnode_t *dvp, char *name, cred_t *cr)
+zfs_remove(vnode_t *dvp, char *name, cred_t *cr, caller_context_t *ct,
+ int flags)
{
znode_t *zp, *dzp = VTOZ(dvp);
znode_t *xzp = NULL;
vnode_t *vp;
zfsvfs_t *zfsvfs = dzp->z_zfsvfs;
- zilog_t *zilog = zfsvfs->z_log;
+ zilog_t *zilog;
uint64_t acl_obj, xattr_obj;
zfs_dirlock_t *dl;
dmu_tx_t *tx;
boolean_t may_delete_now, delete_now = FALSE;
- boolean_t unlinked;
+ boolean_t unlinked, toobig = FALSE;
+ uint64_t txtype;
+ pathname_t *realnmp = NULL;
+ pathname_t realnm;
int error;
+ int zflg = ZEXISTS;
ZFS_ENTER(zfsvfs);
+ ZFS_VERIFY_ZP(dzp);
+ zilog = zfsvfs->z_log;
+
+ if (flags & FIGNORECASE) {
+ zflg |= ZCILOOK;
+ pn_alloc(&realnm);
+ realnmp = &realnm;
+ }
top:
/*
* Attempt to lock directory; fail if entry doesn't exist.
*/
- if (error = zfs_dirent_lock(&dl, dzp, name, &zp, ZEXISTS)) {
+ if (error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg,
+ NULL, realnmp)) {
+ if (realnmp)
+ pn_free(realnmp);
ZFS_EXIT(zfsvfs);
return (error);
}
@@ -1384,9 +1554,12 @@ top:
goto out;
}
- vnevent_remove(vp);
+ vnevent_remove(vp, dvp, name, ct);
- dnlc_remove(dvp, name);
+ if (realnmp)
+ dnlc_remove(dvp, realnmp->pn_buf);
+ else
+ dnlc_remove(dvp, name);
may_delete_now = FALSE;
@@ -1399,8 +1572,13 @@ top:
tx = dmu_tx_create(zfsvfs->z_os);
dmu_tx_hold_zap(tx, dzp->z_id, FALSE, name);
dmu_tx_hold_bonus(tx, zp->z_id);
- if (may_delete_now)
- dmu_tx_hold_free(tx, zp->z_id, 0, DMU_OBJECT_END);
+ if (may_delete_now) {
+ toobig =
+ zp->z_phys->zp_size > zp->z_blksz * DMU_MAX_DELETEBLKCNT;
+ /* if the file is too big, only hold_free a token amount */
+ dmu_tx_hold_free(tx, zp->z_id, 0,
+ (toobig ? DMU_MAX_ACCESS : DMU_OBJECT_END));
+ }
/* are there any extended attributes? */
if ((xattr_obj = zp->z_phys->zp_xattr) != 0) {
@@ -1425,6 +1603,8 @@ top:
dmu_tx_abort(tx);
goto top;
}
+ if (realnmp)
+ pn_free(realnmp);
dmu_tx_abort(tx);
ZFS_EXIT(zfsvfs);
return (error);
@@ -1433,7 +1613,7 @@ top:
/*
* Remove the directory entry.
*/
- error = zfs_link_destroy(dl, zp, tx, 0, &unlinked);
+ error = zfs_link_destroy(dl, zp, tx, zflg, &unlinked);
if (error) {
dmu_tx_commit(tx);
@@ -1442,7 +1622,7 @@ top:
if (0 && unlinked) {
VI_LOCK(vp);
- delete_now = may_delete_now &&
+ delete_now = may_delete_now && !toobig &&
vp->v_count == 1 && !vn_has_cached_data(vp) &&
zp->z_phys->zp_xattr == xattr_obj &&
zp->z_phys->zp_acl.z_acl_extern_obj == acl_obj;
@@ -1469,21 +1649,26 @@ top:
VI_UNLOCK(vp);
mutex_exit(&zp->z_lock);
zfs_znode_delete(zp, tx);
- VFS_RELE(zfsvfs->z_vfs);
} else if (unlinked) {
zfs_unlinked_add(zp, tx);
}
- zfs_log_remove(zilog, tx, TX_REMOVE, dzp, name);
+ txtype = TX_REMOVE;
+ if (flags & FIGNORECASE)
+ txtype |= TX_CI;
+ zfs_log_remove(zilog, tx, txtype, dzp, name);
dmu_tx_commit(tx);
out:
+ if (realnmp)
+ pn_free(realnmp);
+
zfs_dirent_unlock(dl);
if (!delete_now) {
VN_RELE(vp);
} else if (xzp) {
- /* this rele delayed to prevent nesting transactions */
+ /* this rele is delayed to prevent nesting transactions */
VN_RELE(ZTOV(xzp));
}
@@ -1499,6 +1684,8 @@ out:
* dirname - name of new directory.
* vap - attributes of new directory.
* cr - credentials of caller.
+ * ct - caller context
+ * vsecp - ACL to be set
*
* OUT: vpp - vnode of created directory.
*
@@ -1509,49 +1696,104 @@ out:
* dvp - ctime|mtime updated
* vp - ctime|mtime|atime updated
*/
+/*ARGSUSED*/
static int
-zfs_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp, cred_t *cr)
+zfs_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp, cred_t *cr,
+ caller_context_t *ct, int flags, vsecattr_t *vsecp)
{
znode_t *zp, *dzp = VTOZ(dvp);
zfsvfs_t *zfsvfs = dzp->z_zfsvfs;
- zilog_t *zilog = zfsvfs->z_log;
+ zilog_t *zilog;
zfs_dirlock_t *dl;
- uint64_t zoid = 0;
+ uint64_t txtype;
dmu_tx_t *tx;
int error;
+ zfs_acl_t *aclp = NULL;
+ zfs_fuid_info_t *fuidp = NULL;
+ int zf = ZNEW;
ASSERT(vap->va_type == VDIR);
+ /*
+ * If we have an ephemeral id, ACL, or XVATTR then
+ * make sure file system is at proper version
+ */
+
+ if (zfsvfs->z_use_fuids == B_FALSE &&
+ (vsecp || (vap->va_mask & AT_XVATTR) || IS_EPHEMERAL(crgetuid(cr))||
+ IS_EPHEMERAL(crgetgid(cr))))
+ return (EINVAL);
+
ZFS_ENTER(zfsvfs);
+ ZFS_VERIFY_ZP(dzp);
+ zilog = zfsvfs->z_log;
if (dzp->z_phys->zp_flags & ZFS_XATTR) {
ZFS_EXIT(zfsvfs);
return (EINVAL);
}
-top:
- *vpp = NULL;
+
+ if (zfsvfs->z_utf8 && u8_validate(dirname,
+ strlen(dirname), NULL, U8_VALIDATE_ENTIRE, &error) < 0) {
+ ZFS_EXIT(zfsvfs);
+ return (EILSEQ);
+ }
+ if (flags & FIGNORECASE)
+ zf |= ZCILOOK;
+
+ if (vap->va_mask & AT_XVATTR)
+ if ((error = secpolicy_xvattr((xvattr_t *)vap,
+ crgetuid(cr), cr, vap->va_type)) != 0) {
+ ZFS_EXIT(zfsvfs);
+ return (error);
+ }
/*
* First make sure the new directory doesn't exist.
*/
- if (error = zfs_dirent_lock(&dl, dzp, dirname, &zp, ZNEW)) {
+top:
+ *vpp = NULL;
+
+ if (error = zfs_dirent_lock(&dl, dzp, dirname, &zp, zf,
+ NULL, NULL)) {
ZFS_EXIT(zfsvfs);
return (error);
}
- if (error = zfs_zaccess(dzp, ACE_ADD_SUBDIRECTORY, cr)) {
+ if (error = zfs_zaccess(dzp, ACE_ADD_SUBDIRECTORY, 0, B_FALSE, cr)) {
zfs_dirent_unlock(dl);
ZFS_EXIT(zfsvfs);
return (error);
}
+ if (vsecp && aclp == NULL) {
+ error = zfs_vsec_2_aclp(zfsvfs, vap->va_type, vsecp, &aclp);
+ if (error) {
+ zfs_dirent_unlock(dl);
+ ZFS_EXIT(zfsvfs);
+ return (error);
+ }
+ }
/*
* Add a new entry to the directory.
*/
tx = dmu_tx_create(zfsvfs->z_os);
dmu_tx_hold_zap(tx, dzp->z_id, TRUE, dirname);
dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL);
- if (dzp->z_phys->zp_flags & ZFS_INHERIT_ACE)
+ if ((aclp && aclp->z_has_fuids) || IS_EPHEMERAL(crgetuid(cr)) ||
+ IS_EPHEMERAL(crgetgid(cr))) {
+ if (zfsvfs->z_fuid_obj == 0) {
+ dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT);
+ dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0,
+ FUID_SIZE_ESTIMATE(zfsvfs));
+ dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, FALSE, NULL);
+ } else {
+ dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj);
+ dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0,
+ FUID_SIZE_ESTIMATE(zfsvfs));
+ }
+ }
+ if ((dzp->z_phys->zp_flags & ZFS_INHERIT_ACE) || aclp)
dmu_tx_hold_write(tx, DMU_NEW_OBJECT,
0, SPA_MAXBLOCKSIZE);
error = dmu_tx_assign(tx, zfsvfs->z_assign);
@@ -1564,13 +1806,18 @@ top:
}
dmu_tx_abort(tx);
ZFS_EXIT(zfsvfs);
+ if (aclp)
+ zfs_acl_free(aclp);
return (error);
}
/*
* Create new node.
*/
- zfs_mknode(dzp, vap, &zoid, tx, cr, 0, &zp, 0);
+ zfs_mknode(dzp, vap, tx, cr, 0, &zp, 0, aclp, &fuidp);
+
+ if (aclp)
+ zfs_acl_free(aclp);
/*
* Now put new name in parent dir.
@@ -1579,7 +1826,13 @@ top:
*vpp = ZTOV(zp);
- zfs_log_create(zilog, tx, TX_MKDIR, dzp, zp, dirname);
+ txtype = zfs_log_create_txtype(Z_DIR, vsecp, vap);
+ if (flags & FIGNORECASE)
+ txtype |= TX_CI;
+ zfs_log_create(zilog, tx, txtype, dzp, zp, dirname, vsecp, fuidp, vap);
+
+ if (fuidp)
+ zfs_fuid_info_free(fuidp);
dmu_tx_commit(tx);
zfs_dirent_unlock(dl);
@@ -1597,6 +1850,8 @@ top:
* name - name of directory to be removed.
* cwd - vnode of current working directory.
* cr - credentials of caller.
+ * ct - caller context
+ * flags - case flags
*
* RETURN: 0 if success
* error code if failure
@@ -1604,27 +1859,35 @@ top:
* Timestamps:
* dvp - ctime|mtime updated
*/
+/*ARGSUSED*/
static int
-zfs_rmdir(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr)
+zfs_rmdir(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr,
+ caller_context_t *ct, int flags)
{
znode_t *dzp = VTOZ(dvp);
znode_t *zp;
vnode_t *vp;
zfsvfs_t *zfsvfs = dzp->z_zfsvfs;
- zilog_t *zilog = zfsvfs->z_log;
+ zilog_t *zilog;
zfs_dirlock_t *dl;
dmu_tx_t *tx;
int error;
+ int zflg = ZEXISTS;
ZFS_ENTER(zfsvfs);
+ ZFS_VERIFY_ZP(dzp);
+ zilog = zfsvfs->z_log;
+ if (flags & FIGNORECASE)
+ zflg |= ZCILOOK;
top:
zp = NULL;
/*
* Attempt to lock directory; fail if entry doesn't exist.
*/
- if (error = zfs_dirent_lock(&dl, dzp, name, &zp, ZEXISTS)) {
+ if (error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg,
+ NULL, NULL)) {
ZFS_EXIT(zfsvfs);
return (error);
}
@@ -1645,7 +1908,7 @@ top:
goto out;
}
- vnevent_rmdir(vp);
+ vnevent_rmdir(vp, dvp, name, ct);
/*
* Grab a lock on the directory to make sure that noone is
@@ -1683,10 +1946,14 @@ top:
cache_purge(dvp);
#endif
- error = zfs_link_destroy(dl, zp, tx, 0, NULL);
+ error = zfs_link_destroy(dl, zp, tx, zflg, NULL);
- if (error == 0)
- zfs_log_remove(zilog, tx, TX_RMDIR, dzp, name);
+ if (error == 0) {
+ uint64_t txtype = TX_RMDIR;
+ if (flags & FIGNORECASE)
+ txtype |= TX_CI;
+ zfs_log_remove(zilog, tx, txtype, dzp, name);
+ }
dmu_tx_commit(tx);
@@ -1713,6 +1980,8 @@ out:
* uio - structure supplying read location, range info,
* and return buffer.
* cr - credentials of caller.
+ * ct - caller context
+ * flags - case flags
*
* OUT: uio - updated offset and range, buffer filled.
* eofp - set to true if end-of-file detected.
@@ -1734,6 +2003,7 @@ zfs_readdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp, int *ncookies, u_lon
{
znode_t *zp = VTOZ(vp);
iovec_t *iovp;
+ edirent_t *eodp;
dirent64_t *odp;
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
objset_t *os;
@@ -1747,11 +2017,14 @@ zfs_readdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp, int *ncookies, u_lon
int outcount;
int error;
uint8_t prefetch;
+ boolean_t check_sysattrs;
uint8_t type;
int ncooks;
u_long *cooks = NULL;
+ int flags = 0;
ZFS_ENTER(zfsvfs);
+ ZFS_VERIFY_ZP(zp);
/*
* If we are not given an eof variable,
@@ -1809,6 +2082,7 @@ zfs_readdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp, int *ncookies, u_lon
bufsize = bytes_wanted;
odp = (struct dirent64 *)iovp->iov_base;
}
+ eodp = (struct edirent *)odp;
if (ncookies != NULL) {
/*
@@ -1819,6 +2093,19 @@ zfs_readdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp, int *ncookies, u_lon
*cookies = cooks;
*ncookies = ncooks;
}
+ /*
+ * If this VFS supports the system attribute view interface; and
+ * we're looking at an extended attribute directory; and we care
+ * about normalization conflicts on this vfs; then we must check
+ * for normalization conflicts with the sysattr name space.
+ */
+#ifdef TODO
+ check_sysattrs = vfs_has_feature(vp->v_vfsp, VFSFT_SYSATTR_VIEWS) &&
+ (vp->v_flag & V_XATTRDIR) && zfsvfs->z_norm &&
+ (flags & V_RDDIR_ENTFLAGS);
+#else
+ check_sysattrs = 0;
+#endif
/*
* Transform to file-system independent format
@@ -1827,20 +2114,24 @@ zfs_readdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp, int *ncookies, u_lon
while (outcount < bytes_wanted) {
ino64_t objnum;
ushort_t reclen;
+ off64_t *next;
/*
* Special case `.', `..', and `.zfs'.
*/
if (offset == 0) {
(void) strcpy(zap.za_name, ".");
+ zap.za_normalization_conflict = 0;
objnum = zp->z_id;
type = DT_DIR;
} else if (offset == 1) {
(void) strcpy(zap.za_name, "..");
+ zap.za_normalization_conflict = 0;
objnum = zp->z_phys->zp_parent;
type = DT_DIR;
} else if (offset == 2 && zfs_show_ctldir(zp)) {
(void) strcpy(zap.za_name, ZFS_CTLDIR_NAME);
+ zap.za_normalization_conflict = 0;
objnum = ZFSCTL_INO_ROOT;
type = DT_DIR;
} else {
@@ -1870,8 +2161,21 @@ zfs_readdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp, int *ncookies, u_lon
* uint8_t type = ZFS_DIRENT_TYPE(zap.za_first_integer);
*/
type = ZFS_DIRENT_TYPE(zap.za_first_integer);
+
+ if (check_sysattrs && !zap.za_normalization_conflict) {
+#ifdef TODO
+ zap.za_normalization_conflict =
+ xattr_sysattr_casechk(zap.za_name);
+#else
+ panic("%s:%u: TODO", __func__, __LINE__);
+#endif
+ }
}
- reclen = DIRENT64_RECLEN(strlen(zap.za_name));
+
+ if (flags & V_RDDIR_ENTFLAGS)
+ reclen = EDIRENT_RECLEN(strlen(zap.za_name));
+ else
+ reclen = DIRENT64_RECLEN(strlen(zap.za_name));
/*
* Will this entry fit in the buffer?
@@ -1886,16 +2190,31 @@ zfs_readdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp, int *ncookies, u_lon
}
break;
}
- /*
- * Add this entry:
- */
- odp->d_ino = objnum;
- odp->d_reclen = reclen;
- odp->d_namlen = strlen(zap.za_name);
- (void) strlcpy(odp->d_name, zap.za_name, odp->d_namlen + 1);
- odp->d_type = type;
+ if (flags & V_RDDIR_ENTFLAGS) {
+ /*
+ * Add extended flag entry:
+ */
+ eodp->ed_ino = objnum;
+ eodp->ed_reclen = reclen;
+ /* NOTE: ed_off is the offset for the *next* entry */
+ next = &(eodp->ed_off);
+ eodp->ed_eflags = zap.za_normalization_conflict ?
+ ED_CASE_CONFLICT : 0;
+ (void) strncpy(eodp->ed_name, zap.za_name,
+ EDIRENT_NAMELEN(reclen));
+ eodp = (edirent_t *)((intptr_t)eodp + reclen);
+ } else {
+ /*
+ * Add normal entry:
+ */
+ odp->d_ino = objnum;
+ odp->d_reclen = reclen;
+ odp->d_namlen = strlen(zap.za_name);
+ (void) strlcpy(odp->d_name, zap.za_name, odp->d_namlen + 1);
+ odp->d_type = type;
+ odp = (dirent64_t *)((intptr_t)odp + reclen);
+ }
outcount += reclen;
- odp = (dirent64_t *)((intptr_t)odp + reclen);
ASSERT(outcount <= bufsize);
@@ -1956,26 +2275,34 @@ update:
return (error);
}
+ulong_t zfs_fsync_sync_cnt = 4;
+
static int
-zfs_fsync(vnode_t *vp, int syncflag, cred_t *cr)
+zfs_fsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct)
{
znode_t *zp = VTOZ(vp);
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
+ (void) tsd_set(zfs_fsyncer_key, (void *)zfs_fsync_sync_cnt);
+
ZFS_ENTER(zfsvfs);
+ ZFS_VERIFY_ZP(zp);
zil_commit(zfsvfs->z_log, zp->z_last_itx, zp->z_id);
ZFS_EXIT(zfsvfs);
return (0);
}
+
/*
* Get the requested file attributes and place them in the provided
* vattr structure.
*
* IN: vp - vnode of file.
* vap - va_mask identifies requested attributes.
- * flags - [UNUSED]
+ * If AT_XVATTR set, then optional attrs are requested
+ * flags - ATTR_NOACLCHECK (CIFS server context)
* cr - credentials of caller.
+ * ct - caller context
*
* OUT: vap - attribute values.
*
@@ -1983,54 +2310,170 @@ zfs_fsync(vnode_t *vp, int syncflag, cred_t *cr)
*/
/* ARGSUSED */
static int
-zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr)
+zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
+ caller_context_t *ct)
{
znode_t *zp = VTOZ(vp);
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
- znode_phys_t *pzp = zp->z_phys;
+ znode_phys_t *pzp;
+ int error = 0;
uint32_t blksize;
u_longlong_t nblocks;
- int error;
+ uint64_t links;
+ xvattr_t *xvap = (xvattr_t *)vap; /* vap may be an xvattr_t * */
+ xoptattr_t *xoap = NULL;
+ boolean_t skipaclchk = (flags & ATTR_NOACLCHECK) ? B_TRUE : B_FALSE;
ZFS_ENTER(zfsvfs);
+ ZFS_VERIFY_ZP(zp);
+ pzp = zp->z_phys;
+
+ mutex_enter(&zp->z_lock);
+
+ /*
+ * If ACL is trivial don't bother looking for ACE_READ_ATTRIBUTES.
+ * Also, if we are the owner don't bother, since owner should
+ * always be allowed to read basic attributes of file.
+ */
+ if (!(pzp->zp_flags & ZFS_ACL_TRIVIAL) &&
+ (pzp->zp_uid != crgetuid(cr))) {
+ if (error = zfs_zaccess(zp, ACE_READ_ATTRIBUTES, 0,
+ skipaclchk, cr)) {
+ mutex_exit(&zp->z_lock);
+ ZFS_EXIT(zfsvfs);
+ return (error);
+ }
+ }
/*
* Return all attributes. It's cheaper to provide the answer
* than to determine whether we were asked the question.
*/
- mutex_enter(&zp->z_lock);
vap->va_type = IFTOVT(pzp->zp_mode);
vap->va_mode = pzp->zp_mode & ~S_IFMT;
- vap->va_uid = zp->z_phys->zp_uid;
- vap->va_gid = zp->z_phys->zp_gid;
+ zfs_fuid_map_ids(zp, cr, &vap->va_uid, &vap->va_gid);
+// vap->va_fsid = zp->z_zfsvfs->z_vfs->vfs_dev;
vap->va_nodeid = zp->z_id;
- vap->va_nlink = MIN(pzp->zp_links, UINT32_MAX); /* nlink_t limit! */
+ if ((vp->v_flag & VROOT) && zfs_show_ctldir(zp))
+ links = pzp->zp_links + 1;
+ else
+ links = pzp->zp_links;
+ vap->va_nlink = MIN(links, UINT32_MAX); /* nlink_t limit! */
vap->va_size = pzp->zp_size;
vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
vap->va_rdev = zfs_cmpldev(pzp->zp_rdev);
vap->va_seq = zp->z_seq;
vap->va_flags = 0; /* FreeBSD: Reset chflags(2) flags. */
- ZFS_TIME_DECODE(&vap->va_atime, pzp->zp_atime);
- ZFS_TIME_DECODE(&vap->va_mtime, pzp->zp_mtime);
- ZFS_TIME_DECODE(&vap->va_ctime, pzp->zp_ctime);
- ZFS_TIME_DECODE(&vap->va_birthtime, pzp->zp_crtime);
-
/*
- * If ACL is trivial don't bother looking for ACE_READ_ATTRIBUTES.
- * Also, if we are the owner don't bother, since owner should
- * always be allowed to read basic attributes of file.
+ * Add in any requested optional attributes and the create time.
+ * Also set the corresponding bits in the returned attribute bitmap.
*/
- if (!(zp->z_phys->zp_flags & ZFS_ACL_TRIVIAL) &&
- (zp->z_phys->zp_uid != crgetuid(cr))) {
- if (error = zfs_zaccess(zp, ACE_READ_ATTRIBUTES, cr)) {
- mutex_exit(&zp->z_lock);
- ZFS_EXIT(zfsvfs);
- return (error);
+ if ((xoap = xva_getxoptattr(xvap)) != NULL && zfsvfs->z_use_fuids) {
+ if (XVA_ISSET_REQ(xvap, XAT_ARCHIVE)) {
+ xoap->xoa_archive =
+ ((pzp->zp_flags & ZFS_ARCHIVE) != 0);
+ XVA_SET_RTN(xvap, XAT_ARCHIVE);
+ }
+
+ if (XVA_ISSET_REQ(xvap, XAT_READONLY)) {
+ xoap->xoa_readonly =
+ ((pzp->zp_flags & ZFS_READONLY) != 0);
+ XVA_SET_RTN(xvap, XAT_READONLY);
+ }
+
+ if (XVA_ISSET_REQ(xvap, XAT_SYSTEM)) {
+ xoap->xoa_system =
+ ((pzp->zp_flags & ZFS_SYSTEM) != 0);
+ XVA_SET_RTN(xvap, XAT_SYSTEM);
+ }
+
+ if (XVA_ISSET_REQ(xvap, XAT_HIDDEN)) {
+ xoap->xoa_hidden =
+ ((pzp->zp_flags & ZFS_HIDDEN) != 0);
+ XVA_SET_RTN(xvap, XAT_HIDDEN);
+ }
+
+ if (XVA_ISSET_REQ(xvap, XAT_NOUNLINK)) {
+ xoap->xoa_nounlink =
+ ((pzp->zp_flags & ZFS_NOUNLINK) != 0);
+ XVA_SET_RTN(xvap, XAT_NOUNLINK);
+ }
+
+ if (XVA_ISSET_REQ(xvap, XAT_IMMUTABLE)) {
+ xoap->xoa_immutable =
+ ((pzp->zp_flags & ZFS_IMMUTABLE) != 0);
+ XVA_SET_RTN(xvap, XAT_IMMUTABLE);
+ }
+
+ if (XVA_ISSET_REQ(xvap, XAT_APPENDONLY)) {
+ xoap->xoa_appendonly =
+ ((pzp->zp_flags & ZFS_APPENDONLY) != 0);
+ XVA_SET_RTN(xvap, XAT_APPENDONLY);
+ }
+
+ if (XVA_ISSET_REQ(xvap, XAT_NODUMP)) {
+ xoap->xoa_nodump =
+ ((pzp->zp_flags & ZFS_NODUMP) != 0);
+ XVA_SET_RTN(xvap, XAT_NODUMP);
+ }
+
+ if (XVA_ISSET_REQ(xvap, XAT_OPAQUE)) {
+ xoap->xoa_opaque =
+ ((pzp->zp_flags & ZFS_OPAQUE) != 0);
+ XVA_SET_RTN(xvap, XAT_OPAQUE);
+ }
+
+ if (XVA_ISSET_REQ(xvap, XAT_AV_QUARANTINED)) {
+ xoap->xoa_av_quarantined =
+ ((pzp->zp_flags & ZFS_AV_QUARANTINED) != 0);
+ XVA_SET_RTN(xvap, XAT_AV_QUARANTINED);
+ }
+
+ if (XVA_ISSET_REQ(xvap, XAT_AV_MODIFIED)) {
+ xoap->xoa_av_modified =
+ ((pzp->zp_flags & ZFS_AV_MODIFIED) != 0);
+ XVA_SET_RTN(xvap, XAT_AV_MODIFIED);
+ }
+
+ if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP) &&
+ vp->v_type == VREG &&
+ (pzp->zp_flags & ZFS_BONUS_SCANSTAMP)) {
+ size_t len;
+ dmu_object_info_t doi;
+
+ /*
+ * Only VREG files have anti-virus scanstamps, so we
+ * won't conflict with symlinks in the bonus buffer.
+ */
+ dmu_object_info_from_db(zp->z_dbuf, &doi);
+ len = sizeof (xoap->xoa_av_scanstamp) +
+ sizeof (znode_phys_t);
+ if (len <= doi.doi_bonus_size) {
+ /*
+ * pzp points to the start of the
+ * znode_phys_t. pzp + 1 points to the
+ * first byte after the znode_phys_t.
+ */
+ (void) memcpy(xoap->xoa_av_scanstamp,
+ pzp + 1,
+ sizeof (xoap->xoa_av_scanstamp));
+ XVA_SET_RTN(xvap, XAT_AV_SCANSTAMP);
+ }
+ }
+
+ if (XVA_ISSET_REQ(xvap, XAT_CREATETIME)) {
+ ZFS_TIME_DECODE(&xoap->xoa_createtime, pzp->zp_crtime);
+ XVA_SET_RTN(xvap, XAT_CREATETIME);
}
}
+ ZFS_TIME_DECODE(&vap->va_atime, pzp->zp_atime);
+ ZFS_TIME_DECODE(&vap->va_mtime, pzp->zp_mtime);
+ ZFS_TIME_DECODE(&vap->va_ctime, pzp->zp_ctime);
+ ZFS_TIME_DECODE(&vap->va_birthtime, pzp->zp_crtime);
+
mutex_exit(&zp->z_lock);
dmu_object_size_from_db(zp->z_dbuf, &blksize, &nblocks);
@@ -2054,8 +2497,11 @@ zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr)
*
* IN: vp - vnode of file to be modified.
* vap - new attribute values.
+ * If AT_XVATTR set, then optional attrs are being set
* flags - ATTR_UTIME set if non-default time values provided.
+ * - ATTR_NOACLCHECK (CIFS context only).
* cr - credentials of caller.
+ * ct - caller context
*
* RETURN: 0 if success
* error code if failure
@@ -2068,10 +2514,10 @@ static int
zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
caller_context_t *ct)
{
- struct znode *zp = VTOZ(vp);
- znode_phys_t *pzp = zp->z_phys;
+ znode_t *zp = VTOZ(vp);
+ znode_phys_t *pzp;
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
- zilog_t *zilog = zfsvfs->z_log;
+ zilog_t *zilog;
dmu_tx_t *tx;
vattr_t oldva;
uint_t mask = vap->va_mask;
@@ -2081,6 +2527,11 @@ zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
znode_t *attrzp;
int need_policy = FALSE;
int err;
+ zfs_fuid_info_t *fuidp = NULL;
+ xvattr_t *xvap = (xvattr_t *)vap; /* vap may be an xvattr_t * */
+ xoptattr_t *xoap;
+ zfs_acl_t *aclp = NULL;
+ boolean_t skipaclchk = (flags & ATTR_NOACLCHECK) ? B_TRUE : B_FALSE;
if (mask == 0)
return (0);
@@ -2088,13 +2539,69 @@ zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
if (mask & AT_NOSET)
return (EINVAL);
- if (mask & AT_SIZE && vp->v_type == VDIR)
+ ZFS_ENTER(zfsvfs);
+ ZFS_VERIFY_ZP(zp);
+
+ pzp = zp->z_phys;
+ zilog = zfsvfs->z_log;
+
+ /*
+ * Make sure that if we have ephemeral uid/gid or xvattr specified
+ * that file system is at proper version level
+ */
+
+ if (zfsvfs->z_use_fuids == B_FALSE &&
+ (((mask & AT_UID) && IS_EPHEMERAL(vap->va_uid)) ||
+ ((mask & AT_GID) && IS_EPHEMERAL(vap->va_gid)) ||
+ (mask & AT_XVATTR))) {
+ ZFS_EXIT(zfsvfs);
+ return (EINVAL);
+ }
+
+ if (mask & AT_SIZE && vp->v_type == VDIR) {
+ ZFS_EXIT(zfsvfs);
return (EISDIR);
+ }
- if (mask & AT_SIZE && vp->v_type != VREG && vp->v_type != VFIFO)
+ if (mask & AT_SIZE && vp->v_type != VREG && vp->v_type != VFIFO) {
+ ZFS_EXIT(zfsvfs);
return (EINVAL);
+ }
- ZFS_ENTER(zfsvfs);
+ /*
+ * If this is an xvattr_t, then get a pointer to the structure of
+ * optional attributes. If this is NULL, then we have a vattr_t.
+ */
+ xoap = xva_getxoptattr(xvap);
+
+ /*
+ * Immutable files can only alter immutable bit and atime
+ */
+ if ((pzp->zp_flags & ZFS_IMMUTABLE) &&
+ ((mask & (AT_SIZE|AT_UID|AT_GID|AT_MTIME|AT_MODE)) ||
+ ((mask & AT_XVATTR) && XVA_ISSET_REQ(xvap, XAT_CREATETIME)))) {
+ ZFS_EXIT(zfsvfs);
+ return (EPERM);
+ }
+
+ if ((mask & AT_SIZE) && (pzp->zp_flags & ZFS_READONLY)) {
+ ZFS_EXIT(zfsvfs);
+ return (EPERM);
+ }
+
+ /*
+ * Verify timestamps doesn't overflow 32 bits.
+ * ZFS can handle large timestamps, but 32bit syscalls can't
+ * handle times greater than 2039. This check should be removed
+ * once large timestamps are fully supported.
+ */
+ if (mask & (AT_ATIME | AT_MTIME)) {
+ if (((mask & AT_ATIME) && TIMESPEC_OVERFLOW(&vap->va_atime)) ||
+ ((mask & AT_MTIME) && TIMESPEC_OVERFLOW(&vap->va_mtime))) {
+ ZFS_EXIT(zfsvfs);
+ return (EOVERFLOW);
+ }
+ }
top:
attrzp = NULL;
@@ -2109,7 +2616,7 @@ top:
*/
if (mask & AT_SIZE) {
- err = zfs_zaccess(zp, ACE_WRITE_DATA, cr);
+ err = zfs_zaccess(zp, ACE_WRITE_DATA, 0, skipaclchk, cr);
if (err) {
ZFS_EXIT(zfsvfs);
return (err);
@@ -2120,18 +2627,22 @@ top:
* block if there are locks present... this
* should be addressed in openat().
*/
- do {
- err = zfs_freesp(zp, vap->va_size, 0, 0, FALSE);
- /* NB: we already did dmu_tx_wait() if necessary */
- } while (err == ERESTART && zfsvfs->z_assign == TXG_NOWAIT);
+ /* XXX - would it be OK to generate a log record here? */
+ err = zfs_freesp(zp, vap->va_size, 0, 0, FALSE);
if (err) {
ZFS_EXIT(zfsvfs);
return (err);
}
}
- if (mask & (AT_ATIME|AT_MTIME))
- need_policy = zfs_zaccess_v4_perm(zp, ACE_WRITE_ATTRIBUTES, cr);
+ if (mask & (AT_ATIME|AT_MTIME) ||
+ ((mask & AT_XVATTR) && (XVA_ISSET_REQ(xvap, XAT_HIDDEN) ||
+ XVA_ISSET_REQ(xvap, XAT_READONLY) ||
+ XVA_ISSET_REQ(xvap, XAT_ARCHIVE) ||
+ XVA_ISSET_REQ(xvap, XAT_CREATETIME) ||
+ XVA_ISSET_REQ(xvap, XAT_SYSTEM))))
+ need_policy = zfs_zaccess(zp, ACE_WRITE_ATTRIBUTES, 0,
+ skipaclchk, cr);
if (mask & (AT_UID|AT_GID)) {
int idmask = (mask & (AT_UID|AT_GID));
@@ -2151,7 +2662,8 @@ top:
*/
take_owner = (mask & AT_UID) && (vap->va_uid == crgetuid(cr));
- take_group = (mask & AT_GID) && groupmember(vap->va_gid, cr);
+ take_group = (mask & AT_GID) &&
+ zfs_groupmember(zfsvfs, vap->va_gid, cr);
/*
* If both AT_UID and AT_GID are set then take_owner and
@@ -2165,11 +2677,12 @@ top:
if (((idmask == (AT_UID|AT_GID)) && take_owner && take_group) ||
((idmask == AT_UID) && take_owner) ||
((idmask == AT_GID) && take_group)) {
- if (zfs_zaccess_v4_perm(zp, ACE_WRITE_OWNER, cr) == 0) {
+ if (zfs_zaccess(zp, ACE_WRITE_OWNER, 0,
+ skipaclchk, cr) == 0) {
/*
* Remove setuid/setgid for non-privileged users
*/
- secpolicy_setid_clear(vap, cr);
+ secpolicy_setid_clear(vap, vp, cr);
trim_mask = (mask & (AT_UID|AT_GID));
} else {
need_policy = TRUE;
@@ -2181,12 +2694,38 @@ top:
mutex_enter(&zp->z_lock);
oldva.va_mode = pzp->zp_mode;
- oldva.va_uid = zp->z_phys->zp_uid;
- oldva.va_gid = zp->z_phys->zp_gid;
+ zfs_fuid_map_ids(zp, cr, &oldva.va_uid, &oldva.va_gid);
+ if (mask & AT_XVATTR) {
+ if ((need_policy == FALSE) &&
+ (XVA_ISSET_REQ(xvap, XAT_APPENDONLY) &&
+ xoap->xoa_appendonly !=
+ ((pzp->zp_flags & ZFS_APPENDONLY) != 0)) ||
+ (XVA_ISSET_REQ(xvap, XAT_NOUNLINK) &&
+ xoap->xoa_nounlink !=
+ ((pzp->zp_flags & ZFS_NOUNLINK) != 0)) ||
+ (XVA_ISSET_REQ(xvap, XAT_IMMUTABLE) &&
+ xoap->xoa_immutable !=
+ ((pzp->zp_flags & ZFS_IMMUTABLE) != 0)) ||
+ (XVA_ISSET_REQ(xvap, XAT_NODUMP) &&
+ xoap->xoa_nodump !=
+ ((pzp->zp_flags & ZFS_NODUMP) != 0)) ||
+ (XVA_ISSET_REQ(xvap, XAT_AV_MODIFIED) &&
+ xoap->xoa_av_modified !=
+ ((pzp->zp_flags & ZFS_AV_MODIFIED) != 0)) ||
+ ((XVA_ISSET_REQ(xvap, XAT_AV_QUARANTINED) &&
+ ((vp->v_type != VREG && xoap->xoa_av_quarantined) ||
+ xoap->xoa_av_quarantined !=
+ ((pzp->zp_flags & ZFS_AV_QUARANTINED) != 0)))) ||
+ (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP)) ||
+ (XVA_ISSET_REQ(xvap, XAT_OPAQUE))) {
+ need_policy = TRUE;
+ }
+ }
+
mutex_exit(&zp->z_lock);
if (mask & AT_MODE) {
- if (zfs_zaccess_v4_perm(zp, ACE_WRITE_ACL, cr) == 0) {
+ if (zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr) == 0) {
err = secpolicy_setid_setsticky_clear(vp, vap,
&oldva, cr);
if (err) {
@@ -2211,10 +2750,9 @@ top:
if (trim_mask) {
saved_mask = vap->va_mask;
vap->va_mask &= ~trim_mask;
-
}
err = secpolicy_vnode_setattr(cr, vp, vap, &oldva, flags,
- (int (*)(void *, int, cred_t *))zfs_zaccess_rwx, zp);
+ (int (*)(void *, int, cred_t *))zfs_zaccess_unix, zp);
if (err) {
ZFS_EXIT(zfsvfs);
return (err);
@@ -2232,25 +2770,58 @@ top:
tx = dmu_tx_create(zfsvfs->z_os);
dmu_tx_hold_bonus(tx, zp->z_id);
+ if (((mask & AT_UID) && IS_EPHEMERAL(vap->va_uid)) ||
+ ((mask & AT_GID) && IS_EPHEMERAL(vap->va_gid))) {
+ if (zfsvfs->z_fuid_obj == 0) {
+ dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT);
+ dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0,
+ FUID_SIZE_ESTIMATE(zfsvfs));
+ dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, FALSE, NULL);
+ } else {
+ dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj);
+ dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0,
+ FUID_SIZE_ESTIMATE(zfsvfs));
+ }
+ }
if (mask & AT_MODE) {
uint64_t pmode = pzp->zp_mode;
new_mode = (pmode & S_IFMT) | (vap->va_mode & ~S_IFMT);
- if (zp->z_phys->zp_acl.z_acl_extern_obj)
- dmu_tx_hold_write(tx,
- pzp->zp_acl.z_acl_extern_obj, 0, SPA_MAXBLOCKSIZE);
- else
+ if (err = zfs_acl_chmod_setattr(zp, &aclp, new_mode)) {
+ dmu_tx_abort(tx);
+ ZFS_EXIT(zfsvfs);
+ return (err);
+ }
+ if (pzp->zp_acl.z_acl_extern_obj) {
+ /* Are we upgrading ACL from old V0 format to new V1 */
+ if (zfsvfs->z_version <= ZPL_VERSION_FUID &&
+ pzp->zp_acl.z_acl_version ==
+ ZFS_ACL_VERSION_INITIAL) {
+ dmu_tx_hold_free(tx,
+ pzp->zp_acl.z_acl_extern_obj, 0,
+ DMU_OBJECT_END);
+ dmu_tx_hold_write(tx, DMU_NEW_OBJECT,
+ 0, aclp->z_acl_bytes);
+ } else {
+ dmu_tx_hold_write(tx,
+ pzp->zp_acl.z_acl_extern_obj, 0,
+ aclp->z_acl_bytes);
+ }
+ } else if (aclp->z_acl_bytes > ZFS_ACE_SPACE) {
dmu_tx_hold_write(tx, DMU_NEW_OBJECT,
- 0, ZFS_ACL_SIZE(MAX_ACL_SIZE));
+ 0, aclp->z_acl_bytes);
+ }
}
- if ((mask & (AT_UID | AT_GID)) && zp->z_phys->zp_xattr != 0) {
- err = zfs_zget(zp->z_zfsvfs, zp->z_phys->zp_xattr, &attrzp);
+ if ((mask & (AT_UID | AT_GID)) && pzp->zp_xattr != 0) {
+ err = zfs_zget(zp->z_zfsvfs, pzp->zp_xattr, &attrzp);
if (err) {
dmu_tx_abort(tx);
ZFS_EXIT(zfsvfs);
+ if (aclp)
+ zfs_acl_free(aclp);
return (err);
}
dmu_tx_hold_bonus(tx, attrzp->z_id);
@@ -2260,6 +2831,12 @@ top:
if (err) {
if (attrzp)
VN_RELE(ZTOV(attrzp));
+
+ if (aclp) {
+ zfs_acl_free(aclp);
+ aclp = NULL;
+ }
+
if (err == ERESTART && zfsvfs->z_assign == TXG_NOWAIT) {
dmu_tx_wait(tx);
dmu_tx_abort(tx);
@@ -2283,26 +2860,36 @@ top:
mutex_enter(&zp->z_lock);
if (mask & AT_MODE) {
- err = zfs_acl_chmod_setattr(zp, new_mode, tx);
+ mutex_enter(&zp->z_acl_lock);
+ zp->z_phys->zp_mode = new_mode;
+ err = zfs_aclset_common(zp, aclp, cr, &fuidp, tx);
ASSERT3U(err, ==, 0);
+ mutex_exit(&zp->z_acl_lock);
}
if (attrzp)
mutex_enter(&attrzp->z_lock);
if (mask & AT_UID) {
- zp->z_phys->zp_uid = (uint64_t)vap->va_uid;
+ pzp->zp_uid = zfs_fuid_create(zfsvfs,
+ vap->va_uid, cr, ZFS_OWNER, tx, &fuidp);
if (attrzp) {
- attrzp->z_phys->zp_uid = (uint64_t)vap->va_uid;
+ attrzp->z_phys->zp_uid = zfs_fuid_create(zfsvfs,
+ vap->va_uid, cr, ZFS_OWNER, tx, &fuidp);
}
}
if (mask & AT_GID) {
- zp->z_phys->zp_gid = (uint64_t)vap->va_gid;
+ pzp->zp_gid = zfs_fuid_create(zfsvfs, vap->va_gid,
+ cr, ZFS_GROUP, tx, &fuidp);
if (attrzp)
- attrzp->z_phys->zp_gid = (uint64_t)vap->va_gid;
+ attrzp->z_phys->zp_gid = zfs_fuid_create(zfsvfs,
+ vap->va_gid, cr, ZFS_GROUP, tx, &fuidp);
}
+ if (aclp)
+ zfs_acl_free(aclp);
+
if (attrzp)
mutex_exit(&attrzp->z_lock);
@@ -2312,14 +2899,38 @@ top:
if (mask & AT_MTIME)
ZFS_TIME_ENCODE(&vap->va_mtime, pzp->zp_mtime);
+ /* XXX - shouldn't this be done *before* the ATIME/MTIME checks? */
if (mask & AT_SIZE)
zfs_time_stamper_locked(zp, CONTENT_MODIFIED, tx);
else if (mask != 0)
zfs_time_stamper_locked(zp, STATE_CHANGED, tx);
+ /*
+ * Do this after setting timestamps to prevent timestamp
+ * update from toggling bit
+ */
+
+ if (xoap && (mask & AT_XVATTR)) {
+ if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP)) {
+ size_t len;
+ dmu_object_info_t doi;
+
+ ASSERT(vp->v_type == VREG);
+
+ /* Grow the bonus buffer if necessary. */
+ dmu_object_info_from_db(zp->z_dbuf, &doi);
+ len = sizeof (xoap->xoa_av_scanstamp) +
+ sizeof (znode_phys_t);
+ if (len > doi.doi_bonus_size)
+ VERIFY(dmu_set_bonus(zp->z_dbuf, len, tx) == 0);
+ }
+ zfs_xvattr_set(zp, xvap);
+ }
if (mask != 0)
- zfs_log_setattr(zilog, tx, TX_SETATTR, zp, vap, mask);
+ zfs_log_setattr(zilog, tx, TX_SETATTR, zp, vap, mask, fuidp);
+ if (fuidp)
+ zfs_fuid_info_free(fuidp);
mutex_exit(&zp->z_lock);
if (attrzp)
@@ -2436,6 +3047,8 @@ zfs_rename_lock(znode_t *szp, znode_t *tdzp, znode_t *sdzp, zfs_zlock_t **zlpp)
* tdvp - Target directory to contain the "new entry".
* tnm - New entry name.
* cr - credentials of caller.
+ * ct - caller context
+ * flags - case flags
*
* RETURN: 0 if success
* error code if failure
@@ -2443,25 +3056,31 @@ zfs_rename_lock(znode_t *szp, znode_t *tdzp, znode_t *sdzp, zfs_zlock_t **zlpp)
* Timestamps:
* sdvp,tdvp - ctime|mtime updated
*/
+/*ARGSUSED*/
static int
-zfs_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr)
+zfs_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr,
+ caller_context_t *ct, int flags)
{
znode_t *tdzp, *szp, *tzp;
znode_t *sdzp = VTOZ(sdvp);
zfsvfs_t *zfsvfs = sdzp->z_zfsvfs;
- zilog_t *zilog = zfsvfs->z_log;
+ zilog_t *zilog;
vnode_t *realvp;
zfs_dirlock_t *sdl, *tdl;
dmu_tx_t *tx;
zfs_zlock_t *zl;
- int cmp, serr, terr, error;
+ int cmp, serr, terr;
+ int error = 0;
+ int zflg = 0;
ZFS_ENTER(zfsvfs);
+ ZFS_VERIFY_ZP(sdzp);
+ zilog = zfsvfs->z_log;
/*
* Make sure we have the real vp for the target directory.
*/
- if (VOP_REALVP(tdvp, &realvp) == 0)
+ if (VOP_REALVP(tdvp, &realvp, ct) == 0)
tdvp = realvp;
if (tdvp->v_vfsp != sdvp->v_vfsp) {
@@ -2470,6 +3089,16 @@ zfs_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr)
}
tdzp = VTOZ(tdvp);
+ ZFS_VERIFY_ZP(tdzp);
+ if (zfsvfs->z_utf8 && u8_validate(tnm,
+ strlen(tnm), NULL, U8_VALIDATE_ENTIRE, &error) < 0) {
+ ZFS_EXIT(zfsvfs);
+ return (EILSEQ);
+ }
+
+ if (flags & FIGNORECASE)
+ zflg |= ZCILOOK;
+
top:
szp = NULL;
tzp = NULL;
@@ -2497,7 +3126,14 @@ top:
} else if (sdzp->z_id > tdzp->z_id) {
cmp = 1;
} else {
- cmp = strcmp(snm, tnm);
+ /*
+ * First compare the two name arguments without
+ * considering any case folding.
+ */
+ int nofold = (zfsvfs->z_norm & ~U8_TEXTPREP_TOUPPER);
+
+ cmp = u8_strcmp(snm, tnm, 0, nofold, U8_UNICODE_LATEST, &error);
+ ASSERT(error == 0 || !zfsvfs->z_utf8);
if (cmp == 0) {
/*
* POSIX: "If the old argument and the new argument
@@ -2508,13 +3144,49 @@ top:
ZFS_EXIT(zfsvfs);
return (0);
}
+ /*
+ * If the file system is case-folding, then we may
+ * have some more checking to do. A case-folding file
+ * system is either supporting mixed case sensitivity
+ * access or is completely case-insensitive. Note
+ * that the file system is always case preserving.
+ *
+ * In mixed sensitivity mode case sensitive behavior
+ * is the default. FIGNORECASE must be used to
+ * explicitly request case insensitive behavior.
+ *
+ * If the source and target names provided differ only
+ * by case (e.g., a request to rename 'tim' to 'Tim'),
+ * we will treat this as a special case in the
+ * case-insensitive mode: as long as the source name
+ * is an exact match, we will allow this to proceed as
+ * a name-change request.
+ */
+ if ((zfsvfs->z_case == ZFS_CASE_INSENSITIVE ||
+ (zfsvfs->z_case == ZFS_CASE_MIXED &&
+ flags & FIGNORECASE)) &&
+ u8_strcmp(snm, tnm, 0, zfsvfs->z_norm, U8_UNICODE_LATEST,
+ &error) == 0) {
+ /*
+ * case preserving rename request, require exact
+ * name matches
+ */
+ zflg |= ZCIEXACT;
+ zflg &= ~ZCILOOK;
+ }
}
+
if (cmp < 0) {
- serr = zfs_dirent_lock(&sdl, sdzp, snm, &szp, ZEXISTS);
- terr = zfs_dirent_lock(&tdl, tdzp, tnm, &tzp, 0);
+ serr = zfs_dirent_lock(&sdl, sdzp, snm, &szp,
+ ZEXISTS | zflg, NULL, NULL);
+ terr = zfs_dirent_lock(&tdl,
+ tdzp, tnm, &tzp, ZRENAMING | zflg, NULL, NULL);
} else {
- terr = zfs_dirent_lock(&tdl, tdzp, tnm, &tzp, 0);
- serr = zfs_dirent_lock(&sdl, sdzp, snm, &szp, ZEXISTS);
+ terr = zfs_dirent_lock(&tdl,
+ tdzp, tnm, &tzp, zflg, NULL, NULL);
+ serr = zfs_dirent_lock(&sdl,
+ sdzp, snm, &szp, ZEXISTS | ZRENAMING | zflg,
+ NULL, NULL);
}
if (serr) {
@@ -2588,9 +3260,17 @@ top:
}
}
- vnevent_rename_src(ZTOV(szp));
+ vnevent_rename_src(ZTOV(szp), sdvp, snm, ct);
if (tzp)
- vnevent_rename_dest(ZTOV(tzp));
+ vnevent_rename_dest(ZTOV(tzp), tdvp, tnm, ct);
+
+ /*
+ * notify the target directory if it is not the same
+ * as source directory.
+ */
+ if (tdvp != sdvp) {
+ vnevent_rename_dest_dir(tdvp, ct);
+ }
tx = dmu_tx_create(zfsvfs->z_os);
dmu_tx_hold_bonus(tx, szp->z_id); /* nlink changes */
@@ -2622,15 +3302,22 @@ top:
}
if (tzp) /* Attempt to remove the existing target */
- error = zfs_link_destroy(tdl, tzp, tx, 0, NULL);
+ error = zfs_link_destroy(tdl, tzp, tx, zflg, NULL);
if (error == 0) {
error = zfs_link_create(tdl, szp, tx, ZRENAMING);
if (error == 0) {
+ szp->z_phys->zp_flags |= ZFS_AV_MODIFIED;
+
error = zfs_link_destroy(sdl, szp, tx, ZRENAMING, NULL);
ASSERT(error == 0);
- zfs_log_rename(zilog, tx, TX_RENAME, sdzp,
- sdl->dl_name, tdzp, tdl->dl_name, szp);
+
+ zfs_log_rename(zilog, tx,
+ TX_RENAME | (flags & FIGNORECASE ? TX_CI : 0),
+ sdzp, sdl->dl_name, tdzp, tdl->dl_name, szp);
+
+ /* Update path information for the target vnode */
+ vn_renamepath(tdvp, ZTOV(szp), tnm, strlen(tnm));
}
#ifdef FREEBSD_NAMECACHE
if (error == 0) {
@@ -2665,6 +3352,8 @@ out:
* vap - Attributes of new entry.
* target - Target path of new symlink.
* cr - credentials of caller.
+ * ct - caller context
+ * flags - case flags
*
* RETURN: 0 if success
* error code if failure
@@ -2672,23 +3361,37 @@ out:
* Timestamps:
* dvp - ctime|mtime updated
*/
+/*ARGSUSED*/
static int
-zfs_symlink(vnode_t *dvp, vnode_t **vpp, char *name, vattr_t *vap, char *link, cred_t *cr, kthread_t *td)
+zfs_symlink(vnode_t *dvp, vnode_t **vpp, char *name, vattr_t *vap, char *link,
+ cred_t *cr, kthread_t *td)
{
znode_t *zp, *dzp = VTOZ(dvp);
zfs_dirlock_t *dl;
dmu_tx_t *tx;
zfsvfs_t *zfsvfs = dzp->z_zfsvfs;
- zilog_t *zilog = zfsvfs->z_log;
- uint64_t zoid;
+ zilog_t *zilog;
int len = strlen(link);
int error;
+ int zflg = ZNEW;
+ zfs_fuid_info_t *fuidp = NULL;
+ int flags = 0;
ASSERT(vap->va_type == VLNK);
ZFS_ENTER(zfsvfs);
+ ZFS_VERIFY_ZP(dzp);
+ zilog = zfsvfs->z_log;
+
+ if (zfsvfs->z_utf8 && u8_validate(name, strlen(name),
+ NULL, U8_VALIDATE_ENTIRE, &error) < 0) {
+ ZFS_EXIT(zfsvfs);
+ return (EILSEQ);
+ }
+ if (flags & FIGNORECASE)
+ zflg |= ZCILOOK;
top:
- if (error = zfs_zaccess(dzp, ACE_ADD_FILE, cr)) {
+ if (error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr)) {
ZFS_EXIT(zfsvfs);
return (error);
}
@@ -2701,7 +3404,8 @@ top:
/*
* Attempt to lock directory; fail if entry already exists.
*/
- if (error = zfs_dirent_lock(&dl, dzp, name, &zp, ZNEW)) {
+ error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg, NULL, NULL);
+ if (error) {
ZFS_EXIT(zfsvfs);
return (error);
}
@@ -2712,6 +3416,18 @@ top:
dmu_tx_hold_zap(tx, dzp->z_id, TRUE, name);
if (dzp->z_phys->zp_flags & ZFS_INHERIT_ACE)
dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, SPA_MAXBLOCKSIZE);
+ if (IS_EPHEMERAL(crgetuid(cr)) || IS_EPHEMERAL(crgetgid(cr))) {
+ if (zfsvfs->z_fuid_obj == 0) {
+ dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT);
+ dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0,
+ FUID_SIZE_ESTIMATE(zfsvfs));
+ dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, FALSE, NULL);
+ } else {
+ dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj);
+ dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0,
+ FUID_SIZE_ESTIMATE(zfsvfs));
+ }
+ }
error = dmu_tx_assign(tx, zfsvfs->z_assign);
if (error) {
zfs_dirent_unlock(dl);
@@ -2732,23 +3448,22 @@ top:
* Put the link content into bonus buffer if it will fit;
* otherwise, store it just like any other file data.
*/
- zoid = 0;
if (sizeof (znode_phys_t) + len <= dmu_bonus_max()) {
- zfs_mknode(dzp, vap, &zoid, tx, cr, 0, &zp, len);
+ zfs_mknode(dzp, vap, tx, cr, 0, &zp, len, NULL, &fuidp);
if (len != 0)
bcopy(link, zp->z_phys + 1, len);
} else {
dmu_buf_t *dbp;
- zfs_mknode(dzp, vap, &zoid, tx, cr, 0, &zp, 0);
-
+ zfs_mknode(dzp, vap, tx, cr, 0, &zp, 0, NULL, &fuidp);
/*
* Nothing can access the znode yet so no locking needed
* for growing the znode's blocksize.
*/
zfs_grow_blocksize(zp, len, tx);
- VERIFY(0 == dmu_buf_hold(zfsvfs->z_os, zoid, 0, FTAG, &dbp));
+ VERIFY(0 == dmu_buf_hold(zfsvfs->z_os,
+ zp->z_id, 0, FTAG, &dbp));
dmu_buf_will_dirty(dbp, tx);
ASSERT3U(len, <=, dbp->db_size);
@@ -2763,9 +3478,14 @@ top:
(void) zfs_link_create(dl, zp, tx, ZNEW);
out:
if (error == 0) {
- zfs_log_symlink(zilog, tx, TX_SYMLINK, dzp, zp, name, link);
+ uint64_t txtype = TX_SYMLINK;
+ if (flags & FIGNORECASE)
+ txtype |= TX_CI;
+ zfs_log_symlink(zilog, tx, txtype, dzp, zp, name, link);
*vpp = ZTOV(zp);
}
+ if (fuidp)
+ zfs_fuid_info_free(fuidp);
dmu_tx_commit(tx);
@@ -2782,6 +3502,7 @@ out:
* IN: vp - vnode of symbolic link.
* uoip - structure to contain the link path.
* cr - credentials of caller.
+ * ct - caller context
*
* OUT: uio - structure to contain the link path.
*
@@ -2793,7 +3514,7 @@ out:
*/
/* ARGSUSED */
static int
-zfs_readlink(vnode_t *vp, uio_t *uio, cred_t *cr)
+zfs_readlink(vnode_t *vp, uio_t *uio, cred_t *cr, caller_context_t *ct)
{
znode_t *zp = VTOZ(vp);
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
@@ -2801,6 +3522,7 @@ zfs_readlink(vnode_t *vp, uio_t *uio, cred_t *cr)
int error;
ZFS_ENTER(zfsvfs);
+ ZFS_VERIFY_ZP(zp);
bufsz = (size_t)zp->z_phys->zp_size;
if (bufsz + sizeof (znode_phys_t) <= zp->z_dbuf->db_size) {
@@ -2830,6 +3552,7 @@ zfs_readlink(vnode_t *vp, uio_t *uio, cred_t *cr)
* svp - vnode of new entry.
* name - name of new entry.
* cr - credentials of caller.
+ * ct - caller context
*
* RETURN: 0 if success
* error code if failure
@@ -2840,30 +3563,44 @@ zfs_readlink(vnode_t *vp, uio_t *uio, cred_t *cr)
*/
/* ARGSUSED */
static int
-zfs_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr)
+zfs_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr,
+ caller_context_t *ct, int flags)
{
znode_t *dzp = VTOZ(tdvp);
znode_t *tzp, *szp;
zfsvfs_t *zfsvfs = dzp->z_zfsvfs;
- zilog_t *zilog = zfsvfs->z_log;
+ zilog_t *zilog;
zfs_dirlock_t *dl;
dmu_tx_t *tx;
vnode_t *realvp;
int error;
+ int zf = ZNEW;
+ uid_t owner;
ASSERT(tdvp->v_type == VDIR);
ZFS_ENTER(zfsvfs);
+ ZFS_VERIFY_ZP(dzp);
+ zilog = zfsvfs->z_log;
- if (VOP_REALVP(svp, &realvp) == 0)
+ if (VOP_REALVP(svp, &realvp, ct) == 0)
svp = realvp;
if (svp->v_vfsp != tdvp->v_vfsp) {
ZFS_EXIT(zfsvfs);
return (EXDEV);
}
-
szp = VTOZ(svp);
+ ZFS_VERIFY_ZP(szp);
+
+ if (zfsvfs->z_utf8 && u8_validate(name,
+ strlen(name), NULL, U8_VALIDATE_ENTIRE, &error) < 0) {
+ ZFS_EXIT(zfsvfs);
+ return (EILSEQ);
+ }
+ if (flags & FIGNORECASE)
+ zf |= ZCILOOK;
+
top:
/*
* We do not support links between attributes and non-attributes
@@ -2886,13 +3623,14 @@ top:
return (EPERM);
}
- if ((uid_t)szp->z_phys->zp_uid != crgetuid(cr) &&
- secpolicy_basic_link(cr) != 0) {
+ owner = zfs_fuid_map_id(zfsvfs, szp->z_phys->zp_uid, cr, ZFS_OWNER);
+ if (owner != crgetuid(cr) &&
+ secpolicy_basic_link(svp, cr) != 0) {
ZFS_EXIT(zfsvfs);
return (EPERM);
}
- if (error = zfs_zaccess(dzp, ACE_ADD_FILE, cr)) {
+ if (error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr)) {
ZFS_EXIT(zfsvfs);
return (error);
}
@@ -2900,7 +3638,8 @@ top:
/*
* Attempt to lock directory; fail if entry already exists.
*/
- if (error = zfs_dirent_lock(&dl, dzp, name, &tzp, ZNEW)) {
+ error = zfs_dirent_lock(&dl, dzp, name, &tzp, zf, NULL, NULL);
+ if (error) {
ZFS_EXIT(zfsvfs);
return (error);
}
@@ -2923,40 +3662,45 @@ top:
error = zfs_link_create(dl, szp, tx, 0);
- if (error == 0)
- zfs_log_link(zilog, tx, TX_LINK, dzp, szp, name);
+ if (error == 0) {
+ uint64_t txtype = TX_LINK;
+ if (flags & FIGNORECASE)
+ txtype |= TX_CI;
+ zfs_log_link(zilog, tx, txtype, dzp, szp, name);
+ }
dmu_tx_commit(tx);
zfs_dirent_unlock(dl);
+ if (error == 0) {
+ vnevent_link(svp, ct);
+ }
+
ZFS_EXIT(zfsvfs);
return (error);
}
+/*ARGSUSED*/
void
-zfs_inactive(vnode_t *vp, cred_t *cr)
+zfs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
{
znode_t *zp = VTOZ(vp);
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
int error;
- rw_enter(&zfsvfs->z_um_lock, RW_READER);
- if (zfsvfs->z_unmounted2) {
- ASSERT(zp->z_dbuf_held == 0);
-
+ rw_enter(&zfsvfs->z_teardown_inactive_lock, RW_READER);
+ if (zp->z_dbuf == NULL) {
+ /*
+ * The fs has been unmounted, or we did a
+ * suspend/resume and this file no longer exists.
+ */
mutex_enter(&zp->z_lock);
VI_LOCK(vp);
vp->v_count = 0; /* count arrives as 1 */
- VI_UNLOCK(vp);
- if (zp->z_dbuf == NULL) {
- mutex_exit(&zp->z_lock);
- zfs_znode_free(zp);
- } else {
- mutex_exit(&zp->z_lock);
- }
- rw_exit(&zfsvfs->z_um_lock);
- VFS_RELE(zfsvfs->z_vfs);
+ mutex_exit(&zp->z_lock);
+ rw_exit(&zfsvfs->z_teardown_inactive_lock);
+ zfs_znode_free(zp);
return;
}
@@ -2977,23 +3721,26 @@ zfs_inactive(vnode_t *vp, cred_t *cr)
}
zfs_zinactive(zp);
- rw_exit(&zfsvfs->z_um_lock);
+ rw_exit(&zfsvfs->z_teardown_inactive_lock);
}
CTASSERT(sizeof(struct zfid_short) <= sizeof(struct fid));
CTASSERT(sizeof(struct zfid_long) <= sizeof(struct fid));
+/*ARGSUSED*/
static int
-zfs_fid(vnode_t *vp, fid_t *fidp)
+zfs_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
{
znode_t *zp = VTOZ(vp);
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
- uint32_t gen = (uint32_t)zp->z_phys->zp_gen;
+ uint32_t gen;
uint64_t object = zp->z_id;
zfid_short_t *zfid;
int size, i;
ZFS_ENTER(zfsvfs);
+ ZFS_VERIFY_ZP(zp);
+ gen = (uint32_t)zp->z_gen;
size = (zfsvfs->z_parent != zfsvfs) ? LONG_FID_LEN : SHORT_FID_LEN;
fidp->fid_len = size;
@@ -3030,7 +3777,8 @@ zfs_fid(vnode_t *vp, fid_t *fidp)
}
static int
-zfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr)
+zfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
+ caller_context_t *ct)
{
znode_t *zp, *xzp;
zfsvfs_t *zfsvfs;
@@ -3051,9 +3799,10 @@ zfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr)
zp = VTOZ(vp);
zfsvfs = zp->z_zfsvfs;
ZFS_ENTER(zfsvfs);
+ ZFS_VERIFY_ZP(zp);
*valp = 0;
error = zfs_dirent_lock(&dl, zp, "", &xzp,
- ZXATTR | ZEXISTS | ZSHARED);
+ ZXATTR | ZEXISTS | ZSHARED, NULL, NULL);
if (error == 0) {
zfs_dirent_unlock(dl);
if (!zfs_dirempty(xzp))
@@ -3086,14 +3835,17 @@ zfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr)
#ifdef TODO
/*ARGSUSED*/
static int
-zfs_getsecattr(vnode_t *vp, vsecattr_t *vsecp, int flag, cred_t *cr)
+zfs_getsecattr(vnode_t *vp, vsecattr_t *vsecp, int flag, cred_t *cr,
+ caller_context_t *ct)
{
znode_t *zp = VTOZ(vp);
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
int error;
+ boolean_t skipaclchk = (flag & ATTR_NOACLCHECK) ? B_TRUE : B_FALSE;
ZFS_ENTER(zfsvfs);
- error = zfs_getacl(zp, vsecp, cr);
+ ZFS_VERIFY_ZP(zp);
+ error = zfs_getacl(zp, vsecp, skipaclchk, cr);
ZFS_EXIT(zfsvfs);
return (error);
@@ -3103,14 +3855,17 @@ zfs_getsecattr(vnode_t *vp, vsecattr_t *vsecp, int flag, cred_t *cr)
#ifdef TODO
/*ARGSUSED*/
static int
-zfs_setsecattr(vnode_t *vp, vsecattr_t *vsecp, int flag, cred_t *cr)
+zfs_setsecattr(vnode_t *vp, vsecattr_t *vsecp, int flag, cred_t *cr,
+ caller_context_t *ct)
{
znode_t *zp = VTOZ(vp);
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
int error;
+ boolean_t skipaclchk = (flag & ATTR_NOACLCHECK) ? B_TRUE : B_FALSE;
ZFS_ENTER(zfsvfs);
- error = zfs_setacl(zp, vsecp, cr);
+ ZFS_VERIFY_ZP(zp);
+ error = zfs_setacl(zp, vsecp, skipaclchk, cr);
ZFS_EXIT(zfsvfs);
return (error);
}
@@ -3129,7 +3884,7 @@ zfs_freebsd_open(ap)
znode_t *zp = VTOZ(vp);
int error;
- error = zfs_open(&vp, ap->a_mode, ap->a_cred);
+ error = zfs_open(&vp, ap->a_mode, ap->a_cred, NULL);
if (error == 0)
vnode_create_vobject(vp, zp->z_phys->zp_size, ap->a_td);
return (error);
@@ -3145,7 +3900,7 @@ zfs_freebsd_close(ap)
} */ *ap;
{
- return (zfs_close(ap->a_vp, ap->a_fflag, 0, 0, ap->a_cred));
+ return (zfs_close(ap->a_vp, ap->a_fflag, 0, 0, ap->a_cred, NULL));
}
static int
@@ -3161,7 +3916,7 @@ zfs_freebsd_ioctl(ap)
{
return (zfs_ioctl(ap->a_vp, ap->a_command, (intptr_t)ap->a_data,
- ap->a_fflag, ap->a_cred, NULL));
+ ap->a_fflag, ap->a_cred, NULL, NULL));
}
static int
@@ -3194,13 +3949,13 @@ static int
zfs_freebsd_access(ap)
struct vop_access_args /* {
struct vnode *a_vp;
- accmode_t a_accmode;
+ int a_accmode;
struct ucred *a_cred;
struct thread *a_td;
} */ *ap;
{
- return (zfs_access(ap->a_vp, ap->a_accmode, 0, ap->a_cred));
+ return (zfs_access(ap->a_vp, ap->a_accmode, 0, ap->a_cred, NULL));
}
static int
@@ -3218,7 +3973,7 @@ zfs_freebsd_lookup(ap)
strlcpy(nm, cnp->cn_nameptr, MIN(cnp->cn_namelen + 1, sizeof(nm)));
return (zfs_lookup(ap->a_dvp, nm, ap->a_vpp, cnp, cnp->cn_nameiop,
- cnp->cn_cred, cnp->cn_thread));
+ cnp->cn_cred, cnp->cn_thread, 0));
}
static int
@@ -3240,7 +3995,7 @@ zfs_freebsd_create(ap)
mode = vap->va_mode & ALLPERMS;
return (zfs_create(ap->a_dvp, cnp->cn_nameptr, vap, !EXCL, mode,
- ap->a_vpp, cnp->cn_cred));
+ ap->a_vpp, cnp->cn_cred, cnp->cn_thread));
}
static int
@@ -3255,7 +4010,7 @@ zfs_freebsd_remove(ap)
ASSERT(ap->a_cnp->cn_flags & SAVENAME);
return (zfs_remove(ap->a_dvp, ap->a_cnp->cn_nameptr,
- ap->a_cnp->cn_cred));
+ ap->a_cnp->cn_cred, NULL, 0));
}
static int
@@ -3274,7 +4029,7 @@ zfs_freebsd_mkdir(ap)
vattr_init_mask(vap);
return (zfs_mkdir(ap->a_dvp, ap->a_cnp->cn_nameptr, vap, ap->a_vpp,
- ap->a_cnp->cn_cred));
+ ap->a_cnp->cn_cred, NULL, 0, NULL));
}
static int
@@ -3289,7 +4044,7 @@ zfs_freebsd_rmdir(ap)
ASSERT(cnp->cn_flags & SAVENAME);
- return (zfs_rmdir(ap->a_dvp, cnp->cn_nameptr, NULL, cnp->cn_cred));
+ return (zfs_rmdir(ap->a_dvp, cnp->cn_nameptr, NULL, cnp->cn_cred, NULL, 0));
}
static int
@@ -3318,7 +4073,7 @@ zfs_freebsd_fsync(ap)
{
vop_stdfsync(ap);
- return (zfs_fsync(ap->a_vp, 0, ap->a_td->td_ucred));
+ return (zfs_fsync(ap->a_vp, 0, ap->a_td->td_ucred, NULL));
}
static int
@@ -3327,10 +4082,45 @@ zfs_freebsd_getattr(ap)
struct vnode *a_vp;
struct vattr *a_vap;
struct ucred *a_cred;
+ struct thread *a_td;
} */ *ap;
{
+ vattr_t *vap = ap->a_vap;
+ xvattr_t xvap;
+ u_long fflags = 0;
+ int error;
+
+ xva_init(&xvap);
+ xvap.xva_vattr = *vap;
+ xvap.xva_vattr.va_mask |= AT_XVATTR;
+
+ /* Convert chflags into ZFS-type flags. */
+ /* XXX: what about SF_SETTABLE?. */
+ XVA_SET_REQ(&xvap, XAT_IMMUTABLE);
+ XVA_SET_REQ(&xvap, XAT_APPENDONLY);
+ XVA_SET_REQ(&xvap, XAT_NOUNLINK);
+ XVA_SET_REQ(&xvap, XAT_NODUMP);
+ error = zfs_getattr(ap->a_vp, (vattr_t *)&xvap, 0, ap->a_cred, NULL);
+ if (error != 0)
+ return (error);
- return (zfs_getattr(ap->a_vp, ap->a_vap, 0, ap->a_cred));
+ /* Convert ZFS xattr into chflags. */
+#define FLAG_CHECK(fflag, xflag, xfield) do { \
+ if (XVA_ISSET_RTN(&xvap, (xflag)) && (xfield) != 0) \
+ fflags |= (fflag); \
+} while (0)
+ FLAG_CHECK(SF_IMMUTABLE, XAT_IMMUTABLE,
+ xvap.xva_xoptattrs.xoa_immutable);
+ FLAG_CHECK(SF_APPEND, XAT_APPENDONLY,
+ xvap.xva_xoptattrs.xoa_appendonly);
+ FLAG_CHECK(SF_NOUNLINK, XAT_NOUNLINK,
+ xvap.xva_xoptattrs.xoa_nounlink);
+ FLAG_CHECK(UF_NODUMP, XAT_NODUMP,
+ xvap.xva_xoptattrs.xoa_nodump);
+#undef FLAG_CHECK
+ *vap = xvap.xva_vattr;
+ vap->va_flags = fflags;
+ return (0);
}
static int
@@ -3339,18 +4129,46 @@ zfs_freebsd_setattr(ap)
struct vnode *a_vp;
struct vattr *a_vap;
struct ucred *a_cred;
+ struct thread *a_td;
} */ *ap;
{
vattr_t *vap = ap->a_vap;
-
- /* No support for FreeBSD's chflags(2). */
- if (vap->va_flags != VNOVAL)
- return (EOPNOTSUPP);
+ xvattr_t xvap;
+ u_long fflags;
+ uint64_t zflags;
vattr_init_mask(vap);
vap->va_mask &= ~AT_NOSET;
- return (zfs_setattr(ap->a_vp, vap, 0, ap->a_cred, NULL));
+ xva_init(&xvap);
+ xvap.xva_vattr = *vap;
+
+ if (vap->va_flags != VNOVAL) {
+ fflags = vap->va_flags;
+ if ((fflags & ~(SF_IMMUTABLE|SF_APPEND|SF_NOUNLINK|UF_NODUMP)) != 0)
+ return (EOPNOTSUPP);
+ zflags = VTOZ(ap->a_vp)->z_phys->zp_flags;
+
+#define FLAG_CHANGE(fflag, zflag, xflag, xfield) do { \
+ if (((fflags & (fflag)) && !(zflags & (zflag))) || \
+ ((zflags & (zflag)) && !(fflags & (fflag)))) { \
+ XVA_SET_REQ(&xvap, (xflag)); \
+ (xfield) = ((fflags & (fflag)) != 0); \
+ } \
+} while (0)
+ /* Convert chflags into ZFS-type flags. */
+ /* XXX: what about SF_SETTABLE?. */
+ FLAG_CHANGE(SF_IMMUTABLE, ZFS_IMMUTABLE, XAT_IMMUTABLE,
+ xvap.xva_xoptattrs.xoa_immutable);
+ FLAG_CHANGE(SF_APPEND, ZFS_APPENDONLY, XAT_APPENDONLY,
+ xvap.xva_xoptattrs.xoa_appendonly);
+ FLAG_CHANGE(SF_NOUNLINK, ZFS_NOUNLINK, XAT_NOUNLINK,
+ xvap.xva_xoptattrs.xoa_nounlink);
+ FLAG_CHANGE(UF_NODUMP, ZFS_NODUMP, XAT_NODUMP,
+ xvap.xva_xoptattrs.xoa_nounlink);
+#undef FLAG_CHANGE
+ }
+ return (zfs_setattr(ap->a_vp, (vattr_t *)&xvap, 0, ap->a_cred, NULL));
}
static int
@@ -3374,7 +4192,7 @@ zfs_freebsd_rename(ap)
ASSERT(ap->a_tcnp->cn_flags & SAVENAME);
error = zfs_rename(fdvp, ap->a_fcnp->cn_nameptr, tdvp,
- ap->a_tcnp->cn_nameptr, ap->a_fcnp->cn_cred);
+ ap->a_tcnp->cn_nameptr, ap->a_fcnp->cn_cred, NULL, 0);
if (tdvp == tvp)
VN_RELE(tdvp);
@@ -3419,7 +4237,7 @@ zfs_freebsd_readlink(ap)
} */ *ap;
{
- return (zfs_readlink(ap->a_vp, ap->a_uio, ap->a_cred));
+ return (zfs_readlink(ap->a_vp, ap->a_uio, ap->a_cred, NULL));
}
static int
@@ -3434,7 +4252,7 @@ zfs_freebsd_link(ap)
ASSERT(cnp->cn_flags & SAVENAME);
- return (zfs_link(ap->a_tdvp, ap->a_vp, cnp->cn_nameptr, cnp->cn_cred));
+ return (zfs_link(ap->a_tdvp, ap->a_vp, cnp->cn_nameptr, cnp->cn_cred, NULL, 0));
}
static int
@@ -3446,10 +4264,23 @@ zfs_freebsd_inactive(ap)
{
vnode_t *vp = ap->a_vp;
- zfs_inactive(vp, ap->a_td->td_ucred);
+ zfs_inactive(vp, ap->a_td->td_ucred, NULL);
return (0);
}
+static void
+zfs_reclaim_complete(void *arg, int pending)
+{
+ znode_t *zp = arg;
+ zfsvfs_t *zfsvfs = zp->z_zfsvfs;
+
+ ZFS_LOG(1, "zp=%p", zp);
+ ZFS_OBJ_HOLD_ENTER(zfsvfs, zp->z_id);
+ zfs_znode_dmu_fini(zp);
+ ZFS_OBJ_HOLD_EXIT(zfsvfs, zp->z_id);
+ zfs_znode_free(zp);
+}
+
static int
zfs_freebsd_reclaim(ap)
struct vop_reclaim_args /* {
@@ -3460,7 +4291,6 @@ zfs_freebsd_reclaim(ap)
vnode_t *vp = ap->a_vp;
znode_t *zp = VTOZ(vp);
zfsvfs_t *zfsvfs;
- int rele = 1;
ASSERT(zp != NULL);
@@ -3471,24 +4301,34 @@ zfs_freebsd_reclaim(ap)
mutex_enter(&zp->z_lock);
ASSERT(zp->z_phys);
- ASSERT(zp->z_dbuf_held);
- zfsvfs = zp->z_zfsvfs;
+ ZTOV(zp) = NULL;
if (!zp->z_unlinked) {
- zp->z_dbuf_held = 0;
- ZTOV(zp) = NULL;
+ int locked;
+
+ zfsvfs = zp->z_zfsvfs;
mutex_exit(&zp->z_lock);
- dmu_buf_rele(zp->z_dbuf, NULL);
+ locked = MUTEX_HELD(ZFS_OBJ_MUTEX(zfsvfs, zp->z_id)) ? 2 :
+ ZFS_OBJ_HOLD_TRYENTER(zfsvfs, zp->z_id);
+ if (locked == 0) {
+ /*
+ * Lock can't be obtained due to deadlock possibility,
+ * so defer znode destruction.
+ */
+ TASK_INIT(&zp->z_task, 0, zfs_reclaim_complete, zp);
+ taskqueue_enqueue(taskqueue_thread, &zp->z_task);
+ } else {
+ zfs_znode_dmu_fini(zp);
+ if (locked == 1)
+ ZFS_OBJ_HOLD_EXIT(zfsvfs, zp->z_id);
+ zfs_znode_free(zp);
+ }
} else {
mutex_exit(&zp->z_lock);
}
VI_LOCK(vp);
- if (vp->v_count > 0)
- rele = 0;
vp->v_data = NULL;
ASSERT(vp->v_holdcnt >= 1);
VI_UNLOCK(vp);
- if (!zp->z_unlinked && rele)
- VFS_RELE(zfsvfs->z_vfs);
return (0);
}
@@ -3500,7 +4340,7 @@ zfs_freebsd_fid(ap)
} */ *ap;
{
- return (zfs_fid(ap->a_vp, (void *)ap->a_fid));
+ return (zfs_fid(ap->a_vp, (void *)ap->a_fid, NULL));
}
static int
@@ -3514,7 +4354,7 @@ zfs_freebsd_pathconf(ap)
ulong_t val;
int error;
- error = zfs_pathconf(ap->a_vp, ap->a_name, &val, curthread->td_ucred);
+ error = zfs_pathconf(ap->a_vp, ap->a_name, &val, curthread->td_ucred, NULL);
if (error == 0)
*ap->a_retval = val;
else if (error == EOPNOTSUPP)
@@ -3522,52 +4362,408 @@ zfs_freebsd_pathconf(ap)
return (error);
}
+/*
+ * FreeBSD's extended attributes namespace defines file name prefix for ZFS'
+ * extended attribute name:
+ *
+ * NAMESPACE PREFIX
+ * system freebsd:system:
+ * user (none, can be used to access ZFS fsattr(5) attributes
+ * created on Solaris)
+ */
+static int
+zfs_create_attrname(int attrnamespace, const char *name, char *attrname,
+ size_t size)
+{
+ const char *namespace, *prefix, *suffix;
+
+ /* We don't allow '/' character in attribute name. */
+ if (strchr(name, '/') != NULL)
+ return (EINVAL);
+ /* We don't allow attribute names that start with "freebsd:" string. */
+ if (strncmp(name, "freebsd:", 8) == 0)
+ return (EINVAL);
+
+ bzero(attrname, size);
+
+ switch (attrnamespace) {
+ case EXTATTR_NAMESPACE_USER:
+#if 0
+ prefix = "freebsd:";
+ namespace = EXTATTR_NAMESPACE_USER_STRING;
+ suffix = ":";
+#else
+ /*
+ * This is the default namespace by which we can access all
+ * attributes created on Solaris.
+ */
+ prefix = namespace = suffix = "";
+#endif
+ break;
+ case EXTATTR_NAMESPACE_SYSTEM:
+ prefix = "freebsd:";
+ namespace = EXTATTR_NAMESPACE_SYSTEM_STRING;
+ suffix = ":";
+ break;
+ case EXTATTR_NAMESPACE_EMPTY:
+ default:
+ return (EINVAL);
+ }
+ if (snprintf(attrname, size, "%s%s%s%s", prefix, namespace, suffix,
+ name) >= size) {
+ return (ENAMETOOLONG);
+ }
+ return (0);
+}
+
+/*
+ * Vnode operating to retrieve a named extended attribute.
+ */
+static int
+zfs_getextattr(struct vop_getextattr_args *ap)
+/*
+vop_getextattr {
+ IN struct vnode *a_vp;
+ IN int a_attrnamespace;
+ IN const char *a_name;
+ INOUT struct uio *a_uio;
+ OUT size_t *a_size;
+ IN struct ucred *a_cred;
+ IN struct thread *a_td;
+};
+*/
+{
+ zfsvfs_t *zfsvfs = VTOZ(ap->a_vp)->z_zfsvfs;
+ struct thread *td = ap->a_td;
+ struct nameidata nd;
+ char attrname[255];
+ struct vattr va;
+ vnode_t *xvp = NULL, *vp;
+ int error, flags;
+
+ error = zfs_create_attrname(ap->a_attrnamespace, ap->a_name, attrname,
+ sizeof(attrname));
+ if (error != 0)
+ return (error);
+
+ ZFS_ENTER(zfsvfs);
+
+ error = zfs_lookup(ap->a_vp, NULL, &xvp, NULL, 0, ap->a_cred, td,
+ LOOKUP_XATTR);
+ if (error != 0) {
+ ZFS_EXIT(zfsvfs);
+ return (error);
+ }
+
+ flags = FREAD;
+ NDINIT_ATVP(&nd, LOOKUP, NOFOLLOW | MPSAFE, UIO_SYSSPACE, attrname,
+ xvp, td);
+ error = vn_open_cred(&nd, &flags, 0, ap->a_cred, NULL);
+ vp = nd.ni_vp;
+ NDFREE(&nd, NDF_ONLY_PNBUF);
+ if (error != 0) {
+ ZFS_EXIT(zfsvfs);
+ return (error);
+ }
+
+ if (ap->a_size != NULL) {
+ error = VOP_GETATTR(vp, &va, ap->a_cred);
+ if (error == 0)
+ *ap->a_size = (size_t)va.va_size;
+ } else if (ap->a_uio != NULL)
+ error = VOP_READ(vp, ap->a_uio, IO_UNIT | IO_SYNC, ap->a_cred);
+
+ VOP_UNLOCK(vp, 0);
+ vn_close(vp, flags, ap->a_cred, td);
+ ZFS_EXIT(zfsvfs);
+
+ return (error);
+}
+
+/*
+ * Vnode operation to remove a named attribute.
+ */
+int
+zfs_deleteextattr(struct vop_deleteextattr_args *ap)
+/*
+vop_deleteextattr {
+ IN struct vnode *a_vp;
+ IN int a_attrnamespace;
+ IN const char *a_name;
+ IN struct ucred *a_cred;
+ IN struct thread *a_td;
+};
+*/
+{
+ zfsvfs_t *zfsvfs = VTOZ(ap->a_vp)->z_zfsvfs;
+ struct thread *td = ap->a_td;
+ struct nameidata nd;
+ char attrname[255];
+ struct vattr va;
+ vnode_t *xvp = NULL, *vp;
+ int error, flags;
+
+ error = zfs_create_attrname(ap->a_attrnamespace, ap->a_name, attrname,
+ sizeof(attrname));
+ if (error != 0)
+ return (error);
+
+ ZFS_ENTER(zfsvfs);
+
+ error = zfs_lookup(ap->a_vp, NULL, &xvp, NULL, 0, ap->a_cred, td,
+ LOOKUP_XATTR);
+ if (error != 0) {
+ ZFS_EXIT(zfsvfs);
+ return (error);
+ }
+
+ NDINIT_ATVP(&nd, DELETE, NOFOLLOW | LOCKPARENT | LOCKLEAF | MPSAFE,
+ UIO_SYSSPACE, attrname, xvp, td);
+ error = namei(&nd);
+ vp = nd.ni_vp;
+ NDFREE(&nd, NDF_ONLY_PNBUF);
+ if (error != 0) {
+ ZFS_EXIT(zfsvfs);
+ return (error);
+ }
+ VOP_LEASE(nd.ni_dvp, td, ap->a_cred, LEASE_WRITE);
+ error = VOP_REMOVE(nd.ni_dvp, vp, &nd.ni_cnd);
+
+ vput(nd.ni_dvp);
+ if (vp == nd.ni_dvp)
+ vrele(vp);
+ else
+ vput(vp);
+ ZFS_EXIT(zfsvfs);
+
+ return (error);
+}
+
+/*
+ * Vnode operation to set a named attribute.
+ */
+static int
+zfs_setextattr(struct vop_setextattr_args *ap)
+/*
+vop_setextattr {
+ IN struct vnode *a_vp;
+ IN int a_attrnamespace;
+ IN const char *a_name;
+ INOUT struct uio *a_uio;
+ IN struct ucred *a_cred;
+ IN struct thread *a_td;
+};
+*/
+{
+ zfsvfs_t *zfsvfs = VTOZ(ap->a_vp)->z_zfsvfs;
+ struct thread *td = ap->a_td;
+ struct nameidata nd;
+ char attrname[255];
+ struct vattr va;
+ vnode_t *xvp = NULL, *vp;
+ int error, flags;
+
+ error = zfs_create_attrname(ap->a_attrnamespace, ap->a_name, attrname,
+ sizeof(attrname));
+ if (error != 0)
+ return (error);
+
+ ZFS_ENTER(zfsvfs);
+
+ error = zfs_lookup(ap->a_vp, NULL, &xvp, NULL, 0, ap->a_cred, td,
+ LOOKUP_XATTR);
+ if (error != 0) {
+ ZFS_EXIT(zfsvfs);
+ return (error);
+ }
+
+ flags = FFLAGS(O_WRONLY | O_CREAT);
+ NDINIT_ATVP(&nd, LOOKUP, NOFOLLOW | MPSAFE, UIO_SYSSPACE, attrname,
+ xvp, td);
+ error = vn_open_cred(&nd, &flags, 0600, ap->a_cred, NULL);
+ vp = nd.ni_vp;
+ NDFREE(&nd, NDF_ONLY_PNBUF);
+ if (error != 0) {
+ ZFS_EXIT(zfsvfs);
+ return (error);
+ }
+
+ VOP_LEASE(vp, td, ap->a_cred, LEASE_WRITE);
+ VATTR_NULL(&va);
+ va.va_size = 0;
+ error = VOP_SETATTR(vp, &va, ap->a_cred);
+ if (error == 0)
+ VOP_WRITE(vp, ap->a_uio, IO_UNIT | IO_SYNC, ap->a_cred);
+
+ VOP_UNLOCK(vp, 0);
+ vn_close(vp, flags, ap->a_cred, td);
+ ZFS_EXIT(zfsvfs);
+
+ return (error);
+}
+
+/*
+ * Vnode operation to retrieve extended attributes on a vnode.
+ */
+static int
+zfs_listextattr(struct vop_listextattr_args *ap)
+/*
+vop_listextattr {
+ IN struct vnode *a_vp;
+ IN int a_attrnamespace;
+ INOUT struct uio *a_uio;
+ OUT size_t *a_size;
+ IN struct ucred *a_cred;
+ IN struct thread *a_td;
+};
+*/
+{
+ zfsvfs_t *zfsvfs = VTOZ(ap->a_vp)->z_zfsvfs;
+ struct thread *td = ap->a_td;
+ struct nameidata nd;
+ char attrprefix[16];
+ u_char dirbuf[sizeof(struct dirent)];
+ struct dirent *dp;
+ struct iovec aiov;
+ struct uio auio, *uio = ap->a_uio;
+ size_t *sizep = ap->a_size;
+ size_t plen;
+ vnode_t *xvp = NULL, *vp;
+ int done, error, eof, pos;
+
+ error = zfs_create_attrname(ap->a_attrnamespace, "", attrprefix,
+ sizeof(attrprefix));
+ if (error != 0)
+ return (error);
+ plen = strlen(attrprefix);
+
+ ZFS_ENTER(zfsvfs);
+
+ error = zfs_lookup(ap->a_vp, NULL, &xvp, NULL, 0, ap->a_cred, td,
+ LOOKUP_XATTR);
+ if (error != 0) {
+ ZFS_EXIT(zfsvfs);
+ return (error);
+ }
+
+ NDINIT_ATVP(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | MPSAFE, UIO_SYSSPACE,
+ ".", xvp, td);
+ error = namei(&nd);
+ vp = nd.ni_vp;
+ NDFREE(&nd, NDF_ONLY_PNBUF);
+ if (error != 0) {
+ ZFS_EXIT(zfsvfs);
+ return (error);
+ }
+
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_td = td;
+ auio.uio_rw = UIO_READ;
+ auio.uio_offset = 0;
+
+ if (sizep != NULL)
+ *sizep = 0;
+
+ do {
+ u_char nlen;
+
+ aiov.iov_base = (void *)dirbuf;
+ aiov.iov_len = sizeof(dirbuf);
+ auio.uio_resid = sizeof(dirbuf);
+ error = VOP_READDIR(vp, &auio, ap->a_cred, &eof, NULL, NULL);
+ done = sizeof(dirbuf) - auio.uio_resid;
+ if (error != 0)
+ break;
+ for (pos = 0; pos < done;) {
+ dp = (struct dirent *)(dirbuf + pos);
+ pos += dp->d_reclen;
+ /*
+ * XXX: Temporarily we also accept DT_UNKNOWN, as this
+ * is what we get when attribute was created on Solaris.
+ */
+ if (dp->d_type != DT_REG && dp->d_type != DT_UNKNOWN)
+ continue;
+ if (plen == 0 && strncmp(dp->d_name, "freebsd:", 8) == 0)
+ continue;
+ else if (strncmp(dp->d_name, attrprefix, plen) != 0)
+ continue;
+ nlen = dp->d_namlen - plen;
+ if (sizep != NULL)
+ *sizep += 1 + nlen;
+ else if (uio != NULL) {
+ /*
+ * Format of extattr name entry is one byte for
+ * length and the rest for name.
+ */
+ error = uiomove(&nlen, 1, uio->uio_rw, uio);
+ if (error == 0) {
+ error = uiomove(dp->d_name + plen, nlen,
+ uio->uio_rw, uio);
+ }
+ if (error != 0)
+ break;
+ }
+ }
+ } while (!eof && error == 0);
+
+ vput(vp);
+ ZFS_EXIT(zfsvfs);
+
+ return (error);
+}
+
struct vop_vector zfs_vnodeops;
struct vop_vector zfs_fifoops;
struct vop_vector zfs_vnodeops = {
- .vop_default = &default_vnodeops,
- .vop_inactive = zfs_freebsd_inactive,
- .vop_reclaim = zfs_freebsd_reclaim,
- .vop_access = zfs_freebsd_access,
+ .vop_default = &default_vnodeops,
+ .vop_inactive = zfs_freebsd_inactive,
+ .vop_reclaim = zfs_freebsd_reclaim,
+ .vop_access = zfs_freebsd_access,
#ifdef FREEBSD_NAMECACHE
- .vop_lookup = vfs_cache_lookup,
- .vop_cachedlookup = zfs_freebsd_lookup,
+ .vop_lookup = vfs_cache_lookup,
+ .vop_cachedlookup = zfs_freebsd_lookup,
#else
- .vop_lookup = zfs_freebsd_lookup,
+ .vop_lookup = zfs_freebsd_lookup,
#endif
- .vop_getattr = zfs_freebsd_getattr,
- .vop_setattr = zfs_freebsd_setattr,
- .vop_create = zfs_freebsd_create,
- .vop_mknod = zfs_freebsd_create,
- .vop_mkdir = zfs_freebsd_mkdir,
- .vop_readdir = zfs_freebsd_readdir,
- .vop_fsync = zfs_freebsd_fsync,
- .vop_open = zfs_freebsd_open,
- .vop_close = zfs_freebsd_close,
- .vop_rmdir = zfs_freebsd_rmdir,
- .vop_ioctl = zfs_freebsd_ioctl,
- .vop_link = zfs_freebsd_link,
- .vop_symlink = zfs_freebsd_symlink,
- .vop_readlink = zfs_freebsd_readlink,
- .vop_read = zfs_freebsd_read,
- .vop_write = zfs_freebsd_write,
- .vop_remove = zfs_freebsd_remove,
- .vop_rename = zfs_freebsd_rename,
- .vop_pathconf = zfs_freebsd_pathconf,
- .vop_bmap = VOP_EOPNOTSUPP,
- .vop_fid = zfs_freebsd_fid,
+ .vop_getattr = zfs_freebsd_getattr,
+ .vop_setattr = zfs_freebsd_setattr,
+ .vop_create = zfs_freebsd_create,
+ .vop_mknod = zfs_freebsd_create,
+ .vop_mkdir = zfs_freebsd_mkdir,
+ .vop_readdir = zfs_freebsd_readdir,
+ .vop_fsync = zfs_freebsd_fsync,
+ .vop_open = zfs_freebsd_open,
+ .vop_close = zfs_freebsd_close,
+ .vop_rmdir = zfs_freebsd_rmdir,
+ .vop_ioctl = zfs_freebsd_ioctl,
+ .vop_link = zfs_freebsd_link,
+ .vop_symlink = zfs_freebsd_symlink,
+ .vop_readlink = zfs_freebsd_readlink,
+ .vop_read = zfs_freebsd_read,
+ .vop_write = zfs_freebsd_write,
+ .vop_remove = zfs_freebsd_remove,
+ .vop_rename = zfs_freebsd_rename,
+ .vop_pathconf = zfs_freebsd_pathconf,
+ .vop_bmap = VOP_EOPNOTSUPP,
+ .vop_fid = zfs_freebsd_fid,
+ .vop_getextattr = zfs_getextattr,
+ .vop_deleteextattr = zfs_deleteextattr,
+ .vop_setextattr = zfs_setextattr,
+ .vop_listextattr = zfs_listextattr,
};
struct vop_vector zfs_fifoops = {
- .vop_default = &fifo_specops,
- .vop_fsync = VOP_PANIC,
- .vop_access = zfs_freebsd_access,
- .vop_getattr = zfs_freebsd_getattr,
- .vop_inactive = zfs_freebsd_inactive,
- .vop_read = VOP_PANIC,
- .vop_reclaim = zfs_freebsd_reclaim,
- .vop_setattr = zfs_freebsd_setattr,
- .vop_write = VOP_PANIC,
- .vop_fid = zfs_freebsd_fid,
+ .vop_default = &fifo_specops,
+ .vop_fsync = VOP_PANIC,
+ .vop_access = zfs_freebsd_access,
+ .vop_getattr = zfs_freebsd_getattr,
+ .vop_inactive = zfs_freebsd_inactive,
+ .vop_read = VOP_PANIC,
+ .vop_reclaim = zfs_freebsd_reclaim,
+ .vop_setattr = zfs_freebsd_setattr,
+ .vop_write = VOP_PANIC,
+ .vop_fid = zfs_freebsd_fid,
};
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_znode.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_znode.c
index a964ec2..86838df 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_znode.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_znode.c
@@ -19,14 +19,12 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Portions Copyright 2007 Jeremy Teo */
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef _KERNEL
#include <sys/types.h>
#include <sys/param.h>
@@ -35,11 +33,12 @@
#include <sys/sysmacros.h>
#include <sys/resource.h>
#include <sys/mntent.h>
+#include <sys/u8_textprep.h>
+#include <sys/dsl_dataset.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/file.h>
#include <sys/kmem.h>
-#include <sys/cmn_err.h>
#include <sys/errno.h>
#include <sys/unistd.h>
#include <sys/atomic.h>
@@ -47,7 +46,9 @@
#include <sys/zfs_acl.h>
#include <sys/zfs_ioctl.h>
#include <sys/zfs_rlock.h>
+#include <sys/zfs_fuid.h>
#include <sys/fs/zfs.h>
+#include <sys/kidmap.h>
#endif /* _KERNEL */
#include <sys/dmu.h>
@@ -57,26 +58,53 @@
#include <sys/zfs_znode.h>
#include <sys/refcount.h>
+#include "zfs_prop.h"
+
/* Used by fstat(1). */
SYSCTL_INT(_debug_sizeof, OID_AUTO, znode, CTLFLAG_RD, 0, sizeof(znode_t),
"sizeof(znode_t)");
/*
+ * Define ZNODE_STATS to turn on statistic gathering. By default, it is only
+ * turned on when DEBUG is also defined.
+ */
+#ifdef DEBUG
+#define ZNODE_STATS
+#endif /* DEBUG */
+
+#ifdef ZNODE_STATS
+#define ZNODE_STAT_ADD(stat) ((stat)++)
+#else
+#define ZNODE_STAT_ADD(stat) /* nothing */
+#endif /* ZNODE_STATS */
+
+#define POINTER_IS_VALID(p) (!((uintptr_t)(p) & 0x3))
+#define POINTER_INVALIDATE(pp) (*(pp) = (void *)((uintptr_t)(*(pp)) | 0x1))
+
+/*
* Functions needed for userland (ie: libzpool) are not put under
* #ifdef_KERNEL; the rest of the functions have dependencies
* (such as VFS logic) that will not compile easily in userland.
*/
#ifdef _KERNEL
-struct kmem_cache *znode_cache = NULL;
+static kmem_cache_t *znode_cache = NULL;
/*ARGSUSED*/
static void
-znode_pageout_func(dmu_buf_t *dbuf, void *user_ptr)
+znode_evict_error(dmu_buf_t *dbuf, void *user_ptr)
{
+#if 1 /* XXXPJD: From OpenSolaris. */
+ /*
+ * We should never drop all dbuf refs without first clearing
+ * the eviction callback.
+ */
+ panic("evicting znode %p\n", user_ptr);
+#else /* XXXPJD */
znode_t *zp = user_ptr;
vnode_t *vp;
mutex_enter(&zp->z_lock);
+ zp->z_dbuf = NULL;
vp = ZTOV(zp);
if (vp == NULL) {
mutex_exit(&zp->z_lock);
@@ -85,16 +113,15 @@ znode_pageout_func(dmu_buf_t *dbuf, void *user_ptr)
ZTOV(zp) = NULL;
vhold(vp);
mutex_exit(&zp->z_lock);
- vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, curthread);
vrecycle(vp, curthread);
VOP_UNLOCK(vp, 0);
vdrop(vp);
zfs_znode_free(zp);
} else {
- /* signal force unmount that this znode can be freed */
- zp->z_dbuf = NULL;
mutex_exit(&zp->z_lock);
}
+#endif
}
extern struct vop_vector zfs_vnodeops;
@@ -107,24 +134,29 @@ extern struct vop_vector zfs_fifoops;
* 'cdrarg' is defined at kmem_cache_create() time.
*/
static int
-zfs_znode_cache_constructor(void *buf, void *cdrarg, int kmflags)
+zfs_znode_cache_constructor(void *buf, void *arg, int kmflags)
{
znode_t *zp = buf;
vnode_t *vp;
- vfs_t *vfsp = cdrarg;
+ vfs_t *vfsp = arg;
int error;
- if (cdrarg != NULL) {
- error = getnewvnode("zfs", vfsp, &zfs_vnodeops, &vp);
- ASSERT(error == 0);
- vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
- zp->z_vnode = vp;
- vp->v_data = (caddr_t)zp;
- VN_LOCK_AREC(vp);
- VN_LOCK_ASHARE(vp);
- } else {
- zp->z_vnode = NULL;
- }
+ POINTER_INVALIDATE(&zp->z_zfsvfs);
+ ASSERT(!POINTER_IS_VALID(zp->z_zfsvfs));
+ ASSERT(vfsp != NULL);
+
+ error = getnewvnode("zfs", vfsp, &zfs_vnodeops, &vp);
+ if (error != 0 && (kmflags & KM_NOSLEEP))
+ return (-1);
+ ASSERT(error == 0);
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+ zp->z_vnode = vp;
+ vp->v_data = (caddr_t)zp;
+ VN_LOCK_AREC(vp);
+ VN_LOCK_ASHARE(vp);
+
+ list_link_init(&zp->z_link_node);
+
mutex_init(&zp->z_lock, NULL, MUTEX_DEFAULT, NULL);
rw_init(&zp->z_map_lock, NULL, RW_DEFAULT, NULL);
rw_init(&zp->z_parent_lock, NULL, RW_DEFAULT, NULL);
@@ -135,29 +167,189 @@ zfs_znode_cache_constructor(void *buf, void *cdrarg, int kmflags)
avl_create(&zp->z_range_avl, zfs_range_compare,
sizeof (rl_t), offsetof(rl_t, r_node));
- zp->z_dbuf_held = 0;
- zp->z_dirlocks = 0;
+ zp->z_dbuf = NULL;
+ zp->z_dirlocks = NULL;
return (0);
}
/*ARGSUSED*/
static void
-zfs_znode_cache_destructor(void *buf, void *cdarg)
+zfs_znode_cache_destructor(void *buf, void *arg)
{
znode_t *zp = buf;
- ASSERT(zp->z_dirlocks == 0);
+ ASSERT(!POINTER_IS_VALID(zp->z_zfsvfs));
+ ASSERT(ZTOV(zp) == NULL);
+ vn_free(ZTOV(zp));
+ ASSERT(!list_link_active(&zp->z_link_node));
mutex_destroy(&zp->z_lock);
rw_destroy(&zp->z_map_lock);
rw_destroy(&zp->z_parent_lock);
rw_destroy(&zp->z_name_lock);
mutex_destroy(&zp->z_acl_lock);
- mutex_destroy(&zp->z_range_lock);
avl_destroy(&zp->z_range_avl);
+ mutex_destroy(&zp->z_range_lock);
+
+ ASSERT(zp->z_dbuf == NULL);
+ ASSERT(zp->z_dirlocks == NULL);
+}
+
+#ifdef ZNODE_STATS
+static struct {
+ uint64_t zms_zfsvfs_invalid;
+ uint64_t zms_zfsvfs_unmounted;
+ uint64_t zms_zfsvfs_recheck_invalid;
+ uint64_t zms_obj_held;
+ uint64_t zms_vnode_locked;
+ uint64_t zms_not_only_dnlc;
+} znode_move_stats;
+#endif /* ZNODE_STATS */
+
+#if defined(sun)
+static void
+zfs_znode_move_impl(znode_t *ozp, znode_t *nzp)
+{
+ vnode_t *vp;
+
+ /* Copy fields. */
+ nzp->z_zfsvfs = ozp->z_zfsvfs;
+
+ /* Swap vnodes. */
+ vp = nzp->z_vnode;
+ nzp->z_vnode = ozp->z_vnode;
+ ozp->z_vnode = vp; /* let destructor free the overwritten vnode */
+ ZTOV(ozp)->v_data = ozp;
+ ZTOV(nzp)->v_data = nzp;
+
+ nzp->z_id = ozp->z_id;
+ ASSERT(ozp->z_dirlocks == NULL); /* znode not in use */
+ ASSERT(avl_numnodes(&ozp->z_range_avl) == 0);
+ nzp->z_unlinked = ozp->z_unlinked;
+ nzp->z_atime_dirty = ozp->z_atime_dirty;
+ nzp->z_zn_prefetch = ozp->z_zn_prefetch;
+ nzp->z_blksz = ozp->z_blksz;
+ nzp->z_seq = ozp->z_seq;
+ nzp->z_mapcnt = ozp->z_mapcnt;
+ nzp->z_last_itx = ozp->z_last_itx;
+ nzp->z_gen = ozp->z_gen;
+ nzp->z_sync_cnt = ozp->z_sync_cnt;
+ nzp->z_phys = ozp->z_phys;
+ nzp->z_dbuf = ozp->z_dbuf;
+
+ /* Update back pointers. */
+ (void) dmu_buf_update_user(nzp->z_dbuf, ozp, nzp, &nzp->z_phys,
+ znode_evict_error);
- ASSERT(zp->z_dbuf_held == 0);
+ /*
+ * Invalidate the original znode by clearing fields that provide a
+ * pointer back to the znode. Set the low bit of the vfs pointer to
+ * ensure that zfs_znode_move() recognizes the znode as invalid in any
+ * subsequent callback.
+ */
+ ozp->z_dbuf = NULL;
+ POINTER_INVALIDATE(&ozp->z_zfsvfs);
}
+/*
+ * Wrapper function for ZFS_ENTER that returns 0 if successful and otherwise
+ * returns a non-zero error code.
+ */
+static int
+zfs_enter(zfsvfs_t *zfsvfs)
+{
+ ZFS_ENTER(zfsvfs);
+ return (0);
+}
+
+/*ARGSUSED*/
+static kmem_cbrc_t
+zfs_znode_move(void *buf, void *newbuf, size_t size, void *arg)
+{
+ znode_t *ozp = buf, *nzp = newbuf;
+ zfsvfs_t *zfsvfs;
+ vnode_t *vp;
+
+ /*
+ * The znode is on the file system's list of known znodes if the vfs
+ * pointer is valid. We set the low bit of the vfs pointer when freeing
+ * the znode to invalidate it, and the memory patterns written by kmem
+ * (baddcafe and deadbeef) set at least one of the two low bits. A newly
+ * created znode sets the vfs pointer last of all to indicate that the
+ * znode is known and in a valid state to be moved by this function.
+ */
+ zfsvfs = ozp->z_zfsvfs;
+ if (!POINTER_IS_VALID(zfsvfs)) {
+ ZNODE_STAT_ADD(znode_move_stats.zms_zfsvfs_invalid);
+ return (KMEM_CBRC_DONT_KNOW);
+ }
+
+ /*
+ * Ensure that the filesystem is not unmounted during the move.
+ */
+ if (zfs_enter(zfsvfs) != 0) { /* ZFS_ENTER */
+ ZNODE_STAT_ADD(znode_move_stats.zms_zfsvfs_unmounted);
+ return (KMEM_CBRC_DONT_KNOW);
+ }
+
+ mutex_enter(&zfsvfs->z_znodes_lock);
+ /*
+ * Recheck the vfs pointer in case the znode was removed just before
+ * acquiring the lock.
+ */
+ if (zfsvfs != ozp->z_zfsvfs) {
+ mutex_exit(&zfsvfs->z_znodes_lock);
+ ZFS_EXIT(zfsvfs);
+ ZNODE_STAT_ADD(znode_move_stats.zms_zfsvfs_recheck_invalid);
+ return (KMEM_CBRC_DONT_KNOW);
+ }
+
+ /*
+ * At this point we know that as long as we hold z_znodes_lock, the
+ * znode cannot be freed and fields within the znode can be safely
+ * accessed. Now, prevent a race with zfs_zget().
+ */
+ if (ZFS_OBJ_HOLD_TRYENTER(zfsvfs, ozp->z_id) == 0) {
+ mutex_exit(&zfsvfs->z_znodes_lock);
+ ZFS_EXIT(zfsvfs);
+ ZNODE_STAT_ADD(znode_move_stats.zms_obj_held);
+ return (KMEM_CBRC_LATER);
+ }
+
+ vp = ZTOV(ozp);
+ if (mutex_tryenter(&vp->v_lock) == 0) {
+ ZFS_OBJ_HOLD_EXIT(zfsvfs, ozp->z_id);
+ mutex_exit(&zfsvfs->z_znodes_lock);
+ ZFS_EXIT(zfsvfs);
+ ZNODE_STAT_ADD(znode_move_stats.zms_vnode_locked);
+ return (KMEM_CBRC_LATER);
+ }
+
+ /* Only move znodes that are referenced _only_ by the DNLC. */
+ if (vp->v_count != 1 || !vn_in_dnlc(vp)) {
+ mutex_exit(&vp->v_lock);
+ ZFS_OBJ_HOLD_EXIT(zfsvfs, ozp->z_id);
+ mutex_exit(&zfsvfs->z_znodes_lock);
+ ZFS_EXIT(zfsvfs);
+ ZNODE_STAT_ADD(znode_move_stats.zms_not_only_dnlc);
+ return (KMEM_CBRC_LATER);
+ }
+
+ /*
+ * The znode is known and in a valid state to move. We're holding the
+ * locks needed to execute the critical section.
+ */
+ zfs_znode_move_impl(ozp, nzp);
+ mutex_exit(&vp->v_lock);
+ ZFS_OBJ_HOLD_EXIT(zfsvfs, ozp->z_id);
+
+ list_link_replace(&ozp->z_link_node, &nzp->z_link_node);
+ mutex_exit(&zfsvfs->z_znodes_lock);
+ ZFS_EXIT(zfsvfs);
+
+ return (KMEM_CBRC_YES);
+}
+#endif /* sun */
+
void
zfs_znode_init(void)
{
@@ -168,6 +360,9 @@ zfs_znode_init(void)
znode_cache = kmem_cache_create("zfs_znode_cache",
sizeof (znode_t), 0, /* zfs_znode_cache_constructor */ NULL,
zfs_znode_cache_destructor, NULL, NULL, NULL, 0);
+#if defined(sun)
+ kmem_cache_set_move(znode_cache, zfs_znode_move);
+#endif
}
void
@@ -186,44 +381,43 @@ zfs_znode_fini(void)
* incore "master" object. Verify version compatibility.
*/
int
-zfs_init_fs(zfsvfs_t *zfsvfs, znode_t **zpp, cred_t *cr)
+zfs_init_fs(zfsvfs_t *zfsvfs, znode_t **zpp)
{
objset_t *os = zfsvfs->z_os;
- uint64_t version = ZPL_VERSION;
int i, error;
- dmu_object_info_t doi;
uint64_t fsid_guid;
+ uint64_t zval;
*zpp = NULL;
- /*
- * XXX - hack to auto-create the pool root filesystem at
- * the first attempted mount.
- */
- if (dmu_object_info(os, MASTER_NODE_OBJ, &doi) == ENOENT) {
- dmu_tx_t *tx = dmu_tx_create(os);
-
- dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, TRUE, NULL); /* master */
- dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, TRUE, NULL); /* del queue */
- dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT); /* root node */
- error = dmu_tx_assign(tx, TXG_WAIT);
- ASSERT3U(error, ==, 0);
- zfs_create_fs(os, cr, tx);
- dmu_tx_commit(tx);
- }
-
- error = zap_lookup(os, MASTER_NODE_OBJ, ZPL_VERSION_OBJ, 8, 1,
- &version);
+ error = zfs_get_zplprop(os, ZFS_PROP_VERSION, &zfsvfs->z_version);
if (error) {
return (error);
- } else if (version != ZPL_VERSION) {
+ } else if (zfsvfs->z_version > ZPL_VERSION) {
(void) printf("Mismatched versions: File system "
- "is version %lld on-disk format, which is "
+ "is version %llu on-disk format, which is "
"incompatible with this software version %lld!",
- (u_longlong_t)version, ZPL_VERSION);
+ (u_longlong_t)zfsvfs->z_version, ZPL_VERSION);
return (ENOTSUP);
}
+ if ((error = zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &zval)) != 0)
+ return (error);
+ zfsvfs->z_norm = (int)zval;
+ if ((error = zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &zval)) != 0)
+ return (error);
+ zfsvfs->z_utf8 = (zval != 0);
+ if ((error = zfs_get_zplprop(os, ZFS_PROP_CASE, &zval)) != 0)
+ return (error);
+ zfsvfs->z_case = (uint_t)zval;
+ /*
+ * Fold case on file systems that are always or sometimes case
+ * insensitive.
+ */
+ if (zfsvfs->z_case == ZFS_CASE_INSENSITIVE ||
+ zfsvfs->z_case == ZFS_CASE_MIXED)
+ zfsvfs->z_norm |= U8_TEXTPREP_TOUPPER;
+
/*
* The fsid is 64 bits, composed of an 8-bit fs type, which
* separates our fsid from any other filesystem types, and a
@@ -244,9 +438,10 @@ zfs_init_fs(zfsvfs_t *zfsvfs, znode_t **zpp, cred_t *cr)
return (error);
ASSERT(zfsvfs->z_root != 0);
- /*
- * Create the per mount vop tables.
- */
+ error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_UNLINKED_SET, 8, 1,
+ &zfsvfs->z_unlinkedobj);
+ if (error)
+ return (error);
/*
* Initialize zget mutex's
@@ -255,14 +450,21 @@ zfs_init_fs(zfsvfs_t *zfsvfs, znode_t **zpp, cred_t *cr)
mutex_init(&zfsvfs->z_hold_mtx[i], NULL, MUTEX_DEFAULT, NULL);
error = zfs_zget(zfsvfs, zfsvfs->z_root, zpp);
- if (error)
+ if (error) {
+ /*
+ * On error, we destroy the mutexes here since it's not
+ * possible for the caller to determine if the mutexes were
+ * initialized properly.
+ */
+ for (i = 0; i != ZFS_OBJ_MTX_SZ; i++)
+ mutex_destroy(&zfsvfs->z_hold_mtx[i]);
return (error);
+ }
ASSERT3U((*zpp)->z_id, ==, zfsvfs->z_root);
-
- error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_UNLINKED_SET, 8, 1,
- &zfsvfs->z_unlinkedobj);
- if (error)
- return (error);
+ error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_FUID_TABLES, 8, 1,
+ &zfsvfs->z_fuid_obj);
+ if (error == ENOENT)
+ error = 0;
return (0);
}
@@ -307,6 +509,50 @@ zfs_cmpldev(uint64_t dev)
return (makedev((dev >> NBITSMINOR64), (dev & MAXMIN64)));
}
+static void
+zfs_znode_dmu_init(zfsvfs_t *zfsvfs, znode_t *zp, dmu_buf_t *db)
+{
+ znode_t *nzp;
+
+ ASSERT(!POINTER_IS_VALID(zp->z_zfsvfs) || (zfsvfs == zp->z_zfsvfs));
+ ASSERT(MUTEX_HELD(ZFS_OBJ_MUTEX(zfsvfs, zp->z_id)));
+
+ mutex_enter(&zp->z_lock);
+
+ ASSERT(zp->z_dbuf == NULL);
+ zp->z_dbuf = db;
+ nzp = dmu_buf_set_user_ie(db, zp, &zp->z_phys, znode_evict_error);
+
+ /*
+ * there should be no
+ * concurrent zgets on this object.
+ */
+ if (nzp != NULL)
+ panic("existing znode %p for dbuf %p", (void *)nzp, (void *)db);
+
+ /*
+ * Slap on VROOT if we are the root znode
+ */
+ if (zp->z_id == zfsvfs->z_root)
+ ZTOV(zp)->v_flag |= VROOT;
+
+ mutex_exit(&zp->z_lock);
+ vn_exists(ZTOV(zp));
+}
+
+void
+zfs_znode_dmu_fini(znode_t *zp)
+{
+ dmu_buf_t *db = zp->z_dbuf;
+ ASSERT(MUTEX_HELD(ZFS_OBJ_MUTEX(zp->z_zfsvfs, zp->z_id)) ||
+ zp->z_unlinked ||
+ RW_WRITE_HELD(&zp->z_zfsvfs->z_teardown_inactive_lock));
+ ASSERT(zp->z_dbuf != NULL);
+ zp->z_dbuf = NULL;
+ VERIFY(zp == dmu_buf_update_user(db, zp, NULL, NULL, NULL));
+ dmu_buf_rele(db, NULL);
+}
+
/*
* Construct a new znode/vnode and intialize.
*
@@ -315,42 +561,45 @@ zfs_cmpldev(uint64_t dev)
* return the znode
*/
static znode_t *
-zfs_znode_alloc(zfsvfs_t *zfsvfs, dmu_buf_t *db, uint64_t obj_num, int blksz)
+zfs_znode_alloc(zfsvfs_t *zfsvfs, dmu_buf_t *db, int blksz)
{
znode_t *zp;
vnode_t *vp;
- int error;
zp = kmem_cache_alloc(znode_cache, KM_SLEEP);
- zfs_znode_cache_constructor(zp, zfsvfs->z_vfs, 0);
+ zfs_znode_cache_constructor(zp, zfsvfs->z_parent->z_vfs, 0);
ASSERT(zp->z_dirlocks == NULL);
+ ASSERT(zp->z_dbuf == NULL);
+ ASSERT(!POINTER_IS_VALID(zp->z_zfsvfs));
- zp->z_phys = db->db_data;
- zp->z_zfsvfs = zfsvfs;
+ /*
+ * Defer setting z_zfsvfs until the znode is ready to be a candidate for
+ * the zfs_znode_move() callback.
+ */
+ zp->z_phys = NULL;
zp->z_unlinked = 0;
zp->z_atime_dirty = 0;
- zp->z_dbuf_held = 0;
zp->z_mapcnt = 0;
zp->z_last_itx = 0;
- zp->z_dbuf = db;
- zp->z_id = obj_num;
+ zp->z_id = db->db_object;
zp->z_blksz = blksz;
zp->z_seq = 0x7A4653;
zp->z_sync_cnt = 0;
- mutex_enter(&zfsvfs->z_znodes_lock);
- list_insert_tail(&zfsvfs->z_all_znodes, zp);
- mutex_exit(&zfsvfs->z_znodes_lock);
-
vp = ZTOV(zp);
+#ifdef TODO
+ vn_reinit(vp);
+#endif
+
+ zfs_znode_dmu_init(zfsvfs, zp, db);
+
+ zp->z_gen = zp->z_phys->zp_gen;
+
+#if 0
if (vp == NULL)
return (zp);
-
- vp->v_vflag |= VV_FORCEINSMQ;
- error = insmntque(vp, zfsvfs->z_vfs);
- vp->v_vflag &= ~VV_FORCEINSMQ;
- KASSERT(error == 0, ("insmntque() failed: error %d", error));
+#endif
vp->v_type = IFTOVT((mode_t)zp->z_phys->zp_mode);
switch (vp->v_type) {
@@ -362,37 +611,18 @@ zfs_znode_alloc(zfsvfs_t *zfsvfs, dmu_buf_t *db, uint64_t obj_num, int blksz)
break;
}
- return (zp);
-}
-
-static void
-zfs_znode_dmu_init(znode_t *zp)
-{
- znode_t *nzp;
- zfsvfs_t *zfsvfs = zp->z_zfsvfs;
- dmu_buf_t *db = zp->z_dbuf;
-
- mutex_enter(&zp->z_lock);
-
- nzp = dmu_buf_set_user_ie(db, zp, &zp->z_phys, znode_pageout_func);
-
- /*
- * there should be no
- * concurrent zgets on this object.
- */
- ASSERT3P(nzp, ==, NULL);
-
+ mutex_enter(&zfsvfs->z_znodes_lock);
+ list_insert_tail(&zfsvfs->z_all_znodes, zp);
+ membar_producer();
/*
- * Slap on VROOT if we are the root znode
+ * Everything else must be valid before assigning z_zfsvfs makes the
+ * znode eligible for zfs_znode_move().
*/
- if (zp->z_id == zfsvfs->z_root) {
- ZTOV(zp)->v_flag |= VROOT;
- }
+ zp->z_zfsvfs = zfsvfs;
+ mutex_exit(&zfsvfs->z_znodes_lock);
- ASSERT(zp->z_dbuf_held == 0);
- zp->z_dbuf_held = 1;
VFS_HOLD(zfsvfs->z_vfs);
- mutex_exit(&zp->z_lock);
+ return (zp);
}
/*
@@ -406,31 +636,34 @@ zfs_znode_dmu_init(znode_t *zp)
* IS_ROOT_NODE - new object will be root
* IS_XATTR - new object is an attribute
* IS_REPLAY - intent log replay
+ * bonuslen - length of bonus buffer
+ * setaclp - File/Dir initial ACL
+ * fuidp - Tracks fuid allocation.
*
- * OUT: oid - ID of created object
+ * OUT: zpp - allocated znode
*
*/
void
-zfs_mknode(znode_t *dzp, vattr_t *vap, uint64_t *oid, dmu_tx_t *tx, cred_t *cr,
- uint_t flag, znode_t **zpp, int bonuslen)
+zfs_mknode(znode_t *dzp, vattr_t *vap, dmu_tx_t *tx, cred_t *cr,
+ uint_t flag, znode_t **zpp, int bonuslen, zfs_acl_t *setaclp,
+ zfs_fuid_info_t **fuidp)
{
- dmu_buf_t *dbp;
+ dmu_buf_t *db;
znode_phys_t *pzp;
- znode_t *zp;
zfsvfs_t *zfsvfs = dzp->z_zfsvfs;
timestruc_t now;
- uint64_t gen;
+ uint64_t gen, obj;
int err;
ASSERT(vap && (vap->va_mask & (AT_TYPE|AT_MODE)) == (AT_TYPE|AT_MODE));
if (zfsvfs->z_assign >= TXG_INITIAL) { /* ZIL replay */
- *oid = vap->va_nodeid;
+ obj = vap->va_nodeid;
flag |= IS_REPLAY;
now = vap->va_ctime; /* see zfs_replay_create() */
gen = vap->va_nblocks; /* ditto */
} else {
- *oid = 0;
+ obj = 0;
gethrestime(&now);
gen = dmu_tx_get_txg(tx);
}
@@ -446,44 +679,45 @@ zfs_mknode(znode_t *dzp, vattr_t *vap, uint64_t *oid, dmu_tx_t *tx, cred_t *cr,
*/
if (vap->va_type == VDIR) {
if (flag & IS_REPLAY) {
- err = zap_create_claim(zfsvfs->z_os, *oid,
- DMU_OT_DIRECTORY_CONTENTS,
+ err = zap_create_claim_norm(zfsvfs->z_os, obj,
+ zfsvfs->z_norm, DMU_OT_DIRECTORY_CONTENTS,
DMU_OT_ZNODE, sizeof (znode_phys_t) + bonuslen, tx);
ASSERT3U(err, ==, 0);
} else {
- *oid = zap_create(zfsvfs->z_os,
- DMU_OT_DIRECTORY_CONTENTS,
+ obj = zap_create_norm(zfsvfs->z_os,
+ zfsvfs->z_norm, DMU_OT_DIRECTORY_CONTENTS,
DMU_OT_ZNODE, sizeof (znode_phys_t) + bonuslen, tx);
}
} else {
if (flag & IS_REPLAY) {
- err = dmu_object_claim(zfsvfs->z_os, *oid,
+ err = dmu_object_claim(zfsvfs->z_os, obj,
DMU_OT_PLAIN_FILE_CONTENTS, 0,
DMU_OT_ZNODE, sizeof (znode_phys_t) + bonuslen, tx);
ASSERT3U(err, ==, 0);
} else {
- *oid = dmu_object_alloc(zfsvfs->z_os,
+ obj = dmu_object_alloc(zfsvfs->z_os,
DMU_OT_PLAIN_FILE_CONTENTS, 0,
DMU_OT_ZNODE, sizeof (znode_phys_t) + bonuslen, tx);
}
}
- VERIFY(0 == dmu_bonus_hold(zfsvfs->z_os, *oid, NULL, &dbp));
- dmu_buf_will_dirty(dbp, tx);
+ VERIFY(0 == dmu_bonus_hold(zfsvfs->z_os, obj, NULL, &db));
+ dmu_buf_will_dirty(db, tx);
/*
* Initialize the znode physical data to zero.
*/
- ASSERT(dbp->db_size >= sizeof (znode_phys_t));
- bzero(dbp->db_data, dbp->db_size);
- pzp = dbp->db_data;
+ ASSERT(db->db_size >= sizeof (znode_phys_t));
+ bzero(db->db_data, db->db_size);
+ pzp = db->db_data;
/*
* If this is the root, fix up the half-initialized parent pointer
* to reference the just-allocated physical data area.
*/
if (flag & IS_ROOT_NODE) {
+ dzp->z_dbuf = db;
dzp->z_phys = pzp;
- dzp->z_id = *oid;
+ dzp->z_id = obj;
}
/*
@@ -496,6 +730,9 @@ zfs_mknode(znode_t *dzp, vattr_t *vap, uint64_t *oid, dmu_tx_t *tx, cred_t *cr,
pzp->zp_rdev = zfs_expldev(vap->va_rdev);
}
+ if (zfsvfs->z_use_fuids)
+ pzp->zp_flags = ZFS_ARCHIVE | ZFS_AV_MODIFIED;
+
if (vap->va_type == VDIR) {
pzp->zp_size = 2; /* contents ("." and "..") */
pzp->zp_links = (flag & (IS_ROOT_NODE | IS_XATTR)) ? 2 : 1;
@@ -523,25 +760,91 @@ zfs_mknode(znode_t *dzp, vattr_t *vap, uint64_t *oid, dmu_tx_t *tx, cred_t *cr,
}
pzp->zp_mode = MAKEIMODE(vap->va_type, vap->va_mode);
- zp = zfs_znode_alloc(zfsvfs, dbp, *oid, 0);
-
- zfs_perm_init(zp, dzp, flag, vap, tx, cr);
+ if (!(flag & IS_ROOT_NODE)) {
+ ZFS_OBJ_HOLD_ENTER(zfsvfs, obj);
+ *zpp = zfs_znode_alloc(zfsvfs, db, 0);
+ ZFS_OBJ_HOLD_EXIT(zfsvfs, obj);
+ } else {
+ /*
+ * If we are creating the root node, the "parent" we
+ * passed in is the znode for the root.
+ */
+ *zpp = dzp;
+ }
+ zfs_perm_init(*zpp, dzp, flag, vap, tx, cr, setaclp, fuidp);
+ if (!(flag & IS_ROOT_NODE)) {
+ vnode_t *vp;
+
+ vp = ZTOV(*zpp);
+ vp->v_vflag |= VV_FORCEINSMQ;
+ err = insmntque(vp, zfsvfs->z_vfs);
+ vp->v_vflag &= ~VV_FORCEINSMQ;
+ KASSERT(err == 0, ("insmntque() failed: error %d", err));
+ }
+}
- if (zpp) {
- kmutex_t *hash_mtx = ZFS_OBJ_MUTEX(zp);
+void
+zfs_xvattr_set(znode_t *zp, xvattr_t *xvap)
+{
+ xoptattr_t *xoap;
- mutex_enter(hash_mtx);
- zfs_znode_dmu_init(zp);
- mutex_exit(hash_mtx);
+ xoap = xva_getxoptattr(xvap);
+ ASSERT(xoap);
- *zpp = zp;
- } else {
- if (ZTOV(zp) != NULL) {
- ZTOV(zp)->v_count = 0;
- VOP_UNLOCK(ZTOV(zp), 0);
- }
- dmu_buf_rele(dbp, NULL);
- zfs_znode_free(zp);
+ if (XVA_ISSET_REQ(xvap, XAT_CREATETIME)) {
+ ZFS_TIME_ENCODE(&xoap->xoa_createtime, zp->z_phys->zp_crtime);
+ XVA_SET_RTN(xvap, XAT_CREATETIME);
+ }
+ if (XVA_ISSET_REQ(xvap, XAT_READONLY)) {
+ ZFS_ATTR_SET(zp, ZFS_READONLY, xoap->xoa_readonly);
+ XVA_SET_RTN(xvap, XAT_READONLY);
+ }
+ if (XVA_ISSET_REQ(xvap, XAT_HIDDEN)) {
+ ZFS_ATTR_SET(zp, ZFS_HIDDEN, xoap->xoa_hidden);
+ XVA_SET_RTN(xvap, XAT_HIDDEN);
+ }
+ if (XVA_ISSET_REQ(xvap, XAT_SYSTEM)) {
+ ZFS_ATTR_SET(zp, ZFS_SYSTEM, xoap->xoa_system);
+ XVA_SET_RTN(xvap, XAT_SYSTEM);
+ }
+ if (XVA_ISSET_REQ(xvap, XAT_ARCHIVE)) {
+ ZFS_ATTR_SET(zp, ZFS_ARCHIVE, xoap->xoa_archive);
+ XVA_SET_RTN(xvap, XAT_ARCHIVE);
+ }
+ if (XVA_ISSET_REQ(xvap, XAT_IMMUTABLE)) {
+ ZFS_ATTR_SET(zp, ZFS_IMMUTABLE, xoap->xoa_immutable);
+ XVA_SET_RTN(xvap, XAT_IMMUTABLE);
+ }
+ if (XVA_ISSET_REQ(xvap, XAT_NOUNLINK)) {
+ ZFS_ATTR_SET(zp, ZFS_NOUNLINK, xoap->xoa_nounlink);
+ XVA_SET_RTN(xvap, XAT_NOUNLINK);
+ }
+ if (XVA_ISSET_REQ(xvap, XAT_APPENDONLY)) {
+ ZFS_ATTR_SET(zp, ZFS_APPENDONLY, xoap->xoa_appendonly);
+ XVA_SET_RTN(xvap, XAT_APPENDONLY);
+ }
+ if (XVA_ISSET_REQ(xvap, XAT_NODUMP)) {
+ ZFS_ATTR_SET(zp, ZFS_NODUMP, xoap->xoa_nodump);
+ XVA_SET_RTN(xvap, XAT_NODUMP);
+ }
+ if (XVA_ISSET_REQ(xvap, XAT_OPAQUE)) {
+ ZFS_ATTR_SET(zp, ZFS_OPAQUE, xoap->xoa_opaque);
+ XVA_SET_RTN(xvap, XAT_OPAQUE);
+ }
+ if (XVA_ISSET_REQ(xvap, XAT_AV_QUARANTINED)) {
+ ZFS_ATTR_SET(zp, ZFS_AV_QUARANTINED,
+ xoap->xoa_av_quarantined);
+ XVA_SET_RTN(xvap, XAT_AV_QUARANTINED);
+ }
+ if (XVA_ISSET_REQ(xvap, XAT_AV_MODIFIED)) {
+ ZFS_ATTR_SET(zp, ZFS_AV_MODIFIED, xoap->xoa_av_modified);
+ XVA_SET_RTN(xvap, XAT_AV_MODIFIED);
+ }
+ if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP)) {
+ (void) memcpy(zp->z_phys + 1, xoap->xoa_av_scanstamp,
+ sizeof (xoap->xoa_av_scanstamp));
+ zp->z_phys->zp_flags |= ZFS_BONUS_SCANSTAMP;
+ XVA_SET_RTN(xvap, XAT_AV_SCANSTAMP);
}
}
@@ -552,10 +855,10 @@ zfs_zget(zfsvfs_t *zfsvfs, uint64_t obj_num, znode_t **zpp)
dmu_buf_t *db;
znode_t *zp;
vnode_t *vp;
- int err;
+ int err, first = 1;
*zpp = NULL;
-
+again:
ZFS_OBJ_HOLD_ENTER(zfsvfs, obj_num);
err = dmu_bonus_hold(zfsvfs->z_os, obj_num, NULL, &db);
@@ -572,84 +875,118 @@ zfs_zget(zfsvfs_t *zfsvfs, uint64_t obj_num, znode_t **zpp)
return (EINVAL);
}
- ASSERT(db->db_object == obj_num);
- ASSERT(db->db_offset == -1);
- ASSERT(db->db_data != NULL);
-
zp = dmu_buf_get_user(db);
-
if (zp != NULL) {
mutex_enter(&zp->z_lock);
+ /*
+ * Since we do immediate eviction of the z_dbuf, we
+ * should never find a dbuf with a znode that doesn't
+ * know about the dbuf.
+ */
+ ASSERT3P(zp->z_dbuf, ==, db);
ASSERT3U(zp->z_id, ==, obj_num);
if (zp->z_unlinked) {
- dmu_buf_rele(db, NULL);
- mutex_exit(&zp->z_lock);
- ZFS_OBJ_HOLD_EXIT(zfsvfs, obj_num);
- return (ENOENT);
- } else if (zp->z_dbuf_held) {
- dmu_buf_rele(db, NULL);
+ err = ENOENT;
} else {
- zp->z_dbuf_held = 1;
- VFS_HOLD(zfsvfs->z_vfs);
- }
-
- if (ZTOV(zp) != NULL)
- VN_HOLD(ZTOV(zp));
- else {
- err = getnewvnode("zfs", zfsvfs->z_vfs, &zfs_vnodeops,
- &zp->z_vnode);
- ASSERT(err == 0);
- vp = ZTOV(zp);
- vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
- vp->v_data = (caddr_t)zp;
- VN_LOCK_AREC(vp);
- VN_LOCK_ASHARE(vp);
- vp->v_type = IFTOVT((mode_t)zp->z_phys->zp_mode);
- if (vp->v_type == VDIR)
- zp->z_zn_prefetch = B_TRUE; /* z_prefetch default is enabled */
- vp->v_vflag |= VV_FORCEINSMQ;
- err = insmntque(vp, zfsvfs->z_vfs);
- vp->v_vflag &= ~VV_FORCEINSMQ;
- KASSERT(err == 0, ("insmntque() failed: error %d", err));
- VOP_UNLOCK(vp, 0);
+ if (ZTOV(zp) != NULL)
+ VN_HOLD(ZTOV(zp));
+ else {
+ if (first) {
+ ZFS_LOG(1, "dying znode detected (zp=%p)", zp);
+ first = 0;
+ }
+ /*
+ * znode is dying so we can't reuse it, we must
+ * wait until destruction is completed.
+ */
+ dmu_buf_rele(db, NULL);
+ mutex_exit(&zp->z_lock);
+ ZFS_OBJ_HOLD_EXIT(zfsvfs, obj_num);
+ tsleep(zp, 0, "zcollide", 1);
+ goto again;
+ }
+ *zpp = zp;
+ err = 0;
}
+ dmu_buf_rele(db, NULL);
mutex_exit(&zp->z_lock);
ZFS_OBJ_HOLD_EXIT(zfsvfs, obj_num);
- *zpp = zp;
- return (0);
+ return (err);
}
/*
* Not found create new znode/vnode
*/
- zp = zfs_znode_alloc(zfsvfs, db, obj_num, doi.doi_data_block_size);
- ASSERT3U(zp->z_id, ==, obj_num);
- zfs_znode_dmu_init(zp);
+ zp = zfs_znode_alloc(zfsvfs, db, doi.doi_data_block_size);
+
+ vp = ZTOV(zp);
+ vp->v_vflag |= VV_FORCEINSMQ;
+ err = insmntque(vp, zfsvfs->z_vfs);
+ vp->v_vflag &= ~VV_FORCEINSMQ;
+ KASSERT(err == 0, ("insmntque() failed: error %d", err));
+ VOP_UNLOCK(vp, 0);
+
ZFS_OBJ_HOLD_EXIT(zfsvfs, obj_num);
*zpp = zp;
- if ((vp = ZTOV(zp)) != NULL)
- VOP_UNLOCK(vp, 0);
return (0);
}
-void
-zfs_znode_delete(znode_t *zp, dmu_tx_t *tx)
+int
+zfs_rezget(znode_t *zp)
{
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
- int error;
+ dmu_object_info_t doi;
+ dmu_buf_t *db;
+ uint64_t obj_num = zp->z_id;
+ int err;
+
+ ZFS_OBJ_HOLD_ENTER(zfsvfs, obj_num);
- ZFS_OBJ_HOLD_ENTER(zfsvfs, zp->z_id);
- if (zp->z_phys->zp_acl.z_acl_extern_obj) {
- error = dmu_object_free(zfsvfs->z_os,
- zp->z_phys->zp_acl.z_acl_extern_obj, tx);
- ASSERT3U(error, ==, 0);
+ err = dmu_bonus_hold(zfsvfs->z_os, obj_num, NULL, &db);
+ if (err) {
+ ZFS_OBJ_HOLD_EXIT(zfsvfs, obj_num);
+ return (err);
}
- error = dmu_object_free(zfsvfs->z_os, zp->z_id, tx);
- ASSERT3U(error, ==, 0);
- zp->z_dbuf_held = 0;
- ZFS_OBJ_HOLD_EXIT(zfsvfs, zp->z_id);
- dmu_buf_rele(zp->z_dbuf, NULL);
+
+ dmu_object_info_from_db(db, &doi);
+ if (doi.doi_bonus_type != DMU_OT_ZNODE ||
+ doi.doi_bonus_size < sizeof (znode_phys_t)) {
+ dmu_buf_rele(db, NULL);
+ ZFS_OBJ_HOLD_EXIT(zfsvfs, obj_num);
+ return (EINVAL);
+ }
+
+ if (((znode_phys_t *)db->db_data)->zp_gen != zp->z_gen) {
+ dmu_buf_rele(db, NULL);
+ ZFS_OBJ_HOLD_EXIT(zfsvfs, obj_num);
+ return (EIO);
+ }
+
+ zfs_znode_dmu_init(zfsvfs, zp, db);
+ zp->z_unlinked = (zp->z_phys->zp_links == 0);
+ zp->z_blksz = doi.doi_data_block_size;
+
+ ZFS_OBJ_HOLD_EXIT(zfsvfs, obj_num);
+
+ return (0);
+}
+
+void
+zfs_znode_delete(znode_t *zp, dmu_tx_t *tx)
+{
+ zfsvfs_t *zfsvfs = zp->z_zfsvfs;
+ objset_t *os = zfsvfs->z_os;
+ uint64_t obj = zp->z_id;
+ uint64_t acl_obj = zp->z_phys->zp_acl.z_acl_extern_obj;
+
+ ZFS_OBJ_HOLD_ENTER(zfsvfs, obj);
+ if (acl_obj)
+ VERIFY(0 == dmu_object_free(os, acl_obj, tx));
+ VERIFY(0 == dmu_object_free(os, obj, tx));
+ zfs_znode_dmu_fini(zp);
+ ZFS_OBJ_HOLD_EXIT(zfsvfs, obj);
+ zfs_znode_free(zp);
}
void
@@ -659,7 +996,7 @@ zfs_zinactive(znode_t *zp)
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
uint64_t z_id = zp->z_id;
- ASSERT(zp->z_dbuf_held && zp->z_phys);
+ ASSERT(zp->z_dbuf && zp->z_phys);
/*
* Don't allow a zfs_zget() while were trying to release this znode
@@ -686,17 +1023,13 @@ zfs_zinactive(znode_t *zp)
* remove the file from the file system.
*/
if (zp->z_unlinked) {
- ZTOV(zp) = NULL;
mutex_exit(&zp->z_lock);
ZFS_OBJ_HOLD_EXIT(zfsvfs, z_id);
ASSERT(vp->v_count == 0);
vrecycle(vp, curthread);
zfs_rmnode(zp);
- VFS_RELE(zfsvfs->z_vfs);
return;
}
- ASSERT(zp->z_phys);
- ASSERT(zp->z_dbuf_held);
mutex_exit(&zp->z_lock);
ZFS_OBJ_HOLD_EXIT(zfsvfs, z_id);
}
@@ -706,11 +1039,15 @@ zfs_znode_free(znode_t *zp)
{
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
+ ASSERT(ZTOV(zp) == NULL);
mutex_enter(&zfsvfs->z_znodes_lock);
+ POINTER_INVALIDATE(&zp->z_zfsvfs);
list_remove(&zfsvfs->z_all_znodes, zp);
mutex_exit(&zfsvfs->z_znodes_lock);
kmem_cache_free(znode_cache, zp);
+
+ VFS_RELE(zfsvfs->z_vfs);
}
void
@@ -733,11 +1070,17 @@ zfs_time_stamper_locked(znode_t *zp, uint_t flag, dmu_tx_t *tx)
if (flag & AT_ATIME)
ZFS_TIME_ENCODE(&now, zp->z_phys->zp_atime);
- if (flag & AT_MTIME)
+ if (flag & AT_MTIME) {
ZFS_TIME_ENCODE(&now, zp->z_phys->zp_mtime);
+ if (zp->z_zfsvfs->z_use_fuids)
+ zp->z_phys->zp_flags |= (ZFS_ARCHIVE | ZFS_AV_MODIFIED);
+ }
- if (flag & AT_CTIME)
+ if (flag & AT_CTIME) {
ZFS_TIME_ENCODE(&now, zp->z_phys->zp_ctime);
+ if (zp->z_zfsvfs->z_use_fuids)
+ zp->z_phys->zp_flags |= ZFS_ARCHIVE;
+ }
}
/*
@@ -796,113 +1139,195 @@ zfs_grow_blocksize(znode_t *zp, uint64_t size, dmu_tx_t *tx)
}
/*
- * Free space in a file.
+ * Increase the file length
*
* IN: zp - znode of file to free data in.
- * off - start of section to free.
- * len - length of section to free (0 => to EOF).
- * flag - current file open mode flags.
+ * end - new end-of-file
*
* RETURN: 0 if success
* error code if failure
*/
-int
-zfs_freesp(znode_t *zp, uint64_t off, uint64_t len, int flag, boolean_t log)
+static int
+zfs_extend(znode_t *zp, uint64_t end)
{
- vnode_t *vp = ZTOV(zp);
- dmu_tx_t *tx;
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
- zilog_t *zilog = zfsvfs->z_log;
+ dmu_tx_t *tx;
rl_t *rl;
- uint64_t end = off + len;
- uint64_t size, new_blksz;
+ uint64_t newblksz;
int error;
- if (ZTOV(zp)->v_type == VFIFO)
- return (0);
-
/*
- * If we will change zp_size then lock the whole file,
- * otherwise just lock the range being freed.
+ * We will change zp_size, lock the whole file.
*/
- if (len == 0 || off + len > zp->z_phys->zp_size) {
- rl = zfs_range_lock(zp, 0, UINT64_MAX, RL_WRITER);
- } else {
- rl = zfs_range_lock(zp, off, len, RL_WRITER);
- /* recheck, in case zp_size changed */
- if (off + len > zp->z_phys->zp_size) {
- /* lost race: file size changed, lock whole file */
- zfs_range_unlock(rl);
- rl = zfs_range_lock(zp, 0, UINT64_MAX, RL_WRITER);
- }
- }
+ rl = zfs_range_lock(zp, 0, UINT64_MAX, RL_WRITER);
/*
* Nothing to do if file already at desired length.
*/
- size = zp->z_phys->zp_size;
- if (len == 0 && size == off && off != 0) {
+ if (end <= zp->z_phys->zp_size) {
zfs_range_unlock(rl);
return (0);
}
-
+top:
tx = dmu_tx_create(zfsvfs->z_os);
dmu_tx_hold_bonus(tx, zp->z_id);
- new_blksz = 0;
- if (end > size &&
+ if (end > zp->z_blksz &&
(!ISP2(zp->z_blksz) || zp->z_blksz < zfsvfs->z_max_blksz)) {
/*
* We are growing the file past the current block size.
*/
if (zp->z_blksz > zp->z_zfsvfs->z_max_blksz) {
ASSERT(!ISP2(zp->z_blksz));
- new_blksz = MIN(end, SPA_MAXBLOCKSIZE);
+ newblksz = MIN(end, SPA_MAXBLOCKSIZE);
} else {
- new_blksz = MIN(end, zp->z_zfsvfs->z_max_blksz);
+ newblksz = MIN(end, zp->z_zfsvfs->z_max_blksz);
}
- dmu_tx_hold_write(tx, zp->z_id, 0, MIN(end, new_blksz));
- } else if (off < size) {
- /*
- * If len == 0, we are truncating the file.
- */
- dmu_tx_hold_free(tx, zp->z_id, off, len ? len : DMU_OBJECT_END);
+ dmu_tx_hold_write(tx, zp->z_id, 0, newblksz);
+ } else {
+ newblksz = 0;
}
error = dmu_tx_assign(tx, zfsvfs->z_assign);
if (error) {
- if (error == ERESTART && zfsvfs->z_assign == TXG_NOWAIT)
+ if (error == ERESTART && zfsvfs->z_assign == TXG_NOWAIT) {
dmu_tx_wait(tx);
+ dmu_tx_abort(tx);
+ goto top;
+ }
dmu_tx_abort(tx);
zfs_range_unlock(rl);
return (error);
}
+ dmu_buf_will_dirty(zp->z_dbuf, tx);
+
+ if (newblksz)
+ zfs_grow_blocksize(zp, newblksz, tx);
- if (new_blksz)
- zfs_grow_blocksize(zp, new_blksz, tx);
+ zp->z_phys->zp_size = end;
- if (end > size || len == 0)
- zp->z_phys->zp_size = end;
+ zfs_range_unlock(rl);
- if (off < size) {
- objset_t *os = zfsvfs->z_os;
- uint64_t rlen = len;
+ dmu_tx_commit(tx);
- if (len == 0)
- rlen = -1;
- else if (end > size)
- rlen = size - off;
- VERIFY(0 == dmu_free_range(os, zp->z_id, off, rlen, tx));
+ rw_enter(&zp->z_map_lock, RW_WRITER);
+ error = vinvalbuf(ZTOV(zp), V_SAVE, 0, 0);
+ ASSERT(error == 0);
+ vnode_pager_setsize(ZTOV(zp), end);
+ rw_exit(&zp->z_map_lock);
+
+ return (0);
+}
+
+/*
+ * Free space in a file.
+ *
+ * IN: zp - znode of file to free data in.
+ * off - start of section to free.
+ * len - length of section to free.
+ *
+ * RETURN: 0 if success
+ * error code if failure
+ */
+static int
+zfs_free_range(znode_t *zp, uint64_t off, uint64_t len)
+{
+ zfsvfs_t *zfsvfs = zp->z_zfsvfs;
+ rl_t *rl;
+ int error;
+
+ /*
+ * Lock the range being freed.
+ */
+ rl = zfs_range_lock(zp, off, len, RL_WRITER);
+
+ /*
+ * Nothing to do if file already at desired length.
+ */
+ if (off >= zp->z_phys->zp_size) {
+ zfs_range_unlock(rl);
+ return (0);
}
- if (log) {
- zfs_time_stamper(zp, CONTENT_MODIFIED, tx);
- zfs_log_truncate(zilog, tx, TX_TRUNCATE, zp, off, len);
+ if (off + len > zp->z_phys->zp_size)
+ len = zp->z_phys->zp_size - off;
+
+ error = dmu_free_long_range(zfsvfs->z_os, zp->z_id, off, len);
+
+ if (error == 0) {
+ /*
+ * In FreeBSD we cannot free block in the middle of a file,
+ * but only at the end of a file.
+ */
+ rw_enter(&zp->z_map_lock, RW_WRITER);
+ error = vinvalbuf(ZTOV(zp), V_SAVE, 0, 0);
+ ASSERT(error == 0);
+ vnode_pager_setsize(ZTOV(zp), off);
+ rw_exit(&zp->z_map_lock);
}
zfs_range_unlock(rl);
+ return (error);
+}
+
+/*
+ * Truncate a file
+ *
+ * IN: zp - znode of file to free data in.
+ * end - new end-of-file.
+ *
+ * RETURN: 0 if success
+ * error code if failure
+ */
+static int
+zfs_trunc(znode_t *zp, uint64_t end)
+{
+ zfsvfs_t *zfsvfs = zp->z_zfsvfs;
+ vnode_t *vp = ZTOV(zp);
+ dmu_tx_t *tx;
+ rl_t *rl;
+ int error;
+
+ /*
+ * We will change zp_size, lock the whole file.
+ */
+ rl = zfs_range_lock(zp, 0, UINT64_MAX, RL_WRITER);
+
+ /*
+ * Nothing to do if file already at desired length.
+ */
+ if (end >= zp->z_phys->zp_size) {
+ zfs_range_unlock(rl);
+ return (0);
+ }
+
+ error = dmu_free_long_range(zfsvfs->z_os, zp->z_id, end, -1);
+ if (error) {
+ zfs_range_unlock(rl);
+ return (error);
+ }
+top:
+ tx = dmu_tx_create(zfsvfs->z_os);
+ dmu_tx_hold_bonus(tx, zp->z_id);
+ error = dmu_tx_assign(tx, zfsvfs->z_assign);
+ if (error) {
+ if (error == ERESTART && zfsvfs->z_assign == TXG_NOWAIT) {
+ dmu_tx_wait(tx);
+ dmu_tx_abort(tx);
+ goto top;
+ }
+ dmu_tx_abort(tx);
+ zfs_range_unlock(rl);
+ return (error);
+ }
+ dmu_buf_will_dirty(zp->z_dbuf, tx);
+
+ zp->z_phys->zp_size = end;
+
dmu_tx_commit(tx);
+ zfs_range_unlock(rl);
+
/*
* Clear any mapped pages in the truncated region. This has to
* happen outside of the transaction to avoid the possibility of
@@ -910,30 +1335,90 @@ zfs_freesp(znode_t *zp, uint64_t off, uint64_t len, int flag, boolean_t log)
* about to invalidate.
*/
rw_enter(&zp->z_map_lock, RW_WRITER);
- if (end > size)
- vnode_pager_setsize(vp, end);
- else if (len == 0) {
#if 0
- error = vtruncbuf(vp, curthread->td_ucred, curthread, end, PAGE_SIZE);
+ error = vtruncbuf(vp, curthread->td_ucred, curthread, end, PAGE_SIZE);
#else
- error = vinvalbuf(vp, V_SAVE, 0, 0);
- vnode_pager_setsize(vp, end);
+ error = vinvalbuf(vp, V_SAVE, 0, 0);
+ ASSERT(error == 0);
+ vnode_pager_setsize(vp, end);
#endif
- }
rw_exit(&zp->z_map_lock);
return (0);
}
+/*
+ * Free space in a file
+ *
+ * IN: zp - znode of file to free data in.
+ * off - start of range
+ * len - end of range (0 => EOF)
+ * flag - current file open mode flags.
+ * log - TRUE if this action should be logged
+ *
+ * RETURN: 0 if success
+ * error code if failure
+ */
+int
+zfs_freesp(znode_t *zp, uint64_t off, uint64_t len, int flag, boolean_t log)
+{
+ vnode_t *vp = ZTOV(zp);
+ dmu_tx_t *tx;
+ zfsvfs_t *zfsvfs = zp->z_zfsvfs;
+ zilog_t *zilog = zfsvfs->z_log;
+ int error;
+
+ if (off > zp->z_phys->zp_size) {
+ error = zfs_extend(zp, off+len);
+ if (error == 0 && log)
+ goto log;
+ else
+ return (error);
+ }
+
+ if (len == 0) {
+ error = zfs_trunc(zp, off);
+ } else {
+ if ((error = zfs_free_range(zp, off, len)) == 0 &&
+ off + len > zp->z_phys->zp_size)
+ error = zfs_extend(zp, off+len);
+ }
+ if (error || !log)
+ return (error);
+log:
+ tx = dmu_tx_create(zfsvfs->z_os);
+ dmu_tx_hold_bonus(tx, zp->z_id);
+ error = dmu_tx_assign(tx, zfsvfs->z_assign);
+ if (error) {
+ if (error == ERESTART && zfsvfs->z_assign == TXG_NOWAIT) {
+ dmu_tx_wait(tx);
+ dmu_tx_abort(tx);
+ goto log;
+ }
+ dmu_tx_abort(tx);
+ return (error);
+ }
+
+ zfs_time_stamper(zp, CONTENT_MODIFIED, tx);
+ zfs_log_truncate(zilog, tx, TX_TRUNCATE, zp, off, len);
+
+ dmu_tx_commit(tx);
+ return (0);
+}
+
void
-zfs_create_fs(objset_t *os, cred_t *cr, dmu_tx_t *tx)
+zfs_create_fs(objset_t *os, cred_t *cr, nvlist_t *zplprops, dmu_tx_t *tx)
{
zfsvfs_t zfsvfs;
- uint64_t moid, doid, roid = 0;
- uint64_t version = ZPL_VERSION;
+ uint64_t moid, doid, version;
+ uint64_t sense = ZFS_CASE_SENSITIVE;
+ uint64_t norm = 0;
+ nvpair_t *elem;
int error;
znode_t *rootzp = NULL;
+ vnode_t *vp;
vattr_t vattr;
+ znode_t *zp;
/*
* First attempt to create master node.
@@ -950,9 +1435,35 @@ zfs_create_fs(objset_t *os, cred_t *cr, dmu_tx_t *tx)
/*
* Set starting attributes.
*/
-
- error = zap_update(os, moid, ZPL_VERSION_OBJ, 8, 1, &version, tx);
- ASSERT(error == 0);
+ if (spa_version(dmu_objset_spa(os)) >= SPA_VERSION_FUID)
+ version = ZPL_VERSION;
+ else
+ version = ZPL_VERSION_FUID - 1;
+ error = zap_update(os, moid, ZPL_VERSION_STR,
+ 8, 1, &version, tx);
+ elem = NULL;
+ while ((elem = nvlist_next_nvpair(zplprops, elem)) != NULL) {
+ /* For the moment we expect all zpl props to be uint64_ts */
+ uint64_t val;
+ char *name;
+
+ ASSERT(nvpair_type(elem) == DATA_TYPE_UINT64);
+ VERIFY(nvpair_value_uint64(elem, &val) == 0);
+ name = nvpair_name(elem);
+ if (strcmp(name, zfs_prop_to_name(ZFS_PROP_VERSION)) == 0) {
+ version = val;
+ error = zap_update(os, moid, ZPL_VERSION_STR,
+ 8, 1, &version, tx);
+ } else {
+ error = zap_update(os, moid, name, 8, 1, &val, tx);
+ }
+ ASSERT(error == 0);
+ if (strcmp(name, zfs_prop_to_name(ZFS_PROP_NORMALIZE)) == 0)
+ norm = val;
+ else if (strcmp(name, zfs_prop_to_name(ZFS_PROP_CASE)) == 0)
+ sense = val;
+ }
+ ASSERT(version != 0);
/*
* Create a delete queue.
@@ -966,39 +1477,62 @@ zfs_create_fs(objset_t *os, cred_t *cr, dmu_tx_t *tx)
* Create root znode. Create minimal znode/vnode/zfsvfs
* to allow zfs_mknode to work.
*/
+ VATTR_NULL(&vattr);
vattr.va_mask = AT_MODE|AT_UID|AT_GID|AT_TYPE;
vattr.va_type = VDIR;
vattr.va_mode = S_IFDIR|0755;
- vattr.va_uid = UID_ROOT;
- vattr.va_gid = GID_WHEEL;
+ vattr.va_uid = crgetuid(cr);
+ vattr.va_gid = crgetgid(cr);
rootzp = kmem_cache_alloc(znode_cache, KM_SLEEP);
- zfs_znode_cache_constructor(rootzp, NULL, 0);
- rootzp->z_zfsvfs = &zfsvfs;
+ zfs_znode_cache_constructor(rootzp, &zfsvfs, 0);
rootzp->z_unlinked = 0;
rootzp->z_atime_dirty = 0;
- rootzp->z_dbuf_held = 0;
+
+ vp = ZTOV(rootzp);
+ vp->v_type = VDIR;
bzero(&zfsvfs, sizeof (zfsvfs_t));
zfsvfs.z_os = os;
zfsvfs.z_assign = TXG_NOWAIT;
zfsvfs.z_parent = &zfsvfs;
+ zfsvfs.z_version = version;
+ zfsvfs.z_use_fuids = USE_FUIDS(version, os);
+ zfsvfs.z_norm = norm;
+ /*
+ * Fold case on file systems that are always or sometimes case
+ * insensitive.
+ */
+ if (sense == ZFS_CASE_INSENSITIVE || sense == ZFS_CASE_MIXED)
+ zfsvfs.z_norm |= U8_TEXTPREP_TOUPPER;
mutex_init(&zfsvfs.z_znodes_lock, NULL, MUTEX_DEFAULT, NULL);
list_create(&zfsvfs.z_all_znodes, sizeof (znode_t),
offsetof(znode_t, z_link_node));
- zfs_mknode(rootzp, &vattr, &roid, tx, cr, IS_ROOT_NODE, NULL, 0);
- ASSERT3U(rootzp->z_id, ==, roid);
- error = zap_add(os, moid, ZFS_ROOT_OBJ, 8, 1, &roid, tx);
+ ASSERT(!POINTER_IS_VALID(rootzp->z_zfsvfs));
+ rootzp->z_zfsvfs = &zfsvfs;
+ zfs_mknode(rootzp, &vattr, tx, cr, IS_ROOT_NODE, &zp, 0, NULL, NULL);
+ ASSERT3P(zp, ==, rootzp);
+ error = zap_add(os, moid, ZFS_ROOT_OBJ, 8, 1, &rootzp->z_id, tx);
ASSERT(error == 0);
+ POINTER_INVALIDATE(&rootzp->z_zfsvfs);
+ VI_LOCK(vp);
+ ZTOV(rootzp)->v_data = NULL;
+ ZTOV(rootzp)->v_count = 0;
+ ZTOV(rootzp)->v_holdcnt = 0;
+ ZTOV(rootzp) = NULL;
+ VOP_UNLOCK(vp, 0);
+ vdestroy(vp);
+ dmu_buf_rele(rootzp->z_dbuf, NULL);
+ rootzp->z_dbuf = NULL;
mutex_destroy(&zfsvfs.z_znodes_lock);
kmem_cache_free(znode_cache, rootzp);
}
-#endif /* _KERNEL */
+#endif /* _KERNEL */
/*
* Given an object number, return its parent object number and whether
* or not the object is an extended attribute directory.
@@ -1058,7 +1592,8 @@ zfs_obj_to_path(objset_t *osp, uint64_t obj, char *buf, int len)
if (is_xattrdir) {
(void) sprintf(component + 1, "<xattrdir>");
} else {
- error = zap_value_search(osp, pobj, obj, component + 1);
+ error = zap_value_search(osp, pobj, obj,
+ ZFS_DIRENT_OBJ(-1ULL), component + 1);
if (error != 0)
break;
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zil.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zil.c
index 69ee509..1f6fa0d 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zil.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zil.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/zfs_context.h>
#include <sys/spa.h>
#include <sys/dmu.h>
@@ -174,7 +172,11 @@ zil_read_log_block(zilog_t *zilog, const blkptr_t *bp, arc_buf_t **abufpp)
*abufpp = NULL;
- error = arc_read(NULL, zilog->zl_spa, &blk, byteswap_uint64_array,
+ /*
+ * We shouldn't be doing any scrubbing while we're doing log
+ * replay, it's OK to not lock.
+ */
+ error = arc_read_nolock(NULL, zilog->zl_spa, &blk,
arc_getbuf_func, abufpp, ZIO_PRIORITY_SYNC_READ, ZIO_FLAG_CANFAIL |
ZIO_FLAG_SPECULATIVE | ZIO_FLAG_SCRUB, &aflags, &zb);
@@ -185,17 +187,20 @@ zil_read_log_block(zilog_t *zilog, const blkptr_t *bp, arc_buf_t **abufpp)
zio_cksum_t cksum = bp->blk_cksum;
/*
+ * Validate the checksummed log block.
+ *
* Sequence numbers should be... sequential. The checksum
* verifier for the next block should be bp's checksum plus 1.
+ *
+ * Also check the log chain linkage and size used.
*/
cksum.zc_word[ZIL_ZC_SEQ]++;
- if (bcmp(&cksum, &ztp->zit_next_blk.blk_cksum, sizeof (cksum)))
- error = ESTALE;
- else if (BP_IS_HOLE(&ztp->zit_next_blk))
- error = ENOENT;
- else if (ztp->zit_nused > (blksz - sizeof (zil_trailer_t)))
- error = EOVERFLOW;
+ if (bcmp(&cksum, &ztp->zit_next_blk.blk_cksum,
+ sizeof (cksum)) || BP_IS_HOLE(&ztp->zit_next_blk) ||
+ (ztp->zit_nused > (blksz - sizeof (zil_trailer_t)))) {
+ error = ECKSUM;
+ }
if (error) {
VERIFY(arc_buf_remove_ref(*abufpp, abufpp) == 1);
@@ -290,7 +295,8 @@ zil_claim_log_block(zilog_t *zilog, blkptr_t *bp, void *tx, uint64_t first_txg)
*/
if (bp->blk_birth >= first_txg &&
zil_dva_tree_add(&zilog->zl_dva_tree, BP_IDENTITY(bp)) == 0) {
- err = zio_wait(zio_claim(NULL, spa, first_txg, bp, NULL, NULL));
+ err = zio_wait(zio_claim(NULL, spa, first_txg, bp, NULL, NULL,
+ ZIO_FLAG_MUSTSUCCEED));
ASSERT(err == 0);
}
}
@@ -430,6 +436,16 @@ zil_destroy(zilog_t *zilog, boolean_t keep_first)
mutex_enter(&zilog->zl_lock);
+ /*
+ * It is possible for the ZIL to get the previously mounted zilog
+ * structure of the same dataset if quickly remounted and the dbuf
+ * eviction has not completed. In this case we can see a non
+ * empty lwb list and keep_first will be set. We fix this by
+ * clearing the keep_first. This will be slower but it's very rare.
+ */
+ if (!list_is_empty(&zilog->zl_lwb_list) && keep_first)
+ keep_first = B_FALSE;
+
ASSERT3U(zilog->zl_destroy_txg, <, txg);
zilog->zl_destroy_txg = txg;
zilog->zl_keep_first = keep_first;
@@ -453,12 +469,37 @@ zil_destroy(zilog_t *zilog, boolean_t keep_first)
mutex_exit(&zilog->zl_lock);
dmu_tx_commit(tx);
+}
+
+/*
+ * zil_rollback_destroy() is only called by the rollback code.
+ * We already have a syncing tx. Rollback has exclusive access to the
+ * dataset, so we don't have to worry about concurrent zil access.
+ * The actual freeing of any log blocks occurs in zil_sync() later in
+ * this txg syncing phase.
+ */
+void
+zil_rollback_destroy(zilog_t *zilog, dmu_tx_t *tx)
+{
+ const zil_header_t *zh = zilog->zl_header;
+ uint64_t txg;
- if (keep_first) /* no need to wait in this case */
+ if (BP_IS_HOLE(&zh->zh_log))
return;
- txg_wait_synced(zilog->zl_dmu_pool, txg);
- ASSERT(BP_IS_HOLE(&zh->zh_log));
+ txg = dmu_tx_get_txg(tx);
+ ASSERT3U(zilog->zl_destroy_txg, <, txg);
+ zilog->zl_destroy_txg = txg;
+ zilog->zl_keep_first = B_FALSE;
+
+ /*
+ * Ensure there's no outstanding ZIL IO. No lwbs or just the
+ * unused one that allocated in advance is ok.
+ */
+ ASSERT(zilog->zl_lwb_list.list_head.list_next ==
+ zilog->zl_lwb_list.list_head.list_prev);
+ (void) zil_parse(zilog, zil_free_log_block, zil_free_log_record,
+ tx, zh->zh_claim_txg);
}
int
@@ -471,9 +512,9 @@ zil_claim(char *osname, void *txarg)
objset_t *os;
int error;
- error = dmu_objset_open(osname, DMU_OST_ANY, DS_MODE_STANDARD, &os);
+ error = dmu_objset_open(osname, DMU_OST_ANY, DS_MODE_USER, &os);
if (error) {
- cmn_err(CE_WARN, "can't process intent log for %s", osname);
+ cmn_err(CE_WARN, "can't open objset for %s", osname);
return (0);
}
@@ -500,104 +541,164 @@ zil_claim(char *osname, void *txarg)
return (0);
}
-void
-zil_add_vdev(zilog_t *zilog, uint64_t vdev)
+/*
+ * Check the log by walking the log chain.
+ * Checksum errors are ok as they indicate the end of the chain.
+ * Any other error (no device or read failure) returns an error.
+ */
+/* ARGSUSED */
+int
+zil_check_log_chain(char *osname, void *txarg)
{
- zil_vdev_t *zv, *new;
- uint64_t bmap_sz = sizeof (zilog->zl_vdev_bmap) << 3;
- uchar_t *cp;
+ zilog_t *zilog;
+ zil_header_t *zh;
+ blkptr_t blk;
+ arc_buf_t *abuf;
+ objset_t *os;
+ char *lrbuf;
+ zil_trailer_t *ztp;
+ int error;
- if (zfs_nocacheflush)
- return;
+ error = dmu_objset_open(osname, DMU_OST_ANY, DS_MODE_USER, &os);
+ if (error) {
+ cmn_err(CE_WARN, "can't open objset for %s", osname);
+ return (0);
+ }
- if (vdev < bmap_sz) {
- cp = zilog->zl_vdev_bmap + (vdev / 8);
- atomic_or_8(cp, 1 << (vdev % 8));
- } else {
- /*
- * insert into ordered list
- */
- mutex_enter(&zilog->zl_lock);
- for (zv = list_head(&zilog->zl_vdev_list); zv != NULL;
- zv = list_next(&zilog->zl_vdev_list, zv)) {
- if (zv->vdev == vdev) {
- /* duplicate found - just return */
- mutex_exit(&zilog->zl_lock);
- return;
- }
- if (zv->vdev > vdev) {
- /* insert before this entry */
- new = kmem_alloc(sizeof (zil_vdev_t),
- KM_SLEEP);
- new->vdev = vdev;
- list_insert_before(&zilog->zl_vdev_list,
- zv, new);
- mutex_exit(&zilog->zl_lock);
- return;
- }
- }
- /* ran off end of list, insert at the end */
- ASSERT(zv == NULL);
- new = kmem_alloc(sizeof (zil_vdev_t), KM_SLEEP);
- new->vdev = vdev;
- list_insert_tail(&zilog->zl_vdev_list, new);
- mutex_exit(&zilog->zl_lock);
+ zilog = dmu_objset_zil(os);
+ zh = zil_header_in_syncing_context(zilog);
+ blk = zh->zh_log;
+ if (BP_IS_HOLE(&blk)) {
+ dmu_objset_close(os);
+ return (0); /* no chain */
+ }
+
+ for (;;) {
+ error = zil_read_log_block(zilog, &blk, &abuf);
+ if (error)
+ break;
+ lrbuf = abuf->b_data;
+ ztp = (zil_trailer_t *)(lrbuf + BP_GET_LSIZE(&blk)) - 1;
+ blk = ztp->zit_next_blk;
+ VERIFY(arc_buf_remove_ref(abuf, &abuf) == 1);
+ }
+ dmu_objset_close(os);
+ if (error == ECKSUM)
+ return (0); /* normal end of chain */
+ return (error);
+}
+
+/*
+ * Clear a log chain
+ */
+/* ARGSUSED */
+int
+zil_clear_log_chain(char *osname, void *txarg)
+{
+ zilog_t *zilog;
+ zil_header_t *zh;
+ objset_t *os;
+ dmu_tx_t *tx;
+ int error;
+
+ error = dmu_objset_open(osname, DMU_OST_ANY, DS_MODE_USER, &os);
+ if (error) {
+ cmn_err(CE_WARN, "can't open objset for %s", osname);
+ return (0);
}
+
+ zilog = dmu_objset_zil(os);
+ tx = dmu_tx_create(zilog->zl_os);
+ (void) dmu_tx_assign(tx, TXG_WAIT);
+ zh = zil_header_in_syncing_context(zilog);
+ BP_ZERO(&zh->zh_log);
+ dsl_dataset_dirty(dmu_objset_ds(os), tx);
+ dmu_tx_commit(tx);
+ dmu_objset_close(os);
+ return (0);
+}
+
+static int
+zil_vdev_compare(const void *x1, const void *x2)
+{
+ uint64_t v1 = ((zil_vdev_node_t *)x1)->zv_vdev;
+ uint64_t v2 = ((zil_vdev_node_t *)x2)->zv_vdev;
+
+ if (v1 < v2)
+ return (-1);
+ if (v1 > v2)
+ return (1);
+
+ return (0);
}
-/* start an async flush of the write cache for this vdev */
void
-zil_flush_vdev(spa_t *spa, uint64_t vdev, zio_t **zio)
+zil_add_block(zilog_t *zilog, blkptr_t *bp)
{
- vdev_t *vd;
+ avl_tree_t *t = &zilog->zl_vdev_tree;
+ avl_index_t where;
+ zil_vdev_node_t *zv, zvsearch;
+ int ndvas = BP_GET_NDVAS(bp);
+ int i;
- if (*zio == NULL)
- *zio = zio_root(spa, NULL, NULL, ZIO_FLAG_CANFAIL);
+ if (zfs_nocacheflush)
+ return;
- vd = vdev_lookup_top(spa, vdev);
- ASSERT(vd);
+ ASSERT(zilog->zl_writer);
- (void) zio_nowait(zio_ioctl(*zio, spa, vd, DKIOCFLUSHWRITECACHE,
- NULL, NULL, ZIO_PRIORITY_NOW,
- ZIO_FLAG_CANFAIL | ZIO_FLAG_DONT_RETRY));
+ /*
+ * Even though we're zl_writer, we still need a lock because the
+ * zl_get_data() callbacks may have dmu_sync() done callbacks
+ * that will run concurrently.
+ */
+ mutex_enter(&zilog->zl_vdev_lock);
+ for (i = 0; i < ndvas; i++) {
+ zvsearch.zv_vdev = DVA_GET_VDEV(&bp->blk_dva[i]);
+ if (avl_find(t, &zvsearch, &where) == NULL) {
+ zv = kmem_alloc(sizeof (*zv), KM_SLEEP);
+ zv->zv_vdev = zvsearch.zv_vdev;
+ avl_insert(t, zv, where);
+ }
+ }
+ mutex_exit(&zilog->zl_vdev_lock);
}
void
zil_flush_vdevs(zilog_t *zilog)
{
- zil_vdev_t *zv;
- zio_t *zio = NULL;
spa_t *spa = zilog->zl_spa;
- uint64_t vdev;
- uint8_t b;
- int i, j;
+ avl_tree_t *t = &zilog->zl_vdev_tree;
+ void *cookie = NULL;
+ zil_vdev_node_t *zv;
+ zio_t *zio;
ASSERT(zilog->zl_writer);
- for (i = 0; i < sizeof (zilog->zl_vdev_bmap); i++) {
- b = zilog->zl_vdev_bmap[i];
- if (b == 0)
- continue;
- for (j = 0; j < 8; j++) {
- if (b & (1 << j)) {
- vdev = (i << 3) + j;
- zil_flush_vdev(spa, vdev, &zio);
- }
- }
- zilog->zl_vdev_bmap[i] = 0;
- }
+ /*
+ * We don't need zl_vdev_lock here because we're the zl_writer,
+ * and all zl_get_data() callbacks are done.
+ */
+ if (avl_numnodes(t) == 0)
+ return;
+
+ spa_config_enter(spa, SCL_STATE, FTAG, RW_READER);
- while ((zv = list_head(&zilog->zl_vdev_list)) != NULL) {
- zil_flush_vdev(spa, zv->vdev, &zio);
- list_remove(&zilog->zl_vdev_list, zv);
- kmem_free(zv, sizeof (zil_vdev_t));
+ zio = zio_root(spa, NULL, NULL, ZIO_FLAG_CANFAIL);
+
+ while ((zv = avl_destroy_nodes(t, &cookie)) != NULL) {
+ vdev_t *vd = vdev_lookup_top(spa, zv->zv_vdev);
+ if (vd != NULL)
+ zio_flush(zio, vd);
+ kmem_free(zv, sizeof (*zv));
}
+
/*
* Wait for all the flushes to complete. Not all devices actually
* support the DKIOCFLUSHWRITECACHE ioctl, so it's OK if it fails.
*/
- if (zio)
- (void) zio_wait(zio);
+ (void) zio_wait(zio);
+
+ spa_config_exit(spa, SCL_STATE, FTAG);
}
/*
@@ -609,6 +710,15 @@ zil_lwb_write_done(zio_t *zio)
lwb_t *lwb = zio->io_private;
zilog_t *zilog = lwb->lwb_zilog;
+ ASSERT(BP_GET_COMPRESS(zio->io_bp) == ZIO_COMPRESS_OFF);
+ ASSERT(BP_GET_CHECKSUM(zio->io_bp) == ZIO_CHECKSUM_ZILOG);
+ ASSERT(BP_GET_TYPE(zio->io_bp) == DMU_OT_INTENT_LOG);
+ ASSERT(BP_GET_LEVEL(zio->io_bp) == 0);
+ ASSERT(BP_GET_BYTEORDER(zio->io_bp) == ZFS_HOST_BYTEORDER);
+ ASSERT(!BP_IS_GANG(zio->io_bp));
+ ASSERT(!BP_IS_HOLE(zio->io_bp));
+ ASSERT(zio->io_bp->blk_fill == 0);
+
/*
* Now that we've written this log block, we have a stable pointer
* to the next block in the chain, so it's OK to let the txg in
@@ -619,19 +729,13 @@ zil_lwb_write_done(zio_t *zio)
zio_buf_free(lwb->lwb_buf, lwb->lwb_sz);
mutex_enter(&zilog->zl_lock);
lwb->lwb_buf = NULL;
- if (zio->io_error) {
+ if (zio->io_error)
zilog->zl_log_error = B_TRUE;
- mutex_exit(&zilog->zl_lock);
- return;
- }
mutex_exit(&zilog->zl_lock);
}
/*
* Initialize the io for a log block.
- *
- * Note, we should not initialize the IO until we are about
- * to use it, since zio_rewrite() does a spa_config_enter().
*/
static void
zil_lwb_write_init(zilog_t *zilog, lwb_t *lwb)
@@ -649,9 +753,9 @@ zil_lwb_write_init(zilog_t *zilog, lwb_t *lwb)
}
if (lwb->lwb_zio == NULL) {
lwb->lwb_zio = zio_rewrite(zilog->zl_root_zio, zilog->zl_spa,
- ZIO_CHECKSUM_ZILOG, 0, &lwb->lwb_blk, lwb->lwb_buf,
+ 0, &lwb->lwb_blk, lwb->lwb_buf,
lwb->lwb_sz, zil_lwb_write_done, lwb,
- ZIO_PRIORITY_LOG_WRITE, ZIO_FLAG_MUSTSUCCEED, &zb);
+ ZIO_PRIORITY_LOG_WRITE, ZIO_FLAG_CANFAIL, &zb);
}
}
@@ -751,8 +855,8 @@ zil_lwb_write_start(zilog_t *zilog, lwb_t *lwb)
list_insert_tail(&zilog->zl_lwb_list, nlwb);
mutex_exit(&zilog->zl_lock);
- /* Record the vdev for later flushing */
- zil_add_vdev(zilog, DVA_GET_VDEV(BP_IDENTITY(&(lwb->lwb_blk))));
+ /* Record the block for later vdev flushing */
+ zil_add_block(zilog, &lwb->lwb_blk);
/*
* kick off the write for the old log block
@@ -848,7 +952,7 @@ zil_lwb_commit(zilog_t *zilog, itx_t *itx, lwb_t *lwb)
}
itx_t *
-zil_itx_create(int txtype, size_t lrsize)
+zil_itx_create(uint64_t txtype, size_t lrsize)
{
itx_t *itx;
@@ -857,6 +961,7 @@ zil_itx_create(int txtype, size_t lrsize)
itx = kmem_alloc(offsetof(itx_t, itx_lr) + lrsize, KM_SLEEP);
itx->itx_lr.lrc_txtype = txtype;
itx->itx_lr.lrc_reclen = lrsize;
+ itx->itx_sod = lrsize; /* if write & WR_NEED_COPY will be increased */
itx->itx_lr.lrc_seq = 0; /* defensive */
return (itx);
@@ -871,7 +976,7 @@ zil_itx_assign(zilog_t *zilog, itx_t *itx, dmu_tx_t *tx)
mutex_enter(&zilog->zl_lock);
list_insert_tail(&zilog->zl_itx_list, itx);
- zilog->zl_itx_list_sz += itx->itx_lr.lrc_reclen;
+ zilog->zl_itx_list_sz += itx->itx_sod;
itx->itx_lr.lrc_txg = dmu_tx_get_txg(tx);
itx->itx_lr.lrc_seq = seq = ++zilog->zl_itx_seq;
mutex_exit(&zilog->zl_lock);
@@ -907,7 +1012,7 @@ zil_itx_clean(zilog_t *zilog)
while ((itx = list_head(&zilog->zl_itx_list)) != NULL &&
itx->itx_lr.lrc_txg <= MIN(synced_txg, freeze_txg)) {
list_remove(&zilog->zl_itx_list, itx);
- zilog->zl_itx_list_sz -= itx->itx_lr.lrc_reclen;
+ zilog->zl_itx_list_sz -= itx->itx_sod;
list_insert_tail(&clean_list, itx);
}
cv_broadcast(&zilog->zl_cv_writer);
@@ -941,18 +1046,17 @@ zil_clean(zilog_t *zilog)
mutex_exit(&zilog->zl_lock);
}
-void
+static void
zil_commit_writer(zilog_t *zilog, uint64_t seq, uint64_t foid)
{
uint64_t txg;
- uint64_t reclen;
uint64_t commit_seq = 0;
itx_t *itx, *itx_next = (itx_t *)-1;
lwb_t *lwb;
spa_t *spa;
zilog->zl_writer = B_TRUE;
- zilog->zl_root_zio = NULL;
+ ASSERT(zilog->zl_root_zio == NULL);
spa = zilog->zl_spa;
if (zilog->zl_suspend) {
@@ -1009,10 +1113,9 @@ zil_commit_writer(zilog_t *zilog, uint64_t seq, uint64_t foid)
if (itx == NULL)
break;
- reclen = itx->itx_lr.lrc_reclen;
if ((itx->itx_lr.lrc_seq > seq) &&
((lwb == NULL) || (lwb->lwb_nused == 0) ||
- (lwb->lwb_nused + reclen > ZIL_BLK_DATA_SZ(lwb)))) {
+ (lwb->lwb_nused + itx->itx_sod > ZIL_BLK_DATA_SZ(lwb)))) {
break;
}
@@ -1024,6 +1127,7 @@ zil_commit_writer(zilog_t *zilog, uint64_t seq, uint64_t foid)
*/
itx_next = list_next(&zilog->zl_itx_list, itx);
list_remove(&zilog->zl_itx_list, itx);
+ zilog->zl_itx_list_sz -= itx->itx_sod;
mutex_exit(&zilog->zl_lock);
txg = itx->itx_lr.lrc_txg;
ASSERT(txg);
@@ -1034,7 +1138,6 @@ zil_commit_writer(zilog_t *zilog, uint64_t seq, uint64_t foid)
kmem_free(itx, offsetof(itx_t, itx_lr)
+ itx->itx_lr.lrc_reclen);
mutex_enter(&zilog->zl_lock);
- zilog->zl_itx_list_sz -= reclen;
}
DTRACE_PROBE1(zil__cw2, zilog_t *, zilog);
/* determine commit sequence number */
@@ -1058,9 +1161,9 @@ zil_commit_writer(zilog_t *zilog, uint64_t seq, uint64_t foid)
if (zilog->zl_root_zio) {
DTRACE_PROBE1(zil__cw3, zilog_t *, zilog);
(void) zio_wait(zilog->zl_root_zio);
+ zilog->zl_root_zio = NULL;
DTRACE_PROBE1(zil__cw4, zilog_t *, zilog);
- if (!zfs_nocacheflush)
- zil_flush_vdevs(zilog);
+ zil_flush_vdevs(zilog);
}
if (zilog->zl_log_error || lwb == NULL) {
@@ -1195,8 +1298,6 @@ zil_alloc(objset_t *os, zil_header_t *zh_phys)
zilog->zl_destroy_txg = TXG_INITIAL - 1;
mutex_init(&zilog->zl_lock, NULL, MUTEX_DEFAULT, NULL);
- cv_init(&zilog->zl_cv_writer, NULL, CV_DEFAULT, NULL);
- cv_init(&zilog->zl_cv_suspend, NULL, CV_DEFAULT, NULL);
list_create(&zilog->zl_itx_list, sizeof (itx_t),
offsetof(itx_t, itx_node));
@@ -1204,8 +1305,13 @@ zil_alloc(objset_t *os, zil_header_t *zh_phys)
list_create(&zilog->zl_lwb_list, sizeof (lwb_t),
offsetof(lwb_t, lwb_node));
- list_create(&zilog->zl_vdev_list, sizeof (zil_vdev_t),
- offsetof(zil_vdev_t, vdev_seq_node));
+ mutex_init(&zilog->zl_vdev_lock, NULL, MUTEX_DEFAULT, NULL);
+
+ avl_create(&zilog->zl_vdev_tree, zil_vdev_compare,
+ sizeof (zil_vdev_node_t), offsetof(zil_vdev_node_t, zv_node));
+
+ cv_init(&zilog->zl_cv_writer, NULL, CV_DEFAULT, NULL);
+ cv_init(&zilog->zl_cv_suspend, NULL, CV_DEFAULT, NULL);
return (zilog);
}
@@ -1214,7 +1320,6 @@ void
zil_free(zilog_t *zilog)
{
lwb_t *lwb;
- zil_vdev_t *zv;
zilog->zl_stop_sync = 1;
@@ -1226,38 +1331,36 @@ zil_free(zilog_t *zilog)
}
list_destroy(&zilog->zl_lwb_list);
- while ((zv = list_head(&zilog->zl_vdev_list)) != NULL) {
- list_remove(&zilog->zl_vdev_list, zv);
- kmem_free(zv, sizeof (zil_vdev_t));
- }
- list_destroy(&zilog->zl_vdev_list);
+ avl_destroy(&zilog->zl_vdev_tree);
+ mutex_destroy(&zilog->zl_vdev_lock);
ASSERT(list_head(&zilog->zl_itx_list) == NULL);
list_destroy(&zilog->zl_itx_list);
- cv_destroy(&zilog->zl_cv_suspend);
- cv_destroy(&zilog->zl_cv_writer);
mutex_destroy(&zilog->zl_lock);
+ cv_destroy(&zilog->zl_cv_writer);
+ cv_destroy(&zilog->zl_cv_suspend);
+
kmem_free(zilog, sizeof (zilog_t));
}
/*
* return true if the initial log block is not valid
*/
-static int
+static boolean_t
zil_empty(zilog_t *zilog)
{
const zil_header_t *zh = zilog->zl_header;
arc_buf_t *abuf = NULL;
if (BP_IS_HOLE(&zh->zh_log))
- return (1);
+ return (B_TRUE);
if (zil_read_log_block(zilog, &zh->zh_log, &abuf) != 0)
- return (1);
+ return (B_TRUE);
VERIFY(arc_buf_remove_ref(abuf, &abuf) == 1);
- return (0);
+ return (B_FALSE);
}
/*
@@ -1326,7 +1429,6 @@ zil_suspend(zilog_t *zilog)
*/
while (zilog->zl_suspending)
cv_wait(&zilog->zl_cv_suspend, &zilog->zl_lock);
- ASSERT(BP_IS_HOLE(&zh->zh_log));
mutex_exit(&zilog->zl_lock);
return (0);
}
@@ -1346,7 +1448,6 @@ zil_suspend(zilog_t *zilog)
zil_destroy(zilog, B_FALSE);
mutex_enter(&zilog->zl_lock);
- ASSERT(BP_IS_HOLE(&zh->zh_log));
zilog->zl_suspending = B_FALSE;
cv_broadcast(&zilog->zl_cv_suspend);
mutex_exit(&zilog->zl_lock);
@@ -1366,6 +1467,7 @@ zil_resume(zilog_t *zilog)
typedef struct zil_replay_arg {
objset_t *zr_os;
zil_replay_func_t **zr_replay;
+ zil_replay_cleaner_t *zr_replay_cleaner;
void *zr_arg;
uint64_t *zr_txgp;
boolean_t zr_byteswap;
@@ -1391,6 +1493,9 @@ zil_replay_log_record(zilog_t *zilog, lr_t *lr, void *zra, uint64_t claim_txg)
if (lr->lrc_seq <= zh->zh_replay_seq) /* already replayed */
return;
+ /* Strip case-insensitive bit, still present in log record */
+ txtype &= ~TX_CI;
+
/*
* Make a copy of the data so we can revise and extend it.
*/
@@ -1465,10 +1570,12 @@ zil_replay_log_record(zilog_t *zilog, lr_t *lr, void *zra, uint64_t claim_txg)
* On the first pass, arrange for the replay vector
* to fail its dmu_tx_assign(). That's the only way
* to ensure that those code paths remain well tested.
+ *
+ * Only byteswap (if needed) on the 1st pass.
*/
*zr->zr_txgp = replay_txg - (pass == 1);
error = zr->zr_replay[txtype](zr->zr_arg, zr->zr_lrbuf,
- zr->zr_byteswap);
+ zr->zr_byteswap && pass == 1);
*zr->zr_txgp = TXG_NOWAIT;
}
@@ -1491,6 +1598,8 @@ zil_replay_log_record(zilog_t *zilog, lr_t *lr, void *zra, uint64_t claim_txg)
* transaction.
*/
if (error != ERESTART && !sunk) {
+ if (zr->zr_replay_cleaner)
+ zr->zr_replay_cleaner(zr->zr_arg);
txg_wait_synced(spa_get_dsl(zilog->zl_spa), 0);
sunk = B_TRUE;
continue; /* retry */
@@ -1510,8 +1619,9 @@ zil_replay_log_record(zilog_t *zilog, lr_t *lr, void *zra, uint64_t claim_txg)
name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
dmu_objset_name(zr->zr_os, name);
cmn_err(CE_WARN, "ZFS replay transaction error %d, "
- "dataset %s, seq 0x%llx, txtype %llu\n",
- error, name, (u_longlong_t)lr->lrc_seq, (u_longlong_t)txtype);
+ "dataset %s, seq 0x%llx, txtype %llu %s\n",
+ error, name, (u_longlong_t)lr->lrc_seq, (u_longlong_t)txtype,
+ (lr->lrc_txtype & TX_CI) ? "CI" : "");
zilog->zl_stop_replay = 1;
kmem_free(name, MAXNAMELEN);
}
@@ -1528,7 +1638,8 @@ zil_incr_blks(zilog_t *zilog, blkptr_t *bp, void *arg, uint64_t claim_txg)
*/
void
zil_replay(objset_t *os, void *arg, uint64_t *txgp,
- zil_replay_func_t *replay_func[TX_MAX_TYPE])
+ zil_replay_func_t *replay_func[TX_MAX_TYPE],
+ zil_replay_cleaner_t *replay_cleaner)
{
zilog_t *zilog = dmu_objset_zil(os);
const zil_header_t *zh = zilog->zl_header;
@@ -1542,6 +1653,7 @@ zil_replay(objset_t *os, void *arg, uint64_t *txgp,
zr.zr_os = os;
zr.zr_replay = replay_func;
+ zr.zr_replay_cleaner = replay_cleaner;
zr.zr_arg = arg;
zr.zr_txgp = txgp;
zr.zr_byteswap = BP_SHOULD_BYTESWAP(&zh->zh_log);
@@ -1560,6 +1672,7 @@ zil_replay(objset_t *os, void *arg, uint64_t *txgp,
kmem_free(zr.zr_lrbuf, 2 * SPA_MAXBLOCKSIZE);
zil_destroy(zilog, B_FALSE);
+ txg_wait_synced(zilog->zl_dmu_pool, zilog->zl_destroy_txg);
//printf("ZFS: Replay of ZIL on %s finished.\n", os->os->os_spa->spa_name);
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio.c
index b5dd35f..4650d42 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/zfs_context.h>
#include <sys/fm/fs/zfs.h>
#include <sys/spa.h>
@@ -61,23 +59,9 @@ uint8_t zio_priority_table[ZIO_PRIORITY_TABLE_SIZE] = {
char *zio_type_name[ZIO_TYPES] = {
"null", "read", "write", "free", "claim", "ioctl" };
-/* At or above this size, force gang blocking - for testing */
-uint64_t zio_gang_bang = SPA_MAXBLOCKSIZE + 1;
-
-/* Force an allocation failure when non-zero */
-uint16_t zio_zil_fail_shift = 0;
-
-typedef struct zio_sync_pass {
- int zp_defer_free; /* defer frees after this pass */
- int zp_dontcompress; /* don't compress after this pass */
- int zp_rewrite; /* rewrite new bps after this pass */
-} zio_sync_pass_t;
-
-zio_sync_pass_t zio_sync_pass = {
- 1, /* zp_defer_free */
- 4, /* zp_dontcompress */
- 1, /* zp_rewrite */
-};
+#define SYNC_PASS_DEFERRED_FREE 1 /* defer frees after this pass */
+#define SYNC_PASS_DONT_COMPRESS 4 /* don't compress after this pass */
+#define SYNC_PASS_REWRITE 1 /* rewrite new bps after this pass */
/*
* ==========================================================================
@@ -94,6 +78,13 @@ kmem_cache_t *zio_data_buf_cache[SPA_MAXBLOCKSIZE >> SPA_MINBLOCKSHIFT];
extern vmem_t *zio_alloc_arena;
#endif
+/*
+ * An allocating zio is one that either currently has the DVA allocate
+ * stage set or will have it later in its lifetime.
+ */
+#define IO_IS_ALLOCATING(zio) \
+ ((zio)->io_orig_pipeline & (1U << ZIO_STAGE_DVA_ALLOCATE))
+
void
zio_init(void)
{
@@ -107,7 +98,6 @@ zio_init(void)
data_alloc_arena = zio_alloc_arena;
#endif
#endif
-
zio_cache = kmem_cache_create("zio_cache", sizeof (zio_t), 0,
NULL, NULL, NULL, NULL, NULL, 0);
@@ -144,9 +134,6 @@ zio_init(void)
zio_data_buf_cache[c] = kmem_cache_create(name, size,
align, NULL, NULL, NULL, NULL, data_alloc_arena,
KMC_NODEBUG);
-
- dprintf("creating cache for size %5lx align %5lx\n",
- size, align);
}
}
@@ -212,7 +199,7 @@ zio_buf_alloc(size_t size)
ASSERT(c < SPA_MAXBLOCKSIZE >> SPA_MINBLOCKSHIFT);
- return (kmem_cache_alloc(zio_buf_cache[c], KM_SLEEP));
+ return (kmem_cache_alloc(zio_buf_cache[c], KM_PUSHPAGE));
#else
return (kmem_alloc(size, KM_SLEEP));
#endif
@@ -232,7 +219,7 @@ zio_data_buf_alloc(size_t size)
ASSERT(c < SPA_MAXBLOCKSIZE >> SPA_MINBLOCKSHIFT);
- return (kmem_cache_alloc(zio_data_buf_cache[c], KM_SLEEP));
+ return (kmem_cache_alloc(zio_data_buf_cache[c], KM_PUSHPAGE));
#else
return (kmem_alloc(size, KM_SLEEP));
#endif
@@ -272,13 +259,15 @@ zio_data_buf_free(void *buf, size_t size)
* ==========================================================================
*/
static void
-zio_push_transform(zio_t *zio, void *data, uint64_t size, uint64_t bufsize)
+zio_push_transform(zio_t *zio, void *data, uint64_t size, uint64_t bufsize,
+ zio_transform_func_t *transform)
{
zio_transform_t *zt = kmem_alloc(sizeof (zio_transform_t), KM_SLEEP);
- zt->zt_data = data;
- zt->zt_size = size;
+ zt->zt_orig_data = zio->io_data;
+ zt->zt_orig_size = zio->io_size;
zt->zt_bufsize = bufsize;
+ zt->zt_transform = transform;
zt->zt_next = zio->io_transform_stack;
zio->io_transform_stack = zt;
@@ -288,128 +277,233 @@ zio_push_transform(zio_t *zio, void *data, uint64_t size, uint64_t bufsize)
}
static void
-zio_pop_transform(zio_t *zio, void **data, uint64_t *size, uint64_t *bufsize)
+zio_pop_transforms(zio_t *zio)
{
- zio_transform_t *zt = zio->io_transform_stack;
+ zio_transform_t *zt;
+
+ while ((zt = zio->io_transform_stack) != NULL) {
+ if (zt->zt_transform != NULL)
+ zt->zt_transform(zio,
+ zt->zt_orig_data, zt->zt_orig_size);
- *data = zt->zt_data;
- *size = zt->zt_size;
- *bufsize = zt->zt_bufsize;
+ zio_buf_free(zio->io_data, zt->zt_bufsize);
- zio->io_transform_stack = zt->zt_next;
- kmem_free(zt, sizeof (zio_transform_t));
+ zio->io_data = zt->zt_orig_data;
+ zio->io_size = zt->zt_orig_size;
+ zio->io_transform_stack = zt->zt_next;
- if ((zt = zio->io_transform_stack) != NULL) {
- zio->io_data = zt->zt_data;
- zio->io_size = zt->zt_size;
+ kmem_free(zt, sizeof (zio_transform_t));
}
}
+/*
+ * ==========================================================================
+ * I/O transform callbacks for subblocks and decompression
+ * ==========================================================================
+ */
+static void
+zio_subblock(zio_t *zio, void *data, uint64_t size)
+{
+ ASSERT(zio->io_size > size);
+
+ if (zio->io_type == ZIO_TYPE_READ)
+ bcopy(zio->io_data, data, size);
+}
+
static void
-zio_clear_transform_stack(zio_t *zio)
+zio_decompress(zio_t *zio, void *data, uint64_t size)
{
- void *data;
- uint64_t size, bufsize;
+ if (zio->io_error == 0 &&
+ zio_decompress_data(BP_GET_COMPRESS(zio->io_bp),
+ zio->io_data, zio->io_size, data, size) != 0)
+ zio->io_error = EIO;
+}
- ASSERT(zio->io_transform_stack != NULL);
+/*
+ * ==========================================================================
+ * I/O parent/child relationships and pipeline interlocks
+ * ==========================================================================
+ */
- zio_pop_transform(zio, &data, &size, &bufsize);
- while (zio->io_transform_stack != NULL) {
- zio_buf_free(data, bufsize);
- zio_pop_transform(zio, &data, &size, &bufsize);
+static void
+zio_add_child(zio_t *pio, zio_t *zio)
+{
+ mutex_enter(&pio->io_lock);
+ if (zio->io_stage < ZIO_STAGE_READY)
+ pio->io_children[zio->io_child_type][ZIO_WAIT_READY]++;
+ if (zio->io_stage < ZIO_STAGE_DONE)
+ pio->io_children[zio->io_child_type][ZIO_WAIT_DONE]++;
+ zio->io_sibling_prev = NULL;
+ zio->io_sibling_next = pio->io_child;
+ if (pio->io_child != NULL)
+ pio->io_child->io_sibling_prev = zio;
+ pio->io_child = zio;
+ zio->io_parent = pio;
+ mutex_exit(&pio->io_lock);
+}
+
+static void
+zio_remove_child(zio_t *pio, zio_t *zio)
+{
+ zio_t *next, *prev;
+
+ ASSERT(zio->io_parent == pio);
+
+ mutex_enter(&pio->io_lock);
+ next = zio->io_sibling_next;
+ prev = zio->io_sibling_prev;
+ if (next != NULL)
+ next->io_sibling_prev = prev;
+ if (prev != NULL)
+ prev->io_sibling_next = next;
+ if (pio->io_child == zio)
+ pio->io_child = next;
+ mutex_exit(&pio->io_lock);
+}
+
+static boolean_t
+zio_wait_for_children(zio_t *zio, enum zio_child child, enum zio_wait_type wait)
+{
+ uint64_t *countp = &zio->io_children[child][wait];
+ boolean_t waiting = B_FALSE;
+
+ mutex_enter(&zio->io_lock);
+ ASSERT(zio->io_stall == NULL);
+ if (*countp != 0) {
+ zio->io_stage--;
+ zio->io_stall = countp;
+ waiting = B_TRUE;
+ }
+ mutex_exit(&zio->io_lock);
+
+ return (waiting);
+}
+
+static void
+zio_notify_parent(zio_t *pio, zio_t *zio, enum zio_wait_type wait)
+{
+ uint64_t *countp = &pio->io_children[zio->io_child_type][wait];
+ int *errorp = &pio->io_child_error[zio->io_child_type];
+
+ mutex_enter(&pio->io_lock);
+ if (zio->io_error && !(zio->io_flags & ZIO_FLAG_DONT_PROPAGATE))
+ *errorp = zio_worst_error(*errorp, zio->io_error);
+ pio->io_reexecute |= zio->io_reexecute;
+ ASSERT3U(*countp, >, 0);
+ if (--*countp == 0 && pio->io_stall == countp) {
+ pio->io_stall = NULL;
+ mutex_exit(&pio->io_lock);
+ zio_execute(pio);
+ } else {
+ mutex_exit(&pio->io_lock);
}
}
+static void
+zio_inherit_child_errors(zio_t *zio, enum zio_child c)
+{
+ if (zio->io_child_error[c] != 0 && zio->io_error == 0)
+ zio->io_error = zio->io_child_error[c];
+}
+
/*
* ==========================================================================
- * Create the various types of I/O (read, write, free)
+ * Create the various types of I/O (read, write, free, etc)
* ==========================================================================
*/
static zio_t *
zio_create(zio_t *pio, spa_t *spa, uint64_t txg, blkptr_t *bp,
void *data, uint64_t size, zio_done_func_t *done, void *private,
- zio_type_t type, int priority, int flags, uint8_t stage, uint32_t pipeline)
+ zio_type_t type, int priority, int flags, vdev_t *vd, uint64_t offset,
+ const zbookmark_t *zb, uint8_t stage, uint32_t pipeline)
{
zio_t *zio;
ASSERT3U(size, <=, SPA_MAXBLOCKSIZE);
ASSERT(P2PHASE(size, SPA_MINBLOCKSIZE) == 0);
+ ASSERT(P2PHASE(offset, SPA_MINBLOCKSIZE) == 0);
+
+ ASSERT(!vd || spa_config_held(spa, SCL_STATE_ALL, RW_READER));
+ ASSERT(!bp || !(flags & ZIO_FLAG_CONFIG_WRITER));
+ ASSERT(vd || stage == ZIO_STAGE_OPEN);
zio = kmem_cache_alloc(zio_cache, KM_SLEEP);
bzero(zio, sizeof (zio_t));
- zio->io_parent = pio;
- zio->io_spa = spa;
- zio->io_txg = txg;
+
+ mutex_init(&zio->io_lock, NULL, MUTEX_DEFAULT, NULL);
+ cv_init(&zio->io_cv, NULL, CV_DEFAULT, NULL);
+
+ if (vd != NULL)
+ zio->io_child_type = ZIO_CHILD_VDEV;
+ else if (flags & ZIO_FLAG_GANG_CHILD)
+ zio->io_child_type = ZIO_CHILD_GANG;
+ else
+ zio->io_child_type = ZIO_CHILD_LOGICAL;
+
if (bp != NULL) {
zio->io_bp = bp;
zio->io_bp_copy = *bp;
zio->io_bp_orig = *bp;
+ if (type != ZIO_TYPE_WRITE)
+ zio->io_bp = &zio->io_bp_copy; /* so caller can free */
+ if (zio->io_child_type == ZIO_CHILD_LOGICAL) {
+ if (BP_IS_GANG(bp))
+ pipeline |= ZIO_GANG_STAGES;
+ zio->io_logical = zio;
+ }
}
+
+ zio->io_spa = spa;
+ zio->io_txg = txg;
+ zio->io_data = data;
+ zio->io_size = size;
zio->io_done = done;
zio->io_private = private;
zio->io_type = type;
zio->io_priority = priority;
- zio->io_stage = stage;
- zio->io_pipeline = pipeline;
- zio->io_async_stages = ZIO_ASYNC_PIPELINE_STAGES;
- zio->io_timestamp = lbolt64;
- zio->io_flags = flags;
- mutex_init(&zio->io_lock, NULL, MUTEX_DEFAULT, NULL);
- cv_init(&zio->io_cv, NULL, CV_DEFAULT, NULL);
- zio_push_transform(zio, data, size, size);
+ zio->io_vd = vd;
+ zio->io_offset = offset;
+ zio->io_orig_flags = zio->io_flags = flags;
+ zio->io_orig_stage = zio->io_stage = stage;
+ zio->io_orig_pipeline = zio->io_pipeline = pipeline;
- /*
- * Note on config lock:
- *
- * If CONFIG_HELD is set, then the caller already has the config
- * lock, so we don't need it for this io.
- *
- * We set CONFIG_GRABBED to indicate that we have grabbed the
- * config lock on behalf of this io, so it should be released
- * in zio_done.
- *
- * Unless CONFIG_HELD is set, we will grab the config lock for
- * any top-level (parent-less) io, *except* NULL top-level ios.
- * The NULL top-level ios rarely have any children, so we delay
- * grabbing the lock until the first child is added (but it is
- * still grabbed on behalf of the top-level i/o, so additional
- * children don't need to also grab it). This greatly reduces
- * contention on the config lock.
- */
- if (pio == NULL) {
- if (type != ZIO_TYPE_NULL &&
- !(flags & ZIO_FLAG_CONFIG_HELD)) {
- spa_config_enter(zio->io_spa, RW_READER, zio);
- zio->io_flags |= ZIO_FLAG_CONFIG_GRABBED;
- }
- zio->io_root = zio;
- } else {
- zio->io_root = pio->io_root;
- if (!(flags & ZIO_FLAG_NOBOOKMARK))
+ if (zb != NULL)
+ zio->io_bookmark = *zb;
+
+ if (pio != NULL) {
+ /*
+ * Logical I/Os can have logical, gang, or vdev children.
+ * Gang I/Os can have gang or vdev children.
+ * Vdev I/Os can only have vdev children.
+ * The following ASSERT captures all of these constraints.
+ */
+ ASSERT(zio->io_child_type <= pio->io_child_type);
+ if (zio->io_logical == NULL)
zio->io_logical = pio->io_logical;
- mutex_enter(&pio->io_lock);
- if (pio->io_parent == NULL &&
- pio->io_type == ZIO_TYPE_NULL &&
- !(pio->io_flags & ZIO_FLAG_CONFIG_GRABBED) &&
- !(pio->io_flags & ZIO_FLAG_CONFIG_HELD)) {
- pio->io_flags |= ZIO_FLAG_CONFIG_GRABBED;
- spa_config_enter(zio->io_spa, RW_READER, pio);
- }
- if (stage < ZIO_STAGE_READY)
- pio->io_children_notready++;
- pio->io_children_notdone++;
- zio->io_sibling_next = pio->io_child;
- zio->io_sibling_prev = NULL;
- if (pio->io_child != NULL)
- pio->io_child->io_sibling_prev = zio;
- pio->io_child = zio;
- zio->io_ndvas = pio->io_ndvas;
- mutex_exit(&pio->io_lock);
+ zio_add_child(pio, zio);
}
return (zio);
}
+static void
+zio_destroy(zio_t *zio)
+{
+ spa_t *spa = zio->io_spa;
+ uint8_t async_root = zio->io_async_root;
+
+ mutex_destroy(&zio->io_lock);
+ cv_destroy(&zio->io_cv);
+ kmem_cache_free(zio_cache, zio);
+
+ if (async_root) {
+ mutex_enter(&spa->spa_async_root_lock);
+ if (--spa->spa_async_root_count == 0)
+ cv_broadcast(&spa->spa_async_root_cv);
+ mutex_exit(&spa->spa_async_root_lock);
+ }
+}
+
zio_t *
zio_null(zio_t *pio, spa_t *spa, zio_done_func_t *done, void *private,
int flags)
@@ -417,8 +511,8 @@ zio_null(zio_t *pio, spa_t *spa, zio_done_func_t *done, void *private,
zio_t *zio;
zio = zio_create(pio, spa, 0, NULL, NULL, 0, done, private,
- ZIO_TYPE_NULL, ZIO_PRIORITY_NOW, flags, ZIO_STAGE_OPEN,
- ZIO_WAIT_FOR_CHILDREN_PIPELINE);
+ ZIO_TYPE_NULL, ZIO_PRIORITY_NOW, flags, NULL, 0, NULL,
+ ZIO_STAGE_OPEN, ZIO_INTERLOCK_PIPELINE);
return (zio);
}
@@ -430,160 +524,89 @@ zio_root(spa_t *spa, zio_done_func_t *done, void *private, int flags)
}
zio_t *
-zio_read(zio_t *pio, spa_t *spa, blkptr_t *bp, void *data,
- uint64_t size, zio_done_func_t *done, void *private,
- int priority, int flags, zbookmark_t *zb)
+zio_read(zio_t *pio, spa_t *spa, const blkptr_t *bp,
+ void *data, uint64_t size, zio_done_func_t *done, void *private,
+ int priority, int flags, const zbookmark_t *zb)
{
zio_t *zio;
- ASSERT3U(size, ==, BP_GET_LSIZE(bp));
-
- zio = zio_create(pio, spa, bp->blk_birth, bp, data, size, done, private,
- ZIO_TYPE_READ, priority, flags | ZIO_FLAG_USER,
+ zio = zio_create(pio, spa, bp->blk_birth, (blkptr_t *)bp,
+ data, size, done, private,
+ ZIO_TYPE_READ, priority, flags, NULL, 0, zb,
ZIO_STAGE_OPEN, ZIO_READ_PIPELINE);
- zio->io_bookmark = *zb;
-
- zio->io_logical = zio;
-
- /*
- * Work off our copy of the bp so the caller can free it.
- */
- zio->io_bp = &zio->io_bp_copy;
-
- if (BP_GET_COMPRESS(bp) != ZIO_COMPRESS_OFF) {
- uint64_t csize = BP_GET_PSIZE(bp);
- void *cbuf = zio_buf_alloc(csize);
-
- zio_push_transform(zio, cbuf, csize, csize);
- zio->io_pipeline |= 1U << ZIO_STAGE_READ_DECOMPRESS;
- }
-
- if (BP_IS_GANG(bp)) {
- uint64_t gsize = SPA_GANGBLOCKSIZE;
- void *gbuf = zio_buf_alloc(gsize);
-
- zio_push_transform(zio, gbuf, gsize, gsize);
- zio->io_pipeline |= 1U << ZIO_STAGE_READ_GANG_MEMBERS;
- }
return (zio);
}
zio_t *
-zio_write(zio_t *pio, spa_t *spa, int checksum, int compress, int ncopies,
- uint64_t txg, blkptr_t *bp, void *data, uint64_t size,
- zio_done_func_t *ready, zio_done_func_t *done, void *private, int priority,
- int flags, zbookmark_t *zb)
+zio_write(zio_t *pio, spa_t *spa, uint64_t txg, blkptr_t *bp,
+ void *data, uint64_t size, zio_prop_t *zp,
+ zio_done_func_t *ready, zio_done_func_t *done, void *private,
+ int priority, int flags, const zbookmark_t *zb)
{
zio_t *zio;
- ASSERT(checksum >= ZIO_CHECKSUM_OFF &&
- checksum < ZIO_CHECKSUM_FUNCTIONS);
-
- ASSERT(compress >= ZIO_COMPRESS_OFF &&
- compress < ZIO_COMPRESS_FUNCTIONS);
+ ASSERT(zp->zp_checksum >= ZIO_CHECKSUM_OFF &&
+ zp->zp_checksum < ZIO_CHECKSUM_FUNCTIONS &&
+ zp->zp_compress >= ZIO_COMPRESS_OFF &&
+ zp->zp_compress < ZIO_COMPRESS_FUNCTIONS &&
+ zp->zp_type < DMU_OT_NUMTYPES &&
+ zp->zp_level < 32 &&
+ zp->zp_ndvas > 0 &&
+ zp->zp_ndvas <= spa_max_replication(spa));
+ ASSERT(ready != NULL);
zio = zio_create(pio, spa, txg, bp, data, size, done, private,
- ZIO_TYPE_WRITE, priority, flags | ZIO_FLAG_USER,
+ ZIO_TYPE_WRITE, priority, flags, NULL, 0, zb,
ZIO_STAGE_OPEN, ZIO_WRITE_PIPELINE);
zio->io_ready = ready;
-
- zio->io_bookmark = *zb;
-
- zio->io_logical = zio;
-
- zio->io_checksum = checksum;
- zio->io_compress = compress;
- zio->io_ndvas = ncopies;
-
- if (compress != ZIO_COMPRESS_OFF)
- zio->io_async_stages |= 1U << ZIO_STAGE_WRITE_COMPRESS;
-
- if (bp->blk_birth != txg) {
- /* XXX the bp usually (always?) gets re-zeroed later */
- BP_ZERO(bp);
- BP_SET_LSIZE(bp, size);
- BP_SET_PSIZE(bp, size);
- } else {
- /* Make sure someone doesn't change their mind on overwrites */
- ASSERT(MIN(zio->io_ndvas + BP_IS_GANG(bp),
- spa_max_replication(spa)) == BP_GET_NDVAS(bp));
- }
+ zio->io_prop = *zp;
return (zio);
}
zio_t *
-zio_rewrite(zio_t *pio, spa_t *spa, int checksum,
- uint64_t txg, blkptr_t *bp, void *data, uint64_t size,
- zio_done_func_t *done, void *private, int priority, int flags,
- zbookmark_t *zb)
+zio_rewrite(zio_t *pio, spa_t *spa, uint64_t txg, blkptr_t *bp, void *data,
+ uint64_t size, zio_done_func_t *done, void *private, int priority,
+ int flags, zbookmark_t *zb)
{
zio_t *zio;
zio = zio_create(pio, spa, txg, bp, data, size, done, private,
- ZIO_TYPE_WRITE, priority, flags | ZIO_FLAG_USER,
+ ZIO_TYPE_WRITE, priority, flags, NULL, 0, zb,
ZIO_STAGE_OPEN, ZIO_REWRITE_PIPELINE);
- zio->io_bookmark = *zb;
- zio->io_checksum = checksum;
- zio->io_compress = ZIO_COMPRESS_OFF;
-
- if (pio != NULL)
- ASSERT3U(zio->io_ndvas, <=, BP_GET_NDVAS(bp));
-
- return (zio);
-}
-
-static zio_t *
-zio_write_allocate(zio_t *pio, spa_t *spa, int checksum,
- uint64_t txg, blkptr_t *bp, void *data, uint64_t size,
- zio_done_func_t *done, void *private, int priority, int flags)
-{
- zio_t *zio;
-
- BP_ZERO(bp);
- BP_SET_LSIZE(bp, size);
- BP_SET_PSIZE(bp, size);
- BP_SET_COMPRESS(bp, ZIO_COMPRESS_OFF);
-
- zio = zio_create(pio, spa, txg, bp, data, size, done, private,
- ZIO_TYPE_WRITE, priority, flags,
- ZIO_STAGE_OPEN, ZIO_WRITE_ALLOCATE_PIPELINE);
-
- zio->io_checksum = checksum;
- zio->io_compress = ZIO_COMPRESS_OFF;
-
return (zio);
}
zio_t *
zio_free(zio_t *pio, spa_t *spa, uint64_t txg, blkptr_t *bp,
- zio_done_func_t *done, void *private)
+ zio_done_func_t *done, void *private, int flags)
{
zio_t *zio;
ASSERT(!BP_IS_HOLE(bp));
+ if (bp->blk_fill == BLK_FILL_ALREADY_FREED)
+ return (zio_null(pio, spa, NULL, NULL, flags));
+
if (txg == spa->spa_syncing_txg &&
- spa->spa_sync_pass > zio_sync_pass.zp_defer_free) {
+ spa_sync_pass(spa) > SYNC_PASS_DEFERRED_FREE) {
bplist_enqueue_deferred(&spa->spa_sync_bplist, bp);
- return (zio_null(pio, spa, NULL, NULL, 0));
+ return (zio_null(pio, spa, NULL, NULL, flags));
}
- zio = zio_create(pio, spa, txg, bp, NULL, 0, done, private,
- ZIO_TYPE_FREE, ZIO_PRIORITY_FREE, ZIO_FLAG_USER,
- ZIO_STAGE_OPEN, ZIO_FREE_PIPELINE);
-
- zio->io_bp = &zio->io_bp_copy;
+ zio = zio_create(pio, spa, txg, bp, NULL, BP_GET_PSIZE(bp),
+ done, private, ZIO_TYPE_FREE, ZIO_PRIORITY_FREE, flags,
+ NULL, 0, NULL, ZIO_STAGE_OPEN, ZIO_FREE_PIPELINE);
return (zio);
}
zio_t *
zio_claim(zio_t *pio, spa_t *spa, uint64_t txg, blkptr_t *bp,
- zio_done_func_t *done, void *private)
+ zio_done_func_t *done, void *private, int flags)
{
zio_t *zio;
@@ -601,11 +624,9 @@ zio_claim(zio_t *pio, spa_t *spa, uint64_t txg, blkptr_t *bp,
ASSERT3U(spa->spa_uberblock.ub_rootbp.blk_birth, <, spa_first_txg(spa));
ASSERT3U(spa_first_txg(spa), <=, txg);
- zio = zio_create(pio, spa, txg, bp, NULL, 0, done, private,
- ZIO_TYPE_CLAIM, ZIO_PRIORITY_NOW, 0,
- ZIO_STAGE_OPEN, ZIO_CLAIM_PIPELINE);
-
- zio->io_bp = &zio->io_bp_copy;
+ zio = zio_create(pio, spa, txg, bp, NULL, BP_GET_PSIZE(bp),
+ done, private, ZIO_TYPE_CLAIM, ZIO_PRIORITY_NOW, flags,
+ NULL, 0, NULL, ZIO_STAGE_OPEN, ZIO_CLAIM_PIPELINE);
return (zio);
}
@@ -619,10 +640,9 @@ zio_ioctl(zio_t *pio, spa_t *spa, vdev_t *vd, int cmd,
if (vd->vdev_children == 0) {
zio = zio_create(pio, spa, 0, NULL, NULL, 0, done, private,
- ZIO_TYPE_IOCTL, priority, flags,
+ ZIO_TYPE_IOCTL, priority, flags, vd, 0, NULL,
ZIO_STAGE_OPEN, ZIO_IOCTL_PIPELINE);
- zio->io_vd = vd;
zio->io_cmd = cmd;
} else {
zio = zio_null(pio, spa, NULL, NULL, flags);
@@ -635,54 +655,23 @@ zio_ioctl(zio_t *pio, spa_t *spa, vdev_t *vd, int cmd,
return (zio);
}
-static void
-zio_phys_bp_init(vdev_t *vd, blkptr_t *bp, uint64_t offset, uint64_t size,
- int checksum)
-{
- ASSERT(vd->vdev_children == 0);
-
- ASSERT(size <= SPA_MAXBLOCKSIZE);
- ASSERT(P2PHASE(size, SPA_MINBLOCKSIZE) == 0);
- ASSERT(P2PHASE(offset, SPA_MINBLOCKSIZE) == 0);
-
- ASSERT(offset + size <= VDEV_LABEL_START_SIZE ||
- offset >= vd->vdev_psize - VDEV_LABEL_END_SIZE);
- ASSERT3U(offset + size, <=, vd->vdev_psize);
-
- BP_ZERO(bp);
-
- BP_SET_LSIZE(bp, size);
- BP_SET_PSIZE(bp, size);
-
- BP_SET_CHECKSUM(bp, checksum);
- BP_SET_COMPRESS(bp, ZIO_COMPRESS_OFF);
- BP_SET_BYTEORDER(bp, ZFS_HOST_BYTEORDER);
-
- if (checksum != ZIO_CHECKSUM_OFF)
- ZIO_SET_CHECKSUM(&bp->blk_cksum, offset, 0, 0, 0);
-}
-
zio_t *
zio_read_phys(zio_t *pio, vdev_t *vd, uint64_t offset, uint64_t size,
void *data, int checksum, zio_done_func_t *done, void *private,
- int priority, int flags)
+ int priority, int flags, boolean_t labels)
{
zio_t *zio;
- blkptr_t blk;
- zio_phys_bp_init(vd, &blk, offset, size, checksum);
+ ASSERT(vd->vdev_children == 0);
+ ASSERT(!labels || offset + size <= VDEV_LABEL_START_SIZE ||
+ offset >= vd->vdev_psize - VDEV_LABEL_END_SIZE);
+ ASSERT3U(offset + size, <=, vd->vdev_psize);
- zio = zio_create(pio, vd->vdev_spa, 0, &blk, data, size, done, private,
- ZIO_TYPE_READ, priority, flags | ZIO_FLAG_PHYSICAL,
+ zio = zio_create(pio, vd->vdev_spa, 0, NULL, data, size, done, private,
+ ZIO_TYPE_READ, priority, flags, vd, offset, NULL,
ZIO_STAGE_OPEN, ZIO_READ_PHYS_PIPELINE);
- zio->io_vd = vd;
- zio->io_offset = offset;
-
- /*
- * Work off our copy of the bp so the caller can free it.
- */
- zio->io_bp = &zio->io_bp_copy;
+ zio->io_prop.zp_checksum = checksum;
return (zio);
}
@@ -690,53 +679,49 @@ zio_read_phys(zio_t *pio, vdev_t *vd, uint64_t offset, uint64_t size,
zio_t *
zio_write_phys(zio_t *pio, vdev_t *vd, uint64_t offset, uint64_t size,
void *data, int checksum, zio_done_func_t *done, void *private,
- int priority, int flags)
+ int priority, int flags, boolean_t labels)
{
- zio_block_tail_t *zbt;
- void *wbuf;
zio_t *zio;
- blkptr_t blk;
- zio_phys_bp_init(vd, &blk, offset, size, checksum);
+ ASSERT(vd->vdev_children == 0);
+ ASSERT(!labels || offset + size <= VDEV_LABEL_START_SIZE ||
+ offset >= vd->vdev_psize - VDEV_LABEL_END_SIZE);
+ ASSERT3U(offset + size, <=, vd->vdev_psize);
- zio = zio_create(pio, vd->vdev_spa, 0, &blk, data, size, done, private,
- ZIO_TYPE_WRITE, priority, flags | ZIO_FLAG_PHYSICAL,
+ zio = zio_create(pio, vd->vdev_spa, 0, NULL, data, size, done, private,
+ ZIO_TYPE_WRITE, priority, flags, vd, offset, NULL,
ZIO_STAGE_OPEN, ZIO_WRITE_PHYS_PIPELINE);
- zio->io_vd = vd;
- zio->io_offset = offset;
-
- zio->io_bp = &zio->io_bp_copy;
- zio->io_checksum = checksum;
+ zio->io_prop.zp_checksum = checksum;
if (zio_checksum_table[checksum].ci_zbt) {
/*
* zbt checksums are necessarily destructive -- they modify
- * one word of the write buffer to hold the verifier/checksum.
+ * the end of the write buffer to hold the verifier/checksum.
* Therefore, we must make a local copy in case the data is
- * being written to multiple places.
+ * being written to multiple places in parallel.
*/
- wbuf = zio_buf_alloc(size);
+ void *wbuf = zio_buf_alloc(size);
bcopy(data, wbuf, size);
- zio_push_transform(zio, wbuf, size, size);
-
- zbt = (zio_block_tail_t *)((char *)wbuf + size) - 1;
- zbt->zbt_cksum = blk.blk_cksum;
+ zio_push_transform(zio, wbuf, size, size, NULL);
}
return (zio);
}
/*
- * Create a child I/O to do some work for us. It has no associated bp.
+ * Create a child I/O to do some work for us.
*/
zio_t *
-zio_vdev_child_io(zio_t *zio, blkptr_t *bp, vdev_t *vd, uint64_t offset,
+zio_vdev_child_io(zio_t *pio, blkptr_t *bp, vdev_t *vd, uint64_t offset,
void *data, uint64_t size, int type, int priority, int flags,
zio_done_func_t *done, void *private)
{
uint32_t pipeline = ZIO_VDEV_CHILD_PIPELINE;
- zio_t *cio;
+ zio_t *zio;
+
+ ASSERT(vd->vdev_parent ==
+ (pio->io_vd ? pio->io_vd : pio->io_spa->spa_root_vdev));
if (type == ZIO_TYPE_READ && bp != NULL) {
/*
@@ -746,517 +731,754 @@ zio_vdev_child_io(zio_t *zio, blkptr_t *bp, vdev_t *vd, uint64_t offset,
* eliminates redundant checksums in the interior nodes.
*/
pipeline |= 1U << ZIO_STAGE_CHECKSUM_VERIFY;
- zio->io_pipeline &= ~(1U << ZIO_STAGE_CHECKSUM_VERIFY);
+ pio->io_pipeline &= ~(1U << ZIO_STAGE_CHECKSUM_VERIFY);
}
- cio = zio_create(zio, zio->io_spa, zio->io_txg, bp, data, size,
+ if (vd->vdev_children == 0)
+ offset += VDEV_LABEL_START_SIZE;
+
+ zio = zio_create(pio, pio->io_spa, pio->io_txg, bp, data, size,
done, private, type, priority,
- (zio->io_flags & ZIO_FLAG_VDEV_INHERIT) | ZIO_FLAG_CANFAIL | flags,
+ (pio->io_flags & ZIO_FLAG_VDEV_INHERIT) |
+ ZIO_FLAG_CANFAIL | ZIO_FLAG_DONT_PROPAGATE | flags,
+ vd, offset, &pio->io_bookmark,
ZIO_STAGE_VDEV_IO_START - 1, pipeline);
- cio->io_vd = vd;
- cio->io_offset = offset;
-
- return (cio);
+ return (zio);
}
-/*
- * ==========================================================================
- * Initiate I/O, either sync or async
- * ==========================================================================
- */
-int
-zio_wait(zio_t *zio)
+zio_t *
+zio_vdev_delegated_io(vdev_t *vd, uint64_t offset, void *data, uint64_t size,
+ int type, int priority, int flags, zio_done_func_t *done, void *private)
{
- int error;
-
- ASSERT(zio->io_stage == ZIO_STAGE_OPEN);
-
- zio->io_waiter = curthread;
+ zio_t *zio;
- zio_next_stage_async(zio);
+ ASSERT(vd->vdev_ops->vdev_op_leaf);
- mutex_enter(&zio->io_lock);
- while (zio->io_stalled != ZIO_STAGE_DONE)
- cv_wait(&zio->io_cv, &zio->io_lock);
- mutex_exit(&zio->io_lock);
+ zio = zio_create(NULL, vd->vdev_spa, 0, NULL,
+ data, size, done, private, type, priority,
+ flags | ZIO_FLAG_CANFAIL | ZIO_FLAG_DONT_RETRY,
+ vd, offset, NULL,
+ ZIO_STAGE_VDEV_IO_START - 1, ZIO_VDEV_CHILD_PIPELINE);
- error = zio->io_error;
- cv_destroy(&zio->io_cv);
- mutex_destroy(&zio->io_lock);
- kmem_cache_free(zio_cache, zio);
-
- return (error);
+ return (zio);
}
void
-zio_nowait(zio_t *zio)
+zio_flush(zio_t *zio, vdev_t *vd)
{
- zio_next_stage_async(zio);
+ zio_nowait(zio_ioctl(zio, zio->io_spa, vd, DKIOCFLUSHWRITECACHE,
+ NULL, NULL, ZIO_PRIORITY_NOW,
+ ZIO_FLAG_CANFAIL | ZIO_FLAG_DONT_PROPAGATE | ZIO_FLAG_DONT_RETRY));
}
/*
* ==========================================================================
- * I/O pipeline interlocks: parent/child dependency scoreboarding
+ * Prepare to read and write logical blocks
* ==========================================================================
*/
-static void
-zio_wait_for_children(zio_t *zio, uint32_t stage, uint64_t *countp)
+
+static int
+zio_read_bp_init(zio_t *zio)
{
- mutex_enter(&zio->io_lock);
- if (*countp == 0) {
- ASSERT(zio->io_stalled == 0);
- mutex_exit(&zio->io_lock);
- zio_next_stage(zio);
- } else {
- zio->io_stalled = stage;
- mutex_exit(&zio->io_lock);
+ blkptr_t *bp = zio->io_bp;
+
+ if (BP_GET_COMPRESS(bp) != ZIO_COMPRESS_OFF && zio->io_logical == zio) {
+ uint64_t csize = BP_GET_PSIZE(bp);
+ void *cbuf = zio_buf_alloc(csize);
+
+ zio_push_transform(zio, cbuf, csize, csize, zio_decompress);
}
+
+ if (!dmu_ot[BP_GET_TYPE(bp)].ot_metadata && BP_GET_LEVEL(bp) == 0)
+ zio->io_flags |= ZIO_FLAG_DONT_CACHE;
+
+ return (ZIO_PIPELINE_CONTINUE);
}
-static void
-zio_notify_parent(zio_t *zio, uint32_t stage, uint64_t *countp)
+static int
+zio_write_bp_init(zio_t *zio)
{
- zio_t *pio = zio->io_parent;
+ zio_prop_t *zp = &zio->io_prop;
+ int compress = zp->zp_compress;
+ blkptr_t *bp = zio->io_bp;
+ void *cbuf;
+ uint64_t lsize = zio->io_size;
+ uint64_t csize = lsize;
+ uint64_t cbufsize = 0;
+ int pass = 1;
- mutex_enter(&pio->io_lock);
- if (pio->io_error == 0 && !(zio->io_flags & ZIO_FLAG_DONT_PROPAGATE))
- pio->io_error = zio->io_error;
- if (--*countp == 0 && pio->io_stalled == stage) {
- pio->io_stalled = 0;
- mutex_exit(&pio->io_lock);
- zio_next_stage_async(pio);
+ /*
+ * If our children haven't all reached the ready stage,
+ * wait for them and then repeat this pipeline stage.
+ */
+ if (zio_wait_for_children(zio, ZIO_CHILD_GANG, ZIO_WAIT_READY) ||
+ zio_wait_for_children(zio, ZIO_CHILD_LOGICAL, ZIO_WAIT_READY))
+ return (ZIO_PIPELINE_STOP);
+
+ if (!IO_IS_ALLOCATING(zio))
+ return (ZIO_PIPELINE_CONTINUE);
+
+ ASSERT(compress != ZIO_COMPRESS_INHERIT);
+
+ if (bp->blk_birth == zio->io_txg) {
+ /*
+ * We're rewriting an existing block, which means we're
+ * working on behalf of spa_sync(). For spa_sync() to
+ * converge, it must eventually be the case that we don't
+ * have to allocate new blocks. But compression changes
+ * the blocksize, which forces a reallocate, and makes
+ * convergence take longer. Therefore, after the first
+ * few passes, stop compressing to ensure convergence.
+ */
+ pass = spa_sync_pass(zio->io_spa);
+ ASSERT(pass > 1);
+
+ if (pass > SYNC_PASS_DONT_COMPRESS)
+ compress = ZIO_COMPRESS_OFF;
+
+ /*
+ * Only MOS (objset 0) data should need to be rewritten.
+ */
+ ASSERT(zio->io_logical->io_bookmark.zb_objset == 0);
+
+ /* Make sure someone doesn't change their mind on overwrites */
+ ASSERT(MIN(zp->zp_ndvas + BP_IS_GANG(bp),
+ spa_max_replication(zio->io_spa)) == BP_GET_NDVAS(bp));
+ }
+
+ if (compress != ZIO_COMPRESS_OFF) {
+ if (!zio_compress_data(compress, zio->io_data, zio->io_size,
+ &cbuf, &csize, &cbufsize)) {
+ compress = ZIO_COMPRESS_OFF;
+ } else if (csize != 0) {
+ zio_push_transform(zio, cbuf, csize, cbufsize, NULL);
+ }
+ }
+
+ /*
+ * The final pass of spa_sync() must be all rewrites, but the first
+ * few passes offer a trade-off: allocating blocks defers convergence,
+ * but newly allocated blocks are sequential, so they can be written
+ * to disk faster. Therefore, we allow the first few passes of
+ * spa_sync() to allocate new blocks, but force rewrites after that.
+ * There should only be a handful of blocks after pass 1 in any case.
+ */
+ if (bp->blk_birth == zio->io_txg && BP_GET_PSIZE(bp) == csize &&
+ pass > SYNC_PASS_REWRITE) {
+ ASSERT(csize != 0);
+ uint32_t gang_stages = zio->io_pipeline & ZIO_GANG_STAGES;
+ zio->io_pipeline = ZIO_REWRITE_PIPELINE | gang_stages;
+ zio->io_flags |= ZIO_FLAG_IO_REWRITE;
} else {
- mutex_exit(&pio->io_lock);
+ BP_ZERO(bp);
+ zio->io_pipeline = ZIO_WRITE_PIPELINE;
+ }
+
+ if (csize == 0) {
+ zio->io_pipeline = ZIO_INTERLOCK_PIPELINE;
+ } else {
+ ASSERT(zp->zp_checksum != ZIO_CHECKSUM_GANG_HEADER);
+ BP_SET_LSIZE(bp, lsize);
+ BP_SET_PSIZE(bp, csize);
+ BP_SET_COMPRESS(bp, compress);
+ BP_SET_CHECKSUM(bp, zp->zp_checksum);
+ BP_SET_TYPE(bp, zp->zp_type);
+ BP_SET_LEVEL(bp, zp->zp_level);
+ BP_SET_BYTEORDER(bp, ZFS_HOST_BYTEORDER);
}
+
+ return (ZIO_PIPELINE_CONTINUE);
}
+/*
+ * ==========================================================================
+ * Execute the I/O pipeline
+ * ==========================================================================
+ */
+
static void
-zio_wait_children_ready(zio_t *zio)
+zio_taskq_dispatch(zio_t *zio, enum zio_taskq_type q)
{
- zio_wait_for_children(zio, ZIO_STAGE_WAIT_CHILDREN_READY,
- &zio->io_children_notready);
-}
+ zio_type_t t = zio->io_type;
-void
-zio_wait_children_done(zio_t *zio)
-{
- zio_wait_for_children(zio, ZIO_STAGE_WAIT_CHILDREN_DONE,
- &zio->io_children_notdone);
+ /*
+ * If we're a config writer, the normal issue and interrupt threads
+ * may all be blocked waiting for the config lock. In this case,
+ * select the otherwise-unused taskq for ZIO_TYPE_NULL.
+ */
+ if (zio->io_flags & ZIO_FLAG_CONFIG_WRITER)
+ t = ZIO_TYPE_NULL;
+
+ /*
+ * A similar issue exists for the L2ARC write thread until L2ARC 2.0.
+ */
+ if (t == ZIO_TYPE_WRITE && zio->io_vd && zio->io_vd->vdev_aux)
+ t = ZIO_TYPE_NULL;
+
+ (void) taskq_dispatch(zio->io_spa->spa_zio_taskq[t][q],
+ (task_func_t *)zio_execute, zio, TQ_SLEEP);
}
-static void
-zio_ready(zio_t *zio)
+static boolean_t
+zio_taskq_member(zio_t *zio, enum zio_taskq_type q)
{
- zio_t *pio = zio->io_parent;
+ kthread_t *executor = zio->io_executor;
+ spa_t *spa = zio->io_spa;
- if (zio->io_ready)
- zio->io_ready(zio);
+ for (zio_type_t t = 0; t < ZIO_TYPES; t++)
+ if (taskq_member(spa->spa_zio_taskq[t][q], executor))
+ return (B_TRUE);
- if (pio != NULL)
- zio_notify_parent(zio, ZIO_STAGE_WAIT_CHILDREN_READY,
- &pio->io_children_notready);
+ return (B_FALSE);
+}
- if (zio->io_bp)
- zio->io_bp_copy = *zio->io_bp;
+static int
+zio_issue_async(zio_t *zio)
+{
+ zio_taskq_dispatch(zio, ZIO_TASKQ_ISSUE);
- zio_next_stage(zio);
+ return (ZIO_PIPELINE_STOP);
}
-static void
-zio_done(zio_t *zio)
+void
+zio_interrupt(zio_t *zio)
{
- zio_t *pio = zio->io_parent;
- spa_t *spa = zio->io_spa;
- blkptr_t *bp = zio->io_bp;
- vdev_t *vd = zio->io_vd;
+ zio_taskq_dispatch(zio, ZIO_TASKQ_INTERRUPT);
+}
- ASSERT(zio->io_children_notready == 0);
- ASSERT(zio->io_children_notdone == 0);
+/*
+ * Execute the I/O pipeline until one of the following occurs:
+ * (1) the I/O completes; (2) the pipeline stalls waiting for
+ * dependent child I/Os; (3) the I/O issues, so we're waiting
+ * for an I/O completion interrupt; (4) the I/O is delegated by
+ * vdev-level caching or aggregation; (5) the I/O is deferred
+ * due to vdev-level queueing; (6) the I/O is handed off to
+ * another thread. In all cases, the pipeline stops whenever
+ * there's no CPU work; it never burns a thread in cv_wait().
+ *
+ * There's no locking on io_stage because there's no legitimate way
+ * for multiple threads to be attempting to process the same I/O.
+ */
+static zio_pipe_stage_t *zio_pipeline[ZIO_STAGES];
- if (bp != NULL) {
- ASSERT(bp->blk_pad[0] == 0);
- ASSERT(bp->blk_pad[1] == 0);
- ASSERT(bp->blk_pad[2] == 0);
- ASSERT(bcmp(bp, &zio->io_bp_copy, sizeof (blkptr_t)) == 0);
- if (zio->io_type == ZIO_TYPE_WRITE && !BP_IS_HOLE(bp) &&
- !(zio->io_flags & ZIO_FLAG_IO_REPAIR)) {
- ASSERT(!BP_SHOULD_BYTESWAP(bp));
- if (zio->io_ndvas != 0)
- ASSERT3U(zio->io_ndvas, <=, BP_GET_NDVAS(bp));
- ASSERT(BP_COUNT_GANG(bp) == 0 ||
- (BP_COUNT_GANG(bp) == BP_GET_NDVAS(bp)));
- }
- }
+void
+zio_execute(zio_t *zio)
+{
+ zio->io_executor = curthread;
- if (vd != NULL)
- vdev_stat_update(zio);
+ while (zio->io_stage < ZIO_STAGE_DONE) {
+ uint32_t pipeline = zio->io_pipeline;
+ zio_stage_t stage = zio->io_stage;
+ int rv;
- if (zio->io_error) {
- /*
- * If this I/O is attached to a particular vdev,
- * generate an error message describing the I/O failure
- * at the block level. We ignore these errors if the
- * device is currently unavailable.
- */
- if (zio->io_error != ECKSUM && vd != NULL && !vdev_is_dead(vd))
- zfs_ereport_post(FM_EREPORT_ZFS_IO,
- zio->io_spa, vd, zio, 0, 0);
+ ASSERT(!MUTEX_HELD(&zio->io_lock));
- if ((zio->io_error == EIO ||
- !(zio->io_flags & ZIO_FLAG_SPECULATIVE)) &&
- zio->io_logical == zio) {
- /*
- * For root I/O requests, tell the SPA to log the error
- * appropriately. Also, generate a logical data
- * ereport.
- */
- spa_log_error(zio->io_spa, zio);
+ while (((1U << ++stage) & pipeline) == 0)
+ continue;
- zfs_ereport_post(FM_EREPORT_ZFS_DATA,
- zio->io_spa, NULL, zio, 0, 0);
- }
+ ASSERT(stage <= ZIO_STAGE_DONE);
+ ASSERT(zio->io_stall == NULL);
/*
- * For I/O requests that cannot fail, panic appropriately.
+ * If we are in interrupt context and this pipeline stage
+ * will grab a config lock that is held across I/O,
+ * issue async to avoid deadlock.
*/
- if (!(zio->io_flags & ZIO_FLAG_CANFAIL)) {
- char *blkbuf;
-
- blkbuf = kmem_alloc(BP_SPRINTF_LEN, KM_NOSLEEP);
- if (blkbuf) {
- sprintf_blkptr(blkbuf, BP_SPRINTF_LEN,
- bp ? bp : &zio->io_bp_copy);
- }
- panic("ZFS: %s (%s on %s off %llx: zio %p %s): error "
- "%d", zio->io_error == ECKSUM ?
- "bad checksum" : "I/O failure",
- zio_type_name[zio->io_type],
- vdev_description(vd),
- (u_longlong_t)zio->io_offset,
- zio, blkbuf ? blkbuf : "", zio->io_error);
+ if (((1U << stage) & ZIO_CONFIG_LOCK_BLOCKING_STAGES) &&
+ zio->io_vd == NULL &&
+ zio_taskq_member(zio, ZIO_TASKQ_INTERRUPT)) {
+ zio_taskq_dispatch(zio, ZIO_TASKQ_ISSUE);
+ return;
}
+
+ zio->io_stage = stage;
+ rv = zio_pipeline[stage](zio);
+
+ if (rv == ZIO_PIPELINE_STOP)
+ return;
+
+ ASSERT(rv == ZIO_PIPELINE_CONTINUE);
}
- zio_clear_transform_stack(zio);
+}
- if (zio->io_done)
- zio->io_done(zio);
+/*
+ * ==========================================================================
+ * Initiate I/O, either sync or async
+ * ==========================================================================
+ */
+int
+zio_wait(zio_t *zio)
+{
+ int error;
- ASSERT(zio->io_delegate_list == NULL);
- ASSERT(zio->io_delegate_next == NULL);
+ ASSERT(zio->io_stage == ZIO_STAGE_OPEN);
+ ASSERT(zio->io_executor == NULL);
- if (pio != NULL) {
- zio_t *next, *prev;
+ zio->io_waiter = curthread;
- mutex_enter(&pio->io_lock);
- next = zio->io_sibling_next;
- prev = zio->io_sibling_prev;
- if (next != NULL)
- next->io_sibling_prev = prev;
- if (prev != NULL)
- prev->io_sibling_next = next;
- if (pio->io_child == zio)
- pio->io_child = next;
- mutex_exit(&pio->io_lock);
+ zio_execute(zio);
- zio_notify_parent(zio, ZIO_STAGE_WAIT_CHILDREN_DONE,
- &pio->io_children_notdone);
- }
+ mutex_enter(&zio->io_lock);
+ while (zio->io_executor != NULL)
+ cv_wait(&zio->io_cv, &zio->io_lock);
+ mutex_exit(&zio->io_lock);
- /*
- * Note: this I/O is now done, and will shortly be freed, so there is no
- * need to clear this (or any other) flag.
- */
- if (zio->io_flags & ZIO_FLAG_CONFIG_GRABBED)
- spa_config_exit(spa, zio);
+ error = zio->io_error;
+ zio_destroy(zio);
- if (zio->io_waiter != NULL) {
- mutex_enter(&zio->io_lock);
- ASSERT(zio->io_stage == ZIO_STAGE_DONE);
- zio->io_stalled = zio->io_stage;
- cv_broadcast(&zio->io_cv);
- mutex_exit(&zio->io_lock);
- } else {
- cv_destroy(&zio->io_cv);
- mutex_destroy(&zio->io_lock);
- kmem_cache_free(zio_cache, zio);
+ return (error);
+}
+
+void
+zio_nowait(zio_t *zio)
+{
+ ASSERT(zio->io_executor == NULL);
+
+ if (zio->io_parent == NULL && zio->io_child_type == ZIO_CHILD_LOGICAL) {
+ /*
+ * This is a logical async I/O with no parent to wait for it.
+ * Attach it to the pool's global async root zio so that
+ * spa_unload() has a way of waiting for async I/O to finish.
+ */
+ spa_t *spa = zio->io_spa;
+ zio->io_async_root = B_TRUE;
+ mutex_enter(&spa->spa_async_root_lock);
+ spa->spa_async_root_count++;
+ mutex_exit(&spa->spa_async_root_lock);
}
+
+ zio_execute(zio);
}
/*
* ==========================================================================
- * Compression support
+ * Reexecute or suspend/resume failed I/O
* ==========================================================================
*/
+
static void
-zio_write_compress(zio_t *zio)
+zio_reexecute(zio_t *pio)
{
- int compress = zio->io_compress;
- blkptr_t *bp = zio->io_bp;
- void *cbuf;
- uint64_t lsize = zio->io_size;
- uint64_t csize = lsize;
- uint64_t cbufsize = 0;
- int pass;
+ zio_t *zio, *zio_next;
- if (bp->blk_birth == zio->io_txg) {
+ pio->io_flags = pio->io_orig_flags;
+ pio->io_stage = pio->io_orig_stage;
+ pio->io_pipeline = pio->io_orig_pipeline;
+ pio->io_reexecute = 0;
+ pio->io_error = 0;
+ for (int c = 0; c < ZIO_CHILD_TYPES; c++)
+ pio->io_child_error[c] = 0;
+
+ if (IO_IS_ALLOCATING(pio)) {
/*
- * We're rewriting an existing block, which means we're
- * working on behalf of spa_sync(). For spa_sync() to
- * converge, it must eventually be the case that we don't
- * have to allocate new blocks. But compression changes
- * the blocksize, which forces a reallocate, and makes
- * convergence take longer. Therefore, after the first
- * few passes, stop compressing to ensure convergence.
+ * Remember the failed bp so that the io_ready() callback
+ * can update its accounting upon reexecution. The block
+ * was already freed in zio_done(); we indicate this with
+ * a fill count of -1 so that zio_free() knows to skip it.
*/
- pass = spa_sync_pass(zio->io_spa);
- if (pass > zio_sync_pass.zp_dontcompress)
- compress = ZIO_COMPRESS_OFF;
- } else {
- ASSERT(BP_IS_HOLE(bp));
- pass = 1;
+ blkptr_t *bp = pio->io_bp;
+ ASSERT(bp->blk_birth == 0 || bp->blk_birth == pio->io_txg);
+ bp->blk_fill = BLK_FILL_ALREADY_FREED;
+ pio->io_bp_orig = *bp;
+ BP_ZERO(bp);
}
- if (compress != ZIO_COMPRESS_OFF)
- if (!zio_compress_data(compress, zio->io_data, zio->io_size,
- &cbuf, &csize, &cbufsize))
- compress = ZIO_COMPRESS_OFF;
-
- if (compress != ZIO_COMPRESS_OFF && csize != 0)
- zio_push_transform(zio, cbuf, csize, cbufsize);
+ /*
+ * As we reexecute pio's children, new children could be created.
+ * New children go to the head of the io_child list, however,
+ * so we will (correctly) not reexecute them. The key is that
+ * the remainder of the io_child list, from 'zio_next' onward,
+ * cannot be affected by any side effects of reexecuting 'zio'.
+ */
+ for (zio = pio->io_child; zio != NULL; zio = zio_next) {
+ zio_next = zio->io_sibling_next;
+ mutex_enter(&pio->io_lock);
+ pio->io_children[zio->io_child_type][ZIO_WAIT_READY]++;
+ pio->io_children[zio->io_child_type][ZIO_WAIT_DONE]++;
+ mutex_exit(&pio->io_lock);
+ zio_reexecute(zio);
+ }
/*
- * The final pass of spa_sync() must be all rewrites, but the first
- * few passes offer a trade-off: allocating blocks defers convergence,
- * but newly allocated blocks are sequential, so they can be written
- * to disk faster. Therefore, we allow the first few passes of
- * spa_sync() to reallocate new blocks, but force rewrites after that.
- * There should only be a handful of blocks after pass 1 in any case.
+ * Now that all children have been reexecuted, execute the parent.
*/
- if (bp->blk_birth == zio->io_txg && BP_GET_PSIZE(bp) == csize &&
- pass > zio_sync_pass.zp_rewrite) {
- ASSERT(csize != 0);
- BP_SET_LSIZE(bp, lsize);
- BP_SET_COMPRESS(bp, compress);
- zio->io_pipeline = ZIO_REWRITE_PIPELINE;
- } else {
- if (bp->blk_birth == zio->io_txg)
- BP_ZERO(bp);
- if (csize == 0) {
- BP_ZERO(bp);
- zio->io_pipeline = ZIO_WAIT_FOR_CHILDREN_PIPELINE;
- } else {
- ASSERT3U(BP_GET_NDVAS(bp), ==, 0);
- BP_SET_LSIZE(bp, lsize);
- BP_SET_PSIZE(bp, csize);
- BP_SET_COMPRESS(bp, compress);
- zio->io_pipeline = ZIO_WRITE_ALLOCATE_PIPELINE;
- }
+ zio_execute(pio);
+}
+
+void
+zio_suspend(spa_t *spa, zio_t *zio)
+{
+ if (spa_get_failmode(spa) == ZIO_FAILURE_MODE_PANIC)
+ fm_panic("Pool '%s' has encountered an uncorrectable I/O "
+ "failure and the failure mode property for this pool "
+ "is set to panic.", spa_name(spa));
+
+ zfs_ereport_post(FM_EREPORT_ZFS_IO_FAILURE, spa, NULL, NULL, 0, 0);
+
+ mutex_enter(&spa->spa_suspend_lock);
+
+ if (spa->spa_suspend_zio_root == NULL)
+ spa->spa_suspend_zio_root = zio_root(spa, NULL, NULL, 0);
+
+ spa->spa_suspended = B_TRUE;
+
+ if (zio != NULL) {
+ ASSERT(zio != spa->spa_suspend_zio_root);
+ ASSERT(zio->io_child_type == ZIO_CHILD_LOGICAL);
+ ASSERT(zio->io_parent == NULL);
+ ASSERT(zio->io_stage == ZIO_STAGE_DONE);
+ zio_add_child(spa->spa_suspend_zio_root, zio);
}
- zio_next_stage(zio);
+ mutex_exit(&spa->spa_suspend_lock);
}
-static void
-zio_read_decompress(zio_t *zio)
+void
+zio_resume(spa_t *spa)
{
- blkptr_t *bp = zio->io_bp;
- void *data;
- uint64_t size;
- uint64_t bufsize;
- int compress = BP_GET_COMPRESS(bp);
+ zio_t *pio, *zio;
- ASSERT(compress != ZIO_COMPRESS_OFF);
+ /*
+ * Reexecute all previously suspended i/o.
+ */
+ mutex_enter(&spa->spa_suspend_lock);
+ spa->spa_suspended = B_FALSE;
+ cv_broadcast(&spa->spa_suspend_cv);
+ pio = spa->spa_suspend_zio_root;
+ spa->spa_suspend_zio_root = NULL;
+ mutex_exit(&spa->spa_suspend_lock);
+
+ if (pio == NULL)
+ return;
- zio_pop_transform(zio, &data, &size, &bufsize);
+ while ((zio = pio->io_child) != NULL) {
+ zio_remove_child(pio, zio);
+ zio->io_parent = NULL;
+ zio_reexecute(zio);
+ }
- if (zio_decompress_data(compress, data, size,
- zio->io_data, zio->io_size))
- zio->io_error = EIO;
+ ASSERT(pio->io_children[ZIO_CHILD_LOGICAL][ZIO_WAIT_DONE] == 0);
- zio_buf_free(data, bufsize);
+ (void) zio_wait(pio);
+}
- zio_next_stage(zio);
+void
+zio_resume_wait(spa_t *spa)
+{
+ mutex_enter(&spa->spa_suspend_lock);
+ while (spa_suspended(spa))
+ cv_wait(&spa->spa_suspend_cv, &spa->spa_suspend_lock);
+ mutex_exit(&spa->spa_suspend_lock);
}
/*
* ==========================================================================
- * Gang block support
+ * Gang blocks.
+ *
+ * A gang block is a collection of small blocks that looks to the DMU
+ * like one large block. When zio_dva_allocate() cannot find a block
+ * of the requested size, due to either severe fragmentation or the pool
+ * being nearly full, it calls zio_write_gang_block() to construct the
+ * block from smaller fragments.
+ *
+ * A gang block consists of a gang header (zio_gbh_phys_t) and up to
+ * three (SPA_GBH_NBLKPTRS) gang members. The gang header is just like
+ * an indirect block: it's an array of block pointers. It consumes
+ * only one sector and hence is allocatable regardless of fragmentation.
+ * The gang header's bps point to its gang members, which hold the data.
+ *
+ * Gang blocks are self-checksumming, using the bp's <vdev, offset, txg>
+ * as the verifier to ensure uniqueness of the SHA256 checksum.
+ * Critically, the gang block bp's blk_cksum is the checksum of the data,
+ * not the gang header. This ensures that data block signatures (needed for
+ * deduplication) are independent of how the block is physically stored.
+ *
+ * Gang blocks can be nested: a gang member may itself be a gang block.
+ * Thus every gang block is a tree in which root and all interior nodes are
+ * gang headers, and the leaves are normal blocks that contain user data.
+ * The root of the gang tree is called the gang leader.
+ *
+ * To perform any operation (read, rewrite, free, claim) on a gang block,
+ * zio_gang_assemble() first assembles the gang tree (minus data leaves)
+ * in the io_gang_tree field of the original logical i/o by recursively
+ * reading the gang leader and all gang headers below it. This yields
+ * an in-core tree containing the contents of every gang header and the
+ * bps for every constituent of the gang block.
+ *
+ * With the gang tree now assembled, zio_gang_issue() just walks the gang tree
+ * and invokes a callback on each bp. To free a gang block, zio_gang_issue()
+ * calls zio_free_gang() -- a trivial wrapper around zio_free() -- for each bp.
+ * zio_claim_gang() provides a similarly trivial wrapper for zio_claim().
+ * zio_read_gang() is a wrapper around zio_read() that omits reading gang
+ * headers, since we already have those in io_gang_tree. zio_rewrite_gang()
+ * performs a zio_rewrite() of the data or, for gang headers, a zio_rewrite()
+ * of the gang header plus zio_checksum_compute() of the data to update the
+ * gang header's blk_cksum as described above.
+ *
+ * The two-phase assemble/issue model solves the problem of partial failure --
+ * what if you'd freed part of a gang block but then couldn't read the
+ * gang header for another part? Assembling the entire gang tree first
+ * ensures that all the necessary gang header I/O has succeeded before
+ * starting the actual work of free, claim, or write. Once the gang tree
+ * is assembled, free and claim are in-memory operations that cannot fail.
+ *
+ * In the event that a gang write fails, zio_dva_unallocate() walks the
+ * gang tree to immediately free (i.e. insert back into the space map)
+ * everything we've allocated. This ensures that we don't get ENOSPC
+ * errors during repeated suspend/resume cycles due to a flaky device.
+ *
+ * Gang rewrites only happen during sync-to-convergence. If we can't assemble
+ * the gang tree, we won't modify the block, so we can safely defer the free
+ * (knowing that the block is still intact). If we *can* assemble the gang
+ * tree, then even if some of the rewrites fail, zio_dva_unallocate() will free
+ * each constituent bp and we can allocate a new block on the next sync pass.
+ *
+ * In all cases, the gang tree allows complete recovery from partial failure.
* ==========================================================================
*/
-static void
-zio_gang_pipeline(zio_t *zio)
+
+static zio_t *
+zio_read_gang(zio_t *pio, blkptr_t *bp, zio_gang_node_t *gn, void *data)
{
- /*
- * By default, the pipeline assumes that we're dealing with a gang
- * block. If we're not, strip out any gang-specific stages.
- */
- if (!BP_IS_GANG(zio->io_bp))
- zio->io_pipeline &= ~ZIO_GANG_STAGES;
+ if (gn != NULL)
+ return (pio);
- zio_next_stage(zio);
+ return (zio_read(pio, pio->io_spa, bp, data, BP_GET_PSIZE(bp),
+ NULL, NULL, pio->io_priority, ZIO_GANG_CHILD_FLAGS(pio),
+ &pio->io_bookmark));
}
-static void
-zio_gang_byteswap(zio_t *zio)
+zio_t *
+zio_rewrite_gang(zio_t *pio, blkptr_t *bp, zio_gang_node_t *gn, void *data)
{
- ASSERT(zio->io_size == SPA_GANGBLOCKSIZE);
+ zio_t *zio;
- if (BP_SHOULD_BYTESWAP(zio->io_bp))
- byteswap_uint64_array(zio->io_data, zio->io_size);
+ if (gn != NULL) {
+ zio = zio_rewrite(pio, pio->io_spa, pio->io_txg, bp,
+ gn->gn_gbh, SPA_GANGBLOCKSIZE, NULL, NULL, pio->io_priority,
+ ZIO_GANG_CHILD_FLAGS(pio), &pio->io_bookmark);
+ /*
+ * As we rewrite each gang header, the pipeline will compute
+ * a new gang block header checksum for it; but no one will
+ * compute a new data checksum, so we do that here. The one
+ * exception is the gang leader: the pipeline already computed
+ * its data checksum because that stage precedes gang assembly.
+ * (Presently, nothing actually uses interior data checksums;
+ * this is just good hygiene.)
+ */
+ if (gn != pio->io_logical->io_gang_tree) {
+ zio_checksum_compute(zio, BP_GET_CHECKSUM(bp),
+ data, BP_GET_PSIZE(bp));
+ }
+ } else {
+ zio = zio_rewrite(pio, pio->io_spa, pio->io_txg, bp,
+ data, BP_GET_PSIZE(bp), NULL, NULL, pio->io_priority,
+ ZIO_GANG_CHILD_FLAGS(pio), &pio->io_bookmark);
+ }
+
+ return (zio);
}
-static void
-zio_get_gang_header(zio_t *zio)
+/* ARGSUSED */
+zio_t *
+zio_free_gang(zio_t *pio, blkptr_t *bp, zio_gang_node_t *gn, void *data)
{
- blkptr_t *bp = zio->io_bp;
- uint64_t gsize = SPA_GANGBLOCKSIZE;
- void *gbuf = zio_buf_alloc(gsize);
+ return (zio_free(pio, pio->io_spa, pio->io_txg, bp,
+ NULL, NULL, ZIO_GANG_CHILD_FLAGS(pio)));
+}
- ASSERT(BP_IS_GANG(bp));
+/* ARGSUSED */
+zio_t *
+zio_claim_gang(zio_t *pio, blkptr_t *bp, zio_gang_node_t *gn, void *data)
+{
+ return (zio_claim(pio, pio->io_spa, pio->io_txg, bp,
+ NULL, NULL, ZIO_GANG_CHILD_FLAGS(pio)));
+}
- zio_push_transform(zio, gbuf, gsize, gsize);
+static zio_gang_issue_func_t *zio_gang_issue_func[ZIO_TYPES] = {
+ NULL,
+ zio_read_gang,
+ zio_rewrite_gang,
+ zio_free_gang,
+ zio_claim_gang,
+ NULL
+};
- zio_nowait(zio_create(zio, zio->io_spa, bp->blk_birth, bp, gbuf, gsize,
- NULL, NULL, ZIO_TYPE_READ, zio->io_priority,
- zio->io_flags & ZIO_FLAG_GANG_INHERIT,
- ZIO_STAGE_OPEN, ZIO_READ_PIPELINE));
+static void zio_gang_tree_assemble_done(zio_t *zio);
- zio_wait_children_done(zio);
+static zio_gang_node_t *
+zio_gang_node_alloc(zio_gang_node_t **gnpp)
+{
+ zio_gang_node_t *gn;
+
+ ASSERT(*gnpp == NULL);
+
+ gn = kmem_zalloc(sizeof (*gn), KM_SLEEP);
+ gn->gn_gbh = zio_buf_alloc(SPA_GANGBLOCKSIZE);
+ *gnpp = gn;
+
+ return (gn);
}
static void
-zio_read_gang_members(zio_t *zio)
+zio_gang_node_free(zio_gang_node_t **gnpp)
{
- zio_gbh_phys_t *gbh;
- uint64_t gsize, gbufsize, loff, lsize;
- int i;
+ zio_gang_node_t *gn = *gnpp;
- ASSERT(BP_IS_GANG(zio->io_bp));
+ for (int g = 0; g < SPA_GBH_NBLKPTRS; g++)
+ ASSERT(gn->gn_child[g] == NULL);
- zio_gang_byteswap(zio);
- zio_pop_transform(zio, (void **)&gbh, &gsize, &gbufsize);
+ zio_buf_free(gn->gn_gbh, SPA_GANGBLOCKSIZE);
+ kmem_free(gn, sizeof (*gn));
+ *gnpp = NULL;
+}
- for (loff = 0, i = 0; loff != zio->io_size; loff += lsize, i++) {
- blkptr_t *gbp = &gbh->zg_blkptr[i];
- lsize = BP_GET_PSIZE(gbp);
+static void
+zio_gang_tree_free(zio_gang_node_t **gnpp)
+{
+ zio_gang_node_t *gn = *gnpp;
- ASSERT(BP_GET_COMPRESS(gbp) == ZIO_COMPRESS_OFF);
- ASSERT3U(lsize, ==, BP_GET_LSIZE(gbp));
- ASSERT3U(loff + lsize, <=, zio->io_size);
- ASSERT(i < SPA_GBH_NBLKPTRS);
- ASSERT(!BP_IS_HOLE(gbp));
+ if (gn == NULL)
+ return;
- zio_nowait(zio_read(zio, zio->io_spa, gbp,
- (char *)zio->io_data + loff, lsize, NULL, NULL,
- zio->io_priority, zio->io_flags & ZIO_FLAG_GANG_INHERIT,
- &zio->io_bookmark));
- }
+ for (int g = 0; g < SPA_GBH_NBLKPTRS; g++)
+ zio_gang_tree_free(&gn->gn_child[g]);
- zio_buf_free(gbh, gbufsize);
- zio_wait_children_done(zio);
+ zio_gang_node_free(gnpp);
}
static void
-zio_rewrite_gang_members(zio_t *zio)
+zio_gang_tree_assemble(zio_t *lio, blkptr_t *bp, zio_gang_node_t **gnpp)
{
- zio_gbh_phys_t *gbh;
- uint64_t gsize, gbufsize, loff, lsize;
- int i;
+ zio_gang_node_t *gn = zio_gang_node_alloc(gnpp);
- ASSERT(BP_IS_GANG(zio->io_bp));
- ASSERT3U(zio->io_size, ==, SPA_GANGBLOCKSIZE);
+ ASSERT(lio->io_logical == lio);
+ ASSERT(BP_IS_GANG(bp));
+
+ zio_nowait(zio_read(lio, lio->io_spa, bp, gn->gn_gbh,
+ SPA_GANGBLOCKSIZE, zio_gang_tree_assemble_done, gn,
+ lio->io_priority, ZIO_GANG_CHILD_FLAGS(lio), &lio->io_bookmark));
+}
- zio_gang_byteswap(zio);
- zio_pop_transform(zio, (void **)&gbh, &gsize, &gbufsize);
+static void
+zio_gang_tree_assemble_done(zio_t *zio)
+{
+ zio_t *lio = zio->io_logical;
+ zio_gang_node_t *gn = zio->io_private;
+ blkptr_t *bp = zio->io_bp;
- ASSERT(gsize == gbufsize);
+ ASSERT(zio->io_parent == lio);
+ ASSERT(zio->io_child == NULL);
- for (loff = 0, i = 0; loff != zio->io_size; loff += lsize, i++) {
- blkptr_t *gbp = &gbh->zg_blkptr[i];
- lsize = BP_GET_PSIZE(gbp);
+ if (zio->io_error)
+ return;
- ASSERT(BP_GET_COMPRESS(gbp) == ZIO_COMPRESS_OFF);
- ASSERT3U(lsize, ==, BP_GET_LSIZE(gbp));
- ASSERT3U(loff + lsize, <=, zio->io_size);
- ASSERT(i < SPA_GBH_NBLKPTRS);
- ASSERT(!BP_IS_HOLE(gbp));
+ if (BP_SHOULD_BYTESWAP(bp))
+ byteswap_uint64_array(zio->io_data, zio->io_size);
- zio_nowait(zio_rewrite(zio, zio->io_spa, zio->io_checksum,
- zio->io_txg, gbp, (char *)zio->io_data + loff, lsize,
- NULL, NULL, zio->io_priority, zio->io_flags,
- &zio->io_bookmark));
- }
+ ASSERT(zio->io_data == gn->gn_gbh);
+ ASSERT(zio->io_size == SPA_GANGBLOCKSIZE);
+ ASSERT(gn->gn_gbh->zg_tail.zbt_magic == ZBT_MAGIC);
- zio_push_transform(zio, gbh, gsize, gbufsize);
- zio_wait_children_ready(zio);
+ for (int g = 0; g < SPA_GBH_NBLKPTRS; g++) {
+ blkptr_t *gbp = &gn->gn_gbh->zg_blkptr[g];
+ if (!BP_IS_GANG(gbp))
+ continue;
+ zio_gang_tree_assemble(lio, gbp, &gn->gn_child[g]);
+ }
}
static void
-zio_free_gang_members(zio_t *zio)
+zio_gang_tree_issue(zio_t *pio, zio_gang_node_t *gn, blkptr_t *bp, void *data)
{
- zio_gbh_phys_t *gbh;
- uint64_t gsize, gbufsize;
- int i;
+ zio_t *lio = pio->io_logical;
+ zio_t *zio;
- ASSERT(BP_IS_GANG(zio->io_bp));
+ ASSERT(BP_IS_GANG(bp) == !!gn);
+ ASSERT(BP_GET_CHECKSUM(bp) == BP_GET_CHECKSUM(lio->io_bp));
+ ASSERT(BP_GET_LSIZE(bp) == BP_GET_PSIZE(bp) || gn == lio->io_gang_tree);
- zio_gang_byteswap(zio);
- zio_pop_transform(zio, (void **)&gbh, &gsize, &gbufsize);
+ /*
+ * If you're a gang header, your data is in gn->gn_gbh.
+ * If you're a gang member, your data is in 'data' and gn == NULL.
+ */
+ zio = zio_gang_issue_func[lio->io_type](pio, bp, gn, data);
- for (i = 0; i < SPA_GBH_NBLKPTRS; i++) {
- blkptr_t *gbp = &gbh->zg_blkptr[i];
+ if (gn != NULL) {
+ ASSERT(gn->gn_gbh->zg_tail.zbt_magic == ZBT_MAGIC);
- if (BP_IS_HOLE(gbp))
- continue;
- zio_nowait(zio_free(zio, zio->io_spa, zio->io_txg,
- gbp, NULL, NULL));
+ for (int g = 0; g < SPA_GBH_NBLKPTRS; g++) {
+ blkptr_t *gbp = &gn->gn_gbh->zg_blkptr[g];
+ if (BP_IS_HOLE(gbp))
+ continue;
+ zio_gang_tree_issue(zio, gn->gn_child[g], gbp, data);
+ data = (char *)data + BP_GET_PSIZE(gbp);
+ }
}
- zio_buf_free(gbh, gbufsize);
- zio_next_stage(zio);
+ if (gn == lio->io_gang_tree)
+ ASSERT3P((char *)lio->io_data + lio->io_size, ==, data);
+
+ if (zio != pio)
+ zio_nowait(zio);
}
-static void
-zio_claim_gang_members(zio_t *zio)
+static int
+zio_gang_assemble(zio_t *zio)
{
- zio_gbh_phys_t *gbh;
- uint64_t gsize, gbufsize;
- int i;
+ blkptr_t *bp = zio->io_bp;
- ASSERT(BP_IS_GANG(zio->io_bp));
+ ASSERT(BP_IS_GANG(bp) && zio == zio->io_logical);
- zio_gang_byteswap(zio);
- zio_pop_transform(zio, (void **)&gbh, &gsize, &gbufsize);
+ zio_gang_tree_assemble(zio, bp, &zio->io_gang_tree);
- for (i = 0; i < SPA_GBH_NBLKPTRS; i++) {
- blkptr_t *gbp = &gbh->zg_blkptr[i];
- if (BP_IS_HOLE(gbp))
- continue;
- zio_nowait(zio_claim(zio, zio->io_spa, zio->io_txg,
- gbp, NULL, NULL));
- }
+ return (ZIO_PIPELINE_CONTINUE);
+}
+
+static int
+zio_gang_issue(zio_t *zio)
+{
+ zio_t *lio = zio->io_logical;
+ blkptr_t *bp = zio->io_bp;
+
+ if (zio_wait_for_children(zio, ZIO_CHILD_GANG, ZIO_WAIT_DONE))
+ return (ZIO_PIPELINE_STOP);
+
+ ASSERT(BP_IS_GANG(bp) && zio == lio);
+
+ if (zio->io_child_error[ZIO_CHILD_GANG] == 0)
+ zio_gang_tree_issue(lio, lio->io_gang_tree, bp, lio->io_data);
+ else
+ zio_gang_tree_free(&lio->io_gang_tree);
+
+ zio->io_pipeline = ZIO_INTERLOCK_PIPELINE;
- zio_buf_free(gbh, gbufsize);
- zio_next_stage(zio);
+ return (ZIO_PIPELINE_CONTINUE);
}
static void
-zio_write_allocate_gang_member_done(zio_t *zio)
+zio_write_gang_member_ready(zio_t *zio)
{
zio_t *pio = zio->io_parent;
+ zio_t *lio = zio->io_logical;
dva_t *cdva = zio->io_bp->blk_dva;
dva_t *pdva = pio->io_bp->blk_dva;
uint64_t asize;
- int d;
- ASSERT3U(pio->io_ndvas, ==, zio->io_ndvas);
+ if (BP_IS_HOLE(zio->io_bp))
+ return;
+
+ ASSERT(BP_IS_HOLE(&zio->io_bp_orig));
+
+ ASSERT(zio->io_child_type == ZIO_CHILD_GANG);
+ ASSERT3U(zio->io_prop.zp_ndvas, ==, lio->io_prop.zp_ndvas);
+ ASSERT3U(zio->io_prop.zp_ndvas, <=, BP_GET_NDVAS(zio->io_bp));
+ ASSERT3U(pio->io_prop.zp_ndvas, <=, BP_GET_NDVAS(pio->io_bp));
ASSERT3U(BP_GET_NDVAS(zio->io_bp), <=, BP_GET_NDVAS(pio->io_bp));
- ASSERT3U(zio->io_ndvas, <=, BP_GET_NDVAS(zio->io_bp));
- ASSERT3U(pio->io_ndvas, <=, BP_GET_NDVAS(pio->io_bp));
mutex_enter(&pio->io_lock);
- for (d = 0; d < BP_GET_NDVAS(pio->io_bp); d++) {
+ for (int d = 0; d < BP_GET_NDVAS(zio->io_bp); d++) {
ASSERT(DVA_GET_GANG(&pdva[d]));
asize = DVA_GET_ASIZE(&pdva[d]);
asize += DVA_GET_ASIZE(&cdva[d]);
@@ -1265,97 +1487,77 @@ zio_write_allocate_gang_member_done(zio_t *zio)
mutex_exit(&pio->io_lock);
}
-static void
-zio_write_allocate_gang_members(zio_t *zio)
+static int
+zio_write_gang_block(zio_t *pio)
{
- blkptr_t *bp = zio->io_bp;
- dva_t *dva = bp->blk_dva;
- spa_t *spa = zio->io_spa;
+ spa_t *spa = pio->io_spa;
+ blkptr_t *bp = pio->io_bp;
+ zio_t *lio = pio->io_logical;
+ zio_t *zio;
+ zio_gang_node_t *gn, **gnpp;
zio_gbh_phys_t *gbh;
- uint64_t txg = zio->io_txg;
- uint64_t resid = zio->io_size;
- uint64_t maxalloc = P2ROUNDUP(zio->io_size >> 1, SPA_MINBLOCKSIZE);
- uint64_t gsize, loff, lsize;
- uint32_t gbps_left;
- int ndvas = zio->io_ndvas;
+ uint64_t txg = pio->io_txg;
+ uint64_t resid = pio->io_size;
+ uint64_t lsize;
+ int ndvas = lio->io_prop.zp_ndvas;
int gbh_ndvas = MIN(ndvas + 1, spa_max_replication(spa));
+ zio_prop_t zp;
int error;
- int i, d;
-
- gsize = SPA_GANGBLOCKSIZE;
- gbps_left = SPA_GBH_NBLKPTRS;
-
- error = metaslab_alloc(spa, gsize, bp, gbh_ndvas, txg, NULL, B_FALSE);
- if (error == ENOSPC)
- panic("can't allocate gang block header");
- ASSERT(error == 0);
-
- for (d = 0; d < gbh_ndvas; d++)
- DVA_SET_GANG(&dva[d], 1);
-
- bp->blk_birth = txg;
-
- gbh = zio_buf_alloc(gsize);
- bzero(gbh, gsize);
- /* We need to test multi-level gang blocks */
- if (maxalloc >= zio_gang_bang && (LBOLT & 0x1) == 0)
- maxalloc = MAX(maxalloc >> 2, SPA_MINBLOCKSIZE);
+ error = metaslab_alloc(spa, spa->spa_normal_class, SPA_GANGBLOCKSIZE,
+ bp, gbh_ndvas, txg, pio == lio ? NULL : lio->io_bp,
+ METASLAB_HINTBP_FAVOR | METASLAB_GANG_HEADER);
+ if (error) {
+ pio->io_error = error;
+ return (ZIO_PIPELINE_CONTINUE);
+ }
- for (loff = 0, i = 0; loff != zio->io_size;
- loff += lsize, resid -= lsize, gbps_left--, i++) {
- blkptr_t *gbp = &gbh->zg_blkptr[i];
- dva = gbp->blk_dva;
+ if (pio == lio) {
+ gnpp = &lio->io_gang_tree;
+ } else {
+ gnpp = pio->io_private;
+ ASSERT(pio->io_ready == zio_write_gang_member_ready);
+ }
- ASSERT(gbps_left != 0);
- maxalloc = MIN(maxalloc, resid);
+ gn = zio_gang_node_alloc(gnpp);
+ gbh = gn->gn_gbh;
+ bzero(gbh, SPA_GANGBLOCKSIZE);
- while (resid <= maxalloc * gbps_left) {
- error = metaslab_alloc(spa, maxalloc, gbp, ndvas,
- txg, bp, B_FALSE);
- if (error == 0)
- break;
- ASSERT3U(error, ==, ENOSPC);
- if (maxalloc == SPA_MINBLOCKSIZE)
- panic("really out of space");
- maxalloc = P2ROUNDUP(maxalloc >> 1, SPA_MINBLOCKSIZE);
- }
+ /*
+ * Create the gang header.
+ */
+ zio = zio_rewrite(pio, spa, txg, bp, gbh, SPA_GANGBLOCKSIZE, NULL, NULL,
+ pio->io_priority, ZIO_GANG_CHILD_FLAGS(pio), &pio->io_bookmark);
- if (resid <= maxalloc * gbps_left) {
- lsize = maxalloc;
- BP_SET_LSIZE(gbp, lsize);
- BP_SET_PSIZE(gbp, lsize);
- BP_SET_COMPRESS(gbp, ZIO_COMPRESS_OFF);
- gbp->blk_birth = txg;
- zio_nowait(zio_rewrite(zio, spa,
- zio->io_checksum, txg, gbp,
- (char *)zio->io_data + loff, lsize,
- zio_write_allocate_gang_member_done, NULL,
- zio->io_priority, zio->io_flags,
- &zio->io_bookmark));
- } else {
- lsize = P2ROUNDUP(resid / gbps_left, SPA_MINBLOCKSIZE);
- ASSERT(lsize != SPA_MINBLOCKSIZE);
- zio_nowait(zio_write_allocate(zio, spa,
- zio->io_checksum, txg, gbp,
- (char *)zio->io_data + loff, lsize,
- zio_write_allocate_gang_member_done, NULL,
- zio->io_priority, zio->io_flags));
- }
+ /*
+ * Create and nowait the gang children.
+ */
+ for (int g = 0; resid != 0; resid -= lsize, g++) {
+ lsize = P2ROUNDUP(resid / (SPA_GBH_NBLKPTRS - g),
+ SPA_MINBLOCKSIZE);
+ ASSERT(lsize >= SPA_MINBLOCKSIZE && lsize <= resid);
+
+ zp.zp_checksum = lio->io_prop.zp_checksum;
+ zp.zp_compress = ZIO_COMPRESS_OFF;
+ zp.zp_type = DMU_OT_NONE;
+ zp.zp_level = 0;
+ zp.zp_ndvas = lio->io_prop.zp_ndvas;
+
+ zio_nowait(zio_write(zio, spa, txg, &gbh->zg_blkptr[g],
+ (char *)pio->io_data + (pio->io_size - resid), lsize, &zp,
+ zio_write_gang_member_ready, NULL, &gn->gn_child[g],
+ pio->io_priority, ZIO_GANG_CHILD_FLAGS(pio),
+ &pio->io_bookmark));
}
- ASSERT(resid == 0 && loff == zio->io_size);
-
- zio->io_pipeline |= 1U << ZIO_STAGE_GANG_CHECKSUM_GENERATE;
-
- zio_push_transform(zio, gbh, gsize, gsize);
/*
- * As much as we'd like this to be zio_wait_children_ready(),
- * updating our ASIZE doesn't happen until the io_done callback,
- * so we have to wait for that to finish in order for our BP
- * to be stable.
+ * Set pio's pipeline to just wait for zio to finish.
*/
- zio_wait_children_done(zio);
+ pio->io_pipeline = ZIO_INTERLOCK_PIPELINE;
+
+ zio_nowait(zio);
+
+ return (ZIO_PIPELINE_CONTINUE);
}
/*
@@ -1363,59 +1565,139 @@ zio_write_allocate_gang_members(zio_t *zio)
* Allocate and free blocks
* ==========================================================================
*/
-static void
+
+static int
zio_dva_allocate(zio_t *zio)
{
+ spa_t *spa = zio->io_spa;
+ metaslab_class_t *mc = spa->spa_normal_class;
blkptr_t *bp = zio->io_bp;
int error;
ASSERT(BP_IS_HOLE(bp));
ASSERT3U(BP_GET_NDVAS(bp), ==, 0);
- ASSERT3U(zio->io_ndvas, >, 0);
- ASSERT3U(zio->io_ndvas, <=, spa_max_replication(zio->io_spa));
-
- /* For testing, make some blocks above a certain size be gang blocks */
- if (zio->io_size >= zio_gang_bang && (LBOLT & 0x3) == 0) {
- zio_write_allocate_gang_members(zio);
- return;
- }
-
+ ASSERT3U(zio->io_prop.zp_ndvas, >, 0);
+ ASSERT3U(zio->io_prop.zp_ndvas, <=, spa_max_replication(spa));
ASSERT3U(zio->io_size, ==, BP_GET_PSIZE(bp));
- error = metaslab_alloc(zio->io_spa, zio->io_size, bp, zio->io_ndvas,
- zio->io_txg, NULL, B_FALSE);
+ error = metaslab_alloc(spa, mc, zio->io_size, bp,
+ zio->io_prop.zp_ndvas, zio->io_txg, NULL, 0);
- if (error == 0) {
- bp->blk_birth = zio->io_txg;
- } else if (error == ENOSPC) {
- if (zio->io_size == SPA_MINBLOCKSIZE)
- panic("really, truly out of space");
- zio_write_allocate_gang_members(zio);
- return;
- } else {
+ if (error) {
+ if (error == ENOSPC && zio->io_size > SPA_MINBLOCKSIZE)
+ return (zio_write_gang_block(zio));
zio->io_error = error;
}
- zio_next_stage(zio);
+
+ return (ZIO_PIPELINE_CONTINUE);
}
-static void
+static int
zio_dva_free(zio_t *zio)
{
- blkptr_t *bp = zio->io_bp;
+ metaslab_free(zio->io_spa, zio->io_bp, zio->io_txg, B_FALSE);
- metaslab_free(zio->io_spa, bp, zio->io_txg, B_FALSE);
+ return (ZIO_PIPELINE_CONTINUE);
+}
- BP_ZERO(bp);
+static int
+zio_dva_claim(zio_t *zio)
+{
+ int error;
- zio_next_stage(zio);
+ error = metaslab_claim(zio->io_spa, zio->io_bp, zio->io_txg);
+ if (error)
+ zio->io_error = error;
+
+ return (ZIO_PIPELINE_CONTINUE);
}
+/*
+ * Undo an allocation. This is used by zio_done() when an I/O fails
+ * and we want to give back the block we just allocated.
+ * This handles both normal blocks and gang blocks.
+ */
static void
-zio_dva_claim(zio_t *zio)
+zio_dva_unallocate(zio_t *zio, zio_gang_node_t *gn, blkptr_t *bp)
{
- zio->io_error = metaslab_claim(zio->io_spa, zio->io_bp, zio->io_txg);
+ spa_t *spa = zio->io_spa;
+ boolean_t now = !(zio->io_flags & ZIO_FLAG_IO_REWRITE);
+
+ ASSERT(bp->blk_birth == zio->io_txg || BP_IS_HOLE(bp));
+
+ if (zio->io_bp == bp && !now) {
+ /*
+ * This is a rewrite for sync-to-convergence.
+ * We can't do a metaslab_free(NOW) because bp wasn't allocated
+ * during this sync pass, which means that metaslab_sync()
+ * already committed the allocation.
+ */
+ ASSERT(DVA_EQUAL(BP_IDENTITY(bp),
+ BP_IDENTITY(&zio->io_bp_orig)));
+ ASSERT(spa_sync_pass(spa) > 1);
- zio_next_stage(zio);
+ if (BP_IS_GANG(bp) && gn == NULL) {
+ /*
+ * This is a gang leader whose gang header(s) we
+ * couldn't read now, so defer the free until later.
+ * The block should still be intact because without
+ * the headers, we'd never even start the rewrite.
+ */
+ bplist_enqueue_deferred(&spa->spa_sync_bplist, bp);
+ return;
+ }
+ }
+
+ if (!BP_IS_HOLE(bp))
+ metaslab_free(spa, bp, bp->blk_birth, now);
+
+ if (gn != NULL) {
+ for (int g = 0; g < SPA_GBH_NBLKPTRS; g++) {
+ zio_dva_unallocate(zio, gn->gn_child[g],
+ &gn->gn_gbh->zg_blkptr[g]);
+ }
+ }
+}
+
+/*
+ * Try to allocate an intent log block. Return 0 on success, errno on failure.
+ */
+int
+zio_alloc_blk(spa_t *spa, uint64_t size, blkptr_t *new_bp, blkptr_t *old_bp,
+ uint64_t txg)
+{
+ int error;
+
+ error = metaslab_alloc(spa, spa->spa_log_class, size,
+ new_bp, 1, txg, old_bp, METASLAB_HINTBP_AVOID);
+
+ if (error)
+ error = metaslab_alloc(spa, spa->spa_normal_class, size,
+ new_bp, 1, txg, old_bp, METASLAB_HINTBP_AVOID);
+
+ if (error == 0) {
+ BP_SET_LSIZE(new_bp, size);
+ BP_SET_PSIZE(new_bp, size);
+ BP_SET_COMPRESS(new_bp, ZIO_COMPRESS_OFF);
+ BP_SET_CHECKSUM(new_bp, ZIO_CHECKSUM_ZILOG);
+ BP_SET_TYPE(new_bp, DMU_OT_INTENT_LOG);
+ BP_SET_LEVEL(new_bp, 0);
+ BP_SET_BYTEORDER(new_bp, ZFS_HOST_BYTEORDER);
+ }
+
+ return (error);
+}
+
+/*
+ * Free an intent log block. We know it can't be a gang block, so there's
+ * nothing to do except metaslab_free() it.
+ */
+void
+zio_free_blk(spa_t *spa, blkptr_t *bp, uint64_t txg)
+{
+ ASSERT(!BP_IS_GANG(bp));
+
+ metaslab_free(spa, bp, txg, B_FALSE);
}
/*
@@ -1425,150 +1707,223 @@ zio_dva_claim(zio_t *zio)
*/
static void
-zio_vdev_io_start(zio_t *zio)
+zio_vdev_io_probe_done(zio_t *zio)
+{
+ zio_t *dio;
+ vdev_t *vd = zio->io_private;
+
+ mutex_enter(&vd->vdev_probe_lock);
+ ASSERT(vd->vdev_probe_zio == zio);
+ vd->vdev_probe_zio = NULL;
+ mutex_exit(&vd->vdev_probe_lock);
+
+ while ((dio = zio->io_delegate_list) != NULL) {
+ zio->io_delegate_list = dio->io_delegate_next;
+ dio->io_delegate_next = NULL;
+ if (!vdev_accessible(vd, dio))
+ dio->io_error = ENXIO;
+ zio_execute(dio);
+ }
+}
+
+/*
+ * Probe the device to determine whether I/O failure is specific to this
+ * zio (e.g. a bad sector) or affects the entire vdev (e.g. unplugged).
+ */
+static int
+zio_vdev_io_probe(zio_t *zio)
{
vdev_t *vd = zio->io_vd;
- vdev_t *tvd = vd ? vd->vdev_top : NULL;
- blkptr_t *bp = zio->io_bp;
- uint64_t align;
+ zio_t *pio = NULL;
+ boolean_t created_pio = B_FALSE;
- if (vd == NULL) {
- /* The mirror_ops handle multiple DVAs in a single BP */
- vdev_mirror_ops.vdev_op_io_start(zio);
- return;
+ /*
+ * Don't probe the probe.
+ */
+ if (zio->io_flags & ZIO_FLAG_PROBE)
+ return (ZIO_PIPELINE_CONTINUE);
+
+ /*
+ * To prevent 'probe storms' when a device fails, we create
+ * just one probe i/o at a time. All zios that want to probe
+ * this vdev will join the probe zio's io_delegate_list.
+ */
+ mutex_enter(&vd->vdev_probe_lock);
+
+ if ((pio = vd->vdev_probe_zio) == NULL) {
+ vd->vdev_probe_zio = pio = zio_root(zio->io_spa,
+ zio_vdev_io_probe_done, vd, ZIO_FLAG_CANFAIL);
+ created_pio = B_TRUE;
+ vd->vdev_probe_wanted = B_TRUE;
+ spa_async_request(zio->io_spa, SPA_ASYNC_PROBE);
}
- align = 1ULL << tvd->vdev_ashift;
+ zio->io_delegate_next = pio->io_delegate_list;
+ pio->io_delegate_list = zio;
+
+ mutex_exit(&vd->vdev_probe_lock);
- if (zio->io_retries == 0 && vd == tvd)
- zio->io_flags |= ZIO_FLAG_FAILFAST;
+ if (created_pio) {
+ zio_nowait(vdev_probe(vd, pio));
+ zio_nowait(pio);
+ }
+
+ return (ZIO_PIPELINE_STOP);
+}
+
+static int
+zio_vdev_io_start(zio_t *zio)
+{
+ vdev_t *vd = zio->io_vd;
+ uint64_t align;
+ spa_t *spa = zio->io_spa;
+
+ ASSERT(zio->io_error == 0);
+ ASSERT(zio->io_child_error[ZIO_CHILD_VDEV] == 0);
+
+ if (vd == NULL) {
+ if (!(zio->io_flags & ZIO_FLAG_CONFIG_WRITER))
+ spa_config_enter(spa, SCL_ZIO, zio, RW_READER);
- if (!(zio->io_flags & ZIO_FLAG_PHYSICAL) &&
- vd->vdev_children == 0) {
- zio->io_flags |= ZIO_FLAG_PHYSICAL;
- zio->io_offset += VDEV_LABEL_START_SIZE;
+ /*
+ * The mirror_ops handle multiple DVAs in a single BP.
+ */
+ return (vdev_mirror_ops.vdev_op_io_start(zio));
}
+ align = 1ULL << vd->vdev_top->vdev_ashift;
+
if (P2PHASE(zio->io_size, align) != 0) {
uint64_t asize = P2ROUNDUP(zio->io_size, align);
char *abuf = zio_buf_alloc(asize);
- ASSERT(vd == tvd);
+ ASSERT(vd == vd->vdev_top);
if (zio->io_type == ZIO_TYPE_WRITE) {
bcopy(zio->io_data, abuf, zio->io_size);
bzero(abuf + zio->io_size, asize - zio->io_size);
}
- zio_push_transform(zio, abuf, asize, asize);
- ASSERT(!(zio->io_flags & ZIO_FLAG_SUBBLOCK));
- zio->io_flags |= ZIO_FLAG_SUBBLOCK;
+ zio_push_transform(zio, abuf, asize, asize, zio_subblock);
}
ASSERT(P2PHASE(zio->io_offset, align) == 0);
ASSERT(P2PHASE(zio->io_size, align) == 0);
- ASSERT(bp == NULL ||
- P2ROUNDUP(ZIO_GET_IOSIZE(zio), align) == zio->io_size);
ASSERT(zio->io_type != ZIO_TYPE_WRITE || (spa_mode & FWRITE));
- vdev_io_start(zio);
+ if (vd->vdev_ops->vdev_op_leaf &&
+ (zio->io_type == ZIO_TYPE_READ || zio->io_type == ZIO_TYPE_WRITE)) {
- /* zio_next_stage_async() gets called from io completion interrupt */
-}
+ if (zio->io_type == ZIO_TYPE_READ && vdev_cache_read(zio) == 0)
+ return (ZIO_PIPELINE_STOP);
-static void
-zio_vdev_io_done(zio_t *zio)
-{
- if (zio->io_vd == NULL)
- /* The mirror_ops handle multiple DVAs in a single BP */
- vdev_mirror_ops.vdev_op_io_done(zio);
- else
- vdev_io_done(zio);
+ if ((zio = vdev_queue_io(zio)) == NULL)
+ return (ZIO_PIPELINE_STOP);
+
+ if (!vdev_accessible(vd, zio)) {
+ zio->io_error = ENXIO;
+ zio_interrupt(zio);
+ return (ZIO_PIPELINE_STOP);
+ }
+
+ }
+
+ return (vd->vdev_ops->vdev_op_io_start(zio));
}
-/* XXPOLICY */
-boolean_t
-zio_should_retry(zio_t *zio)
+static int
+zio_vdev_io_done(zio_t *zio)
{
vdev_t *vd = zio->io_vd;
+ vdev_ops_t *ops = vd ? vd->vdev_ops : &vdev_mirror_ops;
+ boolean_t unexpected_error = B_FALSE;
- if (zio->io_error == 0)
- return (B_FALSE);
- if (zio->io_delegate_list != NULL)
- return (B_FALSE);
- if (vd && vd != vd->vdev_top)
- return (B_FALSE);
- if (zio->io_flags & ZIO_FLAG_DONT_RETRY)
- return (B_FALSE);
- if (zio->io_retries > 0)
- return (B_FALSE);
+ if (zio_wait_for_children(zio, ZIO_CHILD_VDEV, ZIO_WAIT_DONE))
+ return (ZIO_PIPELINE_STOP);
- return (B_TRUE);
+ ASSERT(zio->io_type == ZIO_TYPE_READ || zio->io_type == ZIO_TYPE_WRITE);
+
+ if (vd != NULL && vd->vdev_ops->vdev_op_leaf) {
+
+ vdev_queue_io_done(zio);
+
+ if (zio->io_type == ZIO_TYPE_WRITE)
+ vdev_cache_write(zio);
+
+ if (zio_injection_enabled && zio->io_error == 0)
+ zio->io_error = zio_handle_device_injection(vd, EIO);
+
+ if (zio_injection_enabled && zio->io_error == 0)
+ zio->io_error = zio_handle_label_injection(zio, EIO);
+
+ if (zio->io_error) {
+ if (!vdev_accessible(vd, zio)) {
+ zio->io_error = ENXIO;
+ } else {
+ unexpected_error = B_TRUE;
+ }
+ }
+ }
+
+ ops->vdev_op_io_done(zio);
+
+ if (unexpected_error)
+ return (zio_vdev_io_probe(zio));
+
+ return (ZIO_PIPELINE_CONTINUE);
}
-static void
+static int
zio_vdev_io_assess(zio_t *zio)
{
vdev_t *vd = zio->io_vd;
- vdev_t *tvd = vd ? vd->vdev_top : NULL;
-
- ASSERT(zio->io_vsd == NULL);
-
- if (zio->io_flags & ZIO_FLAG_SUBBLOCK) {
- void *abuf;
- uint64_t asize;
- ASSERT(vd == tvd);
- zio_pop_transform(zio, &abuf, &asize, &asize);
- if (zio->io_type == ZIO_TYPE_READ)
- bcopy(abuf, zio->io_data, zio->io_size);
- zio_buf_free(abuf, asize);
- zio->io_flags &= ~ZIO_FLAG_SUBBLOCK;
+
+ if (zio_wait_for_children(zio, ZIO_CHILD_VDEV, ZIO_WAIT_DONE))
+ return (ZIO_PIPELINE_STOP);
+
+ if (vd == NULL && !(zio->io_flags & ZIO_FLAG_CONFIG_WRITER))
+ spa_config_exit(zio->io_spa, SCL_ZIO, zio);
+
+ if (zio->io_vsd != NULL) {
+ zio->io_vsd_free(zio);
+ zio->io_vsd = NULL;
}
- if (zio_injection_enabled && !zio->io_error)
+ if (zio_injection_enabled && zio->io_error == 0)
zio->io_error = zio_handle_fault_injection(zio, EIO);
/*
* If the I/O failed, determine whether we should attempt to retry it.
*/
- /* XXPOLICY */
- if (zio_should_retry(zio)) {
- ASSERT(tvd == vd);
-
- zio->io_retries++;
+ if (zio->io_error && vd == NULL &&
+ !(zio->io_flags & (ZIO_FLAG_DONT_RETRY | ZIO_FLAG_IO_RETRY))) {
+ ASSERT(!(zio->io_flags & ZIO_FLAG_DONT_QUEUE)); /* not a leaf */
+ ASSERT(!(zio->io_flags & ZIO_FLAG_IO_BYPASS)); /* not a leaf */
zio->io_error = 0;
- zio->io_flags &= ZIO_FLAG_VDEV_INHERIT |
- ZIO_FLAG_CONFIG_GRABBED;
- /* XXPOLICY */
- zio->io_flags &= ~ZIO_FLAG_FAILFAST;
- zio->io_flags |= ZIO_FLAG_DONT_CACHE;
+ zio->io_flags |= ZIO_FLAG_IO_RETRY |
+ ZIO_FLAG_DONT_CACHE | ZIO_FLAG_DONT_AGGREGATE;
zio->io_stage = ZIO_STAGE_VDEV_IO_START - 1;
+ zio_taskq_dispatch(zio, ZIO_TASKQ_ISSUE);
+ return (ZIO_PIPELINE_STOP);
+ }
- dprintf("retry #%d for %s to %s offset %llx\n",
- zio->io_retries, zio_type_name[zio->io_type],
- vdev_description(vd), zio->io_offset);
+ /*
+ * If we got an error on a leaf device, convert it to ENXIO
+ * if the device is not accessible at all.
+ */
+ if (zio->io_error && vd != NULL && vd->vdev_ops->vdev_op_leaf &&
+ !vdev_accessible(vd, zio))
+ zio->io_error = ENXIO;
- zio_next_stage_async(zio);
- return;
- }
+ /*
+ * If we can't write to an interior vdev (mirror or RAID-Z),
+ * set vdev_cant_write so that we stop trying to allocate from it.
+ */
+ if (zio->io_error == ENXIO && zio->io_type == ZIO_TYPE_WRITE &&
+ vd != NULL && !vd->vdev_ops->vdev_op_leaf)
+ vd->vdev_cant_write = B_TRUE;
- if (zio->io_error != 0 && zio->io_error != ECKSUM &&
- !(zio->io_flags & ZIO_FLAG_SPECULATIVE) && vd) {
- /*
- * Poor man's hotplug support. Even if we're done retrying this
- * I/O, try to reopen the vdev to see if it's still attached.
- * To avoid excessive thrashing, we only try it once a minute.
- * This also has the effect of detecting when missing devices
- * have come back, by polling the device once a minute.
- *
- * We need to do this asynchronously because we can't grab
- * all the necessary locks way down here.
- */
- if (gethrtime() - vd->vdev_last_try > 60ULL * NANOSEC) {
- vd->vdev_last_try = gethrtime();
- tvd->vdev_reopen_wanted = 1;
- spa_async_request(vd->vdev_spa, SPA_ASYNC_REOPEN);
- }
- }
+ if (zio->io_error)
+ zio->io_pipeline = ZIO_INTERLOCK_PIPELINE;
- zio_next_stage(zio);
+ return (ZIO_PIPELINE_CONTINUE);
}
void
@@ -1603,49 +1958,63 @@ zio_vdev_io_bypass(zio_t *zio)
* Generate and verify checksums
* ==========================================================================
*/
-static void
+static int
zio_checksum_generate(zio_t *zio)
{
- int checksum = zio->io_checksum;
blkptr_t *bp = zio->io_bp;
+ enum zio_checksum checksum;
- ASSERT3U(zio->io_size, ==, BP_GET_PSIZE(bp));
+ if (bp == NULL) {
+ /*
+ * This is zio_write_phys().
+ * We're either generating a label checksum, or none at all.
+ */
+ checksum = zio->io_prop.zp_checksum;
- BP_SET_CHECKSUM(bp, checksum);
- BP_SET_BYTEORDER(bp, ZFS_HOST_BYTEORDER);
+ if (checksum == ZIO_CHECKSUM_OFF)
+ return (ZIO_PIPELINE_CONTINUE);
- zio_checksum(checksum, &bp->blk_cksum, zio->io_data, zio->io_size);
+ ASSERT(checksum == ZIO_CHECKSUM_LABEL);
+ } else {
+ if (BP_IS_GANG(bp) && zio->io_child_type == ZIO_CHILD_GANG) {
+ ASSERT(!IO_IS_ALLOCATING(zio));
+ checksum = ZIO_CHECKSUM_GANG_HEADER;
+ } else {
+ checksum = BP_GET_CHECKSUM(bp);
+ }
+ }
- zio_next_stage(zio);
+ zio_checksum_compute(zio, checksum, zio->io_data, zio->io_size);
+
+ return (ZIO_PIPELINE_CONTINUE);
}
-static void
-zio_gang_checksum_generate(zio_t *zio)
+static int
+zio_checksum_verify(zio_t *zio)
{
- zio_cksum_t zc;
- zio_gbh_phys_t *gbh = zio->io_data;
-
- ASSERT(BP_IS_GANG(zio->io_bp));
- ASSERT3U(zio->io_size, ==, SPA_GANGBLOCKSIZE);
-
- zio_set_gang_verifier(zio, &gbh->zg_tail.zbt_cksum);
+ blkptr_t *bp = zio->io_bp;
+ int error;
- zio_checksum(ZIO_CHECKSUM_GANG_HEADER, &zc, zio->io_data, zio->io_size);
+ if (bp == NULL) {
+ /*
+ * This is zio_read_phys().
+ * We're either verifying a label checksum, or nothing at all.
+ */
+ if (zio->io_prop.zp_checksum == ZIO_CHECKSUM_OFF)
+ return (ZIO_PIPELINE_CONTINUE);
- zio_next_stage(zio);
-}
+ ASSERT(zio->io_prop.zp_checksum == ZIO_CHECKSUM_LABEL);
+ }
-static void
-zio_checksum_verify(zio_t *zio)
-{
- if (zio->io_bp != NULL) {
- zio->io_error = zio_checksum_error(zio);
- if (zio->io_error && !(zio->io_flags & ZIO_FLAG_SPECULATIVE))
+ if ((error = zio_checksum_error(zio)) != 0) {
+ zio->io_error = error;
+ if (!(zio->io_flags & ZIO_FLAG_SPECULATIVE)) {
zfs_ereport_post(FM_EREPORT_ZFS_CHECKSUM,
zio->io_spa, zio->io_vd, zio, 0, 0);
+ }
}
- zio_next_stage(zio);
+ return (ZIO_PIPELINE_CONTINUE);
}
/*
@@ -1658,204 +2027,263 @@ zio_checksum_verified(zio_t *zio)
}
/*
- * Set the external verifier for a gang block based on stuff in the bp
+ * ==========================================================================
+ * Error rank. Error are ranked in the order 0, ENXIO, ECKSUM, EIO, other.
+ * An error of 0 indictes success. ENXIO indicates whole-device failure,
+ * which may be transient (e.g. unplugged) or permament. ECKSUM and EIO
+ * indicate errors that are specific to one I/O, and most likely permanent.
+ * Any other error is presumed to be worse because we weren't expecting it.
+ * ==========================================================================
*/
-void
-zio_set_gang_verifier(zio_t *zio, zio_cksum_t *zcp)
+int
+zio_worst_error(int e1, int e2)
{
- blkptr_t *bp = zio->io_bp;
+ static int zio_error_rank[] = { 0, ENXIO, ECKSUM, EIO };
+ int r1, r2;
- zcp->zc_word[0] = DVA_GET_VDEV(BP_IDENTITY(bp));
- zcp->zc_word[1] = DVA_GET_OFFSET(BP_IDENTITY(bp));
- zcp->zc_word[2] = bp->blk_birth;
- zcp->zc_word[3] = 0;
+ for (r1 = 0; r1 < sizeof (zio_error_rank) / sizeof (int); r1++)
+ if (e1 == zio_error_rank[r1])
+ break;
+
+ for (r2 = 0; r2 < sizeof (zio_error_rank) / sizeof (int); r2++)
+ if (e2 == zio_error_rank[r2])
+ break;
+
+ return (r1 > r2 ? e1 : e2);
}
/*
* ==========================================================================
- * Define the pipeline
+ * I/O completion
* ==========================================================================
*/
-typedef void zio_pipe_stage_t(zio_t *zio);
-
-static void
-zio_badop(zio_t *zio)
+static int
+zio_ready(zio_t *zio)
{
- panic("Invalid I/O pipeline stage %u for zio %p", zio->io_stage, zio);
-}
-
-zio_pipe_stage_t *zio_pipeline[ZIO_STAGE_DONE + 2] = {
- zio_badop,
- zio_wait_children_ready,
- zio_write_compress,
- zio_checksum_generate,
- zio_gang_pipeline,
- zio_get_gang_header,
- zio_rewrite_gang_members,
- zio_free_gang_members,
- zio_claim_gang_members,
- zio_dva_allocate,
- zio_dva_free,
- zio_dva_claim,
- zio_gang_checksum_generate,
- zio_ready,
- zio_vdev_io_start,
- zio_vdev_io_done,
- zio_vdev_io_assess,
- zio_wait_children_done,
- zio_checksum_verify,
- zio_read_gang_members,
- zio_read_decompress,
- zio_done,
- zio_badop
-};
+ blkptr_t *bp = zio->io_bp;
+ zio_t *pio = zio->io_parent;
-/*
- * Move an I/O to the next stage of the pipeline and execute that stage.
- * There's no locking on io_stage because there's no legitimate way for
- * multiple threads to be attempting to process the same I/O.
- */
-void
-zio_next_stage(zio_t *zio)
-{
- uint32_t pipeline = zio->io_pipeline;
+ if (zio->io_ready) {
+ if (BP_IS_GANG(bp) &&
+ zio_wait_for_children(zio, ZIO_CHILD_GANG, ZIO_WAIT_READY))
+ return (ZIO_PIPELINE_STOP);
- ASSERT(!MUTEX_HELD(&zio->io_lock));
+ ASSERT(IO_IS_ALLOCATING(zio));
+ ASSERT(bp->blk_birth == zio->io_txg || BP_IS_HOLE(bp));
+ ASSERT(zio->io_children[ZIO_CHILD_GANG][ZIO_WAIT_READY] == 0);
- if (zio->io_error) {
- dprintf("zio %p vdev %s offset %llx stage %d error %d\n",
- zio, vdev_description(zio->io_vd),
- zio->io_offset, zio->io_stage, zio->io_error);
- if (((1U << zio->io_stage) & ZIO_VDEV_IO_PIPELINE) == 0)
- pipeline &= ZIO_ERROR_PIPELINE_MASK;
+ zio->io_ready(zio);
}
- while (((1U << ++zio->io_stage) & pipeline) == 0)
- continue;
+ if (bp != NULL && bp != &zio->io_bp_copy)
+ zio->io_bp_copy = *bp;
- ASSERT(zio->io_stage <= ZIO_STAGE_DONE);
- ASSERT(zio->io_stalled == 0);
+ if (zio->io_error)
+ zio->io_pipeline = ZIO_INTERLOCK_PIPELINE;
- /*
- * See the comment in zio_next_stage_async() about per-CPU taskqs.
- */
- if (((1U << zio->io_stage) & zio->io_async_stages) &&
- (zio->io_stage == ZIO_STAGE_WRITE_COMPRESS) &&
- !(zio->io_flags & ZIO_FLAG_METADATA)) {
- taskq_t *tq = zio->io_spa->spa_zio_issue_taskq[zio->io_type];
- (void) taskq_dispatch(tq,
- (task_func_t *)zio_pipeline[zio->io_stage], zio, TQ_SLEEP);
- } else {
- zio_pipeline[zio->io_stage](zio);
- }
+ if (pio != NULL)
+ zio_notify_parent(pio, zio, ZIO_WAIT_READY);
+
+ return (ZIO_PIPELINE_CONTINUE);
}
-void
-zio_next_stage_async(zio_t *zio)
+static int
+zio_done(zio_t *zio)
{
- taskq_t *tq;
- uint32_t pipeline = zio->io_pipeline;
-
- ASSERT(!MUTEX_HELD(&zio->io_lock));
+ spa_t *spa = zio->io_spa;
+ zio_t *pio = zio->io_parent;
+ zio_t *lio = zio->io_logical;
+ blkptr_t *bp = zio->io_bp;
+ vdev_t *vd = zio->io_vd;
+ uint64_t psize = zio->io_size;
- if (zio->io_error) {
- dprintf("zio %p vdev %s offset %llx stage %d error %d\n",
- zio, vdev_description(zio->io_vd),
- zio->io_offset, zio->io_stage, zio->io_error);
- if (((1U << zio->io_stage) & ZIO_VDEV_IO_PIPELINE) == 0)
- pipeline &= ZIO_ERROR_PIPELINE_MASK;
- }
+ /*
+ * If our of children haven't all completed,
+ * wait for them and then repeat this pipeline stage.
+ */
+ if (zio_wait_for_children(zio, ZIO_CHILD_VDEV, ZIO_WAIT_DONE) ||
+ zio_wait_for_children(zio, ZIO_CHILD_GANG, ZIO_WAIT_DONE) ||
+ zio_wait_for_children(zio, ZIO_CHILD_LOGICAL, ZIO_WAIT_DONE))
+ return (ZIO_PIPELINE_STOP);
- while (((1U << ++zio->io_stage) & pipeline) == 0)
- continue;
+ for (int c = 0; c < ZIO_CHILD_TYPES; c++)
+ for (int w = 0; w < ZIO_WAIT_TYPES; w++)
+ ASSERT(zio->io_children[c][w] == 0);
- ASSERT(zio->io_stage <= ZIO_STAGE_DONE);
- ASSERT(zio->io_stalled == 0);
+ if (bp != NULL) {
+ ASSERT(bp->blk_pad[0] == 0);
+ ASSERT(bp->blk_pad[1] == 0);
+ ASSERT(bp->blk_pad[2] == 0);
+ ASSERT(bcmp(bp, &zio->io_bp_copy, sizeof (blkptr_t)) == 0 ||
+ (pio != NULL && bp == pio->io_bp));
+ if (zio->io_type == ZIO_TYPE_WRITE && !BP_IS_HOLE(bp) &&
+ !(zio->io_flags & ZIO_FLAG_IO_REPAIR)) {
+ ASSERT(!BP_SHOULD_BYTESWAP(bp));
+ ASSERT3U(zio->io_prop.zp_ndvas, <=, BP_GET_NDVAS(bp));
+ ASSERT(BP_COUNT_GANG(bp) == 0 ||
+ (BP_COUNT_GANG(bp) == BP_GET_NDVAS(bp)));
+ }
+ }
/*
- * For performance, we'll probably want two sets of task queues:
- * per-CPU issue taskqs and per-CPU completion taskqs. The per-CPU
- * part is for read performance: since we have to make a pass over
- * the data to checksum it anyway, we want to do this on the same CPU
- * that issued the read, because (assuming CPU scheduling affinity)
- * that thread is probably still there. Getting this optimization
- * right avoids performance-hostile cache-to-cache transfers.
- *
- * Note that having two sets of task queues is also necessary for
- * correctness: if all of the issue threads get bogged down waiting
- * for dependent reads (e.g. metaslab freelist) to complete, then
- * there won't be any threads available to service I/O completion
- * interrupts.
+ * If there were child vdev or gang errors, they apply to us now.
*/
- if ((1U << zio->io_stage) & zio->io_async_stages) {
- if (zio->io_stage < ZIO_STAGE_VDEV_IO_DONE)
- tq = zio->io_spa->spa_zio_issue_taskq[zio->io_type];
- else
- tq = zio->io_spa->spa_zio_intr_taskq[zio->io_type];
- (void) taskq_dispatch(tq,
- (task_func_t *)zio_pipeline[zio->io_stage], zio, TQ_SLEEP);
- } else {
- zio_pipeline[zio->io_stage](zio);
- }
-}
+ zio_inherit_child_errors(zio, ZIO_CHILD_VDEV);
+ zio_inherit_child_errors(zio, ZIO_CHILD_GANG);
-static boolean_t
-zio_alloc_should_fail(void)
-{
- static uint16_t allocs = 0;
+ zio_pop_transforms(zio); /* note: may set zio->io_error */
- return (P2PHASE(allocs++, 1U<<zio_zil_fail_shift) == 0);
-}
+ vdev_stat_update(zio, psize);
-/*
- * Try to allocate an intent log block. Return 0 on success, errno on failure.
- */
-int
-zio_alloc_blk(spa_t *spa, uint64_t size, blkptr_t *new_bp, blkptr_t *old_bp,
- uint64_t txg)
-{
- int error;
+ if (zio->io_error) {
+ /*
+ * If this I/O is attached to a particular vdev,
+ * generate an error message describing the I/O failure
+ * at the block level. We ignore these errors if the
+ * device is currently unavailable.
+ */
+ if (zio->io_error != ECKSUM && vd != NULL && !vdev_is_dead(vd))
+ zfs_ereport_post(FM_EREPORT_ZFS_IO, spa, vd, zio, 0, 0);
- spa_config_enter(spa, RW_READER, FTAG);
+ if ((zio->io_error == EIO ||
+ !(zio->io_flags & ZIO_FLAG_SPECULATIVE)) && zio == lio) {
+ /*
+ * For logical I/O requests, tell the SPA to log the
+ * error and generate a logical data ereport.
+ */
+ spa_log_error(spa, zio);
+ zfs_ereport_post(FM_EREPORT_ZFS_DATA, spa, NULL, zio,
+ 0, 0);
+ }
+ }
- if (zio_zil_fail_shift && zio_alloc_should_fail()) {
- spa_config_exit(spa, FTAG);
- return (ENOSPC);
+ if (zio->io_error && zio == lio) {
+ /*
+ * Determine whether zio should be reexecuted. This will
+ * propagate all the way to the root via zio_notify_parent().
+ */
+ ASSERT(vd == NULL && bp != NULL);
+
+ if (IO_IS_ALLOCATING(zio))
+ if (zio->io_error != ENOSPC)
+ zio->io_reexecute |= ZIO_REEXECUTE_NOW;
+ else
+ zio->io_reexecute |= ZIO_REEXECUTE_SUSPEND;
+
+ if ((zio->io_type == ZIO_TYPE_READ ||
+ zio->io_type == ZIO_TYPE_FREE) &&
+ zio->io_error == ENXIO &&
+ spa_get_failmode(spa) != ZIO_FAILURE_MODE_CONTINUE)
+ zio->io_reexecute |= ZIO_REEXECUTE_SUSPEND;
+
+ if (!(zio->io_flags & ZIO_FLAG_CANFAIL) && !zio->io_reexecute)
+ zio->io_reexecute |= ZIO_REEXECUTE_SUSPEND;
}
/*
- * We were passed the previous log blocks dva_t in bp->blk_dva[0].
+ * If there were logical child errors, they apply to us now.
+ * We defer this until now to avoid conflating logical child
+ * errors with errors that happened to the zio itself when
+ * updating vdev stats and reporting FMA events above.
*/
- error = metaslab_alloc(spa, size, new_bp, 1, txg, old_bp, B_TRUE);
+ zio_inherit_child_errors(zio, ZIO_CHILD_LOGICAL);
- if (error == 0) {
- BP_SET_LSIZE(new_bp, size);
- BP_SET_PSIZE(new_bp, size);
- BP_SET_COMPRESS(new_bp, ZIO_COMPRESS_OFF);
- BP_SET_CHECKSUM(new_bp, ZIO_CHECKSUM_ZILOG);
- BP_SET_TYPE(new_bp, DMU_OT_INTENT_LOG);
- BP_SET_LEVEL(new_bp, 0);
- BP_SET_BYTEORDER(new_bp, ZFS_HOST_BYTEORDER);
- new_bp->blk_birth = txg;
+ if (zio->io_reexecute) {
+ /*
+ * This is a logical I/O that wants to reexecute.
+ *
+ * Reexecute is top-down. When an i/o fails, if it's not
+ * the root, it simply notifies its parent and sticks around.
+ * The parent, seeing that it still has children in zio_done(),
+ * does the same. This percolates all the way up to the root.
+ * The root i/o will reexecute or suspend the entire tree.
+ *
+ * This approach ensures that zio_reexecute() honors
+ * all the original i/o dependency relationships, e.g.
+ * parents not executing until children are ready.
+ */
+ ASSERT(zio->io_child_type == ZIO_CHILD_LOGICAL);
+
+ if (IO_IS_ALLOCATING(zio))
+ zio_dva_unallocate(zio, zio->io_gang_tree, bp);
+
+ zio_gang_tree_free(&zio->io_gang_tree);
+
+ if (pio != NULL) {
+ /*
+ * We're not a root i/o, so there's nothing to do
+ * but notify our parent. Don't propagate errors
+ * upward since we haven't permanently failed yet.
+ */
+ zio->io_flags |= ZIO_FLAG_DONT_PROPAGATE;
+ zio_notify_parent(pio, zio, ZIO_WAIT_DONE);
+ } else if (zio->io_reexecute & ZIO_REEXECUTE_SUSPEND) {
+ /*
+ * We'd fail again if we reexecuted now, so suspend
+ * until conditions improve (e.g. device comes online).
+ */
+ zio_suspend(spa, zio);
+ } else {
+ /*
+ * Reexecution is potentially a huge amount of work.
+ * Hand it off to the otherwise-unused claim taskq.
+ */
+ (void) taskq_dispatch(
+ spa->spa_zio_taskq[ZIO_TYPE_CLAIM][ZIO_TASKQ_ISSUE],
+ (task_func_t *)zio_reexecute, zio, TQ_SLEEP);
+ }
+ return (ZIO_PIPELINE_STOP);
}
- spa_config_exit(spa, FTAG);
+ ASSERT(zio->io_child == NULL);
+ ASSERT(zio->io_reexecute == 0);
+ ASSERT(zio->io_error == 0 || (zio->io_flags & ZIO_FLAG_CANFAIL));
- return (error);
-}
+ if (zio->io_done)
+ zio->io_done(zio);
-/*
- * Free an intent log block. We know it can't be a gang block, so there's
- * nothing to do except metaslab_free() it.
- */
-void
-zio_free_blk(spa_t *spa, blkptr_t *bp, uint64_t txg)
-{
- ASSERT(!BP_IS_GANG(bp));
+ zio_gang_tree_free(&zio->io_gang_tree);
- spa_config_enter(spa, RW_READER, FTAG);
+ ASSERT(zio->io_delegate_list == NULL);
+ ASSERT(zio->io_delegate_next == NULL);
- metaslab_free(spa, bp, txg, B_FALSE);
+ if (pio != NULL) {
+ zio_remove_child(pio, zio);
+ zio_notify_parent(pio, zio, ZIO_WAIT_DONE);
+ }
+
+ if (zio->io_waiter != NULL) {
+ mutex_enter(&zio->io_lock);
+ zio->io_executor = NULL;
+ cv_broadcast(&zio->io_cv);
+ mutex_exit(&zio->io_lock);
+ } else {
+ zio_destroy(zio);
+ }
- spa_config_exit(spa, FTAG);
+ return (ZIO_PIPELINE_STOP);
}
+
+/*
+ * ==========================================================================
+ * I/O pipeline definition
+ * ==========================================================================
+ */
+static zio_pipe_stage_t *zio_pipeline[ZIO_STAGES] = {
+ NULL,
+ zio_issue_async,
+ zio_read_bp_init,
+ zio_write_bp_init,
+ zio_checksum_generate,
+ zio_gang_assemble,
+ zio_gang_issue,
+ zio_dva_allocate,
+ zio_dva_free,
+ zio_dva_claim,
+ zio_ready,
+ zio_vdev_io_start,
+ zio_vdev_io_done,
+ zio_vdev_io_assess,
+ zio_checksum_verify,
+ zio_done
+};
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio_checksum.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio_checksum.c
index f0d9a14..bf7fe73 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio_checksum.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio_checksum.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/zfs_context.h>
#include <sys/spa.h>
#include <sys/zio.h>
@@ -96,25 +94,59 @@ zio_checksum_select(uint8_t child, uint8_t parent)
}
/*
+ * Set the external verifier for a gang block based on <vdev, offset, txg>,
+ * a tuple which is guaranteed to be unique for the life of the pool.
+ */
+static void
+zio_checksum_gang_verifier(zio_cksum_t *zcp, blkptr_t *bp)
+{
+ dva_t *dva = BP_IDENTITY(bp);
+ uint64_t txg = bp->blk_birth;
+
+ ASSERT(BP_IS_GANG(bp));
+
+ ZIO_SET_CHECKSUM(zcp, DVA_GET_VDEV(dva), DVA_GET_OFFSET(dva), txg, 0);
+}
+
+/*
+ * Set the external verifier for a label block based on its offset.
+ * The vdev is implicit, and the txg is unknowable at pool open time --
+ * hence the logic in vdev_uberblock_load() to find the most recent copy.
+ */
+static void
+zio_checksum_label_verifier(zio_cksum_t *zcp, uint64_t offset)
+{
+ ZIO_SET_CHECKSUM(zcp, offset, 0, 0, 0);
+}
+
+/*
* Generate the checksum.
*/
void
-zio_checksum(uint_t checksum, zio_cksum_t *zcp, void *data, uint64_t size)
+zio_checksum_compute(zio_t *zio, enum zio_checksum checksum,
+ void *data, uint64_t size)
{
+ blkptr_t *bp = zio->io_bp;
+ uint64_t offset = zio->io_offset;
zio_block_tail_t *zbt = (zio_block_tail_t *)((char *)data + size) - 1;
zio_checksum_info_t *ci = &zio_checksum_table[checksum];
zio_cksum_t zbt_cksum;
- ASSERT(checksum < ZIO_CHECKSUM_FUNCTIONS);
+ ASSERT((uint_t)checksum < ZIO_CHECKSUM_FUNCTIONS);
ASSERT(ci->ci_func[0] != NULL);
if (ci->ci_zbt) {
- *zcp = zbt->zbt_cksum;
+ if (checksum == ZIO_CHECKSUM_GANG_HEADER)
+ zio_checksum_gang_verifier(&zbt->zbt_cksum, bp);
+ else if (checksum == ZIO_CHECKSUM_LABEL)
+ zio_checksum_label_verifier(&zbt->zbt_cksum, offset);
+ else
+ bp->blk_cksum = zbt->zbt_cksum;
zbt->zbt_magic = ZBT_MAGIC;
ci->ci_func[0](data, size, &zbt_cksum);
zbt->zbt_cksum = zbt_cksum;
} else {
- ci->ci_func[0](data, size, zcp);
+ ci->ci_func[0](data, size, &bp->blk_cksum);
}
}
@@ -122,47 +154,49 @@ int
zio_checksum_error(zio_t *zio)
{
blkptr_t *bp = zio->io_bp;
- zio_cksum_t zc = bp->blk_cksum;
- uint_t checksum = BP_IS_GANG(bp) ? ZIO_CHECKSUM_GANG_HEADER :
- BP_GET_CHECKSUM(bp);
- int byteswap = BP_SHOULD_BYTESWAP(bp);
+ uint_t checksum = (bp == NULL ? zio->io_prop.zp_checksum :
+ (BP_IS_GANG(bp) ? ZIO_CHECKSUM_GANG_HEADER : BP_GET_CHECKSUM(bp)));
+ int byteswap;
void *data = zio->io_data;
- uint64_t size = ZIO_GET_IOSIZE(zio);
+ uint64_t size = (bp == NULL ? zio->io_size :
+ (BP_IS_GANG(bp) ? SPA_GANGBLOCKSIZE : BP_GET_PSIZE(bp)));
+ uint64_t offset = zio->io_offset;
zio_block_tail_t *zbt = (zio_block_tail_t *)((char *)data + size) - 1;
zio_checksum_info_t *ci = &zio_checksum_table[checksum];
- zio_cksum_t actual_cksum, expected_cksum;
+ zio_cksum_t actual_cksum, expected_cksum, verifier;
if (checksum >= ZIO_CHECKSUM_FUNCTIONS || ci->ci_func[0] == NULL)
return (EINVAL);
if (ci->ci_zbt) {
if (checksum == ZIO_CHECKSUM_GANG_HEADER)
- zio_set_gang_verifier(zio, &zc);
+ zio_checksum_gang_verifier(&verifier, bp);
+ else if (checksum == ZIO_CHECKSUM_LABEL)
+ zio_checksum_label_verifier(&verifier, offset);
+ else
+ verifier = bp->blk_cksum;
+
+ byteswap = (zbt->zbt_magic == BSWAP_64(ZBT_MAGIC));
- if (zbt->zbt_magic == BSWAP_64(ZBT_MAGIC)) {
- expected_cksum = zbt->zbt_cksum;
+ if (byteswap)
+ byteswap_uint64_array(&verifier, sizeof (zio_cksum_t));
+
+ expected_cksum = zbt->zbt_cksum;
+ zbt->zbt_cksum = verifier;
+ ci->ci_func[byteswap](data, size, &actual_cksum);
+ zbt->zbt_cksum = expected_cksum;
+
+ if (byteswap)
byteswap_uint64_array(&expected_cksum,
sizeof (zio_cksum_t));
- zbt->zbt_cksum = zc;
- byteswap_uint64_array(&zbt->zbt_cksum,
- sizeof (zio_cksum_t));
- ci->ci_func[1](data, size, &actual_cksum);
- zbt->zbt_cksum = expected_cksum;
- byteswap_uint64_array(&zbt->zbt_cksum,
- sizeof (zio_cksum_t));
- } else {
- expected_cksum = zbt->zbt_cksum;
- zbt->zbt_cksum = zc;
- ci->ci_func[0](data, size, &actual_cksum);
- zbt->zbt_cksum = expected_cksum;
- }
- zc = expected_cksum;
} else {
ASSERT(!BP_IS_GANG(bp));
+ byteswap = BP_SHOULD_BYTESWAP(bp);
+ expected_cksum = bp->blk_cksum;
ci->ci_func[byteswap](data, size, &actual_cksum);
}
- if (!ZIO_CHECKSUM_EQUAL(actual_cksum, zc))
+ if (!ZIO_CHECKSUM_EQUAL(actual_cksum, expected_cksum))
return (ECKSUM);
if (zio_injection_enabled && !zio->io_error)
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio_inject.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio_inject.c
index 4cada09..b3469fd 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio_inject.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio_inject.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* ZFS fault injection
*
@@ -47,6 +45,7 @@
#include <sys/zfs_ioctl.h>
#include <sys/spa_impl.h>
#include <sys/vdev_impl.h>
+#include <sys/fs/zfs.h>
uint32_t zio_injection_enabled;
@@ -145,6 +144,56 @@ zio_handle_fault_injection(zio_t *zio, int error)
return (ret);
}
+/*
+ * Determine if the zio is part of a label update and has an injection
+ * handler associated with that portion of the label. Currently, we
+ * allow error injection in either the nvlist or the uberblock region of
+ * of the vdev label.
+ */
+int
+zio_handle_label_injection(zio_t *zio, int error)
+{
+ inject_handler_t *handler;
+ vdev_t *vd = zio->io_vd;
+ uint64_t offset = zio->io_offset;
+ int label;
+ int ret = 0;
+
+ if (offset + zio->io_size > VDEV_LABEL_START_SIZE &&
+ offset < vd->vdev_psize - VDEV_LABEL_END_SIZE)
+ return (0);
+
+ rw_enter(&inject_lock, RW_READER);
+
+ for (handler = list_head(&inject_handlers); handler != NULL;
+ handler = list_next(&inject_handlers, handler)) {
+ uint64_t start = handler->zi_record.zi_start;
+ uint64_t end = handler->zi_record.zi_end;
+
+ /* Ignore device only faults */
+ if (handler->zi_record.zi_start == 0)
+ continue;
+
+ /*
+ * The injection region is the relative offsets within a
+ * vdev label. We must determine the label which is being
+ * updated and adjust our region accordingly.
+ */
+ label = vdev_label_number(vd->vdev_psize, offset);
+ start = vdev_label_offset(vd->vdev_psize, label, start);
+ end = vdev_label_offset(vd->vdev_psize, label, end);
+
+ if (zio->io_vd->vdev_guid == handler->zi_record.zi_guid &&
+ (offset >= start && offset <= end)) {
+ ret = error;
+ break;
+ }
+ }
+ rw_exit(&inject_lock);
+ return (ret);
+}
+
+
int
zio_handle_device_injection(vdev_t *vd, int error)
{
@@ -156,6 +205,10 @@ zio_handle_device_injection(vdev_t *vd, int error)
for (handler = list_head(&inject_handlers); handler != NULL;
handler = list_next(&inject_handlers, handler)) {
+ /* Ignore label specific faults */
+ if (handler->zi_record.zi_start != 0)
+ continue;
+
if (vd->vdev_guid == handler->zi_record.zi_guid) {
if (handler->zi_record.zi_error == error) {
/*
@@ -230,7 +283,7 @@ zio_inject_fault(char *name, int flags, int *id, zinject_record_t *record)
* fault injection isn't a performance critical path.
*/
if (flags & ZINJECT_FLUSH_ARC)
- arc_flush();
+ arc_flush(NULL);
return (0);
}
@@ -304,6 +357,7 @@ zio_clear_fault(int id)
void
zio_inject_init(void)
{
+ rw_init(&inject_lock, NULL, RW_DEFAULT, NULL);
list_create(&inject_handlers, sizeof (inject_handler_t),
offsetof(inject_handler_t, zi_link));
}
@@ -312,4 +366,5 @@ void
zio_inject_fini(void)
{
list_destroy(&inject_handlers);
+ rw_destroy(&inject_lock);
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c
index fedae03..db0ebf2 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c
@@ -23,12 +23,10 @@
* All rights reserved.
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* ZFS volume emulation driver.
*
@@ -57,6 +55,9 @@
#include <sys/zap.h>
#include <sys/spa.h>
#include <sys/zio.h>
+#include <sys/dmu_traverse.h>
+#include <sys/dnode.h>
+#include <sys/dsl_dataset.h>
#include <sys/dsl_prop.h>
#include <sys/dkio.h>
#include <sys/byteorder.h>
@@ -69,10 +70,14 @@
#include <sys/refcount.h>
#include <sys/zfs_znode.h>
#include <sys/zfs_rlock.h>
+#include <sys/vdev_impl.h>
+#include <sys/zvol.h>
#include <geom/geom.h>
#include "zfs_namecheck.h"
+#define ZVOL_DUMPSIZE "dumpsize"
+
struct g_class zfs_zvol_class = {
.name = "ZFS::ZVOL",
.version = G_VERSION,
@@ -80,11 +85,31 @@ struct g_class zfs_zvol_class = {
DECLARE_GEOM_CLASS(zfs_zvol_class, zfs_zvol);
-#define ZVOL_OBJ 1ULL
-#define ZVOL_ZAP_OBJ 2ULL
-
+/*
+ * This lock protects the zvol_state structure from being modified
+ * while it's being used, e.g. an open that comes in before a create
+ * finishes. It also protects temporary opens of the dataset so that,
+ * e.g., an open doesn't get a spurious EBUSY.
+ */
+static kmutex_t zvol_state_lock;
static uint32_t zvol_minors;
+#define NUM_EXTENTS ((SPA_MAXBLOCKSIZE) / sizeof (zvol_extent_t))
+
+typedef struct zvol_extent {
+ dva_t ze_dva; /* dva associated with this extent */
+ uint64_t ze_stride; /* extent stride */
+ uint64_t ze_size; /* number of blocks in extent */
+} zvol_extent_t;
+
+/*
+ * The list of extents associated with the dump device
+ */
+typedef struct zvol_ext_list {
+ zvol_extent_t zl_extents[NUM_EXTENTS];
+ struct zvol_ext_list *zl_next;
+} zvol_ext_list_t;
+
/*
* The in-core state of each volume.
*/
@@ -94,11 +119,12 @@ typedef struct zvol_state {
uint64_t zv_volblocksize; /* volume block size */
struct g_provider *zv_provider; /* GEOM provider */
uint8_t zv_min_bs; /* minimum addressable block shift */
- uint8_t zv_readonly; /* hard readonly; like write-protect */
+ uint8_t zv_flags; /* readonly; dumpified */
objset_t *zv_objset; /* objset handle */
uint32_t zv_mode; /* DS_MODE_* flags at open time */
uint32_t zv_total_opens; /* total open count */
zilog_t *zv_zilog; /* ZIL handle */
+ zvol_ext_list_t *zv_list; /* List of extents for dump */
uint64_t zv_txg_assign; /* txg to assign during ZIL replay */
znode_t zv_znode; /* for range locking */
int zv_state;
@@ -107,11 +133,28 @@ typedef struct zvol_state {
} zvol_state_t;
/*
+ * zvol specific flags
+ */
+#define ZVOL_RDONLY 0x1
+#define ZVOL_DUMPIFIED 0x2
+#define ZVOL_EXCL 0x4
+
+/*
* zvol maximum transfer in one DMU tx.
*/
int zvol_maxphys = DMU_MAX_ACCESS/2;
+extern int zfs_set_prop_nvlist(const char *, nvlist_t *);
static int zvol_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio);
+static int zvol_dumpify(zvol_state_t *zv);
+static int zvol_dump_fini(zvol_state_t *zv);
+static int zvol_dump_init(zvol_state_t *zv, boolean_t resize);
+
+static void
+zvol_size_changed(zvol_state_t *zv, major_t maj)
+{
+
+}
int
zvol_check_volsize(uint64_t volsize, uint64_t blocksize)
@@ -145,7 +188,10 @@ zvol_readonly_changed_cb(void *arg, uint64_t newval)
{
zvol_state_t *zv = arg;
- zv->zv_readonly = (uint8_t)newval;
+ if (newval)
+ zv->zv_flags |= ZVOL_RDONLY;
+ else
+ zv->zv_flags &= ~ZVOL_RDONLY;
}
int
@@ -179,6 +225,7 @@ zvol_minor_lookup(const char *name)
struct g_geom *gp;
g_topology_assert();
+ ASSERT(MUTEX_HELD(&zvol_state_lock));
LIST_FOREACH(gp, &zfs_zvol_class.geom, geom) {
LIST_FOREACH(pp, &gp->provider, provider) {
@@ -196,21 +243,29 @@ zvol_access(struct g_provider *pp, int acr, int acw, int ace)
zvol_state_t *zv;
g_topology_assert();
+ mutex_enter(&zvol_state_lock);
zv = pp->private;
if (zv == NULL) {
if (acr <= 0 && acw <= 0 && ace <= 0)
return (0);
+ mutex_exit(&zvol_state_lock);
return (pp->error);
}
ASSERT(zv->zv_objset != NULL);
- if (acw > 0 && (zv->zv_readonly || (zv->zv_mode & DS_MODE_READONLY)))
+ if (acw > 0 &&
+ ((zv->zv_flags & ZVOL_RDONLY) ||
+ (zv->zv_mode & DS_MODE_READONLY))) {
+ mutex_exit(&zvol_state_lock);
return (EROFS);
+ }
zv->zv_total_opens += acr + acw + ace;
+ mutex_exit(&zvol_state_lock);
+
return (0);
}
@@ -324,8 +379,12 @@ zvol_serve_one(zvol_state_t *zv, struct bio *bp)
dmu_tx_commit(tx);
}
}
- if (error)
+ if (error) {
+ /* convert checksum errors into IO errors */
+ if (error == ECKSUM)
+ error = EIO;
break;
+ }
off += size;
addr += size;
resid -= size;
@@ -368,7 +427,7 @@ zvol_worker(void *arg)
break;
}
- if (bp->bio_cmd != BIO_READ && !zil_disable)
+ if (bp->bio_cmd == BIO_FLUSH && !zil_disable)
zil_commit(zv->zv_zilog, UINT64_MAX, ZVOL_OBJ);
g_io_deliver(bp, bp->bio_error);
@@ -376,25 +435,152 @@ zvol_worker(void *arg)
}
void
-zvol_create_cb(objset_t *os, void *arg, dmu_tx_t *tx)
+zvol_init_extent(zvol_extent_t *ze, blkptr_t *bp)
{
- zfs_create_data_t *zc = arg;
+ ze->ze_dva = bp->blk_dva[0]; /* structure assignment */
+ ze->ze_stride = 0;
+ ze->ze_size = 1;
+}
+
+/* extent mapping arg */
+struct maparg {
+ zvol_ext_list_t *ma_list;
+ zvol_extent_t *ma_extent;
+ int ma_gang;
+};
+
+/*ARGSUSED*/
+static int
+zvol_map_block(traverse_blk_cache_t *bc, spa_t *spa, void *arg)
+{
+ zbookmark_t *zb = &bc->bc_bookmark;
+ blkptr_t *bp = &bc->bc_blkptr;
+ void *data = bc->bc_data;
+ dnode_phys_t *dnp = bc->bc_dnode;
+ struct maparg *ma = (struct maparg *)arg;
+ uint64_t stride;
+
+ /* If there is an error, then keep trying to make progress */
+ if (bc->bc_errno)
+ return (ERESTART);
+
+#ifdef ZFS_DEBUG
+ if (zb->zb_level == -1) {
+ ASSERT3U(BP_GET_TYPE(bp), ==, DMU_OT_OBJSET);
+ ASSERT3U(BP_GET_LEVEL(bp), ==, 0);
+ } else {
+ ASSERT3U(BP_GET_TYPE(bp), ==, dnp->dn_type);
+ ASSERT3U(BP_GET_LEVEL(bp), ==, zb->zb_level);
+ }
+
+ if (zb->zb_level > 0) {
+ uint64_t fill = 0;
+ blkptr_t *bpx, *bpend;
+
+ for (bpx = data, bpend = bpx + BP_GET_LSIZE(bp) / sizeof (*bpx);
+ bpx < bpend; bpx++) {
+ if (bpx->blk_birth != 0) {
+ fill += bpx->blk_fill;
+ } else {
+ ASSERT(bpx->blk_fill == 0);
+ }
+ }
+ ASSERT3U(fill, ==, bp->blk_fill);
+ }
+
+ if (zb->zb_level == 0 && dnp->dn_type == DMU_OT_DNODE) {
+ uint64_t fill = 0;
+ dnode_phys_t *dnx, *dnend;
+
+ for (dnx = data, dnend = dnx + (BP_GET_LSIZE(bp)>>DNODE_SHIFT);
+ dnx < dnend; dnx++) {
+ if (dnx->dn_type != DMU_OT_NONE)
+ fill++;
+ }
+ ASSERT3U(fill, ==, bp->blk_fill);
+ }
+#endif
+
+ if (zb->zb_level || dnp->dn_type == DMU_OT_DNODE)
+ return (0);
+
+ /* Abort immediately if we have encountered gang blocks */
+ if (BP_IS_GANG(bp)) {
+ ma->ma_gang++;
+ return (EINTR);
+ }
+
+ /* first time? */
+ if (ma->ma_extent->ze_size == 0) {
+ zvol_init_extent(ma->ma_extent, bp);
+ return (0);
+ }
+
+ stride = (DVA_GET_OFFSET(&bp->blk_dva[0])) -
+ ((DVA_GET_OFFSET(&ma->ma_extent->ze_dva)) +
+ (ma->ma_extent->ze_size - 1) * (ma->ma_extent->ze_stride));
+ if (DVA_GET_VDEV(BP_IDENTITY(bp)) ==
+ DVA_GET_VDEV(&ma->ma_extent->ze_dva)) {
+ if (ma->ma_extent->ze_stride == 0) {
+ /* second block in this extent */
+ ma->ma_extent->ze_stride = stride;
+ ma->ma_extent->ze_size++;
+ return (0);
+ } else if (ma->ma_extent->ze_stride == stride) {
+ /*
+ * the block we allocated has the same
+ * stride
+ */
+ ma->ma_extent->ze_size++;
+ return (0);
+ }
+ }
+
+ /*
+ * dtrace -n 'zfs-dprintf
+ * /stringof(arg0) == "zvol.c"/
+ * {
+ * printf("%s: %s", stringof(arg1), stringof(arg3))
+ * } '
+ */
+ dprintf("ma_extent 0x%lx mrstride 0x%lx stride %lx\n",
+ ma->ma_extent->ze_size, ma->ma_extent->ze_stride, stride);
+ dprintf_bp(bp, "%s", "next blkptr:");
+ /* start a new extent */
+ if (ma->ma_extent == &ma->ma_list->zl_extents[NUM_EXTENTS - 1]) {
+ ma->ma_list->zl_next = kmem_zalloc(sizeof (zvol_ext_list_t),
+ KM_SLEEP);
+ ma->ma_list = ma->ma_list->zl_next;
+ ma->ma_extent = &ma->ma_list->zl_extents[0];
+ } else {
+ ma->ma_extent++;
+ }
+ zvol_init_extent(ma->ma_extent, bp);
+ return (0);
+}
+
+/* ARGSUSED */
+void
+zvol_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx)
+{
+ zfs_creat_t *zct = arg;
+ nvlist_t *nvprops = zct->zct_props;
int error;
uint64_t volblocksize, volsize;
- VERIFY(nvlist_lookup_uint64(zc->zc_props,
+ VERIFY(nvlist_lookup_uint64(nvprops,
zfs_prop_to_name(ZFS_PROP_VOLSIZE), &volsize) == 0);
- if (nvlist_lookup_uint64(zc->zc_props,
+ if (nvlist_lookup_uint64(nvprops,
zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), &volblocksize) != 0)
volblocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
/*
- * These properites must be removed from the list so the generic
+ * These properties must be removed from the list so the generic
* property setting step won't apply to them.
*/
- VERIFY(nvlist_remove_all(zc->zc_props,
+ VERIFY(nvlist_remove_all(nvprops,
zfs_prop_to_name(ZFS_PROP_VOLSIZE)) == 0);
- (void) nvlist_remove_all(zc->zc_props,
+ (void) nvlist_remove_all(nvprops,
zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE));
error = dmu_object_claim(os, ZVOL_OBJ, DMU_OT_ZVOL, volblocksize,
@@ -467,10 +653,110 @@ zil_replay_func_t *zvol_replay_vector[TX_MAX_TYPE] = {
};
/*
- * Create a minor node for the specified volume.
+ * reconstruct dva that gets us to the desired offset (offset
+ * is in bytes)
*/
int
-zvol_create_minor(const char *name, dev_t dev)
+zvol_get_dva(zvol_state_t *zv, uint64_t offset, dva_t *dva)
+{
+ zvol_ext_list_t *zl;
+ zvol_extent_t *ze;
+ int idx;
+ uint64_t tmp;
+
+ if ((zl = zv->zv_list) == NULL)
+ return (EIO);
+ idx = 0;
+ ze = &zl->zl_extents[0];
+ while (offset >= ze->ze_size * zv->zv_volblocksize) {
+ offset -= ze->ze_size * zv->zv_volblocksize;
+
+ if (idx == NUM_EXTENTS - 1) {
+ /* we've reached the end of this array */
+ ASSERT(zl->zl_next != NULL);
+ if (zl->zl_next == NULL)
+ return (-1);
+ zl = zl->zl_next;
+ ze = &zl->zl_extents[0];
+ idx = 0;
+ } else {
+ ze++;
+ idx++;
+ }
+ }
+ DVA_SET_VDEV(dva, DVA_GET_VDEV(&ze->ze_dva));
+ tmp = DVA_GET_OFFSET((&ze->ze_dva));
+ tmp += (ze->ze_stride * (offset / zv->zv_volblocksize));
+ DVA_SET_OFFSET(dva, tmp);
+ return (0);
+}
+
+static void
+zvol_free_extents(zvol_state_t *zv)
+{
+ zvol_ext_list_t *zl;
+ zvol_ext_list_t *tmp;
+
+ if (zv->zv_list != NULL) {
+ zl = zv->zv_list;
+ while (zl != NULL) {
+ tmp = zl->zl_next;
+ kmem_free(zl, sizeof (zvol_ext_list_t));
+ zl = tmp;
+ }
+ zv->zv_list = NULL;
+ }
+}
+
+int
+zvol_get_lbas(zvol_state_t *zv)
+{
+ struct maparg ma;
+ zvol_ext_list_t *zl;
+ zvol_extent_t *ze;
+ uint64_t blocks = 0;
+ int err;
+
+ ma.ma_list = zl = kmem_zalloc(sizeof (zvol_ext_list_t), KM_SLEEP);
+ ma.ma_extent = &ma.ma_list->zl_extents[0];
+ ma.ma_gang = 0;
+ zv->zv_list = ma.ma_list;
+
+ err = traverse_zvol(zv->zv_objset, ADVANCE_PRE, zvol_map_block, &ma);
+ if (err == EINTR && ma.ma_gang) {
+ /*
+ * We currently don't support dump devices when the pool
+ * is so fragmented that our allocation has resulted in
+ * gang blocks.
+ */
+ zvol_free_extents(zv);
+ return (EFRAGS);
+ }
+ ASSERT3U(err, ==, 0);
+
+ ze = &zl->zl_extents[0];
+ while (ze) {
+ blocks += ze->ze_size;
+ if (ze == &zl->zl_extents[NUM_EXTENTS - 1]) {
+ zl = zl->zl_next;
+ ze = &zl->zl_extents[0];
+ } else {
+ ze++;
+ }
+ }
+ if (blocks != (zv->zv_volsize / zv->zv_volblocksize)) {
+ zvol_free_extents(zv);
+ return (EIO);
+ }
+
+ return (0);
+}
+
+/*
+ * Create a minor node (plus a whole lot more) for the specified volume.
+ */
+int
+zvol_create_minor(const char *name, major_t maj)
{
struct g_provider *pp;
struct g_geom *gp;
@@ -478,11 +764,12 @@ zvol_create_minor(const char *name, dev_t dev)
objset_t *os;
dmu_object_info_t doi;
uint64_t volsize;
- int ds_mode = DS_MODE_PRIMARY;
+ int ds_mode = DS_MODE_OWNER;
int error;
DROP_GIANT();
g_topology_lock();
+ mutex_enter(&zvol_state_lock);
if ((zv = zvol_minor_lookup(name)) != NULL) {
error = EEXIST;
@@ -496,11 +783,7 @@ zvol_create_minor(const char *name, dev_t dev)
if (error)
goto end;
- g_topology_unlock();
- PICKUP_GIANT();
error = zap_lookup(os, ZVOL_ZAP_OBJ, "size", 8, 1, &volsize);
- DROP_GIANT();
- g_topology_lock();
if (error) {
dmu_objset_close(os);
goto end;
@@ -524,14 +807,12 @@ zvol_create_minor(const char *name, dev_t dev)
mutex_init(&zv->zv_znode.z_range_lock, NULL, MUTEX_DEFAULT, NULL);
avl_create(&zv->zv_znode.z_range_avl, zfs_range_compare,
sizeof (rl_t), offsetof(rl_t, r_node));
-
-
/* get and cache the blocksize */
error = dmu_object_info(os, ZVOL_OBJ, &doi);
ASSERT(error == 0);
zv->zv_volblocksize = doi.doi_data_block_size;
- zil_replay(os, zv, &zv->zv_txg_assign, zvol_replay_vector);
+ zil_replay(os, zv, &zv->zv_txg_assign, zvol_replay_vector, NULL);
/* XXX this should handle the possible i/o error */
VERIFY(dsl_prop_register(dmu_objset_ds(zv->zv_objset),
@@ -547,6 +828,7 @@ zvol_create_minor(const char *name, dev_t dev)
zvol_minors++;
end:
+ mutex_exit(&zvol_state_lock);
g_topology_unlock();
PICKUP_GIANT();
@@ -565,6 +847,7 @@ zvol_remove_minor(const char *name)
DROP_GIANT();
g_topology_lock();
+ mutex_enter(&zvol_state_lock);
if ((zv = zvol_minor_lookup(name)) == NULL) {
error = ENXIO;
@@ -602,6 +885,7 @@ zvol_remove_minor(const char *name)
zvol_minors--;
end:
+ mutex_exit(&zvol_state_lock);
g_topology_unlock();
PICKUP_GIANT();
@@ -609,55 +893,143 @@ end:
}
int
-zvol_set_volsize(const char *name, dev_t dev, uint64_t volsize)
+zvol_prealloc(zvol_state_t *zv)
+{
+ objset_t *os = zv->zv_objset;
+ dmu_tx_t *tx;
+ void *data;
+ uint64_t refd, avail, usedobjs, availobjs;
+ uint64_t resid = zv->zv_volsize;
+ uint64_t off = 0;
+
+ /* Check the space usage before attempting to allocate the space */
+ dmu_objset_space(os, &refd, &avail, &usedobjs, &availobjs);
+ if (avail < zv->zv_volsize)
+ return (ENOSPC);
+
+ /* Free old extents if they exist */
+ zvol_free_extents(zv);
+
+ /* allocate the blocks by writing each one */
+ data = kmem_zalloc(SPA_MAXBLOCKSIZE, KM_SLEEP);
+
+ while (resid != 0) {
+ int error;
+ uint64_t bytes = MIN(resid, SPA_MAXBLOCKSIZE);
+
+ tx = dmu_tx_create(os);
+ dmu_tx_hold_write(tx, ZVOL_OBJ, off, bytes);
+ error = dmu_tx_assign(tx, TXG_WAIT);
+ if (error) {
+ dmu_tx_abort(tx);
+ kmem_free(data, SPA_MAXBLOCKSIZE);
+ (void) dmu_free_long_range(os, ZVOL_OBJ, 0, off);
+ return (error);
+ }
+ dmu_write(os, ZVOL_OBJ, off, bytes, data, tx);
+ dmu_tx_commit(tx);
+ off += bytes;
+ resid -= bytes;
+ }
+ kmem_free(data, SPA_MAXBLOCKSIZE);
+ txg_wait_synced(dmu_objset_pool(os), 0);
+
+ return (0);
+}
+
+int
+zvol_update_volsize(zvol_state_t *zv, major_t maj, uint64_t volsize)
{
- zvol_state_t *zv;
dmu_tx_t *tx;
int error;
+
+ ASSERT(MUTEX_HELD(&zvol_state_lock));
+
+ tx = dmu_tx_create(zv->zv_objset);
+ dmu_tx_hold_zap(tx, ZVOL_ZAP_OBJ, TRUE, NULL);
+ error = dmu_tx_assign(tx, TXG_WAIT);
+ if (error) {
+ dmu_tx_abort(tx);
+ return (error);
+ }
+
+ error = zap_update(zv->zv_objset, ZVOL_ZAP_OBJ, "size", 8, 1,
+ &volsize, tx);
+ dmu_tx_commit(tx);
+
+ if (error == 0)
+ error = dmu_free_long_range(zv->zv_objset,
+ ZVOL_OBJ, volsize, DMU_OBJECT_END);
+
+ /*
+ * If we are using a faked-up state (zv_provider == NULL) then don't
+ * try to update the in-core zvol state.
+ */
+ if (error == 0 && zv->zv_provider) {
+ zv->zv_volsize = volsize;
+ zvol_size_changed(zv, maj);
+ }
+ return (error);
+}
+
+int
+zvol_set_volsize(const char *name, major_t maj, uint64_t volsize)
+{
+ zvol_state_t *zv;
+ int error;
dmu_object_info_t doi;
+ uint64_t old_volsize = 0ULL;
+ zvol_state_t state = { 0 };
DROP_GIANT();
g_topology_lock();
+ mutex_enter(&zvol_state_lock);
if ((zv = zvol_minor_lookup(name)) == NULL) {
- error = ENXIO;
- goto end;
+ /*
+ * If we are doing a "zfs clone -o volsize=", then the
+ * minor node won't exist yet.
+ */
+ error = dmu_objset_open(name, DMU_OST_ZVOL, DS_MODE_OWNER,
+ &state.zv_objset);
+ if (error != 0)
+ goto out;
+ zv = &state;
}
+ old_volsize = zv->zv_volsize;
if ((error = dmu_object_info(zv->zv_objset, ZVOL_OBJ, &doi)) != 0 ||
(error = zvol_check_volsize(volsize,
- doi.doi_data_block_size)) != 0) {
- goto end;
- }
+ doi.doi_data_block_size)) != 0)
+ goto out;
- if (zv->zv_readonly || (zv->zv_mode & DS_MODE_READONLY)) {
+ if (zv->zv_flags & ZVOL_RDONLY || (zv->zv_mode & DS_MODE_READONLY)) {
error = EROFS;
- goto end;
+ goto out;
}
- tx = dmu_tx_create(zv->zv_objset);
- dmu_tx_hold_zap(tx, ZVOL_ZAP_OBJ, TRUE, NULL);
- dmu_tx_hold_free(tx, ZVOL_OBJ, volsize, DMU_OBJECT_END);
- error = dmu_tx_assign(tx, TXG_WAIT);
- if (error) {
- dmu_tx_abort(tx);
- goto end;
- }
+ error = zvol_update_volsize(zv, maj, volsize);
- error = zap_update(zv->zv_objset, ZVOL_ZAP_OBJ, "size", 8, 1,
- &volsize, tx);
- if (error == 0) {
- error = dmu_free_range(zv->zv_objset, ZVOL_OBJ, volsize,
- DMU_OBJECT_END, tx);
+#if 0
+ /*
+ * Reinitialize the dump area to the new size. If we
+ * failed to resize the dump area then restore the it back to
+ * it's original size.
+ */
+ if (error == 0 && zv->zv_flags & ZVOL_DUMPIFIED) {
+ if ((error = zvol_dumpify(zv)) != 0 ||
+ (error = dumpvp_resize()) != 0) {
+ (void) zvol_update_volsize(zv, maj, old_volsize);
+ error = zvol_dumpify(zv);
+ }
}
+#endif
- dmu_tx_commit(tx);
+out:
+ if (state.zv_objset)
+ dmu_objset_close(state.zv_objset);
- if (error == 0) {
- zv->zv_volsize = volsize;
- zv->zv_provider->mediasize = volsize; /* XXX: Not supported. */
- }
-end:
+ mutex_exit(&zvol_state_lock);
g_topology_unlock();
PICKUP_GIANT();
@@ -673,13 +1045,13 @@ zvol_set_volblocksize(const char *name, uint64_t volblocksize)
DROP_GIANT();
g_topology_lock();
+ mutex_enter(&zvol_state_lock);
if ((zv = zvol_minor_lookup(name)) == NULL) {
error = ENXIO;
goto end;
}
-
- if (zv->zv_readonly || (zv->zv_mode & DS_MODE_READONLY)) {
+ if (zv->zv_flags & ZVOL_RDONLY || (zv->zv_mode & DS_MODE_READONLY)) {
error = EROFS;
goto end;
}
@@ -702,6 +1074,7 @@ zvol_set_volblocksize(const char *name, uint64_t volblocksize)
#endif
}
end:
+ mutex_exit(&zvol_state_lock);
g_topology_unlock();
PICKUP_GIANT();
@@ -716,7 +1089,7 @@ zvol_get_done(dmu_buf_t *db, void *vzgd)
dmu_buf_rele(db, vzgd);
zfs_range_unlock(rl);
- zil_add_vdev(zgd->zgd_zilog, DVA_GET_VDEV(BP_IDENTITY(zgd->zgd_bp)));
+ zil_add_block(zgd->zgd_zilog, zgd->zgd_bp);
kmem_free(zgd, sizeof (zgd_t));
}
@@ -754,7 +1127,7 @@ zvol_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio)
/*
* Lock the range of the block to ensure that when the data is
- * written out and it's checksum is being calculated that no other
+ * written out and its checksum is being calculated that no other
* thread can change the block.
*/
boff = P2ALIGN_TYPED(lr->lr_offset, zv->zv_volblocksize, uint64_t);
@@ -766,8 +1139,7 @@ zvol_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio)
error = dmu_sync(zio, db, &lr->lr_blkptr,
lr->lr_common.lrc_txg, zvol_get_done, zgd);
if (error == 0)
- zil_add_vdev(zv->zv_zilog,
- DVA_GET_VDEV(BP_IDENTITY(&lr->lr_blkptr)));
+ zil_add_block(zv->zv_zilog, &lr->lr_blkptr);
/*
* If we get EINPROGRESS, then we need to wait for a
* write IO initiated by dmu_sync() to complete before
@@ -791,11 +1163,230 @@ zvol_busy(void)
void
zvol_init(void)
{
+ mutex_init(&zvol_state_lock, NULL, MUTEX_DEFAULT, NULL);
ZFS_LOG(1, "ZVOL Initialized.");
}
void
zvol_fini(void)
{
+ mutex_destroy(&zvol_state_lock);
ZFS_LOG(1, "ZVOL Deinitialized.");
}
+
+static boolean_t
+zvol_is_swap(zvol_state_t *zv)
+{
+ vnode_t *vp;
+ boolean_t ret = B_FALSE;
+ char *devpath;
+ size_t devpathlen;
+ int error;
+
+#if 0
+ devpathlen = strlen(ZVOL_FULL_DEV_DIR) + strlen(zv->zv_name) + 1;
+ devpath = kmem_alloc(devpathlen, KM_SLEEP);
+ (void) sprintf(devpath, "%s%s", ZVOL_FULL_DEV_DIR, zv->zv_name);
+ error = lookupname(devpath, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp);
+ kmem_free(devpath, devpathlen);
+
+ ret = !error && IS_SWAPVP(common_specvp(vp));
+
+ if (vp != NULL)
+ VN_RELE(vp);
+#endif
+
+ return (ret);
+}
+
+static int
+zvol_dump_init(zvol_state_t *zv, boolean_t resize)
+{
+ dmu_tx_t *tx;
+ int error = 0;
+ objset_t *os = zv->zv_objset;
+ nvlist_t *nv = NULL;
+ uint64_t checksum, compress, refresrv;
+
+ ASSERT(MUTEX_HELD(&zvol_state_lock));
+
+ tx = dmu_tx_create(os);
+ dmu_tx_hold_zap(tx, ZVOL_ZAP_OBJ, TRUE, NULL);
+ error = dmu_tx_assign(tx, TXG_WAIT);
+ if (error) {
+ dmu_tx_abort(tx);
+ return (error);
+ }
+
+ /*
+ * If we are resizing the dump device then we only need to
+ * update the refreservation to match the newly updated
+ * zvolsize. Otherwise, we save off the original state of the
+ * zvol so that we can restore them if the zvol is ever undumpified.
+ */
+ if (resize) {
+ error = zap_update(os, ZVOL_ZAP_OBJ,
+ zfs_prop_to_name(ZFS_PROP_REFRESERVATION), 8, 1,
+ &zv->zv_volsize, tx);
+ } else {
+ error = dsl_prop_get_integer(zv->zv_name,
+ zfs_prop_to_name(ZFS_PROP_COMPRESSION), &compress, NULL);
+ error = error ? error : dsl_prop_get_integer(zv->zv_name,
+ zfs_prop_to_name(ZFS_PROP_CHECKSUM), &checksum, NULL);
+ error = error ? error : dsl_prop_get_integer(zv->zv_name,
+ zfs_prop_to_name(ZFS_PROP_REFRESERVATION), &refresrv, NULL);
+
+ error = error ? error : zap_update(os, ZVOL_ZAP_OBJ,
+ zfs_prop_to_name(ZFS_PROP_COMPRESSION), 8, 1,
+ &compress, tx);
+ error = error ? error : zap_update(os, ZVOL_ZAP_OBJ,
+ zfs_prop_to_name(ZFS_PROP_CHECKSUM), 8, 1, &checksum, tx);
+ error = error ? error : zap_update(os, ZVOL_ZAP_OBJ,
+ zfs_prop_to_name(ZFS_PROP_REFRESERVATION), 8, 1,
+ &refresrv, tx);
+ }
+ dmu_tx_commit(tx);
+
+ /* Truncate the file */
+ if (!error)
+ error = dmu_free_long_range(zv->zv_objset,
+ ZVOL_OBJ, 0, DMU_OBJECT_END);
+
+ if (error)
+ return (error);
+
+ /*
+ * We only need update the zvol's property if we are initializing
+ * the dump area for the first time.
+ */
+ if (!resize) {
+ VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0);
+ VERIFY(nvlist_add_uint64(nv,
+ zfs_prop_to_name(ZFS_PROP_REFRESERVATION), 0) == 0);
+ VERIFY(nvlist_add_uint64(nv,
+ zfs_prop_to_name(ZFS_PROP_COMPRESSION),
+ ZIO_COMPRESS_OFF) == 0);
+ VERIFY(nvlist_add_uint64(nv,
+ zfs_prop_to_name(ZFS_PROP_CHECKSUM),
+ ZIO_CHECKSUM_OFF) == 0);
+
+ error = zfs_set_prop_nvlist(zv->zv_name, nv);
+ nvlist_free(nv);
+
+ if (error)
+ return (error);
+ }
+
+ /* Allocate the space for the dump */
+ error = zvol_prealloc(zv);
+ return (error);
+}
+
+static int
+zvol_dumpify(zvol_state_t *zv)
+{
+ int error = 0;
+ uint64_t dumpsize = 0;
+ dmu_tx_t *tx;
+ objset_t *os = zv->zv_objset;
+
+ if (zv->zv_flags & ZVOL_RDONLY || (zv->zv_mode & DS_MODE_READONLY))
+ return (EROFS);
+
+ /*
+ * We do not support swap devices acting as dump devices.
+ */
+ if (zvol_is_swap(zv))
+ return (ENOTSUP);
+
+ if (zap_lookup(zv->zv_objset, ZVOL_ZAP_OBJ, ZVOL_DUMPSIZE,
+ 8, 1, &dumpsize) != 0 || dumpsize != zv->zv_volsize) {
+ boolean_t resize = (dumpsize > 0) ? B_TRUE : B_FALSE;
+
+ if ((error = zvol_dump_init(zv, resize)) != 0) {
+ (void) zvol_dump_fini(zv);
+ return (error);
+ }
+ }
+
+ /*
+ * Build up our lba mapping.
+ */
+ error = zvol_get_lbas(zv);
+ if (error) {
+ (void) zvol_dump_fini(zv);
+ return (error);
+ }
+
+ tx = dmu_tx_create(os);
+ dmu_tx_hold_zap(tx, ZVOL_ZAP_OBJ, TRUE, NULL);
+ error = dmu_tx_assign(tx, TXG_WAIT);
+ if (error) {
+ dmu_tx_abort(tx);
+ (void) zvol_dump_fini(zv);
+ return (error);
+ }
+
+ zv->zv_flags |= ZVOL_DUMPIFIED;
+ error = zap_update(os, ZVOL_ZAP_OBJ, ZVOL_DUMPSIZE, 8, 1,
+ &zv->zv_volsize, tx);
+ dmu_tx_commit(tx);
+
+ if (error) {
+ (void) zvol_dump_fini(zv);
+ return (error);
+ }
+
+ txg_wait_synced(dmu_objset_pool(os), 0);
+ return (0);
+}
+
+static int
+zvol_dump_fini(zvol_state_t *zv)
+{
+ dmu_tx_t *tx;
+ objset_t *os = zv->zv_objset;
+ nvlist_t *nv;
+ int error = 0;
+ uint64_t checksum, compress, refresrv;
+
+ /*
+ * Attempt to restore the zvol back to its pre-dumpified state.
+ * This is a best-effort attempt as it's possible that not all
+ * of these properties were initialized during the dumpify process
+ * (i.e. error during zvol_dump_init).
+ */
+
+ tx = dmu_tx_create(os);
+ dmu_tx_hold_zap(tx, ZVOL_ZAP_OBJ, TRUE, NULL);
+ error = dmu_tx_assign(tx, TXG_WAIT);
+ if (error) {
+ dmu_tx_abort(tx);
+ return (error);
+ }
+ (void) zap_remove(os, ZVOL_ZAP_OBJ, ZVOL_DUMPSIZE, tx);
+ dmu_tx_commit(tx);
+
+ (void) zap_lookup(zv->zv_objset, ZVOL_ZAP_OBJ,
+ zfs_prop_to_name(ZFS_PROP_CHECKSUM), 8, 1, &checksum);
+ (void) zap_lookup(zv->zv_objset, ZVOL_ZAP_OBJ,
+ zfs_prop_to_name(ZFS_PROP_COMPRESSION), 8, 1, &compress);
+ (void) zap_lookup(zv->zv_objset, ZVOL_ZAP_OBJ,
+ zfs_prop_to_name(ZFS_PROP_REFRESERVATION), 8, 1, &refresrv);
+
+ VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0);
+ (void) nvlist_add_uint64(nv,
+ zfs_prop_to_name(ZFS_PROP_CHECKSUM), checksum);
+ (void) nvlist_add_uint64(nv,
+ zfs_prop_to_name(ZFS_PROP_COMPRESSION), compress);
+ (void) nvlist_add_uint64(nv,
+ zfs_prop_to_name(ZFS_PROP_REFRESERVATION), refresrv);
+ (void) zfs_set_prop_nvlist(zv->zv_name, nv);
+ nvlist_free(nv);
+
+ zvol_free_extents(zv);
+ zv->zv_flags &= ~ZVOL_DUMPIFIED;
+ (void) dmu_free_long_range(os, ZVOL_OBJ, 0, DMU_OBJECT_END);
+
+ return (0);
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/os/callb.c b/sys/cddl/contrib/opensolaris/uts/common/os/callb.c
index c6e357e..59ee781 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/os/callb.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/os/callb.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -109,14 +108,24 @@ void
callb_fini(void *dummy __unused)
{
callb_t *cp;
+ int i;
mutex_enter(&ct->ct_lock);
- while ((cp = ct->ct_freelist) != NULL) {
- ct->ct_freelist = cp->c_next;
- ct->ct_ncallb--;
- kmem_free(cp, sizeof (callb_t));
+ for (i = 0; i < 16; i++) {
+ while ((cp = ct->ct_freelist) != NULL) {
+ ct->ct_freelist = cp->c_next;
+ ct->ct_ncallb--;
+ kmem_free(cp, sizeof (callb_t));
+ }
+ if (ct->ct_ncallb == 0)
+ break;
+ /* Not all callbacks finished, waiting for the rest. */
+ mutex_exit(&ct->ct_lock);
+ tsleep(ct, 0, "callb", hz / 4);
+ mutex_enter(&ct->ct_lock);
}
- ASSERT(ct->ct_ncallb == 0);
+ if (ct->ct_ncallb > 0)
+ printf("%s: Leaked %d callbacks!\n", __func__, ct->ct_ncallb);
mutex_exit(&ct->ct_lock);
mutex_destroy(&callb_safe_mutex);
mutex_destroy(&callb_table.ct_lock);
@@ -268,7 +277,7 @@ callb_execute_class(int class, int code)
#ifdef CALLB_DEBUG
printf("callb_execute: name=%s func=%p arg=%p\n",
- cp->c_name, (void *)cp->c_func, (void *)cp->c_arg);
+ cp->c_name, (void *)cp->c_func, (void *)cp->c_arg);
#endif /* CALLB_DEBUG */
mutex_exit(&ct->ct_lock);
@@ -307,12 +316,14 @@ callb_generic_cpr(void *arg, int code)
switch (code) {
case CB_CODE_CPR_CHKPT:
cp->cc_events |= CALLB_CPR_START;
+#ifdef CPR_NOT_THREAD_SAFE
while (!(cp->cc_events & CALLB_CPR_SAFE))
/* cv_timedwait() returns -1 if it times out. */
if ((ret = cv_timedwait(&cp->cc_callb_cv,
cp->cc_lockp,
callb_timeout_sec * hz)) == -1)
break;
+#endif
break;
case CB_CODE_CPR_RESUME:
diff --git a/sys/cddl/contrib/opensolaris/uts/common/os/list.c b/sys/cddl/contrib/opensolaris/uts/common/os/list.c
index f9b6fcb..e8db13a 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/os/list.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/os/list.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -41,20 +41,25 @@
#define list_insert_after_node(list, node, object) { \
list_node_t *lnew = list_d2l(list, object); \
- lnew->list_prev = node; \
- lnew->list_next = node->list_next; \
- node->list_next->list_prev = lnew; \
- node->list_next = lnew; \
+ lnew->list_prev = (node); \
+ lnew->list_next = (node)->list_next; \
+ (node)->list_next->list_prev = lnew; \
+ (node)->list_next = lnew; \
}
#define list_insert_before_node(list, node, object) { \
list_node_t *lnew = list_d2l(list, object); \
- lnew->list_next = node; \
- lnew->list_prev = node->list_prev; \
- node->list_prev->list_next = lnew; \
- node->list_prev = lnew; \
+ lnew->list_next = (node); \
+ lnew->list_prev = (node)->list_prev; \
+ (node)->list_prev->list_next = lnew; \
+ (node)->list_prev = lnew; \
}
+#define list_remove_node(node) \
+ (node)->list_prev->list_next = (node)->list_next; \
+ (node)->list_next->list_prev = (node)->list_prev; \
+ (node)->list_next = (node)->list_prev = NULL
+
void
list_create(list_t *list, size_t size, size_t offset)
{
@@ -83,15 +88,23 @@ list_destroy(list_t *list)
void
list_insert_after(list_t *list, void *object, void *nobject)
{
- list_node_t *lold = list_d2l(list, object);
- list_insert_after_node(list, lold, nobject);
+ if (object == NULL) {
+ list_insert_head(list, nobject);
+ } else {
+ list_node_t *lold = list_d2l(list, object);
+ list_insert_after_node(list, lold, nobject);
+ }
}
void
list_insert_before(list_t *list, void *object, void *nobject)
{
- list_node_t *lold = list_d2l(list, object);
- list_insert_before_node(list, lold, nobject)
+ if (object == NULL) {
+ list_insert_tail(list, nobject);
+ } else {
+ list_node_t *lold = list_d2l(list, object);
+ list_insert_before_node(list, lold, nobject);
+ }
}
void
@@ -113,9 +126,28 @@ list_remove(list_t *list, void *object)
{
list_node_t *lold = list_d2l(list, object);
ASSERT(!list_empty(list));
- lold->list_prev->list_next = lold->list_next;
- lold->list_next->list_prev = lold->list_prev;
- lold->list_next = lold->list_prev = NULL;
+ ASSERT(lold->list_next != NULL);
+ list_remove_node(lold);
+}
+
+void *
+list_remove_head(list_t *list)
+{
+ list_node_t *head = list->list_head.list_next;
+ if (head == &list->list_head)
+ return (NULL);
+ list_remove_node(head);
+ return (list_object(list, head));
+}
+
+void *
+list_remove_tail(list_t *list)
+{
+ list_node_t *tail = list->list_head.list_prev;
+ if (tail == &list->list_head)
+ return (NULL);
+ list_remove_node(tail);
+ return (list_object(list, tail));
}
void *
@@ -180,6 +212,26 @@ list_move_tail(list_t *dst, list_t *src)
srcnode->list_next = srcnode->list_prev = srcnode;
}
+void
+list_link_replace(list_node_t *lold, list_node_t *lnew)
+{
+ ASSERT(list_link_active(lold));
+ ASSERT(!list_link_active(lnew));
+
+ lnew->list_next = lold->list_next;
+ lnew->list_prev = lold->list_prev;
+ lold->list_prev->list_next = lnew;
+ lold->list_next->list_prev = lnew;
+ lold->list_next = lold->list_prev = NULL;
+}
+
+void
+list_link_init(list_node_t *link)
+{
+ link->list_next = NULL;
+ link->list_prev = NULL;
+}
+
int
list_link_active(list_node_t *link)
{
diff --git a/sys/cddl/contrib/opensolaris/uts/common/os/taskq.c b/sys/cddl/contrib/opensolaris/uts/common/os/taskq.c
index 1558c1f..154ead4 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/os/taskq.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/os/taskq.c
@@ -794,6 +794,27 @@ taskq_resume(taskq_t *tq)
rw_exit(&tq->tq_threadlock);
}
+
+int
+taskq_member(taskq_t *tq, kthread_t *thread)
+{
+ if (tq->tq_nthreads == 1)
+ return (tq->tq_thread == thread);
+ else {
+ int i, found = 0;
+
+ mutex_enter(&tq->tq_lock);
+ for (i = 0; i < tq->tq_nthreads; i++) {
+ if (tq->tq_threadlist[i] == thread) {
+ found = 1;
+ break;
+ }
+ }
+ mutex_exit(&tq->tq_lock);
+ return (found);
+ }
+}
+
/*
* Worker thread for processing task queue.
*/
diff --git a/sys/cddl/contrib/opensolaris/uts/common/rpc/xdr.c b/sys/cddl/contrib/opensolaris/uts/common/rpc/xdr.c
index e934668..b38833a 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/rpc/xdr.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/rpc/xdr.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -32,8 +31,6 @@
* under license from the Regents of the University of California.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* xdr.c, generic XDR routines implementation.
* These are the "generic" xdr routines used to serialize and de-serialize
@@ -116,9 +113,6 @@ xdr_int(XDR *xdrs, int *ip)
if (xdrs->x_op == XDR_FREE)
return (TRUE);
-#ifdef DEBUG
- printf("xdr_int: FAILED\n");
-#endif
return (FALSE);
}
@@ -142,9 +136,6 @@ xdr_u_int(XDR *xdrs, uint_t *up)
if (xdrs->x_op == XDR_FREE)
return (TRUE);
-#ifdef DEBUG
- printf("xdr_int: FAILED\n");
-#endif
return (FALSE);
}
@@ -288,9 +279,6 @@ xdr_u_short(XDR *xdrs, ushort_t *usp)
case XDR_DECODE:
if (!XDR_GETINT32(xdrs, (int32_t *)&l)) {
-#ifdef DEBUG
- printf("xdr_u_short: decode FAILED\n");
-#endif
return (FALSE);
}
*usp = (ushort_t)l;
@@ -299,9 +287,6 @@ xdr_u_short(XDR *xdrs, ushort_t *usp)
case XDR_FREE:
return (TRUE);
}
-#ifdef DEBUG
- printf("xdr_u_short: bad op FAILED\n");
-#endif
return (FALSE);
}
@@ -343,9 +328,6 @@ xdr_bool(XDR *xdrs, bool_t *bp)
case XDR_DECODE:
if (!XDR_GETINT32(xdrs, &i32b)) {
-#ifdef DEBUG
- printf("xdr_bool: decode FAILED\n");
-#endif
return (FALSE);
}
*bp = (i32b == XDR_FALSE) ? FALSE : TRUE;
@@ -354,9 +336,6 @@ xdr_bool(XDR *xdrs, bool_t *bp)
case XDR_FREE:
return (TRUE);
}
-#ifdef DEBUG
- printf("xdr_bool: bad op FAILED\n");
-#endif
return (FALSE);
}
@@ -423,9 +402,6 @@ xdr_opaque(XDR *xdrs, caddr_t cp, const uint_t cnt)
if (xdrs->x_op == XDR_DECODE) {
if (!XDR_GETBYTES(xdrs, cp, cnt)) {
-#ifdef DEBUG
- printf("xdr_opaque: decode FAILED\n");
-#endif
return (FALSE);
}
if (rndup == 0)
@@ -435,9 +411,6 @@ xdr_opaque(XDR *xdrs, caddr_t cp, const uint_t cnt)
if (xdrs->x_op == XDR_ENCODE) {
if (!XDR_PUTBYTES(xdrs, cp, cnt)) {
-#ifdef DEBUG
- printf("xdr_opaque: encode FAILED\n");
-#endif
return (FALSE);
}
if (rndup == 0)
@@ -448,9 +421,6 @@ xdr_opaque(XDR *xdrs, caddr_t cp, const uint_t cnt)
if (xdrs->x_op == XDR_FREE)
return (TRUE);
-#ifdef DEBUG
- printf("xdr_opaque: bad op FAILED\n");
-#endif
return (FALSE);
}
@@ -474,17 +444,10 @@ xdr_bytes(XDR *xdrs, char **cpp, uint_t *sizep, const uint_t maxsize)
* first deal with the length since xdr bytes are counted
*/
if (!xdr_u_int(xdrs, sizep)) {
-#ifdef DEBUG
- printf("xdr_bytes: size FAILED\n");
-#endif
return (FALSE);
}
nodesize = *sizep;
if ((nodesize > maxsize) && (xdrs->x_op != XDR_FREE)) {
-#ifdef DEBUG
- printf("xdr_bytes: bad size (%d) FAILED (%d max)\n",
- nodesize, maxsize);
-#endif
return (FALSE);
}
@@ -509,9 +472,6 @@ xdr_bytes(XDR *xdrs, char **cpp, uint_t *sizep, const uint_t maxsize)
}
return (TRUE);
}
-#ifdef DEBUG
- printf("xdr_bytes: bad op FAILED\n");
-#endif
return (FALSE);
}
@@ -545,9 +505,6 @@ xdr_union(XDR *xdrs, enum_t *dscmp, char *unp,
* we deal with the discriminator; it's an enum
*/
if (!xdr_enum(xdrs, dscmp)) {
-#ifdef DEBUG
- printf("xdr_enum: dscmp FAILED\n");
-#endif
return (FALSE);
}
dscm = *dscmp;
@@ -605,15 +562,9 @@ xdr_string(XDR *xdrs, char **cpp, const uint_t maxsize)
break;
}
if (!xdr_u_int(xdrs, &size)) {
-#ifdef DEBUG
- printf("xdr_string: size FAILED\n");
-#endif
return (FALSE);
}
if (size > maxsize) {
-#ifdef DEBUG
- printf("xdr_string: bad size FAILED\n");
-#endif
return (FALSE);
}
nodesize = size + 1;
@@ -654,9 +605,6 @@ xdr_string(XDR *xdrs, char **cpp, const uint_t maxsize)
*cpp = NULL;
return (TRUE);
}
-#ifdef DEBUG
- printf("xdr_string: bad op FAILED\n");
-#endif
return (FALSE);
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/rpc/xdr.h b/sys/cddl/contrib/opensolaris/uts/common/rpc/xdr.h
index d60809e..75fd1dd 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/rpc/xdr.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/rpc/xdr.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,7 +18,7 @@
*
* CDDL HEADER END
*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
@@ -38,8 +37,6 @@
#ifndef _RPC_XDR_H
#define _RPC_XDR_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/byteorder.h> /* For all ntoh* and hton*() kind of macros */
#include <rpc/types.h> /* For all ntoh* and hton*() kind of macros */
#ifndef _KERNEL
@@ -365,7 +362,7 @@ struct xdr_discrim {
#endif
-#if BYTE_ORDER == _BIG_ENDIAN
+#if BYTE_ORDER == _LITTLE_ENDIAN
#define IXDR_GET_HYPER(buf, v) { \
*((int32_t *)(&v)) = ntohl(*(uint32_t *)buf++); \
*((int32_t *)(((char *)&v) + BYTES_PER_XDR_UNIT)) \
@@ -543,8 +540,14 @@ typedef struct xdr_bytesrec xdr_bytesrec;
#ifdef _KERNEL
#define XDR_PEEK 2
#define XDR_SKIPBYTES 3
-#define XDR_RDMAGET 4
-#define XDR_RDMASET 5
+#define XDR_RDMA_GET_FLAGS 4
+#define XDR_RDMA_SET_FLAGS 5
+#define XDR_RDMA_ADD_CHUNK 6
+#define XDR_RDMA_GET_CHUNK_LEN 7
+#define XDR_RDMA_SET_WLIST 8
+#define XDR_RDMA_GET_WLIST 9
+#define XDR_RDMA_GET_WCINFO 10
+#define XDR_RDMA_GET_RLIST 11
#endif
/*
@@ -578,8 +581,9 @@ extern uint_t xdrrec_readbytes();
#else
extern void xdrmem_create(XDR *, caddr_t, uint_t, enum xdr_op);
-
extern struct xdr_ops xdrmblk_ops;
+extern struct xdr_ops xdrrdmablk_ops;
+extern struct xdr_ops xdrrdma_ops;
struct rpc_msg;
extern bool_t xdr_callmsg(XDR *, struct rpc_msg *);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/rpc/xdr_array.c b/sys/cddl/contrib/opensolaris/uts/common/rpc/xdr_array.c
index 3711e53..d37dda6 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/rpc/xdr_array.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/rpc/xdr_array.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -32,8 +31,6 @@
* under license from the Regents of the University of California.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* xdr_array.c, Generic XDR routines impelmentation.
* These are the "non-trivial" xdr primitives used to serialize and de-serialize
@@ -69,17 +66,11 @@ xdr_array(XDR *xdrs, caddr_t *addrp, uint_t *sizep, const uint_t maxsize,
/* like strings, arrays are really counted arrays */
if (!xdr_u_int(xdrs, sizep)) {
-#ifdef DEBUG
- printf("xdr_array: size FAILED\n");
-#endif
return (FALSE);
}
c = *sizep;
if ((c > maxsize || LASTUNSIGNED / elsize < c) &&
xdrs->x_op != XDR_FREE) {
-#ifdef DEBUG
- printf("xdr_array: bad size FAILED\n");
-#endif
return (FALSE);
}
nodesize = c * elsize;
diff --git a/sys/cddl/compat/opensolaris/sys/acl.h b/sys/cddl/contrib/opensolaris/uts/common/sys/acl.h
index a7c9566..27fd577 100644
--- a/sys/cddl/compat/opensolaris/sys/acl.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/acl.h
@@ -17,25 +17,30 @@
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
- *
- * $FreeBSD$
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#ifndef _OPENSOLARIS_SYS_ACL_H_
-#define _OPENSOLARIS_SYS_ACL_H_
+#ifndef _SYS_ACL_H
+#define _SYS_ACL_H
-#include_next <sys/acl.h>
+#pragma ident "%Z%%M% %I% %E% SMI"
-#define MAX_ACL_ENTRIES (1024) /* max entries of each type */
+#include <sys/types.h>
+#include <sys/acl_impl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
-typedef struct acl_entry aclent_t;
-#define a_type ae_tag
-#define a_id ae_id
-#define a_perm ae_perm
+#define MAX_ACL_ENTRIES (1024) /* max entries of each type */
+typedef struct acl {
+ int a_type; /* the type of ACL entry */
+ uid_t a_id; /* the entry in -uid or gid */
+ o_mode_t a_perm; /* the permission field */
+} aclent_t;
typedef struct ace {
uid_t a_who; /* uid or gid */
@@ -44,7 +49,8 @@ typedef struct ace {
uint16_t a_type; /* allow or deny */
} ace_t;
-#if 0
+typedef struct acl_info acl_t;
+
/*
* The following are Defined types for an aclent_t.
*/
@@ -57,7 +63,7 @@ typedef struct ace {
#define ACL_DEFAULT (0x1000) /* default flag */
/* default object owner */
#define DEF_USER_OBJ (ACL_DEFAULT | USER_OBJ)
-/* defalut additional users */
+/* default additional users */
#define DEF_USER (ACL_DEFAULT | USER)
/* default owning group */
#define DEF_GROUP_OBJ (ACL_DEFAULT | GROUP_OBJ)
@@ -67,7 +73,6 @@ typedef struct ace {
#define DEF_CLASS_OBJ (ACL_DEFAULT | CLASS_OBJ)
/* default other entry */
#define DEF_OTHER_OBJ (ACL_DEFAULT | OTHER_OBJ)
-#endif
/*
* The following are defined for ace_t.
@@ -97,6 +102,7 @@ typedef struct ace {
#define ACE_SUCCESSFUL_ACCESS_ACE_FLAG 0x0010
#define ACE_FAILED_ACCESS_ACE_FLAG 0x0020
#define ACE_IDENTIFIER_GROUP 0x0040
+#define ACE_INHERITED_ACE 0x0080
#define ACE_OWNER 0x1000
#define ACE_GROUP 0x2000
#define ACE_EVERYONE 0x4000
@@ -106,12 +112,60 @@ typedef struct ace {
#define ACE_SYSTEM_AUDIT_ACE_TYPE 0x0002
#define ACE_SYSTEM_ALARM_ACE_TYPE 0x0003
+#define ACL_AUTO_INHERIT 0x0001
+#define ACL_PROTECTED 0x0002
+#define ACL_DEFAULTED 0x0004
+#define ACL_FLAGS_ALL (ACL_AUTO_INHERIT|ACL_PROTECTED| \
+ ACL_DEFAULTED)
+
+#ifdef _KERNEL
+
+/*
+ * These are only applicable in a CIFS context.
+ */
+#define ACE_ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x04
+#define ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05
+#define ACE_ACCESS_DENIED_OBJECT_ACE_TYPE 0x06
+#define ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07
+#define ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08
+#define ACE_ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x09
+#define ACE_ACCESS_DENIED_CALLBACK_ACE_TYPE 0x0A
+#define ACE_ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0x0B
+#define ACE_ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0x0C
+#define ACE_SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0x0D
+#define ACE_SYSTEM_ALARM_CALLBACK_ACE_TYPE 0x0E
+#define ACE_SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0x0F
+#define ACE_SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10
+
+#define ACE_ALL_TYPES 0x001F
+
+typedef struct ace_object {
+ uid_t a_who; /* uid or gid */
+ uint32_t a_access_mask; /* read,write,... */
+ uint16_t a_flags; /* see below */
+ uint16_t a_type; /* allow or deny */
+ uint8_t a_obj_type[16]; /* obj type */
+ uint8_t a_inherit_obj_type[16]; /* inherit obj */
+} ace_object_t;
+
+#endif
+
#define ACE_ALL_PERMS (ACE_READ_DATA|ACE_LIST_DIRECTORY|ACE_WRITE_DATA| \
ACE_ADD_FILE|ACE_APPEND_DATA|ACE_ADD_SUBDIRECTORY|ACE_READ_NAMED_ATTRS| \
ACE_WRITE_NAMED_ATTRS|ACE_EXECUTE|ACE_DELETE_CHILD|ACE_READ_ATTRIBUTES| \
ACE_WRITE_ATTRIBUTES|ACE_DELETE|ACE_READ_ACL|ACE_WRITE_ACL| \
ACE_WRITE_OWNER|ACE_SYNCHRONIZE)
+#define ACE_READ_PERMS (ACE_READ_DATA|ACE_READ_ACL|ACE_READ_ATTRIBUTES| \
+ ACE_READ_NAMED_ATTRS)
+
+#define ACE_WRITE_PERMS (ACE_WRITE_DATA|ACE_APPEND_DATA|ACE_WRITE_ATTRIBUTES| \
+ ACE_WRITE_NAMED_ATTRS)
+
+#define ACE_MODIFY_PERMS (ACE_READ_DATA|ACE_LIST_DIRECTORY|ACE_WRITE_DATA| \
+ ACE_ADD_FILE|ACE_APPEND_DATA|ACE_ADD_SUBDIRECTORY|ACE_READ_NAMED_ATTRS| \
+ ACE_WRITE_NAMED_ATTRS|ACE_EXECUTE|ACE_DELETE_CHILD|ACE_READ_ATTRIBUTES| \
+ ACE_WRITE_ATTRIBUTES|ACE_DELETE|ACE_READ_ACL|ACE_SYNCHRONIZE)
/*
* The following flags are supported by both NFSv4 ACLs and ace_t.
*/
@@ -121,21 +175,21 @@ typedef struct ace {
ACE_INHERIT_ONLY_ACE | \
ACE_IDENTIFIER_GROUP)
-#define ACE_TYPE_FLAGS (ACE_OWNER|ACE_GROUP|ACE_EVERYONE|ACE_IDENTIFIER_GROUP)
+#define ACE_TYPE_FLAGS (ACE_OWNER|ACE_GROUP|ACE_EVERYONE| \
+ ACE_IDENTIFIER_GROUP)
+#define ACE_INHERIT_FLAGS (ACE_FILE_INHERIT_ACE| \
+ ACE_DIRECTORY_INHERIT_ACE|ACE_NO_PROPAGATE_INHERIT_ACE|ACE_INHERIT_ONLY_ACE)
-#if 0
/* cmd args to acl(2) for aclent_t */
#define GETACL 1
#define SETACL 2
#define GETACLCNT 3
-#endif
/* cmd's to manipulate ace acls. */
#define ACE_GETACL 4
#define ACE_SETACL 5
#define ACE_GETACLCNT 6
-#if 0
/* minimal acl entries from GETACLCNT */
#define MIN_ACL_ENTRIES 4
@@ -175,6 +229,7 @@ typedef struct ace {
#define ACL_APPEND_ID 0x1 /* append uid/gid to user/group entries */
#define ACL_COMPACT_FMT 0x2 /* build ACL in ls -V format */
#define ACL_NORESOLVE 0x4 /* don't do name service lookups */
+#define ACL_SID_FMT 0x8 /* use usersid/groupsid when appropriate */
/*
* Legacy aclcheck errors for aclent_t ACLs
@@ -238,6 +293,8 @@ extern int acl();
extern int facl();
#endif /* defined(__STDC__) */
+#ifdef __cplusplus
+}
#endif
-#endif /* _OPENSOLARIS_SYS_ACL_H */
+#endif /* _SYS_ACL_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/acl_impl.h b/sys/cddl/contrib/opensolaris/uts/common/sys/acl_impl.h
new file mode 100644
index 0000000..b82f259
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/acl_impl.h
@@ -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.
+ */
+
+#ifndef _SYS_ACL_IMPL_H
+#define _SYS_ACL_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * acl flags
+ *
+ * ACL_AUTO_INHERIT, ACL_PROTECTED and ACL_DEFAULTED
+ * flags can also be stored in this field.
+ */
+#define ACL_IS_TRIVIAL 0x10000
+#define ACL_IS_DIR 0x20000
+
+typedef enum acl_type {
+ ACLENT_T = 0,
+ ACE_T = 1
+} acl_type_t;
+
+struct acl_info {
+ acl_type_t acl_type; /* style of acl */
+ int acl_cnt; /* number of acl entries */
+ int acl_entry_size; /* sizeof acl entry */
+ int acl_flags; /* special flags about acl */
+ void *acl_aclp; /* the acl */
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_ACL_IMPL_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/avl.h b/sys/cddl/contrib/opensolaris/uts/common/sys/avl.h
index bf9af89..02263a5 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/avl.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/avl.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -38,6 +37,7 @@
extern "C" {
#endif
+#include <sys/types.h>
#include <sys/avl_impl.h>
/*
@@ -128,7 +128,6 @@ typedef uintptr_t avl_index_t;
#define AVL_AFTER (1)
-
/*
* Prototypes
*
@@ -182,7 +181,7 @@ extern void avl_insert(avl_tree_t *tree, void *node, avl_index_t where);
* data to avoid doing avl_find() again for insertion.
*
* new_data - new data to insert
- * here - existing node in "tree"
+ * here - existing node in "tree"
* direction - either AVL_AFTER or AVL_BEFORE the data "here".
*/
extern void avl_insert_here(avl_tree_t *tree, void *new_data, void *here,
@@ -251,12 +250,26 @@ extern void avl_add(avl_tree_t *tree, void *node);
*/
extern void avl_remove(avl_tree_t *tree, void *node);
+/*
+ * Reinsert a node only if its order has changed relative to its nearest
+ * neighbors. To optimize performance avl_update_lt() checks only the previous
+ * node and avl_update_gt() checks only the next node. Use avl_update_lt() and
+ * avl_update_gt() only if you know the direction in which the order of the
+ * node may change.
+ */
+extern boolean_t avl_update(avl_tree_t *, void *);
+extern boolean_t avl_update_lt(avl_tree_t *, void *);
+extern boolean_t avl_update_gt(avl_tree_t *, void *);
/*
* Return the number of nodes in the tree
*/
extern ulong_t avl_numnodes(avl_tree_t *tree);
+/*
+ * Return B_TRUE if there are zero nodes in the tree, B_FALSE otherwise.
+ */
+extern boolean_t avl_is_empty(avl_tree_t *tree);
/*
* Used to destroy any remaining nodes in a tree. The cookie argument should
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/byteorder.h b/sys/cddl/contrib/opensolaris/uts/common/sys/byteorder.h
index 00afdd5..a2bab58 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/byteorder.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/byteorder.h
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -40,8 +40,6 @@
#ifndef _SYS_BYTEORDER_H
#define _SYS_BYTEORDER_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/isa_defs.h>
#include <sys/int_types.h>
@@ -64,6 +62,10 @@ extern "C" {
#define ntohs(x) (x)
#define htonl(x) (x)
#define htons(x) (x)
+#if !defined(_XPG4_2) || defined(__EXTENSIONS__)
+#define ntohll(x) (x)
+#define htonll(x) (x)
+#endif /* !_XPG4_2 || __EXTENSIONS__ */
#elif !defined(ntohl) /* little-endian */
@@ -80,14 +82,21 @@ typedef uint32_t in_addr_t;
#if !defined(_XPG4_2) || defined(__EXTENSIONS__) || defined(_XPG5)
extern uint32_t htonl(uint32_t);
extern uint16_t htons(uint16_t);
-extern uint32_t ntohl(uint32_t);
+extern uint32_t ntohl(uint32_t);
extern uint16_t ntohs(uint16_t);
#else
extern in_addr_t htonl(in_addr_t);
extern in_port_t htons(in_port_t);
-extern in_addr_t ntohl(in_addr_t);
+extern in_addr_t ntohl(in_addr_t);
extern in_port_t ntohs(in_port_t);
-#endif /* !defined(_XPG4_2) || defined(__EXTENSIONS__) || defined(_XPG5) */
+#endif /* !_XPG4_2 || __EXTENSIONS__ || _XPG5 */
+
+#if defined(_LP64) || defined(_LONGLONG_TYPE)
+#if !defined(_XPG4_2) || defined(__EXTENSIONS__)
+extern uint64_t htonll(uint64_t);
+extern uint64_t ntohll(uint64_t);
+#endif /* !_XPG4_2 || __EXTENSIONS__ */
+#endif /* _LP64 || _LONGLONG_TYPE */
#endif
#if !defined(_XPG4_2) || defined(__EXTENSIONS__)
@@ -96,9 +105,33 @@ extern in_port_t ntohs(in_port_t);
* Macros to reverse byte order
*/
#define BSWAP_8(x) ((x) & 0xff)
+#if !defined(__i386) && !defined(__amd64)
#define BSWAP_16(x) ((BSWAP_8(x) << 8) | BSWAP_8((x) >> 8))
-#define BSWAP_32(x) ((BSWAP_16(x) << 16) | BSWAP_16((x) >> 16))
+#define BSWAP_32(x) (((uint32_t)(x) << 24) | \
+ (((uint32_t)(x) << 8) & 0xff0000) | \
+ (((uint32_t)(x) >> 8) & 0xff00) | \
+ ((uint32_t)(x) >> 24))
+#else /* x86 */
+#define BSWAP_16(x) htons(x)
+#define BSWAP_32(x) htonl(x)
+#endif /* !__i386 && !__amd64 */
+
+#if defined(_LP64) || defined(_LONGLONG_TYPE)
+#if (!defined(__i386) && !defined(__amd64))
+#define BSWAP_64(x) (((uint64_t)(x) << 56) | \
+ (((uint64_t)(x) << 40) & 0xff000000000000ULL) | \
+ (((uint64_t)(x) << 24) & 0xff0000000000ULL) | \
+ (((uint64_t)(x) << 8) & 0xff00000000ULL) | \
+ (((uint64_t)(x) >> 8) & 0xff000000ULL) | \
+ (((uint64_t)(x) >> 24) & 0xff0000ULL) | \
+ (((uint64_t)(x) >> 40) & 0xff00ULL) | \
+ ((uint64_t)(x) >> 56))
+#else /* x86 */
+#define BSWAP_64(x) htonll(x)
+#endif /* !__i386 && !__amd64 */
+#else /* no uint64_t */
#define BSWAP_64(x) ((BSWAP_32(x) << 32) | BSWAP_32((x) >> 32))
+#endif /* _LP64 || _LONGLONG_TYPE */
#define BMASK_8(x) ((x) & 0xff)
#define BMASK_16(x) ((x) & 0xffff)
@@ -128,7 +161,7 @@ extern in_port_t ntohs(in_port_t);
#define BE_64(x) BSWAP_64(x)
#endif
-#endif /* !defined(_XPG4_2) || defined(__EXTENSIONS__) */
+#endif /* !_XPG4_2 || __EXTENSIONS__ */
#ifdef __cplusplus
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/callb.h b/sys/cddl/contrib/opensolaris/uts/common/sys/callb.h
index b12b2e2..54f35ac 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/callb.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/callb.h
@@ -27,10 +27,9 @@
#ifndef _SYS_CALLB_H
#define _SYS_CALLB_H
-#pragma ident "%Z%%M% %I% %E% SMI"
+#pragma ident "@(#)callb.h 1.29 05/06/23 SMI"
-#include <sys/t_lock.h>
-#include <sys/thread.h>
+#include <sys/kcondvar.h>
#ifdef __cplusplus
extern "C" {
@@ -134,10 +133,16 @@ typedef struct callb_cpr {
* later on. No lock held is needed for this initialization.
*/
#define CALLB_CPR_INIT(cp, lockp, func, name) { \
+ strlcpy(curthread->td_name, (name), \
+ sizeof(curthread->td_name)); \
+ strlcpy(curthread->td_proc->p_comm, (name), \
+ sizeof(curthread->td_proc->p_comm)); \
bzero((caddr_t)(cp), sizeof (callb_cpr_t)); \
(cp)->cc_lockp = lockp; \
(cp)->cc_id = callb_add(func, (void *)(cp), \
CB_CL_CPR_DAEMON, name); \
+ cv_init(&(cp)->cc_callb_cv, NULL, CV_DEFAULT, NULL); \
+ cv_init(&(cp)->cc_stop_cv, NULL, CV_DEFAULT, NULL); \
}
#ifndef __lock_lint
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/cpuvar.h b/sys/cddl/contrib/opensolaris/uts/common/sys/cpuvar.h
index cd0d027..0a038e0 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/cpuvar.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/cpuvar.h
@@ -27,8 +27,6 @@
#ifndef _SYS_CPUVAR_H
#define _SYS_CPUVAR_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/thread.h>
#include <sys/sysinfo.h> /* has cpu_stat_t definition */
#include <sys/disp.h>
@@ -524,6 +522,7 @@ extern int ncpus; /* number of CPUs present */
extern int ncpus_online; /* number of CPUs not quiesced */
extern int max_ncpus; /* max present before ncpus is known */
extern int boot_max_ncpus; /* like max_ncpus but for real */
+extern int boot_ncpus; /* # cpus present @ boot */
extern processorid_t max_cpuid; /* maximum CPU number */
extern struct cpu *cpu_inmotion; /* offline or partition move target */
extern cpu_t *clock_cpu_list;
@@ -725,6 +724,7 @@ extern void cpu_state_change_notify(int, cpu_setup_t);
#define CPU_IDSTRLEN 100
extern void init_cpu_info(struct cpu *);
+extern void populate_idstr(struct cpu *);
extern void cpu_vm_data_init(struct cpu *);
extern void cpu_vm_data_destroy(struct cpu *);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/cred.h b/sys/cddl/contrib/opensolaris/uts/common/sys/cred.h
index c1400b8..e84f1e0 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/cred.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/cred.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -55,6 +55,9 @@ typedef struct cred cred_t;
struct proc; /* cred.h is included in proc.h */
struct prcred;
+struct ksid;
+struct ksidlist;
+struct credklpd;
struct auditinfo_addr; /* cred.h is included in audit.h */
@@ -68,6 +71,7 @@ extern void cred_init(void);
extern void crhold(cred_t *);
extern void crfree(cred_t *);
extern cred_t *cralloc(void); /* all but ref uninitialized */
+extern cred_t *cralloc_ksid(void); /* cralloc() + ksid alloc'ed */
extern cred_t *crget(void); /* initialized */
extern cred_t *crcopy(cred_t *);
extern void crcopy_to(cred_t *, cred_t *);
@@ -91,6 +95,8 @@ extern gid_t crgetsgid(const cred_t *);
extern zoneid_t crgetzoneid(const cred_t *);
extern projid_t crgetprojid(const cred_t *);
+extern cred_t *crgetmapped(const cred_t *);
+
extern const struct auditinfo_addr *crgetauinfo(const cred_t *);
extern struct auditinfo_addr *crgetauinfo_modifiable(cred_t *);
@@ -145,6 +151,32 @@ struct ts_label_s;
extern struct ts_label_s *crgetlabel(const cred_t *);
extern boolean_t crisremote(const cred_t *);
+/*
+ * Private interfaces for ephemeral uids.
+ */
+#define VALID_UID(id, zn) \
+ ((id) <= MAXUID || valid_ephemeral_uid((zn), (id)))
+
+#define VALID_GID(id, zn) \
+ ((id) <= MAXUID || valid_ephemeral_gid((zn), (id)))
+
+extern boolean_t valid_ephemeral_uid(struct zone *, uid_t);
+extern boolean_t valid_ephemeral_gid(struct zone *, gid_t);
+
+extern int eph_uid_alloc(struct zone *, int, uid_t *, int);
+extern int eph_gid_alloc(struct zone *, int, gid_t *, int);
+
+extern void crsetsid(cred_t *, struct ksid *, int);
+extern void crsetsidlist(cred_t *, struct ksidlist *);
+
+extern struct ksid *crgetsid(const cred_t *, int);
+extern struct ksidlist *crgetsidlist(const cred_t *);
+
+extern int crsetpriv(cred_t *, ...);
+
+extern struct credklpd *crgetcrklpd(const cred_t *);
+extern void crsetcrklpd(cred_t *, struct credklpd *);
+
#endif /* _KERNEL */
#ifdef __cplusplus
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/dkio.h b/sys/cddl/contrib/opensolaris/uts/common/sys/dkio.h
index b0ddd07..18f49e5 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/dkio.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/dkio.h
@@ -18,16 +18,15 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_DKIO_H
#define _SYS_DKIO_H
-#pragma ident "%Z%%M% %I% %E% SMI" /* SunOS-4.0 5.19 */
-
#include <sys/dklabel.h> /* Needed for NDKMAP define */
#ifdef __cplusplus
@@ -85,6 +84,7 @@ struct dk_cinfo {
#define DKC_DIRECT 20 /* Intel direct attached device i.e. IDE */
#define DKC_PCMCIA_MEM 21 /* PCMCIA memory disk-like type */
#define DKC_PCMCIA_ATA 22 /* PCMCIA AT Attached type */
+#define DKC_VBD 23 /* virtual block device */
/*
* Sun reserves up through 1023
@@ -165,6 +165,9 @@ struct dk_geom {
#define DKIOCGVTOC (DKIOC|11) /* Get VTOC */
#define DKIOCSVTOC (DKIOC|12) /* Set VTOC & Write to Disk */
+#define DKIOCGEXTVTOC (DKIOC|23) /* Get extended VTOC */
+#define DKIOCSEXTVTOC (DKIOC|24) /* Set extended VTOC, Write to Disk */
+
/*
* Disk Cache Controls. These ioctls should be supported by
* all disk drivers.
@@ -190,8 +193,14 @@ struct dk_geom {
struct dk_callback {
void (*dkc_callback)(void *dkc_cookie, int error);
void *dkc_cookie;
+ int dkc_flag;
};
+/* bit flag definitions for dkc_flag */
+#define FLUSH_VOLATILE 0x1 /* Bit 0: if set, only flush */
+ /* volatile cache; otherwise, flush */
+ /* volatile and non-volatile cache */
+
#define DKIOCGETWCE (DKIOC|36) /* Get current write cache */
/* enablement status */
#define DKIOCSETWCE (DKIOC|37) /* Enable/Disable write cache */
@@ -245,6 +254,9 @@ struct defect_header {
};
#define DKIOCPARTINFO (DKIOC|22) /* Get partition or slice parameters */
+#define DKIOCEXTPARTINFO (DKIOC|19) /* Get extended partition or slice */
+ /* parameters */
+
/*
* Used by applications to get partition or slice information
@@ -261,6 +273,11 @@ struct part_info {
int p_length;
};
+struct extpart_info {
+ diskaddr_t p_start;
+ diskaddr_t p_length;
+};
+
/* The following ioctls are for Optical Memory Device */
#define DKIOC_EBP_ENABLE (DKIOC|40) /* enable by pass erase on write */
#define DKIOC_EBP_DISABLE (DKIOC|41) /* disable by pass erase on write */
@@ -351,6 +368,9 @@ struct dk_minfo {
#define DKIOCSETVOLCAP (DKIOC | 26) /* Set volume capabilities */
#define DKIOCDMR (DKIOC | 27) /* Issue a directed read */
+#define DKIOCDUMPINIT (DKIOC | 28) /* Dumpify a zvol */
+#define DKIOCDUMPFINI (DKIOC | 29) /* Un-Dumpify a zvol */
+
typedef uint_t volcapinfo_t;
typedef uint_t volcapset_t;
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/dklabel.h b/sys/cddl/contrib/opensolaris/uts/common/sys/dklabel.h
index 92cb47a..01baa71 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/dklabel.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/dklabel.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,16 +18,15 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 1990-2002 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_DKLABEL_H
#define _SYS_DKLABEL_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/isa_defs.h>
#include <sys/types32.h>
#include <sys/isa_defs.h>
@@ -76,13 +74,24 @@ extern "C" {
* at the beginning of the sector.
*/
+#if !defined(BLKADDR_TYPE)
+#define BLKADDR_TYPE
+#if defined(_EXTVTOC)
+typedef unsigned long blkaddr_t;
+typedef unsigned int blkaddr32_t;
+#else
+typedef daddr_t blkaddr_t;
+typedef daddr32_t blkaddr32_t;
+#endif
+#endif
+
/*
* partition headers: section 1
* Returned in struct dk_allmap by ioctl DKIOC[SG]APART (dkio(7I))
*/
struct dk_map {
- daddr_t dkl_cylno; /* starting cylinder */
- daddr_t dkl_nblk; /* number of blocks; if == 0, */
+ blkaddr_t dkl_cylno; /* starting cylinder */
+ blkaddr_t dkl_nblk; /* number of blocks; if == 0, */
/* partition is undefined */
};
@@ -91,8 +100,8 @@ struct dk_map {
* Fixed size for on-disk dk_label
*/
struct dk_map32 {
- daddr32_t dkl_cylno; /* starting cylinder */
- daddr32_t dkl_nblk; /* number of blocks; if == 0, */
+ blkaddr32_t dkl_cylno; /* starting cylinder */
+ blkaddr32_t dkl_nblk; /* number of blocks; if == 0, */
/* partition is undefined */
};
@@ -108,8 +117,8 @@ struct dk_map2 {
struct dkl_partition {
uint16_t p_tag; /* ID tag of partition */
uint16_t p_flag; /* permision flags */
- daddr32_t p_start; /* start sector no of partition */
- int32_t p_size; /* # of blocks in partition */
+ blkaddr32_t p_start; /* start sector no of partition */
+ blkaddr32_t p_size; /* # of blocks in partition */
};
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/extdirent.h b/sys/cddl/contrib/opensolaris/uts/common/sys/extdirent.h
new file mode 100644
index 0000000..3f9a665
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/extdirent.h
@@ -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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_EXTDIRENT_H
+#define _SYS_EXTDIRENT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+#if defined(_KERNEL)
+
+/*
+ * Extended file-system independent directory entry. This style of
+ * dirent provides additional informational flag bits for each
+ * directory entry. This dirent will be returned instead of the
+ * standard dirent if a VOP_READDIR() requests dirent flags via
+ * V_RDDIR_ENTFLAGS, and if the file system supports the flags.
+ */
+typedef struct edirent {
+ ino64_t ed_ino; /* "inode number" of entry */
+ off64_t ed_off; /* offset of disk directory entry */
+ uint32_t ed_eflags; /* per-entry flags */
+ unsigned short ed_reclen; /* length of this record */
+ char ed_name[1]; /* name of file */
+} edirent_t;
+
+#define EDIRENT_RECLEN(namelen) \
+ ((offsetof(edirent_t, ed_name[0]) + 1 + (namelen) + 7) & ~ 7)
+#define EDIRENT_NAMELEN(reclen) \
+ ((reclen) - (offsetof(edirent_t, ed_name[0])))
+
+/*
+ * Extended entry flags
+ * Extended entries include a bitfield of extra information
+ * regarding that entry.
+ */
+#define ED_CASE_CONFLICT 0x10 /* Disconsidering case, entry is not unique */
+
+/*
+ * Extended flags accessor function
+ */
+#define ED_CASE_CONFLICTS(x) ((x)->ed_eflags & ED_CASE_CONFLICT)
+
+#endif /* defined(_KERNEL) */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_EXTDIRENT_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/fm/fs/zfs.h b/sys/cddl/contrib/opensolaris/uts/common/sys/fm/fs/zfs.h
index aa5c7ee..66ca9c5 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/fm/fs/zfs.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/fm/fs/zfs.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -45,8 +45,12 @@ extern "C" {
#define FM_EREPORT_ZFS_DEVICE_BAD_GUID_SUM "vdev.bad_guid_sum"
#define FM_EREPORT_ZFS_DEVICE_TOO_SMALL "vdev.too_small"
#define FM_EREPORT_ZFS_DEVICE_BAD_LABEL "vdev.bad_label"
+#define FM_EREPORT_ZFS_IO_FAILURE "io_failure"
+#define FM_EREPORT_ZFS_PROBE_FAILURE "probe_failure"
+#define FM_EREPORT_ZFS_LOG_REPLAY "log_replay"
#define FM_EREPORT_PAYLOAD_ZFS_POOL "pool"
+#define FM_EREPORT_PAYLOAD_ZFS_POOL_FAILMODE "pool_failmode"
#define FM_EREPORT_PAYLOAD_ZFS_POOL_GUID "pool_guid"
#define FM_EREPORT_PAYLOAD_ZFS_POOL_CONTEXT "pool_context"
#define FM_EREPORT_PAYLOAD_ZFS_VDEV_GUID "vdev_guid"
@@ -66,7 +70,12 @@ extern "C" {
#define FM_EREPORT_PAYLOAD_ZFS_ZIO_SIZE "zio_size"
#define FM_EREPORT_PAYLOAD_ZFS_PREV_STATE "prev_state"
-#define FM_RESOURCE_OK "ok"
+#define FM_EREPORT_FAILMODE_WAIT "wait"
+#define FM_EREPORT_FAILMODE_CONTINUE "continue"
+#define FM_EREPORT_FAILMODE_PANIC "panic"
+
+#define FM_RESOURCE_REMOVED "removed"
+#define FM_RESOURCE_AUTOREPLACE "autoreplace"
#ifdef __cplusplus
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/fm/protocol.h b/sys/cddl/contrib/opensolaris/uts/common/sys/fm/protocol.h
index a9980fe..20c0789 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/fm/protocol.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/fm/protocol.h
@@ -20,15 +20,13 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_FM_PROTOCOL_H
#define _SYS_FM_PROTOCOL_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -55,6 +53,8 @@ extern "C" {
#define FM_LIST_SUSPECT_CLASS FM_LIST_EVENT ".suspect"
#define FM_LIST_ISOLATED_CLASS FM_LIST_EVENT ".isolated"
#define FM_LIST_REPAIRED_CLASS FM_LIST_EVENT ".repaired"
+#define FM_LIST_UPDATED_CLASS FM_LIST_EVENT ".updated"
+#define FM_LIST_RESOLVED_CLASS FM_LIST_EVENT ".resolved"
/* ereport class subcategory values */
#define FM_ERROR_CPU "cpu"
@@ -71,7 +71,10 @@ extern "C" {
/* list.* event payload member names */
#define FM_LIST_EVENT_SIZE "list-sz"
-/* list.suspect, isolated, and repaired versions and payload member names */
+/*
+ * list.suspect, isolated, updated, repaired and resolved
+ * versions/payload member names.
+ */
#define FM_SUSPECT_UUID "uuid"
#define FM_SUSPECT_DIAG_CODE "code"
#define FM_SUSPECT_DIAG_TIME "diag-time"
@@ -80,10 +83,20 @@ extern "C" {
#define FM_SUSPECT_FAULT_SZ "fault-list-sz"
#define FM_SUSPECT_FAULT_STATUS "fault-status"
#define FM_SUSPECT_MESSAGE "message"
+#define FM_SUSPECT_RETIRE "retire"
+#define FM_SUSPECT_RESPONSE "response"
#define FM_SUSPECT_VERS0 0
#define FM_SUSPECT_VERSION FM_SUSPECT_VERS0
+#define FM_SUSPECT_FAULTY 0x1
+#define FM_SUSPECT_UNUSABLE 0x2
+#define FM_SUSPECT_NOT_PRESENT 0x4
+#define FM_SUSPECT_DEGRADED 0x8
+#define FM_SUSPECT_REPAIRED 0x10
+#define FM_SUSPECT_REPLACED 0x20
+#define FM_SUSPECT_ACQUITTED 0x40
+
/* fault event versions and payload member names */
#define FM_FAULT_VERS0 0
#define FM_FAULT_VERSION FM_FAULT_VERS0
@@ -104,6 +117,9 @@ extern "C" {
#define FM_RSRC_ASRU_UUID "uuid"
#define FM_RSRC_ASRU_CODE "code"
#define FM_RSRC_ASRU_FAULTY "faulty"
+#define FM_RSRC_ASRU_REPAIRED "repaired"
+#define FM_RSRC_ASRU_REPLACED "replaced"
+#define FM_RSRC_ASRU_ACQUITTED "acquitted"
#define FM_RSRC_ASRU_UNUSABLE "unusable"
#define FM_RSRC_ASRU_EVENT "event"
@@ -146,6 +162,7 @@ extern "C" {
#define FM_FMRI_AUTHORITY "authority"
#define FM_FMRI_SCHEME "scheme"
#define FM_FMRI_SVC_AUTHORITY "svc-authority"
+#define FM_FMRI_FACILITY "facility"
/* FMRI authority-type member names */
#define FM_FMRI_AUTH_CHASSIS "chassis-id"
@@ -199,6 +216,10 @@ extern "C" {
#define FM_FMRI_HC_LIST "hc-list"
#define FM_FMRI_HC_SPECIFIC "hc-specific"
+/* facility member names */
+#define FM_FMRI_FACILITY_NAME "facility-name"
+#define FM_FMRI_FACILITY_TYPE "facility-type"
+
/* hc-list version and member names */
#define FM_FMRI_HC_NAME "hc-name"
#define FM_FMRI_HC_ID "hc-id"
@@ -208,6 +229,7 @@ extern "C" {
/* hc-specific member names */
#define FM_FMRI_HC_SPECIFIC_OFFSET "offset"
+#define FM_FMRI_HC_SPECIFIC_PHYSADDR "physaddr"
/* fmd module scheme member names */
#define FM_FMRI_FMD_NAME "mod-name"
@@ -238,6 +260,13 @@ extern "C" {
#define FM_FMRI_CPU_MASK "cpumask"
#define FM_FMRI_CPU_VID "cpuvid"
#define FM_FMRI_CPU_CPUFRU "cpufru"
+#define FM_FMRI_CPU_CACHE_INDEX "cacheindex"
+#define FM_FMRI_CPU_CACHE_WAY "cacheway"
+#define FM_FMRI_CPU_CACHE_BIT "cachebit"
+#define FM_FMRI_CPU_CACHE_TYPE "cachetype"
+
+#define FM_FMRI_CPU_CACHE_TYPE_L2 0
+#define FM_FMRI_CPU_CACHE_TYPE_L3 1
/* legacy-hc scheme member names */
#define FM_FMRI_LEGACY_HC "component"
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/fm/util.h b/sys/cddl/contrib/opensolaris/uts/common/sys/fm/util.h
index f65e0ab4..cd176f0 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/fm/util.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/fm/util.h
@@ -88,7 +88,7 @@ extern size_t ereport_dumplen;
extern void fm_init(void);
extern void fm_nvprint(nvlist_t *);
-extern void fm_panic(const char *, ...);
+#define fm_panic panic
extern void fm_banner(void);
extern void fm_ereport_dump(void);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h b/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h
index bcf8594..70da8ae 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h
@@ -19,17 +19,16 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_FS_ZFS_H
#define _SYS_FS_ZFS_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#include <sys/ioccom.h>
+#include <sys/time.h>
#ifdef __cplusplus
extern "C" {
@@ -50,19 +49,16 @@ typedef enum {
ZFS_TYPE_POOL = 0x8
} zfs_type_t;
-#define ZFS_TYPE_ANY \
+#define ZFS_TYPE_DATASET \
(ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME | ZFS_TYPE_SNAPSHOT)
/*
- * Properties are identified by these constants and must be added to the
- * end of this list to ensure that external conumsers are not affected
- * by the change. The property list also determines how 'zfs get' will
- * display them. If you make any changes to this list, be sure to update
+ * Dataset properties are identified by these constants and must be added to
+ * the end of this list to ensure that external consumers are not affected
+ * by the change. If you make any changes to this list, be sure to update
* the property table in usr/src/common/zfs/zfs_prop.c.
*/
typedef enum {
- ZFS_PROP_CONT = -2,
- ZFS_PROP_INVAL = -1,
ZFS_PROP_TYPE,
ZFS_PROP_CREATION,
ZFS_PROP_USED,
@@ -97,85 +93,227 @@ typedef enum {
ZFS_PROP_XATTR,
ZFS_PROP_NUMCLONES, /* not exposed to the user */
ZFS_PROP_COPIES,
- ZFS_PROP_BOOTFS
+ ZFS_PROP_VERSION,
+ ZFS_PROP_UTF8ONLY,
+ ZFS_PROP_NORMALIZE,
+ ZFS_PROP_CASE,
+ ZFS_PROP_VSCAN,
+ ZFS_PROP_NBMAND,
+ ZFS_PROP_SHARESMB,
+ ZFS_PROP_REFQUOTA,
+ ZFS_PROP_REFRESERVATION,
+ ZFS_PROP_GUID,
+ ZFS_PROP_PRIMARYCACHE,
+ ZFS_PROP_SECONDARYCACHE,
+ ZFS_PROP_USEDSNAP,
+ ZFS_PROP_USEDDS,
+ ZFS_PROP_USEDCHILD,
+ ZFS_PROP_USEDREFRESERV,
+ ZFS_NUM_PROPS
} zfs_prop_t;
-typedef zfs_prop_t zpool_prop_t;
-
-#define ZFS_PROP_VALUE "value"
-#define ZFS_PROP_SOURCE "source"
+/*
+ * Pool properties are identified by these constants and must be added to the
+ * end of this list to ensure that external consumers are not affected
+ * by the change. If you make any changes to this list, be sure to update
+ * the property table in usr/src/common/zfs/zpool_prop.c.
+ */
+typedef enum {
+ ZPOOL_PROP_NAME,
+ ZPOOL_PROP_SIZE,
+ ZPOOL_PROP_USED,
+ ZPOOL_PROP_AVAILABLE,
+ ZPOOL_PROP_CAPACITY,
+ ZPOOL_PROP_ALTROOT,
+ ZPOOL_PROP_HEALTH,
+ ZPOOL_PROP_GUID,
+ ZPOOL_PROP_VERSION,
+ ZPOOL_PROP_BOOTFS,
+ ZPOOL_PROP_DELEGATION,
+ ZPOOL_PROP_AUTOREPLACE,
+ ZPOOL_PROP_CACHEFILE,
+ ZPOOL_PROP_FAILUREMODE,
+ ZPOOL_PROP_LISTSNAPS,
+ ZPOOL_NUM_PROPS
+} zpool_prop_t;
+
+#define ZPROP_CONT -2
+#define ZPROP_INVAL -1
+
+#define ZPROP_VALUE "value"
+#define ZPROP_SOURCE "source"
typedef enum {
- ZFS_SRC_NONE = 0x1,
- ZFS_SRC_DEFAULT = 0x2,
- ZFS_SRC_TEMPORARY = 0x4,
- ZFS_SRC_LOCAL = 0x8,
- ZFS_SRC_INHERITED = 0x10
-} zfs_source_t;
+ ZPROP_SRC_NONE = 0x1,
+ ZPROP_SRC_DEFAULT = 0x2,
+ ZPROP_SRC_TEMPORARY = 0x4,
+ ZPROP_SRC_LOCAL = 0x8,
+ ZPROP_SRC_INHERITED = 0x10
+} zprop_source_t;
+
+#define ZPROP_SRC_ALL 0x1f
-#define ZFS_SRC_ALL 0x1f
+typedef int (*zprop_func)(int, void *);
/*
- * The following functions are shared between libzfs and the kernel.
+ * Properties to be set on the root file system of a new pool
+ * are stuffed into their own nvlist, which is then included in
+ * the properties nvlist with the pool properties.
+ */
+#define ZPOOL_ROOTFS_PROPS "root-props-nvl"
+
+/*
+ * Dataset property functions shared between libzfs and kernel.
*/
-zfs_prop_t zfs_name_to_prop(const char *);
-zpool_prop_t zpool_name_to_prop(const char *);
-boolean_t zfs_prop_user(const char *);
-int zfs_prop_readonly(zfs_prop_t);
const char *zfs_prop_default_string(zfs_prop_t);
-const char *zfs_prop_to_name(zfs_prop_t);
-const char *zpool_prop_to_name(zfs_prop_t);
uint64_t zfs_prop_default_numeric(zfs_prop_t);
-int zfs_prop_inheritable(zfs_prop_t);
-int zfs_prop_string_to_index(zfs_prop_t, const char *, uint64_t *);
+boolean_t zfs_prop_readonly(zfs_prop_t);
+boolean_t zfs_prop_inheritable(zfs_prop_t);
+boolean_t zfs_prop_setonce(zfs_prop_t);
+const char *zfs_prop_to_name(zfs_prop_t);
+zfs_prop_t zfs_name_to_prop(const char *);
+boolean_t zfs_prop_user(const char *);
int zfs_prop_index_to_string(zfs_prop_t, uint64_t, const char **);
+int zfs_prop_string_to_index(zfs_prop_t, const char *, uint64_t *);
+boolean_t zfs_prop_valid_for_type(int, zfs_type_t);
/*
- * Property Iterator
+ * Pool property functions shared between libzfs and kernel.
*/
-typedef zfs_prop_t (*zfs_prop_f)(zfs_prop_t, void *);
-typedef zfs_prop_f zpool_prop_f;
-extern zfs_prop_t zfs_prop_iter(zfs_prop_f, void *, boolean_t);
-extern zpool_prop_t zpool_prop_iter(zpool_prop_f, void *, boolean_t);
+zpool_prop_t zpool_name_to_prop(const char *);
+const char *zpool_prop_to_name(zpool_prop_t);
+const char *zpool_prop_default_string(zpool_prop_t);
+uint64_t zpool_prop_default_numeric(zpool_prop_t);
+boolean_t zpool_prop_readonly(zpool_prop_t);
+int zpool_prop_index_to_string(zpool_prop_t, uint64_t, const char **);
+int zpool_prop_string_to_index(zpool_prop_t, const char *, uint64_t *);
+
+/*
+ * Definitions for the Delegation.
+ */
+typedef enum {
+ ZFS_DELEG_WHO_UNKNOWN = 0,
+ ZFS_DELEG_USER = 'u',
+ ZFS_DELEG_USER_SETS = 'U',
+ ZFS_DELEG_GROUP = 'g',
+ ZFS_DELEG_GROUP_SETS = 'G',
+ ZFS_DELEG_EVERYONE = 'e',
+ ZFS_DELEG_EVERYONE_SETS = 'E',
+ ZFS_DELEG_CREATE = 'c',
+ ZFS_DELEG_CREATE_SETS = 'C',
+ ZFS_DELEG_NAMED_SET = 's',
+ ZFS_DELEG_NAMED_SET_SETS = 'S'
+} zfs_deleg_who_type_t;
+
+typedef enum {
+ ZFS_DELEG_NONE = 0,
+ ZFS_DELEG_PERM_LOCAL = 1,
+ ZFS_DELEG_PERM_DESCENDENT = 2,
+ ZFS_DELEG_PERM_LOCALDESCENDENT = 3,
+ ZFS_DELEG_PERM_CREATE = 4
+} zfs_deleg_inherit_t;
+
+#define ZFS_DELEG_PERM_UID "uid"
+#define ZFS_DELEG_PERM_GID "gid"
+#define ZFS_DELEG_PERM_GROUPS "groups"
+
+typedef enum {
+ ZFS_CANMOUNT_OFF = 0,
+ ZFS_CANMOUNT_ON = 1,
+ ZFS_CANMOUNT_NOAUTO = 2
+} zfs_canmount_type_t;
+
+typedef enum zfs_share_op {
+ ZFS_SHARE_NFS = 0,
+ ZFS_UNSHARE_NFS = 1,
+ ZFS_SHARE_SMB = 2,
+ ZFS_UNSHARE_SMB = 3
+} zfs_share_op_t;
+
+typedef enum zfs_cache_type {
+ ZFS_CACHE_NONE = 0,
+ ZFS_CACHE_METADATA = 1,
+ ZFS_CACHE_ALL = 2
+} zfs_cache_type_t;
+
/*
* On-disk version number.
*/
-#define ZFS_VERSION_1 1ULL
-#define ZFS_VERSION_2 2ULL
-#define ZFS_VERSION_3 3ULL
-#define ZFS_VERSION_4 4ULL
-#define ZFS_VERSION_5 5ULL
-#define ZFS_VERSION_6 6ULL
+#define SPA_VERSION_1 1ULL
+#define SPA_VERSION_2 2ULL
+#define SPA_VERSION_3 3ULL
+#define SPA_VERSION_4 4ULL
+#define SPA_VERSION_5 5ULL
+#define SPA_VERSION_6 6ULL
+#define SPA_VERSION_7 7ULL
+#define SPA_VERSION_8 8ULL
+#define SPA_VERSION_9 9ULL
+#define SPA_VERSION_10 10ULL
+#define SPA_VERSION_11 11ULL
+#define SPA_VERSION_12 12ULL
+#define SPA_VERSION_13 13ULL
/*
- * When bumping up ZFS_VERSION, make sure GRUB ZFS understand the on-disk
+ * When bumping up SPA_VERSION, make sure GRUB ZFS understands the on-disk
* format change. Go to usr/src/grub/grub-0.95/stage2/{zfs-include/, fsys_zfs*},
* and do the appropriate changes.
*/
-#define ZFS_VERSION ZFS_VERSION_6
-#define ZFS_VERSION_STRING "6"
+#define SPA_VERSION SPA_VERSION_13
+#define SPA_VERSION_STRING "13"
/*
- * Symbolic names for the changes that caused a ZFS_VERSION switch.
+ * Symbolic names for the changes that caused a SPA_VERSION switch.
* Used in the code when checking for presence or absence of a feature.
* Feel free to define multiple symbolic names for each version if there
* were multiple changes to on-disk structures during that version.
*
- * NOTE: When checking the current ZFS_VERSION in your code, be sure
+ * NOTE: When checking the current SPA_VERSION in your code, be sure
* to use spa_version() since it reports the version of the
* last synced uberblock. Checking the in-flight version can
* be dangerous in some cases.
*/
-#define ZFS_VERSION_INITIAL ZFS_VERSION_1
-#define ZFS_VERSION_DITTO_BLOCKS ZFS_VERSION_2
-#define ZFS_VERSION_SPARES ZFS_VERSION_3
-#define ZFS_VERSION_RAID6 ZFS_VERSION_3
-#define ZFS_VERSION_BPLIST_ACCOUNT ZFS_VERSION_3
-#define ZFS_VERSION_RAIDZ_DEFLATE ZFS_VERSION_3
-#define ZFS_VERSION_DNODE_BYTES ZFS_VERSION_3
-#define ZFS_VERSION_ZPOOL_HISTORY ZFS_VERSION_4
-#define ZFS_VERSION_GZIP_COMPRESSION ZFS_VERSION_5
-#define ZFS_VERSION_BOOTFS ZFS_VERSION_6
+#define SPA_VERSION_INITIAL SPA_VERSION_1
+#define SPA_VERSION_DITTO_BLOCKS SPA_VERSION_2
+#define SPA_VERSION_SPARES SPA_VERSION_3
+#define SPA_VERSION_RAID6 SPA_VERSION_3
+#define SPA_VERSION_BPLIST_ACCOUNT SPA_VERSION_3
+#define SPA_VERSION_RAIDZ_DEFLATE SPA_VERSION_3
+#define SPA_VERSION_DNODE_BYTES SPA_VERSION_3
+#define SPA_VERSION_ZPOOL_HISTORY SPA_VERSION_4
+#define SPA_VERSION_GZIP_COMPRESSION SPA_VERSION_5
+#define SPA_VERSION_BOOTFS SPA_VERSION_6
+#define SPA_VERSION_SLOGS SPA_VERSION_7
+#define SPA_VERSION_DELEGATED_PERMS SPA_VERSION_8
+#define SPA_VERSION_FUID SPA_VERSION_9
+#define SPA_VERSION_REFRESERVATION SPA_VERSION_9
+#define SPA_VERSION_REFQUOTA SPA_VERSION_9
+#define SPA_VERSION_UNIQUE_ACCURATE SPA_VERSION_9
+#define SPA_VERSION_L2CACHE SPA_VERSION_10
+#define SPA_VERSION_NEXT_CLONES SPA_VERSION_11
+#define SPA_VERSION_ORIGIN SPA_VERSION_11
+#define SPA_VERSION_DSL_SCRUB SPA_VERSION_11
+#define SPA_VERSION_SNAP_PROPS SPA_VERSION_12
+#define SPA_VERSION_USED_BREAKDOWN SPA_VERSION_13
+
+/*
+ * ZPL version - rev'd whenever an incompatible on-disk format change
+ * occurs. This is independent of SPA/DMU/ZAP versioning. You must
+ * also update the version_table[] and help message in zfs_prop.c.
+ *
+ * When changing, be sure to teach GRUB how to read the new format!
+ * See usr/src/grub/grub-0.95/stage2/{zfs-include/,fsys_zfs*}
+ */
+#define ZPL_VERSION_1 1ULL
+#define ZPL_VERSION_2 2ULL
+#define ZPL_VERSION_3 3ULL
+#define ZPL_VERSION ZPL_VERSION_3
+#define ZPL_VERSION_STRING "3"
+
+#define ZPL_VERSION_INITIAL ZPL_VERSION_1
+#define ZPL_VERSION_DIRENT_TYPE ZPL_VERSION_2
+#define ZPL_VERSION_FUID ZPL_VERSION_3
+#define ZPL_VERSION_NORMALIZATION ZPL_VERSION_3
+#define ZPL_VERSION_SYSATTR ZPL_VERSION_3
/*
* The following are configuration names used in the nvlist describing a pool's
@@ -202,7 +340,6 @@ extern zpool_prop_t zpool_prop_iter(zpool_prop_f, void *, boolean_t);
#define ZPOOL_CONFIG_DTL "DTL"
#define ZPOOL_CONFIG_STATS "stats"
#define ZPOOL_CONFIG_WHOLE_DISK "whole_disk"
-#define ZPOOL_CONFIG_OFFLINE "offline"
#define ZPOOL_CONFIG_ERRCOUNT "error_count"
#define ZPOOL_CONFIG_NOT_PRESENT "not_present"
#define ZPOOL_CONFIG_SPARES "spares"
@@ -210,7 +347,22 @@ extern zpool_prop_t zpool_prop_iter(zpool_prop_f, void *, boolean_t);
#define ZPOOL_CONFIG_NPARITY "nparity"
#define ZPOOL_CONFIG_HOSTID "hostid"
#define ZPOOL_CONFIG_HOSTNAME "hostname"
-#define ZPOOL_CONFIG_TIMESTAMP "timestamp" /* not stored on disk */
+#define ZPOOL_CONFIG_UNSPARE "unspare"
+#define ZPOOL_CONFIG_PHYS_PATH "phys_path"
+#define ZPOOL_CONFIG_IS_LOG "is_log"
+#define ZPOOL_CONFIG_L2CACHE "l2cache"
+#define ZPOOL_CONFIG_SUSPENDED "suspended" /* not stored on disk */
+#define ZPOOL_CONFIG_TIMESTAMP "timestamp" /* not stored on disk */
+#define ZPOOL_CONFIG_BOOTFS "bootfs" /* not stored on disk */
+/*
+ * The persistent vdev state is stored as separate values rather than a single
+ * 'vdev_state' entry. This is because a device can be in multiple states, such
+ * as offline and degraded.
+ */
+#define ZPOOL_CONFIG_OFFLINE "offline"
+#define ZPOOL_CONFIG_FAULTED "faulted"
+#define ZPOOL_CONFIG_DEGRADED "degraded"
+#define ZPOOL_CONFIG_REMOVED "removed"
#define VDEV_TYPE_ROOT "root"
#define VDEV_TYPE_MIRROR "mirror"
@@ -220,6 +372,8 @@ extern zpool_prop_t zpool_prop_iter(zpool_prop_f, void *, boolean_t);
#define VDEV_TYPE_FILE "file"
#define VDEV_TYPE_MISSING "missing"
#define VDEV_TYPE_SPARE "spare"
+#define VDEV_TYPE_LOG "log"
+#define VDEV_TYPE_L2CACHE "l2cache"
/*
* This is needed in userland to report the minimum necessary device size.
@@ -230,11 +384,7 @@ extern zpool_prop_t zpool_prop_iter(zpool_prop_f, void *, boolean_t);
* The location of the pool configuration repository, shared between kernel and
* userland.
*/
-#define ZPOOL_CACHE_DIR "/boot/zfs"
-#define ZPOOL_CACHE_FILE "zpool.cache"
-#define ZPOOL_CACHE_TMP ".zpool.cache"
-
-#define ZPOOL_CACHE ZPOOL_CACHE_DIR "/" ZPOOL_CACHE_FILE
+#define ZPOOL_CACHE "/boot/zfs/zpool.cache"
/*
* vdev states are ordered from least to most healthy.
@@ -244,11 +394,15 @@ typedef enum vdev_state {
VDEV_STATE_UNKNOWN = 0, /* Uninitialized vdev */
VDEV_STATE_CLOSED, /* Not currently open */
VDEV_STATE_OFFLINE, /* Not allowed to open */
+ VDEV_STATE_REMOVED, /* Explicitly removed from system */
VDEV_STATE_CANT_OPEN, /* Tried to open, but failed */
+ VDEV_STATE_FAULTED, /* External request to fault device */
VDEV_STATE_DEGRADED, /* Replicated vdev with unhealthy kids */
VDEV_STATE_HEALTHY /* Presumed good */
} vdev_state_t;
+#define VDEV_STATE_ONLINE VDEV_STATE_HEALTHY
+
/*
* vdev aux states. When a vdev is in the CANT_OPEN state, the aux field
* of the vdev stats structure uses these constants to distinguish why.
@@ -263,19 +417,24 @@ typedef enum vdev_aux {
VDEV_AUX_BAD_LABEL, /* the label is OK but invalid */
VDEV_AUX_VERSION_NEWER, /* on-disk version is too new */
VDEV_AUX_VERSION_OLDER, /* on-disk version is too old */
- VDEV_AUX_SPARED /* hot spare used in another pool */
+ VDEV_AUX_SPARED, /* hot spare used in another pool */
+ VDEV_AUX_ERR_EXCEEDED, /* too many errors */
+ VDEV_AUX_IO_FAILURE, /* experienced I/O failure */
+ VDEV_AUX_BAD_LOG /* cannot read log chain(s) */
} vdev_aux_t;
/*
* pool state. The following states are written to disk as part of the normal
- * SPA lifecycle: ACTIVE, EXPORTED, DESTROYED, SPARE. The remaining states are
- * software abstractions used at various levels to communicate pool state.
+ * SPA lifecycle: ACTIVE, EXPORTED, DESTROYED, SPARE, L2CACHE. The remaining
+ * states are software abstractions used at various levels to communicate
+ * pool state.
*/
typedef enum pool_state {
POOL_STATE_ACTIVE = 0, /* In active use */
POOL_STATE_EXPORTED, /* Explicitly exported */
POOL_STATE_DESTROYED, /* Explicitly destroyed */
POOL_STATE_SPARE, /* Reserved for hot spare use */
+ POOL_STATE_L2CACHE, /* Level 2 ARC device */
POOL_STATE_UNINITIALIZED, /* Internal spa_t state */
POOL_STATE_UNAVAIL, /* Internal libzfs state */
POOL_STATE_POTENTIALLY_ACTIVE /* Internal libzfs state */
@@ -331,6 +490,7 @@ typedef struct vdev_stat {
uint64_t vs_scrub_end; /* UTC scrub end time */
} vdev_stat_t;
+#define ZVOL_DRIVER "zvol"
#define ZFS_DRIVER "zfs"
#define ZFS_DEV_NAME "zfs"
#define ZFS_DEV "/dev/" ZFS_DEV_NAME
@@ -346,7 +506,7 @@ typedef struct vdev_stat {
* And here are the things we need with /dev, etc. in front of them.
*/
#define ZVOL_PSEUDO_DEV "/devices/pseudo/zvol@0:"
-#define ZVOL_FULL_DEV_DIR "/dev/" ZVOL_DEV_DIR
+#define ZVOL_FULL_DEV_DIR "/dev/" ZVOL_DEV_DIR "/"
#define ZVOL_PROP_NAME "name"
@@ -368,40 +528,44 @@ typedef unsigned long zfs_ioc_t;
#define ZFS_IOC_POOL_FREEZE _IOWR('Z', 8, struct zfs_cmd)
#define ZFS_IOC_POOL_UPGRADE _IOWR('Z', 9, struct zfs_cmd)
#define ZFS_IOC_POOL_GET_HISTORY _IOWR('Z', 10, struct zfs_cmd)
-#define ZFS_IOC_POOL_LOG_HISTORY _IOWR('Z', 11, struct zfs_cmd)
-#define ZFS_IOC_VDEV_ADD _IOWR('Z', 12, struct zfs_cmd)
-#define ZFS_IOC_VDEV_REMOVE _IOWR('Z', 13, struct zfs_cmd)
-#define ZFS_IOC_VDEV_ONLINE _IOWR('Z', 14, struct zfs_cmd)
-#define ZFS_IOC_VDEV_OFFLINE _IOWR('Z', 15, struct zfs_cmd)
-#define ZFS_IOC_VDEV_ATTACH _IOWR('Z', 16, struct zfs_cmd)
-#define ZFS_IOC_VDEV_DETACH _IOWR('Z', 17, struct zfs_cmd)
-#define ZFS_IOC_VDEV_SETPATH _IOWR('Z', 18, struct zfs_cmd)
-#define ZFS_IOC_OBJSET_STATS _IOWR('Z', 19, struct zfs_cmd)
-#define ZFS_IOC_DATASET_LIST_NEXT _IOWR('Z', 20, struct zfs_cmd)
-#define ZFS_IOC_SNAPSHOT_LIST_NEXT _IOWR('Z', 21, struct zfs_cmd)
-#define ZFS_IOC_SET_PROP _IOWR('Z', 22, struct zfs_cmd)
-#define ZFS_IOC_CREATE_MINOR _IOWR('Z', 23, struct zfs_cmd)
-#define ZFS_IOC_REMOVE_MINOR _IOWR('Z', 24, struct zfs_cmd)
-#define ZFS_IOC_CREATE _IOWR('Z', 25, struct zfs_cmd)
-#define ZFS_IOC_DESTROY _IOWR('Z', 26, struct zfs_cmd)
-#define ZFS_IOC_ROLLBACK _IOWR('Z', 27, struct zfs_cmd)
-#define ZFS_IOC_RENAME _IOWR('Z', 28, struct zfs_cmd)
-#define ZFS_IOC_RECVBACKUP _IOWR('Z', 29, struct zfs_cmd)
-#define ZFS_IOC_SENDBACKUP _IOWR('Z', 30, struct zfs_cmd)
-#define ZFS_IOC_INJECT_FAULT _IOWR('Z', 31, struct zfs_cmd)
-#define ZFS_IOC_CLEAR_FAULT _IOWR('Z', 32, struct zfs_cmd)
-#define ZFS_IOC_INJECT_LIST_NEXT _IOWR('Z', 33, struct zfs_cmd)
-#define ZFS_IOC_ERROR_LOG _IOWR('Z', 34, struct zfs_cmd)
-#define ZFS_IOC_CLEAR _IOWR('Z', 35, struct zfs_cmd)
-#define ZFS_IOC_PROMOTE _IOWR('Z', 36, struct zfs_cmd)
-#define ZFS_IOC_DESTROY_SNAPS _IOWR('Z', 37, struct zfs_cmd)
-#define ZFS_IOC_SNAPSHOT _IOWR('Z', 38, struct zfs_cmd)
-#define ZFS_IOC_DSOBJ_TO_DSNAME _IOWR('Z', 39, struct zfs_cmd)
-#define ZFS_IOC_OBJ_TO_PATH _IOWR('Z', 40, struct zfs_cmd)
-#define ZFS_IOC_POOL_SET_PROPS _IOWR('Z', 41, struct zfs_cmd)
-#define ZFS_IOC_POOL_GET_PROPS _IOWR('Z', 42, struct zfs_cmd)
-#define ZFS_IOC_JAIL _IOWR('Z', 43, struct zfs_cmd)
-#define ZFS_IOC_UNJAIL _IOWR('Z', 44, struct zfs_cmd)
+#define ZFS_IOC_VDEV_ADD _IOWR('Z', 11, struct zfs_cmd)
+#define ZFS_IOC_VDEV_REMOVE _IOWR('Z', 12, struct zfs_cmd)
+#define ZFS_IOC_VDEV_SET_STATE _IOWR('Z', 13, struct zfs_cmd)
+#define ZFS_IOC_VDEV_ATTACH _IOWR('Z', 14, struct zfs_cmd)
+#define ZFS_IOC_VDEV_DETACH _IOWR('Z', 15, struct zfs_cmd)
+#define ZFS_IOC_VDEV_SETPATH _IOWR('Z', 16, struct zfs_cmd)
+#define ZFS_IOC_OBJSET_STATS _IOWR('Z', 17, struct zfs_cmd)
+#define ZFS_IOC_OBJSET_ZPLPROPS _IOWR('Z', 18, struct zfs_cmd)
+#define ZFS_IOC_DATASET_LIST_NEXT _IOWR('Z', 19, struct zfs_cmd)
+#define ZFS_IOC_SNAPSHOT_LIST_NEXT _IOWR('Z', 20, struct zfs_cmd)
+#define ZFS_IOC_SET_PROP _IOWR('Z', 21, struct zfs_cmd)
+#define ZFS_IOC_CREATE_MINOR _IOWR('Z', 22, struct zfs_cmd)
+#define ZFS_IOC_REMOVE_MINOR _IOWR('Z', 23, struct zfs_cmd)
+#define ZFS_IOC_CREATE _IOWR('Z', 24, struct zfs_cmd)
+#define ZFS_IOC_DESTROY _IOWR('Z', 25, struct zfs_cmd)
+#define ZFS_IOC_ROLLBACK _IOWR('Z', 26, struct zfs_cmd)
+#define ZFS_IOC_RENAME _IOWR('Z', 27, struct zfs_cmd)
+#define ZFS_IOC_RECV _IOWR('Z', 28, struct zfs_cmd)
+#define ZFS_IOC_SEND _IOWR('Z', 29, struct zfs_cmd)
+#define ZFS_IOC_INJECT_FAULT _IOWR('Z', 30, struct zfs_cmd)
+#define ZFS_IOC_CLEAR_FAULT _IOWR('Z', 31, struct zfs_cmd)
+#define ZFS_IOC_INJECT_LIST_NEXT _IOWR('Z', 32, struct zfs_cmd)
+#define ZFS_IOC_ERROR_LOG _IOWR('Z', 33, struct zfs_cmd)
+#define ZFS_IOC_CLEAR _IOWR('Z', 34, struct zfs_cmd)
+#define ZFS_IOC_PROMOTE _IOWR('Z', 35, struct zfs_cmd)
+#define ZFS_IOC_DESTROY_SNAPS _IOWR('Z', 36, struct zfs_cmd)
+#define ZFS_IOC_SNAPSHOT _IOWR('Z', 37, struct zfs_cmd)
+#define ZFS_IOC_DSOBJ_TO_DSNAME _IOWR('Z', 38, struct zfs_cmd)
+#define ZFS_IOC_OBJ_TO_PATH _IOWR('Z', 39, struct zfs_cmd)
+#define ZFS_IOC_POOL_SET_PROPS _IOWR('Z', 40, struct zfs_cmd)
+#define ZFS_IOC_POOL_GET_PROPS _IOWR('Z', 41, struct zfs_cmd)
+#define ZFS_IOC_SET_FSACL _IOWR('Z', 42, struct zfs_cmd)
+#define ZFS_IOC_GET_FSACL _IOWR('Z', 43, struct zfs_cmd)
+#define ZFS_IOC_ISCSI_PERM_CHECK _IOWR('Z', 44, struct zfs_cmd)
+#define ZFS_IOC_SHARE _IOWR('Z', 45, struct zfs_cmd)
+#define ZFS_IOC_INHERIT_PROP _IOWR('Z', 46, struct zfs_cmd)
+#define ZFS_IOC_JAIL _IOWR('Z', 47, struct zfs_cmd)
+#define ZFS_IOC_UNJAIL _IOWR('Z', 48, struct zfs_cmd)
/*
* Internal SPA load state. Used by FMA diagnosis engine.
@@ -429,6 +593,92 @@ typedef enum {
#define ZPOOL_HIST_RECORD "history record"
#define ZPOOL_HIST_TIME "history time"
#define ZPOOL_HIST_CMD "history command"
+#define ZPOOL_HIST_WHO "history who"
+#define ZPOOL_HIST_ZONE "history zone"
+#define ZPOOL_HIST_HOST "history hostname"
+#define ZPOOL_HIST_TXG "history txg"
+#define ZPOOL_HIST_INT_EVENT "history internal event"
+#define ZPOOL_HIST_INT_STR "history internal str"
+
+/*
+ * Flags for ZFS_IOC_VDEV_SET_STATE
+ */
+#define ZFS_ONLINE_CHECKREMOVE 0x1
+#define ZFS_ONLINE_UNSPARE 0x2
+#define ZFS_ONLINE_FORCEFAULT 0x4
+#define ZFS_OFFLINE_TEMPORARY 0x1
+
+/*
+ * Sysevent payload members. ZFS will generate the following sysevents with the
+ * given payloads:
+ *
+ * ESC_ZFS_RESILVER_START
+ * ESC_ZFS_RESILVER_END
+ * ESC_ZFS_POOL_DESTROY
+ *
+ * ZFS_EV_POOL_NAME DATA_TYPE_STRING
+ * ZFS_EV_POOL_GUID DATA_TYPE_UINT64
+ *
+ * ESC_ZFS_VDEV_REMOVE
+ * ESC_ZFS_VDEV_CLEAR
+ * ESC_ZFS_VDEV_CHECK
+ *
+ * ZFS_EV_POOL_NAME DATA_TYPE_STRING
+ * ZFS_EV_POOL_GUID DATA_TYPE_UINT64
+ * ZFS_EV_VDEV_PATH DATA_TYPE_STRING (optional)
+ * ZFS_EV_VDEV_GUID DATA_TYPE_UINT64
+ */
+#define ZFS_EV_POOL_NAME "pool_name"
+#define ZFS_EV_POOL_GUID "pool_guid"
+#define ZFS_EV_VDEV_PATH "vdev_path"
+#define ZFS_EV_VDEV_GUID "vdev_guid"
+
+/*
+ * Note: This is encoded on-disk, so new events must be added to the
+ * end, and unused events can not be removed. Be sure to edit
+ * zpool_main.c: hist_event_table[].
+ */
+typedef enum history_internal_events {
+ LOG_NO_EVENT = 0,
+ LOG_POOL_CREATE,
+ LOG_POOL_VDEV_ADD,
+ LOG_POOL_REMOVE,
+ LOG_POOL_DESTROY,
+ LOG_POOL_EXPORT,
+ LOG_POOL_IMPORT,
+ LOG_POOL_VDEV_ATTACH,
+ LOG_POOL_VDEV_REPLACE,
+ LOG_POOL_VDEV_DETACH,
+ LOG_POOL_VDEV_ONLINE,
+ LOG_POOL_VDEV_OFFLINE,
+ LOG_POOL_UPGRADE,
+ LOG_POOL_CLEAR,
+ LOG_POOL_SCRUB,
+ LOG_POOL_PROPSET,
+ LOG_DS_CREATE,
+ LOG_DS_CLONE,
+ LOG_DS_DESTROY,
+ LOG_DS_DESTROY_BEGIN,
+ LOG_DS_INHERIT,
+ LOG_DS_PROPSET,
+ LOG_DS_QUOTA,
+ LOG_DS_PERM_UPDATE,
+ LOG_DS_PERM_REMOVE,
+ LOG_DS_PERM_WHO_REMOVE,
+ LOG_DS_PROMOTE,
+ LOG_DS_RECEIVE,
+ LOG_DS_RENAME,
+ LOG_DS_RESERVATION,
+ LOG_DS_REPLAY_INC_SYNC,
+ LOG_DS_REPLAY_FULL_SYNC,
+ LOG_DS_ROLLBACK,
+ LOG_DS_SNAPSHOT,
+ LOG_DS_UPGRADE,
+ LOG_DS_REFQUOTA,
+ LOG_DS_REFRESERV,
+ LOG_POOL_SCRUB_DONE,
+ LOG_END
+} history_internal_events_t;
#ifdef __cplusplus
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/gfs.h b/sys/cddl/contrib/opensolaris/uts/common/sys/gfs.h
index 8e70f29..97f7ed6 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/gfs.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/gfs.h
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -33,6 +33,7 @@
#include <sys/vnode.h>
#include <sys/mutex.h>
#include <sys/dirent.h>
+#include <sys/extdirent.h>
#include <sys/uio.h>
#include <sys/list.h>
@@ -64,9 +65,10 @@ typedef struct gfs_file {
ino64_t gfs_ino; /* inode for this vnode */
} gfs_file_t;
-typedef int (*gfs_readdir_cb)(vnode_t *, struct dirent64 *, int *, offset_t *,
- offset_t *, void *);
-typedef int (*gfs_lookup_cb)(vnode_t *, const char *, vnode_t **, ino64_t *);
+typedef int (*gfs_readdir_cb)(vnode_t *, void *, int *, offset_t *,
+ offset_t *, void *, int);
+typedef int (*gfs_lookup_cb)(vnode_t *, const char *, vnode_t **, ino64_t *,
+ cred_t *, int, int *, pathname_t *);
typedef ino64_t (*gfs_inode_cb)(vnode_t *, int);
typedef struct gfs_dir {
@@ -93,11 +95,18 @@ extern vnode_t *gfs_root_create_file(size_t, struct vfs *, vnodeops_t *,
extern void *gfs_file_inactive(vnode_t *);
extern void *gfs_dir_inactive(vnode_t *);
-extern int gfs_dir_lookup(vnode_t *, const char *, vnode_t **);
-extern int gfs_dir_readdir(vnode_t *, uio_t *, int *, int *, u_long **, void *);
+extern int gfs_dir_case_lookup(vnode_t *, const char *, vnode_t **, cred_t *,
+ int, int *, pathname_t *);
+extern int gfs_dir_lookup(vnode_t *, const char *, vnode_t **, cred_t *,
+ int, int *, pathname_t *);
+extern int gfs_vop_lookup(vnode_t *, char *, vnode_t **, pathname_t *,
+ int, vnode_t *, cred_t *, caller_context_t *, int *, pathname_t *);
+extern int gfs_dir_readdir(vnode_t *, uio_t *, int *, int *, u_long **, void *,
+ cred_t *, int flags);
#define gfs_dir_lock(gd) mutex_enter(&(gd)->gfsd_lock)
#define gfs_dir_unlock(gd) mutex_exit(&(gd)->gfsd_lock)
+#define GFS_DIR_LOCKED(gd) MUTEX_HELD(&(gd)->gfsd_lock)
#define gfs_file_parent(vp) (((gfs_file_t *)(vp)->v_data)->gfs_parent)
@@ -110,21 +119,31 @@ extern int gfs_dir_readdir(vnode_t *, uio_t *, int *, int *, u_long **, void *);
(((gfs_file_t *)(vp)->v_data)->gfs_ino = (ino))
typedef struct gfs_readdir_state {
- struct dirent64 *grd_dirent; /* directory entry buffer */
+ void *grd_dirent; /* directory entry buffer */
size_t grd_namlen; /* max file name length */
size_t grd_ureclen; /* exported record size */
ssize_t grd_oresid; /* original uio_resid */
ino64_t grd_parent; /* inode of parent */
ino64_t grd_self; /* inode of self */
+ int grd_flags; /* flags from VOP_READDIR */
} gfs_readdir_state_t;
extern int gfs_readdir_init(gfs_readdir_state_t *, int, int, uio_t *, ino64_t,
- ino64_t);
+ ino64_t, int);
extern int gfs_readdir_emit(gfs_readdir_state_t *, uio_t *, offset_t, ino64_t,
- const char *, int *, u_long **);
+ const char *, int, int *, u_long **);
extern int gfs_readdir_pred(gfs_readdir_state_t *, uio_t *, offset_t *, int *,
u_long **);
extern int gfs_readdir_fini(gfs_readdir_state_t *, int, int *, int);
+extern int gfs_get_parent_ino(vnode_t *, cred_t *, caller_context_t *,
+ ino64_t *, ino64_t *);
+
+/*
+ * Objects with real extended attributes will get their . and ..
+ * readdir entries from the real xattr directory. GFS_STATIC_ENTRY_OFFSET
+ * lets us skip right to the static entries in the GFS directory.
+ */
+#define GFS_STATIC_ENTRY_OFFSET ((offset_t)2)
extern int gfs_lookup_dot(vnode_t **, vnode_t *, vnode_t *, const char *);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/idmap.h b/sys/cddl/contrib/opensolaris/uts/common/sys/idmap.h
new file mode 100644
index 0000000..3a405e4
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/idmap.h
@@ -0,0 +1,93 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL 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.
+ */
+
+#ifndef _SYS_IDMAP_H
+#define _SYS_IDMAP_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* Idmap status codes */
+#define IDMAP_SUCCESS 0
+#define IDMAP_NEXT 1
+#define IDMAP_ERR_OTHER -10000
+#define IDMAP_ERR_INTERNAL -9999
+#define IDMAP_ERR_MEMORY -9998
+#define IDMAP_ERR_NORESULT -9997
+#define IDMAP_ERR_NOTUSER -9996
+#define IDMAP_ERR_NOTGROUP -9995
+#define IDMAP_ERR_NOTSUPPORTED -9994
+#define IDMAP_ERR_W2U_NAMERULE -9993
+#define IDMAP_ERR_U2W_NAMERULE -9992
+#define IDMAP_ERR_CACHE -9991
+#define IDMAP_ERR_DB -9990
+#define IDMAP_ERR_ARG -9989
+#define IDMAP_ERR_SID -9988
+#define IDMAP_ERR_IDTYPE -9987
+#define IDMAP_ERR_RPC_HANDLE -9986
+#define IDMAP_ERR_RPC -9985
+#define IDMAP_ERR_CLIENT_HANDLE -9984
+#define IDMAP_ERR_BUSY -9983
+#define IDMAP_ERR_PERMISSION_DENIED -9982
+#define IDMAP_ERR_NOMAPPING -9981
+#define IDMAP_ERR_NEW_ID_ALLOC_REQD -9980
+#define IDMAP_ERR_DOMAIN -9979
+#define IDMAP_ERR_SECURITY -9978
+#define IDMAP_ERR_NOTFOUND -9977
+#define IDMAP_ERR_DOMAIN_NOTFOUND -9976
+#define IDMAP_ERR_UPDATE_NOTALLOWED -9975
+#define IDMAP_ERR_CFG -9974
+#define IDMAP_ERR_CFG_CHANGE -9973
+#define IDMAP_ERR_NOTMAPPED_WELLKNOWN -9972
+#define IDMAP_ERR_RETRIABLE_NET_ERR -9971
+#define IDMAP_ERR_W2U_NAMERULE_CONFLICT -9970
+#define IDMAP_ERR_U2W_NAMERULE_CONFLICT -9969
+#define IDMAP_ERR_BAD_UTF8 -9968
+#define IDMAP_ERR_NONEGENERATED -9967
+#define IDMAP_ERR_PROP_UNKNOWN -9966
+#define IDMAP_ERR_NS_LDAP_OP_FAILED -9965
+#define IDMAP_ERR_NS_LDAP_PARTIAL -9964
+#define IDMAP_ERR_NS_LDAP_CFG -9963
+#define IDMAP_ERR_NS_LDAP_BAD_WINNAME -9962
+
+/* Reserved GIDs for some well-known SIDs */
+#define IDMAP_WK_LOCAL_SYSTEM_GID 2147483648U /* 0x80000000 */
+#define IDMAP_WK_CREATOR_GROUP_GID 2147483649U
+#define IDMAP_WK__MAX_GID 2147483649U
+
+/* Reserved UIDs for some well-known SIDs */
+#define IDMAP_WK_CREATOR_OWNER_UID 2147483648U
+#define IDMAP_WK__MAX_UID 2147483648U
+
+/* Reserved SIDs */
+#define IDMAP_WK_CREATOR_SID_AUTHORITY "S-1-3"
+
+/*
+ * Max door RPC size for ID mapping (can't be too large relative to the
+ * default user-land thread stack size, since clnt_door_call()
+ * alloca()s). See libidmap:idmap_init().
+ */
+#define IDMAP_MAX_DOOR_RPC (256 * 1024)
+
+#endif /* _SYS_IDMAP_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/isa_defs.h b/sys/cddl/contrib/opensolaris/uts/common/sys/isa_defs.h
index c12a641..e0df24a 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/isa_defs.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/isa_defs.h
@@ -10,6 +10,7 @@
* See the License for the specific language governing permissions
* and limitations under the License.
*
+ *
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
@@ -20,15 +21,13 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_ISA_DEFS_H
#define _SYS_ISA_DEFS_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* This header file serves to group a set of well known defines and to
* set these for each instruction set architecture. These defines may
@@ -209,6 +208,10 @@
*
* _OBP
* This indicates the firmware interface is OBP.
+ *
+ * _SOFT_HOSTID
+ * This indicates that the implementation obtains the hostid
+ * from the file /etc/hostid, rather than from hardware.
*/
#ifdef __cplusplus
@@ -277,6 +280,7 @@ extern "C" {
#define __i386_COMPAT
#define _PSM_MODULES
#define _RTC_CONFIG
+#define _SOFT_HOSTID
#define _DONT_USE_1275_GENERIC_NAMES
#define _HAVE_CPUID_INSN
@@ -336,6 +340,7 @@ extern "C" {
#define _FIRMWARE_NEEDS_FDISK
#define _PSM_MODULES
#define _RTC_CONFIG
+#define _SOFT_HOSTID
#define _DONT_USE_1275_GENERIC_NAMES
#define _HAVE_CPUID_INSN
@@ -382,49 +387,6 @@ extern "C" {
#define _DONT_USE_1275_GENERIC_NAMES
#define _HAVE_CPUID_INSN
-#elif defined(__mips__)
-
-/*
- * Define the appropriate "processor characteristics"
- */
-#define _STACK_GROWS_DOWNWARD
-#define _LONG_LONG_LTOH
-#define _BIT_FIELDS_LTOH
-#define _IEEE_754
-#define _CHAR_IS_SIGNED
-#define _BOOL_ALIGNMENT 1
-#define _CHAR_ALIGNMENT 1
-#define _SHORT_ALIGNMENT 2
-#define _INT_ALIGNMENT 4
-#define _FLOAT_ALIGNMENT 4
-#define _FLOAT_COMPLEX_ALIGNMENT 4
-#define _LONG_ALIGNMENT 4
-#define _LONG_LONG_ALIGNMENT 4
-#define _DOUBLE_ALIGNMENT 4
-#define _DOUBLE_COMPLEX_ALIGNMENT 4
-#define _LONG_DOUBLE_ALIGNMENT 4
-#define _LONG_DOUBLE_COMPLEX_ALIGNMENT 4
-#define _POINTER_ALIGNMENT 4
-#define _MAX_ALIGNMENT 4
-#define _ALIGNMENT_REQUIRED 0
-
-#define _LONG_LONG_ALIGNMENT_32 _LONG_LONG_ALIGNMENT
-
-/*
- * Define the appropriate "implementation choices".
- */
-#define _ILP32
-#if !defined(_I32LPx) && defined(_KERNEL)
-#define _I32LPx
-#endif
-#define _SUNOS_VTOC_16
-#define _DMA_USES_PHYSADDR
-#define _FIRMWARE_NEEDS_FDISK
-#define _PSM_MODULES
-#define _RTC_CONFIG
-#define _DONT_USE_1275_GENERIC_NAMES
-#define _HAVE_CPUID_INSN
-
#elif defined(__powerpc__)
/*
@@ -541,7 +503,7 @@ extern "C" {
#define _POINTER_ALIGNMENT 8
#define _MAX_ALIGNMENT 16
-#define _LONG_LONG_ALIGNMENT_32 _LONG_LONG_ALIGMENT
+#define _LONG_LONG_ALIGNMENT_32 _LONG_LONG_ALIGNMENT
/*
* Define the appropriate "implementation choices"
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/list.h b/sys/cddl/contrib/opensolaris/uts/common/sys/list.h
index 7e9d9aa..8339b62 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/list.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/list.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -46,15 +45,20 @@ void list_insert_before(list_t *, void *, void *);
void list_insert_head(list_t *, void *);
void list_insert_tail(list_t *, void *);
void list_remove(list_t *, void *);
+void *list_remove_head(list_t *);
+void *list_remove_tail(list_t *);
void list_move_tail(list_t *, list_t *);
void *list_head(list_t *);
void *list_tail(list_t *);
void *list_next(list_t *, void *);
void *list_prev(list_t *, void *);
+int list_is_empty(list_t *);
+
+void list_link_init(list_node_t *);
+void list_link_replace(list_node_t *, list_node_t *);
int list_link_active(list_node_t *);
-int list_is_empty(list_t *);
#ifdef __cplusplus
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/nvpair.h b/sys/cddl/contrib/opensolaris/uts/common/sys/nvpair.h
index 306e30f..cc57867 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/nvpair.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/nvpair.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -68,7 +67,12 @@ typedef enum {
DATA_TYPE_UINT8,
DATA_TYPE_BOOLEAN_ARRAY,
DATA_TYPE_INT8_ARRAY,
+#if !defined(_KERNEL)
+ DATA_TYPE_UINT8_ARRAY,
+ DATA_TYPE_DOUBLE
+#else
DATA_TYPE_UINT8_ARRAY
+#endif
} data_type_t;
typedef struct nvpair {
@@ -189,6 +193,9 @@ int nvlist_add_uint64_array(nvlist_t *, const char *, uint64_t *, uint_t);
int nvlist_add_string_array(nvlist_t *, const char *, char *const *, uint_t);
int nvlist_add_nvlist_array(nvlist_t *, const char *, nvlist_t **, uint_t);
int nvlist_add_hrtime(nvlist_t *, const char *, hrtime_t);
+#if !defined(_KERNEL)
+int nvlist_add_double(nvlist_t *, const char *, double);
+#endif
int nvlist_remove(nvlist_t *, const char *, data_type_t);
int nvlist_remove_all(nvlist_t *, const char *);
@@ -221,12 +228,21 @@ int nvlist_lookup_string_array(nvlist_t *, const char *, char ***, uint_t *);
int nvlist_lookup_nvlist_array(nvlist_t *, const char *,
nvlist_t ***, uint_t *);
int nvlist_lookup_hrtime(nvlist_t *, const char *, hrtime_t *);
-int nvlist_lookup_pairs(nvlist_t *nvl, int, ...);
+int nvlist_lookup_pairs(nvlist_t *, int, ...);
+#if !defined(_KERNEL)
+int nvlist_lookup_double(nvlist_t *, const char *, double *);
+#endif
+
+int nvlist_lookup_nvpair(nvlist_t *, const char *, nvpair_t **);
+int nvlist_lookup_nvpair_embedded_index(nvlist_t *, const char *, nvpair_t **,
+ int *, char **);
+boolean_t nvlist_exists(nvlist_t *, const char *);
/* processing nvpair */
-nvpair_t *nvlist_next_nvpair(nvlist_t *nvl, nvpair_t *);
+nvpair_t *nvlist_next_nvpair(nvlist_t *, nvpair_t *);
char *nvpair_name(nvpair_t *);
data_type_t nvpair_type(nvpair_t *);
+int nvpair_type_is_array(nvpair_t *);
int nvpair_value_boolean_value(nvpair_t *, boolean_t *);
int nvpair_value_byte(nvpair_t *, uchar_t *);
int nvpair_value_int8(nvpair_t *, int8_t *);
@@ -252,6 +268,9 @@ int nvpair_value_uint64_array(nvpair_t *, uint64_t **, uint_t *);
int nvpair_value_string_array(nvpair_t *, char ***, uint_t *);
int nvpair_value_nvlist_array(nvpair_t *, nvlist_t ***, uint_t *);
int nvpair_value_hrtime(nvpair_t *, hrtime_t *);
+#if !defined(_KERNEL)
+int nvpair_value_double(nvpair_t *, double *);
+#endif
#ifdef __cplusplus
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/processor.h b/sys/cddl/contrib/opensolaris/uts/common/sys/processor.h
index 063f7dacb..3a76c8c 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/processor.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/processor.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -26,7 +25,7 @@
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -83,7 +82,9 @@ typedef int chipid_t;
#define PS_SPARE "spare"
/*
- * Structure filled in by processor_info(2).
+ * Structure filled in by processor_info(2). This structure
+ * SHOULD NOT BE MODIFIED. Changes to the structure would
+ * negate ABI compatibility.
*
* The string fields are guaranteed to contain a NULL.
*
@@ -105,6 +106,9 @@ typedef struct {
*/
#define PBIND_NONE -1 /* LWP/thread is not bound */
#define PBIND_QUERY -2 /* don't set, just return the binding */
+#define PBIND_HARD -3 /* prevents offlining CPU (default) */
+#define PBIND_SOFT -4 /* allows offlining CPU */
+#define PBIND_QUERY_TYPE -5 /* Return binding type */
/*
* User-level system call interface prototypes
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/synch.h b/sys/cddl/contrib/opensolaris/uts/common/sys/synch.h
index 8f52d72..6431bf2 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/synch.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/synch.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -116,12 +116,12 @@ typedef struct _lwp_sema {
* for rwlock_t in head/sync.h that we cannot change.
*/
typedef struct _lwp_rwlock {
- int32_t readers; /* -1 == writer else # of readers */
+ int32_t readers; /* rwstate word */
uint16_t type;
uint16_t magic;
- lwp_mutex_t mutex; /* used to indicate ownership */
- lwp_cond_t readercv; /* unused */
- lwp_cond_t writercv; /* unused */
+ lwp_mutex_t mutex; /* used with process-shared rwlocks */
+ lwp_cond_t readercv; /* used only to indicate ownership */
+ lwp_cond_t writercv; /* used only to indicate ownership */
} lwp_rwlock_t;
#endif /* _ASM */
@@ -131,20 +131,21 @@ typedef struct _lwp_rwlock {
#define USYNC_THREAD 0x00 /* private to a process */
#define USYNC_PROCESS 0x01 /* shared by processes */
-/* Keep the following 3 fields in sync with pthread.h */
-#define LOCK_NORMAL 0x00 /* same as USYNC_THREAD */
-#define LOCK_ERRORCHECK 0x02 /* error check lock */
-#define LOCK_RECURSIVE 0x04 /* recursive lock */
-
-#define USYNC_PROCESS_ROBUST 0x08 /* shared by processes robustly */
-
-/* Keep the following 5 fields in sync with pthread.h */
+/* Keep the following values in sync with pthread.h */
+#define LOCK_NORMAL 0x00 /* same as USYNC_THREAD */
+#define LOCK_SHARED 0x01 /* same as USYNC_PROCESS */
+#define LOCK_ERRORCHECK 0x02 /* error check lock */
+#define LOCK_RECURSIVE 0x04 /* recursive lock */
+#define LOCK_PRIO_INHERIT 0x10 /* priority inheritance lock */
+#define LOCK_PRIO_PROTECT 0x20 /* priority ceiling lock */
+#define LOCK_ROBUST 0x40 /* robust lock */
-#define LOCK_PRIO_NONE 0x00
-#define LOCK_PRIO_INHERIT 0x10
-#define LOCK_PRIO_PROTECT 0x20
-#define LOCK_STALL_NP 0x00
-#define LOCK_ROBUST_NP 0x40
+/*
+ * USYNC_PROCESS_ROBUST is a deprecated historical type. It is mapped
+ * into (USYNC_PROCESS | LOCK_ROBUST) by mutex_init(). Application code
+ * should be revised to use (USYNC_PROCESS | LOCK_ROBUST) rather than this.
+ */
+#define USYNC_PROCESS_ROBUST 0x08
/*
* lwp_mutex_t flags
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/sysevent/eventdefs.h b/sys/cddl/contrib/opensolaris/uts/common/sys/sysevent/eventdefs.h
new file mode 100644
index 0000000..ac21686
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/sysevent/eventdefs.h
@@ -0,0 +1,247 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL 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.
+ */
+
+#ifndef _SYS_SYSEVENT_EVENTDEFS_H
+#define _SYS_SYSEVENT_EVENTDEFS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * eventdefs.h contains public definitions for sysevent types (classes
+ * and subclasses). All additions/removal/changes are subject
+ * to PSARC approval.
+ */
+
+/* Sysevent Class definitions */
+#define EC_NONE "EC_none"
+#define EC_PRIV "EC_priv"
+#define EC_PLATFORM "EC_platform" /* events private to platform */
+#define EC_DR "EC_dr" /* Dynamic reconfiguration event class */
+#define EC_ENV "EC_env" /* Environmental monitor event class */
+#define EC_DOMAIN "EC_domain" /* Domain event class */
+#define EC_AP_DRIVER "EC_ap_driver" /* Alternate Pathing event class */
+#define EC_IPMP "EC_ipmp" /* IP Multipathing event class */
+#define EC_DEV_ADD "EC_dev_add" /* device add event class */
+#define EC_DEV_REMOVE "EC_dev_remove" /* device remove event class */
+#define EC_DEV_BRANCH "EC_dev_branch" /* device tree branch event class */
+#define EC_DEV_STATUS "EC_dev_status" /* device status event class */
+#define EC_FM "EC_fm" /* FMA error report event */
+#define EC_ZFS "EC_zfs" /* ZFS event */
+#define EC_DATALINK "EC_datalink" /* datalink event */
+
+/*
+ * The following event class is reserved for exclusive use
+ * by Sun Cluster software.
+ */
+#define EC_CLUSTER "EC_Cluster"
+
+/*
+ * The following classes are exclusively reserved for use by the
+ * Solaris Volume Manager (SVM)
+ */
+#define EC_SVM_CONFIG "EC_SVM_Config"
+#define EC_SVM_STATE "EC_SVM_State"
+
+/*
+ * EC_SVM_CONFIG subclass definitions - supporting attributes (name/value pairs)
+ * are found in sys/sysevent/svm.h
+ */
+#define ESC_SVM_CREATE "ESC_SVM_Create"
+#define ESC_SVM_DELETE "ESC_SVM_Delete"
+#define ESC_SVM_ADD "ESC_SVM_Add"
+#define ESC_SVM_REMOVE "ESC_SVM_Remove"
+#define ESC_SVM_REPLACE "ESC_SVM_Replace"
+#define ESC_SVM_GROW "ESC_SVM_Grow"
+#define ESC_SVM_RENAME_SRC "ESC_SVM_Rename_Src"
+#define ESC_SVM_RENAME_DST "ESC_SVM_Rename_Dst"
+#define ESC_SVM_MEDIATOR_ADD "ESC_SVM_Mediator_Add"
+#define ESC_SVM_MEDIATOR_DELETE "ESC_SVM_Mediator_Delete"
+#define ESC_SVM_HOST_ADD "ESC_SVM_Host_Add"
+#define ESC_SVM_HOST_DELETE "ESC_SVM_Host_Delete"
+#define ESC_SVM_DRIVE_ADD "ESC_SVM_Drive_Add"
+#define ESC_SVM_DRIVE_DELETE "ESC_SVM_Drive_Delete"
+#define ESC_SVM_DETACH "ESC_SVM_Detach"
+#define ESC_SVM_DETACHING "ESC_SVM_Detaching"
+#define ESC_SVM_ATTACH "ESC_SVM_Attach"
+#define ESC_SVM_ATTACHING "ESC_SVM_Attaching"
+
+/*
+ * EC_SVM_STATE subclass definitions - supporting attributes (name/value pairs)
+ * are found in sys/sysevent/svm.h
+ */
+#define ESC_SVM_INIT_START "ESC_SVM_Init_Start"
+#define ESC_SVM_INIT_FAILED "ESC_SVM_Init_Failed"
+#define ESC_SVM_INIT_FATAL "ESC_SVM_Init_Fatal"
+#define ESC_SVM_INIT_SUCCESS "ESC_SVM_Init_Success"
+#define ESC_SVM_IOERR "ESC_SVM_Ioerr"
+#define ESC_SVM_ERRED "ESC_SVM_Erred"
+#define ESC_SVM_LASTERRED "ESC_SVM_Lasterred"
+#define ESC_SVM_OK "ESC_SVM_Ok"
+#define ESC_SVM_ENABLE "ESC_SVM_Enable"
+#define ESC_SVM_RESYNC_START "ESC_SVM_Resync_Start"
+#define ESC_SVM_RESYNC_FAILED "ESC_SVM_Resync_Failed"
+#define ESC_SVM_RESYNC_SUCCESS "ESC_SVM_Resync_Success"
+#define ESC_SVM_RESYNC_DONE "ESC_SVM_Resync_Done"
+#define ESC_SVM_HOTSPARED "ESC_SVM_Hotspared"
+#define ESC_SVM_HS_FREED "ESC_SVM_HS_Freed"
+#define ESC_SVM_HS_CHANGED "ESC_SVM_HS_Changed"
+#define ESC_SVM_TAKEOVER "ESC_SVM_Takeover"
+#define ESC_SVM_RELEASE "ESC_SVM_Release"
+#define ESC_SVM_OPEN_FAIL "ESC_SVM_Open_Fail"
+#define ESC_SVM_OFFLINE "ESC_SVM_Offline"
+#define ESC_SVM_ONLINE "ESC_SVM_Online"
+#define ESC_SVM_CHANGE "ESC_SVM_Change"
+#define ESC_SVM_EXCHANGE "ESC_SVM_Exchange"
+#define ESC_SVM_REGEN_START "ESC_SVM_Regen_Start"
+#define ESC_SVM_REGEN_DONE "ESC_SVM_Regen_Done"
+#define ESC_SVM_REGEN_FAILED "ESC_SVM_Regen_Failed"
+
+/*
+ * EC_DR subclass definitions - supporting attributes (name/value pairs)
+ * are found in sys/sysevent/dr.h
+ */
+
+/* Attachment point state change */
+#define ESC_DR_AP_STATE_CHANGE "ESC_dr_ap_state_change"
+#define ESC_DR_REQ "ESC_dr_req" /* Request DR */
+#define ESC_DR_TARGET_STATE_CHANGE "ESC_dr_target_state_change"
+
+/*
+ * EC_ENV subclass definitions - supporting attributes (name/value pairs)
+ * are found in sys/sysevent/env.h
+ */
+#define ESC_ENV_TEMP "ESC_env_temp" /* Temperature change event subclass */
+#define ESC_ENV_FAN "ESC_env_fan" /* Fan status change event subclass */
+#define ESC_ENV_POWER "ESC_env_power" /* Power supply change event subclass */
+#define ESC_ENV_LED "ESC_env_led" /* LED change event subclass */
+
+/*
+ * EC_DOMAIN subclass definitions - supporting attributes (name/value pairs)
+ * are found in sys/sysevent/domain.h
+ */
+
+/* Domain state change */
+#define ESC_DOMAIN_STATE_CHANGE "ESC_domain_state_change"
+/* Domain loghost name change */
+#define ESC_DOMAIN_LOGHOST_CHANGE "ESC_domain_loghost_change"
+
+/*
+ * EC_AP_DRIVER subclass definitions - supporting attributes (name/value pairs)
+ * are found in sys/sysevent/ap_driver.h
+ */
+
+/* Alternate Pathing path switch */
+#define ESC_AP_DRIVER_PATHSWITCH "ESC_ap_driver_pathswitch"
+/* Alternate Pathing database commit */
+#define ESC_AP_DRIVER_COMMIT "ESC_ap_driver_commit"
+/* Alternate Pathing physical path status change */
+#define ESC_AP_DRIVER_PHYS_PATH_STATUS_CHANGE \
+ "ESC_ap_driver_phys_path_status_change"
+
+/*
+ * EC_IPMP subclass definitions - supporting attributes (name/value pairs)
+ * are found in sys/sysevent/ipmp.h
+ */
+
+/* IPMP group has changed state */
+#define ESC_IPMP_GROUP_STATE "ESC_ipmp_group_state"
+
+/* IPMP group has been created or removed */
+#define ESC_IPMP_GROUP_CHANGE "ESC_ipmp_group_change"
+
+/* IPMP group has had an interface added or removed */
+#define ESC_IPMP_GROUP_MEMBER_CHANGE "ESC_ipmp_group_member_change"
+
+/* Interface within an IPMP group has changed state or type */
+#define ESC_IPMP_IF_CHANGE "ESC_ipmp_if_change"
+
+
+/*
+ * EC_DEV_ADD and EC_DEV_REMOVE subclass definitions - supporting attributes
+ * (name/value pairs) are found in sys/sysevent/dev.h
+ */
+#define ESC_DISK "disk" /* disk device */
+#define ESC_NETWORK "network" /* network interface */
+#define ESC_PRINTER "printer" /* printer device */
+#define ESC_LOFI "lofi" /* lofi device */
+
+/*
+ * EC_DEV_BRANCH subclass definitions - supporting attributes (name/value pairs)
+ * are found in sys/sysevent/dev.h
+ */
+
+/* device tree branch added */
+#define ESC_DEV_BRANCH_ADD "ESC_dev_branch_add"
+
+/* device tree branch removed */
+#define ESC_DEV_BRANCH_REMOVE "ESC_dev_branch_remove"
+
+/* device capacity dynamically changed */
+#define ESC_DEV_DLE "ESC_dev_dle"
+
+/* FMA Fault and Error event protocol subclass */
+#define ESC_FM_ERROR "ESC_FM_error"
+#define ESC_FM_ERROR_REPLAY "ESC_FM_error_replay"
+
+/* Service processor subclass definitions */
+#define ESC_PLATFORM_SP_RESET "ESC_platform_sp_reset"
+
+/*
+ * EC_PWRCTL subclass definitions
+ */
+#define EC_PWRCTL "EC_pwrctl"
+#define ESC_PWRCTL_ADD "ESC_pwrctl_add"
+#define ESC_PWRCTL_REMOVE "ESC_pwrctl_remove"
+#define ESC_PWRCTL_WARN "ESC_pwrctl_warn"
+#define ESC_PWRCTL_LOW "ESC_pwrctl_low"
+#define ESC_PWRCTL_STATE_CHANGE "ESC_pwrctl_state_change"
+#define ESC_PWRCTL_POWER_BUTTON "ESC_pwrctl_power_button"
+#define ESC_PWRCTL_BRIGHTNESS_UP "ESC_pwrctl_brightness_up"
+#define ESC_PWRCTL_BRIGHTNESS_DOWN "ESC_pwrctl_brightness_down"
+
+/*
+ * ZFS subclass definitions. supporting attributes (name/value paris) are found
+ * in sys/fs/zfs.h
+ */
+#define ESC_ZFS_RESILVER_START "ESC_ZFS_resilver_start"
+#define ESC_ZFS_RESILVER_FINISH "ESC_ZFS_resilver_finish"
+#define ESC_ZFS_VDEV_REMOVE "ESC_ZFS_vdev_remove"
+#define ESC_ZFS_POOL_DESTROY "ESC_ZFS_pool_destroy"
+#define ESC_ZFS_VDEV_CLEAR "ESC_ZFS_vdev_clear"
+#define ESC_ZFS_VDEV_CHECK "ESC_ZFS_vdev_check"
+#define ESC_ZFS_CONFIG_SYNC "ESC_ZFS_config_sync"
+
+/*
+ * datalink subclass definitions.
+ */
+#define ESC_DATALINK_PHYS_ADD "ESC_datalink_phys_add" /* new physical link */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_SYSEVENT_EVENTDEFS_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/sysmacros.h b/sys/cddl/contrib/opensolaris/uts/common/sys/sysmacros.h
index 9f16a07..22f9fe3 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/sysmacros.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/sysmacros.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -24,15 +23,13 @@
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_SYSMACROS_H
#define _SYS_SYSMACROS_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/param.h>
#ifdef __cplusplus
@@ -226,18 +223,69 @@ extern unsigned char bcd_to_byte[256];
#define ISP2(x) (((x) & ((x) - 1)) == 0)
/*
- * Macros for various sorts of alignment and rounding when the alignment
- * is known to be a power of 2.
+ * Macros for various sorts of alignment and rounding. The "align" must
+ * be a power of 2. Often times it is a block, sector, or page.
+ */
+
+/*
+ * return x rounded down to an align boundary
+ * eg, P2ALIGN(1200, 1024) == 1024 (1*align)
+ * eg, P2ALIGN(1024, 1024) == 1024 (1*align)
+ * eg, P2ALIGN(0x1234, 0x100) == 0x1200 (0x12*align)
+ * eg, P2ALIGN(0x5600, 0x100) == 0x5600 (0x56*align)
*/
#define P2ALIGN(x, align) ((x) & -(align))
+
+/*
+ * return x % (mod) align
+ * eg, P2PHASE(0x1234, 0x100) == 0x34 (x-0x12*align)
+ * eg, P2PHASE(0x5600, 0x100) == 0x00 (x-0x56*align)
+ */
#define P2PHASE(x, align) ((x) & ((align) - 1))
+
+/*
+ * return how much space is left in this block (but if it's perfectly
+ * aligned, return 0).
+ * eg, P2NPHASE(0x1234, 0x100) == 0xcc (0x13*align-x)
+ * eg, P2NPHASE(0x5600, 0x100) == 0x00 (0x56*align-x)
+ */
#define P2NPHASE(x, align) (-(x) & ((align) - 1))
+
+/*
+ * return x rounded up to an align boundary
+ * eg, P2ROUNDUP(0x1234, 0x100) == 0x1300 (0x13*align)
+ * eg, P2ROUNDUP(0x5600, 0x100) == 0x5600 (0x56*align)
+ */
#define P2ROUNDUP(x, align) (-(-(x) & -(align)))
+
+/*
+ * return the ending address of the block that x is in
+ * eg, P2END(0x1234, 0x100) == 0x12ff (0x13*align - 1)
+ * eg, P2END(0x5600, 0x100) == 0x56ff (0x57*align - 1)
+ */
#define P2END(x, align) (-(~(x) & -(align)))
+
+/*
+ * return x rounded up to the next phase (offset) within align.
+ * phase should be < align.
+ * eg, P2PHASEUP(0x1234, 0x100, 0x10) == 0x1310 (0x13*align + phase)
+ * eg, P2PHASEUP(0x5600, 0x100, 0x10) == 0x5610 (0x56*align + phase)
+ */
#define P2PHASEUP(x, align, phase) ((phase) - (((phase) - (x)) & -(align)))
-#define P2CROSS(x, y, align) (((x) ^ (y)) > (align) - 1)
+
/*
- * Determine whether two numbers have the same high-order bit.
+ * return TRUE if adding len to off would cause it to cross an align
+ * boundary.
+ * eg, P2BOUNDARY(0x1234, 0xe0, 0x100) == TRUE (0x1234 + 0xe0 == 0x1314)
+ * eg, P2BOUNDARY(0x1234, 0x50, 0x100) == FALSE (0x1234 + 0x50 == 0x1284)
+ */
+#define P2BOUNDARY(off, len, align) \
+ (((off) ^ ((off) + (len) - 1)) > (align) - 1)
+
+/*
+ * Return TRUE if they have the same highest bit set.
+ * eg, P2SAMEHIGHBIT(0x1234, 0x1001) == TRUE (the high bit is 0x1000)
+ * eg, P2SAMEHIGHBIT(0x1234, 0x3010) == FALSE (high bit of 0x3010 is 0x2000)
*/
#define P2SAMEHIGHBIT(x, y) (((x) ^ (y)) < ((x) & (y)))
@@ -276,6 +324,46 @@ extern unsigned char bcd_to_byte[256];
#define INCR_COUNT(var, mutex) mutex_enter(mutex), (*(var))++, mutex_exit(mutex)
#define DECR_COUNT(var, mutex) mutex_enter(mutex), (*(var))--, mutex_exit(mutex)
+/*
+ * Macros to declare bitfields - the order in the parameter list is
+ * Low to High - that is, declare bit 0 first. We only support 8-bit bitfields
+ * because if a field crosses a byte boundary it's not likely to be meaningful
+ * without reassembly in its nonnative endianness.
+ */
+#if defined(_BIT_FIELDS_LTOH)
+#define DECL_BITFIELD2(_a, _b) \
+ uint8_t _a, _b
+#define DECL_BITFIELD3(_a, _b, _c) \
+ uint8_t _a, _b, _c
+#define DECL_BITFIELD4(_a, _b, _c, _d) \
+ uint8_t _a, _b, _c, _d
+#define DECL_BITFIELD5(_a, _b, _c, _d, _e) \
+ uint8_t _a, _b, _c, _d, _e
+#define DECL_BITFIELD6(_a, _b, _c, _d, _e, _f) \
+ uint8_t _a, _b, _c, _d, _e, _f
+#define DECL_BITFIELD7(_a, _b, _c, _d, _e, _f, _g) \
+ uint8_t _a, _b, _c, _d, _e, _f, _g
+#define DECL_BITFIELD8(_a, _b, _c, _d, _e, _f, _g, _h) \
+ uint8_t _a, _b, _c, _d, _e, _f, _g, _h
+#elif defined(_BIT_FIELDS_HTOL)
+#define DECL_BITFIELD2(_a, _b) \
+ uint8_t _b, _a
+#define DECL_BITFIELD3(_a, _b, _c) \
+ uint8_t _c, _b, _a
+#define DECL_BITFIELD4(_a, _b, _c, _d) \
+ uint8_t _d, _c, _b, _a
+#define DECL_BITFIELD5(_a, _b, _c, _d, _e) \
+ uint8_t _e, _d, _c, _b, _a
+#define DECL_BITFIELD6(_a, _b, _c, _d, _e, _f) \
+ uint8_t _f, _e, _d, _c, _b, _a
+#define DECL_BITFIELD7(_a, _b, _c, _d, _e, _f, _g) \
+ uint8_t _g, _f, _e, _d, _c, _b, _a
+#define DECL_BITFIELD8(_a, _b, _c, _d, _e, _f, _g, _h) \
+ uint8_t _h, _g, _f, _e, _d, _c, _b, _a
+#else
+#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined
+#endif /* _BIT_FIELDS_LTOH */
+
#if defined(_KERNEL) && !defined(_KMEMUSER) && !defined(offsetof)
/* avoid any possibility of clashing with <stddef.h> version */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/u8_textprep.h b/sys/cddl/contrib/opensolaris/uts/common/sys/u8_textprep.h
new file mode 100644
index 0000000..d60721c
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/u8_textprep.h
@@ -0,0 +1,91 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: 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 _SYS_U8_TEXTPREP_H
+#define _SYS_U8_TEXTPREP_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/isa_defs.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * UTF-8 text preparation functions and their macros.
+ *
+ * Among the macros defined, U8_CANON_DECOMP, U8_COMPAT_DECOMP, and
+ * U8_CANON_COMP are not public interfaces and must not be used directly
+ * at the flag input argument.
+ */
+#define U8_STRCMP_CS (0x00000001)
+#define U8_STRCMP_CI_UPPER (0x00000002)
+#define U8_STRCMP_CI_LOWER (0x00000004)
+
+#define U8_CANON_DECOMP (0x00000010)
+#define U8_COMPAT_DECOMP (0x00000020)
+#define U8_CANON_COMP (0x00000040)
+
+#define U8_STRCMP_NFD (U8_CANON_DECOMP)
+#define U8_STRCMP_NFC (U8_CANON_DECOMP | U8_CANON_COMP)
+#define U8_STRCMP_NFKD (U8_COMPAT_DECOMP)
+#define U8_STRCMP_NFKC (U8_COMPAT_DECOMP | U8_CANON_COMP)
+
+#define U8_TEXTPREP_TOUPPER (U8_STRCMP_CI_UPPER)
+#define U8_TEXTPREP_TOLOWER (U8_STRCMP_CI_LOWER)
+
+#define U8_TEXTPREP_NFD (U8_STRCMP_NFD)
+#define U8_TEXTPREP_NFC (U8_STRCMP_NFC)
+#define U8_TEXTPREP_NFKD (U8_STRCMP_NFKD)
+#define U8_TEXTPREP_NFKC (U8_STRCMP_NFKC)
+
+#define U8_TEXTPREP_IGNORE_NULL (0x00010000)
+#define U8_TEXTPREP_IGNORE_INVALID (0x00020000)
+#define U8_TEXTPREP_NOWAIT (0x00040000)
+
+#define U8_UNICODE_320 (0)
+#define U8_UNICODE_500 (1)
+#define U8_UNICODE_LATEST (U8_UNICODE_500)
+
+#define U8_VALIDATE_ENTIRE (0x00100000)
+#define U8_VALIDATE_CHECK_ADDITIONAL (0x00200000)
+#define U8_VALIDATE_UCS2_RANGE (0x00400000)
+
+#define U8_ILLEGAL_CHAR (-1)
+#define U8_OUT_OF_RANGE_CHAR (-2)
+
+extern int u8_validate(char *, size_t, char **, int, int *);
+extern int u8_strcmp(const char *, const char *, size_t, int, size_t, int *);
+extern size_t u8_textprep_str(char *, size_t *, char *, size_t *, int, size_t,
+ int *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_U8_TEXTPREP_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/u8_textprep_data.h b/sys/cddl/contrib/opensolaris/uts/common/sys/u8_textprep_data.h
new file mode 100644
index 0000000..de68660
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/u8_textprep_data.h
@@ -0,0 +1,35376 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: 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 AND PERMISSION NOTICE
+ *
+ * Copyright (c) 1991-2006 Unicode, Inc. All rights reserved. Distributed under
+ * the Terms of Use in http://www.unicode.org/copyright.html.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of the Unicode data files and any associated documentation (the
+ * "Data Files") or Unicode software and any associated documentation (the
+ * "Software") to deal in the Data Files or Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, and/or sell copies of the Data Files or Software, and
+ * to permit persons to whom the Data Files or Software are furnished to do so,
+ * provided that (a) the above copyright notice(s) and this permission notice
+ * appear with all copies of the Data Files or Software, (b) both the above
+ * copyright notice(s) and this permission notice appear in associated
+ * documentation, and (c) there is clear notice in each modified Data File or
+ * in the Software as well as in the documentation associated with the Data
+ * File(s) or Software that the data or software has been modified.
+ *
+ * THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
+ * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+ * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL 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 THE DATA FILES OR SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall not
+ * be used in advertising or otherwise to promote the sale, use or other
+ * dealings in these Data Files or Software without prior written authorization
+ * of the copyright holder.
+ *
+ * Unicode and the Unicode logo are trademarks of Unicode, Inc., and may be
+ * registered in some jurisdictions. All other trademarks and registered
+ * trademarks mentioned herein are the property of their respective owners.
+ */
+/*
+ * This file has been modified by Sun Microsystems, Inc.
+ */
+
+#ifndef _SYS_U8_TEXTPREP_DATA_H
+#define _SYS_U8_TEXTPREP_DATA_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * To get to the combining class data, composition mappings, decomposition
+ * mappings, and case conversion mappings of Unicode, the data structures
+ * formulated and their meanings are like the following:
+ *
+ * Each UTF-8 character is seen as a 4-byte entity so that U+0061 (or 0x61 in
+ * UTF-8) would be seen as 0x00 0x00 0x00 0x61. Similarly, U+1D15E would be
+ * 0xF0 0x9D 0x85 0x9E in UTF-8.
+ *
+ * The first byte (MSB) value is an index to the b1_tbl, such as
+ * u8_common_b1_tbl and u8_composition_b1_tbl tables. A b1_tbl has
+ * indices to b2_tbl tables that have indices to b3_tbl. Each b3_tbl has
+ * either indices to b4_tbl or indices to b4_tbl and base values for
+ * displacement calculations later by using the u8_displacement_t type at
+ * below. Each b4_tbl table then has indices to the final tables.
+ *
+ * As an example, if we have a character with code value of U+1D15E which is
+ * 0xF0 0x9D 0x85 0x9E in UTF-8, the target decomposition character bytes
+ * that will be mapped by the mapping procedure would be the ones between
+ * the start_index and the end_index computed as like the following:
+ *
+ * b2_tbl_id = u8_common_b1_tbl[0][0xF0];
+ * b3_tbl_id = u8_decomp_b2_tbl[0][b2_tbl_id][0x9D];
+ * b4_tbl_id = u8_decomp_b3_tbl[0][b3_tbl_id][0x85].tbl_id;
+ * b4_base = u8_decomp_b3_tbl[0][b3_tbl_id][0x85].base;
+ * if (b4_tbl_id >= 0x8000) {
+ * b4_tbl_id -= 0x8000;
+ * start_index = u8_decomp_b4_16bit_tbl[0][b4_tbl_id][0x9E];
+ * end_index = u8_decomp_b4_16bit_tbl[0][b4_tbl_id][0x9E + 1];
+ * } else {
+ * start_index = u8_decomp_b4_tbl[0][b4_tbl_id][0x9E];
+ * end_index = u8_decomp_b4_tbl[0][b4_tbl_id][0x9E + 1];
+ * }
+ *
+ * The start_index and the end_index can be used to retrieve the bytes
+ * possibly of multiple UTF-8 characters from the final tables.
+ *
+ * The "[0]" at the above indicates this is for Unicode Version 3.2.0 data
+ * as of today. Consequently, the "[1]" indicates another Unicode version
+ * data and it is Unicode 5.0.0 as of today.
+ *
+ * The mapping procedures and the data structures are more or less similar or
+ * alike among different mappings. You might want to read the u8_textprep.c
+ * for specific details.
+ *
+ * The tool programs created and used to generate the tables in this file are
+ * saved at PSARC/2007/149/materials/ as tools.tar.gz file.
+ */
+
+/* The following is a component type for the b4_tbl vectors. */
+typedef struct {
+ uint16_t tbl_id;
+ uint16_t base;
+} u8_displacement_t;
+
+/*
+ * The U8_TBL_ELEMENT_NOT_DEF macro indicates a byte that is not defined or
+ * used. The U8_TBL_ELEMENT_FILLER indicates the end of a UTF-8 character at
+ * the final tables.
+ */
+#define U8_TBL_ELEMENT_NOT_DEF (0xff)
+#define N_ U8_TBL_ELEMENT_NOT_DEF
+
+#define U8_TBL_ELEMENT_FILLER (0xf7)
+#define FIL_ U8_TBL_ELEMENT_FILLER
+
+/*
+ * The common b1_tbl for combining class, decompositions, tolower, and
+ * toupper case conversion mappings.
+ */
+static const uchar_t u8_common_b1_tbl[2][256] = {
+ {
+ 0, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ 1, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ {
+ 0, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ 1, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+};
+
+static const uchar_t u8_combining_class_b2_tbl[2][2][256] = {
+ {
+ {
+ 0, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ 1, 2, 3, 4, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, 5,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ {
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, 6, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+
+ },
+ {
+ {
+ 0, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ 1, 2, 3, 4, N_, N_, N_, N_,
+ N_, N_, 5, N_, N_, N_, N_, 6,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ {
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ 7, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, 8, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+
+ },
+
+};
+
+static const uchar_t u8_combining_class_b3_tbl[2][9][256] = {
+ {
+ { /* Third byte table 0. */
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, 0, 1, N_, N_,
+ N_, N_, 2, N_, N_, N_, 3, 4,
+ N_, 5, N_, 6, 7, 8, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ { /* Third byte table 1. */
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, N_, 19,
+ N_, 20, N_, 21, N_, 22, N_, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ { /* Third byte table 2. */
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ 32, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, 33, N_, N_, 34,
+ N_, N_, 35, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ { /* Third byte table 3. */
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, 36, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ { /* Third byte table 4. */
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ 37, N_, 38, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ { /* Third byte table 5. */
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, 39, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ 40, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ { /* Third byte table 6. */
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, 41, 42, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ { /* Third byte table 7. */
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ { /* Third byte table 8. */
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ },
+ {
+ { /* Third byte table 0. */
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, 0, 1, N_, N_,
+ N_, N_, 2, N_, N_, N_, 3, 4,
+ 5, 6, N_, 7, 8, 9, N_, 10,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ { /* Third byte table 1. */
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, N_, 21,
+ N_, 22, 23, 24, N_, 25, N_, 26,
+ 27, 28, 29, 30, 31, 32, 33, 34,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ { /* Third byte table 2. */
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ 35, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, 36, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, 37, N_, N_, 38,
+ N_, N_, 39, N_, 40, N_, N_, N_,
+ 41, N_, N_, N_, 42, 43, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, 44,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ { /* Third byte table 3. */
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, 45, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ { /* Third byte table 4. */
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ 46, N_, 47, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ { /* Third byte table 5. */
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ 48, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ { /* Third byte table 6. */
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, 49, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ 50, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ { /* Third byte table 7. */
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ 51, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ { /* Third byte table 8. */
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, 52, 53, N_,
+ N_, 54, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ },
+};
+
+/*
+ * Unlike other b4_tbl, the b4_tbl for combining class data has
+ * the combining class values not indices to the final tables.
+ */
+static const uchar_t u8_combining_class_b4_tbl[2][55][256] = {
+ {
+ { /* Fourth byte table 0. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 230, 230, 230, 230, 230, 230, 230, 230,
+ 230, 230, 230, 230, 230, 230, 230, 230,
+ 230, 230, 230, 230, 230, 232, 220, 220,
+ 220, 220, 232, 216, 220, 220, 220, 220,
+ 220, 202, 202, 220, 220, 220, 220, 202,
+ 202, 220, 220, 220, 220, 220, 220, 220,
+ 220, 220, 220, 220, 1, 1, 1, 1,
+ 1, 220, 220, 220, 220, 230, 230, 230,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 1. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 230, 230, 230, 230, 230, 240, 230, 220,
+ 220, 220, 230, 230, 230, 220, 220, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 234, 234, 233, 230, 230, 230, 230, 230,
+ 230, 230, 230, 230, 230, 230, 230, 230,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 2. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 230, 230, 230, 230, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 3. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 220, 230, 230, 230, 230, 220, 230,
+ 230, 230, 222, 220, 230, 230, 230, 230,
+ 230, 230, 0, 220, 220, 220, 220, 220,
+ 230, 230, 220, 230, 230, 222, 228, 230,
+ 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 0, 20, 21, 22, 0, 23,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 4. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 24, 25, 0, 230, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 5. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 27, 28, 29, 30, 31,
+ 32, 33, 34, 230, 230, 220, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 35, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 6. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 230, 230,
+ 230, 230, 230, 230, 230, 0, 0, 230,
+ 230, 230, 230, 220, 230, 0, 0, 230,
+ 230, 0, 220, 230, 230, 220, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 7. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 36, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 230, 220, 230, 230, 220, 230, 230, 220,
+ 220, 220, 230, 220, 220, 230, 220, 230,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 8. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 230, 230, 220, 230, 220, 230, 220, 230,
+ 220, 230, 230, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 9. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 10. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 9, 0, 0,
+ 0, 230, 220, 230, 230, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 11. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 12. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 9, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 13. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 14. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 9, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 15. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 16. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 9, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 17. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 18. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 9, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 19. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 9, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 20. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 9, 0, 0,
+ 0, 0, 0, 0, 0, 84, 91, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 21. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 9, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 22. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 9, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 23. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 24. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 103, 103, 9, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 25. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 107, 107, 107, 107, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 26. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 118, 118, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 27. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 122, 122, 122, 122, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 28. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 220, 220, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 220, 0, 220,
+ 0, 216, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 29. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 129, 130, 0, 132, 0, 0, 0,
+ 0, 0, 130, 130, 130, 130, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 30. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 130, 0, 230, 230, 9, 0, 230, 230,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 31. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 220, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 32. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 7,
+ 0, 9, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 33. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 9, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 9, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 34. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 35. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 228, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 36. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 230, 230, 1, 1, 230, 230, 230, 230,
+ 1, 1, 1, 230, 230, 0, 0, 0,
+ 0, 230, 0, 0, 0, 1, 1, 230,
+ 220, 230, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 37. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 218, 228, 232, 222, 224, 224,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 38. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 8, 8, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 39. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 26, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 40. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 230, 230, 230, 230, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 41. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 216, 216, 1,
+ 1, 1, 0, 0, 0, 226, 216, 216,
+ 216, 216, 216, 0, 0, 0, 0, 0,
+ 0, 0, 0, 220, 220, 220, 220, 220,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 42. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 220, 220, 220, 0, 0, 230, 230, 230,
+ 230, 230, 220, 220, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 230, 230, 230, 230, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 43. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 44. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 45. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 46. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 47. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 48. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 49. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 50. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 51. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 52. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 53. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 54. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ },
+ {
+ { /* Fourth byte table 0. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 230, 230, 230, 230, 230, 230, 230, 230,
+ 230, 230, 230, 230, 230, 230, 230, 230,
+ 230, 230, 230, 230, 230, 232, 220, 220,
+ 220, 220, 232, 216, 220, 220, 220, 220,
+ 220, 202, 202, 220, 220, 220, 220, 202,
+ 202, 220, 220, 220, 220, 220, 220, 220,
+ 220, 220, 220, 220, 1, 1, 1, 1,
+ 1, 220, 220, 220, 220, 230, 230, 230,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 1. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 230, 230, 230, 230, 230, 240, 230, 220,
+ 220, 220, 230, 230, 230, 220, 220, 0,
+ 230, 230, 230, 220, 220, 220, 220, 230,
+ 232, 220, 220, 230, 233, 234, 234, 233,
+ 234, 234, 233, 230, 230, 230, 230, 230,
+ 230, 230, 230, 230, 230, 230, 230, 230,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 2. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 230, 230, 230, 230, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 3. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 220, 230, 230, 230, 230, 220, 230,
+ 230, 230, 222, 220, 230, 230, 230, 230,
+ 230, 230, 220, 220, 220, 220, 220, 220,
+ 230, 230, 220, 230, 230, 222, 228, 230,
+ 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 19, 20, 21, 22, 0, 23,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 4. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 24, 25, 0, 230, 220, 0, 18,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 5. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 230, 230, 230, 230, 230, 230, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 6. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 27, 28, 29, 30, 31,
+ 32, 33, 34, 230, 230, 220, 220, 230,
+ 230, 230, 230, 230, 220, 230, 230, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 35, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 7. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 230, 230,
+ 230, 230, 230, 230, 230, 0, 0, 230,
+ 230, 230, 230, 220, 230, 0, 0, 230,
+ 230, 0, 220, 230, 230, 220, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 8. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 36, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 230, 220, 230, 230, 220, 230, 230, 220,
+ 220, 220, 230, 220, 220, 230, 220, 230,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 9. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 230, 230, 220, 230, 220, 230, 220, 230,
+ 220, 230, 230, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 10. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 230, 230, 230, 230, 230,
+ 230, 230, 220, 230, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 11. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 12. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 9, 0, 0,
+ 0, 230, 220, 230, 230, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 13. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 14. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 9, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 15. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 16. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 9, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 17. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 18. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 9, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 19. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 20. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 9, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 21. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 9, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 22. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 9, 0, 0,
+ 0, 0, 0, 0, 0, 84, 91, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 23. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 24. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 9, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 25. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 9, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 26. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 27. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 103, 103, 9, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 28. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 107, 107, 107, 107, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 29. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 118, 118, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 30. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 122, 122, 122, 122, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 31. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 220, 220, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 220, 0, 220,
+ 0, 216, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 32. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 129, 130, 0, 132, 0, 0, 0,
+ 0, 0, 130, 130, 130, 130, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 33. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 130, 0, 230, 230, 9, 0, 230, 230,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 34. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 220, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 35. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 7,
+ 0, 9, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 36. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 230,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 37. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 9, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 9, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 38. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 9, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 230, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 39. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 228, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 40. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 222, 230, 220, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 41. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 230,
+ 220, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 42. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 43. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 9, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 230, 220, 230, 230, 230,
+ 230, 230, 230, 230, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 44. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 230, 230, 220, 230, 230, 230, 230, 230,
+ 230, 230, 220, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 230, 220,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 45. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 230, 230, 1, 1, 230, 230, 230, 230,
+ 1, 1, 1, 230, 230, 0, 0, 0,
+ 0, 230, 0, 0, 0, 1, 1, 230,
+ 220, 230, 1, 1, 220, 220, 220, 220,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 46. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 218, 228, 232, 222, 224, 224,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 47. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 8, 8, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 48. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 9, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 49. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 26, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 50. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 230, 230, 230, 230, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 51. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 220, 0, 230,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 230, 1, 220, 0, 0, 0, 0, 9,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 52. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 216, 216, 1,
+ 1, 1, 0, 0, 0, 226, 216, 216,
+ 216, 216, 216, 0, 0, 0, 0, 0,
+ 0, 0, 0, 220, 220, 220, 220, 220,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 53. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 220, 220, 220, 0, 0, 230, 230, 230,
+ 230, 230, 220, 220, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 230, 230, 230, 230, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ { /* Fourth byte table 54. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 230, 230, 230, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ },
+};
+
+static const uchar_t u8_composition_b1_tbl[2][256] = {
+ {
+ 0, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ {
+ 0, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+};
+
+static const uchar_t u8_composition_b2_tbl[2][1][256] = {
+ {
+ {
+ 0, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ 1, 2, 3, 4, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+
+ },
+ {
+ {
+ 0, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ 1, 2, 3, 4, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+
+ },
+
+};
+
+static const u8_displacement_t u8_composition_b3_tbl[2][5][256] = {
+ {
+ { /* Third byte table 0. */
+ { 0x8000, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 0, 2470 },
+ { 0x8001, 2491 }, { 1, 2871 }, { 2, 2959 },
+ { 3, 3061 }, { 4, 3212 }, { 5, 3226 },
+ { N_, 0 }, { 6, 3270 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 0x8002, 3277 },
+ { 7, 3774 }, { 8, 3949 }, { 9, 4198 },
+ { N_, 0 }, { 10, 4265 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 11, 4293 }, { 12, 4312 }, { N_, 0 },
+ { 13, 4326 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ { /* Third byte table 1. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 14, 4347 },
+ { N_, 0 }, { N_, 0 }, { 15, 4374 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 16, 4391 },
+ { 17, 4416 }, { 18, 4425 }, { N_, 0 },
+ { 19, 4451 }, { 20, 4460 }, { 21, 4469 },
+ { N_, 0 }, { 22, 4503 }, { N_, 0 },
+ { 23, 4529 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ { /* Third byte table 2. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 24, 4563 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { 25, 4572 }, { 26, 4588 },
+ { 27, 4620 }, { 28, 4666 }, { 0x8003, 4682 },
+ { 0x8004, 5254 }, { 29, 5616 }, { 30, 5646 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ { /* Third byte table 3. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 31, 5684 },
+ { 32, 5708 }, { 33, 5732 }, { 34, 5780 },
+ { 35, 5900 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ { /* Third byte table 4. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 36, 6012 }, { 37, 6241 }, { 38, 6358 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ },
+ {
+ { /* Third byte table 0. */
+ { 0x8000, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 0, 2470 },
+ { 0x8001, 2491 }, { 1, 2871 }, { 2, 2959 },
+ { 3, 3061 }, { 4, 3212 }, { 5, 3226 },
+ { N_, 0 }, { 6, 3270 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 0x8002, 3277 },
+ { 7, 3774 }, { 8, 3949 }, { 9, 4198 },
+ { N_, 0 }, { 10, 4265 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 11, 4293 }, { 12, 4312 }, { N_, 0 },
+ { 13, 4326 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ { /* Third byte table 1. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 14, 4347 },
+ { N_, 0 }, { N_, 0 }, { 15, 4374 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 16, 4391 },
+ { 17, 4416 }, { 18, 4425 }, { N_, 0 },
+ { 19, 4451 }, { 20, 4460 }, { 21, 4469 },
+ { N_, 0 }, { 22, 4503 }, { N_, 0 },
+ { 23, 4529 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ { /* Third byte table 2. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 24, 4563 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { 25, 4572 }, { 26, 4662 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { 27, 4671 }, { 28, 4687 },
+ { 29, 4719 }, { 30, 4765 }, { 0x8003, 4781 },
+ { 0x8004, 5353 }, { 31, 5715 }, { 32, 5745 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ { /* Third byte table 3. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 33, 5783 },
+ { 34, 5807 }, { 35, 5831 }, { 36, 5879 },
+ { 37, 5999 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ { /* Third byte table 4. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 38, 6111 }, { 39, 6340 }, { 40, 6457 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ },
+};
+
+static const uchar_t u8_composition_b4_tbl[2][41][257] = {
+ {
+ { /* Fourth byte table 0. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21,
+ },
+ { /* Fourth byte table 1. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 29, 58, 58, 58, 58,
+ 58, 58, 58, 58, 58, 58, 58, 58,
+ 58, 58, 58, 73, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88,
+ },
+ { /* Fourth byte table 2. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 15, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 38, 46, 46, 46, 46,
+ 46, 54, 62, 62, 62, 62, 62, 62,
+ 62, 70, 78, 86, 94, 94, 94, 94,
+ 94, 94, 94, 94, 94, 94, 94, 94,
+ 94, 94, 94, 94, 94, 94, 94, 94,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102,
+ },
+ { /* Fourth byte table 3. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 36, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 108, 144, 144, 144, 144, 144, 144, 144,
+ 151, 151, 151, 151, 151, 151, 151, 151,
+ 151, 151, 151, 151, 151, 151, 151, 151,
+ 151, 151, 151, 151, 151, 151, 151, 151,
+ 151, 151, 151, 151, 151, 151, 151, 151,
+ 151, 151, 151, 151, 151, 151, 151, 151,
+ 151, 151, 151, 151, 151, 151, 151, 151,
+ 151, 151, 151, 151, 151, 151, 151, 151,
+ 151, 151, 151, 151, 151, 151, 151, 151,
+ 151, 151, 151, 151, 151, 151, 151, 151,
+ 151,
+ },
+ { /* Fourth byte table 4. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14,
+ },
+ { /* Fourth byte table 5. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 7,
+ 14, 22, 30, 30, 30, 30, 30, 37,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44,
+ },
+ { /* Fourth byte table 6. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7,
+ },
+ { /* Fourth byte table 7. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 15, 15, 15, 15, 70, 70,
+ 70, 70, 112, 133, 154, 154, 154, 162,
+ 162, 162, 162, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175,
+ },
+ { /* Fourth byte table 8. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 20, 20, 20, 27, 27, 46, 59,
+ 66, 91, 91, 98, 98, 98, 98, 105,
+ 105, 105, 105, 105, 130, 130, 130, 130,
+ 137, 137, 137, 137, 144, 144, 151, 151,
+ 151, 164, 164, 164, 171, 171, 190, 203,
+ 210, 235, 235, 242, 242, 242, 242, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249,
+ 249,
+ },
+ { /* Fourth byte table 9. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 25, 25, 25, 25,
+ 32, 32, 32, 32, 39, 39, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 53,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 53, 53, 53, 53, 53, 60, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67,
+ },
+ { /* Fourth byte table 10. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 7, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 21, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28,
+ },
+ { /* Fourth byte table 11. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19,
+ },
+ { /* Fourth byte table 12. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 7, 7, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14,
+ },
+ { /* Fourth byte table 13. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 14, 14, 14, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21,
+ },
+ { /* Fourth byte table 14. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 9, 9, 9, 9, 9, 9, 9,
+ 9, 18, 18, 18, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27,
+ },
+ { /* Fourth byte table 15. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17,
+ },
+ { /* Fourth byte table 16. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25,
+ },
+ { /* Fourth byte table 17. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9,
+ },
+ { /* Fourth byte table 18. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 17,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26,
+ },
+ { /* Fourth byte table 19. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9,
+ },
+ { /* Fourth byte table 20. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9,
+ },
+ { /* Fourth byte table 21. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 25,
+ 25, 25, 25, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34,
+ },
+ { /* Fourth byte table 22. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 17,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26,
+ },
+ { /* Fourth byte table 23. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 25, 25, 25, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34,
+ },
+ { /* Fourth byte table 24. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9,
+ },
+ { /* Fourth byte table 25. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 8,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16,
+ },
+ { /* Fourth byte table 26. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 8, 16, 16, 16, 16,
+ 16, 16, 16, 24, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32,
+ },
+ { /* Fourth byte table 27. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 15, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 38, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46,
+ },
+ { /* Fourth byte table 28. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16,
+ },
+ { /* Fourth byte table 29. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30,
+ },
+ { /* Fourth byte table 30. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 16,
+ 16, 16, 16, 16, 16, 16, 16, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38,
+ },
+ { /* Fourth byte table 31. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 8, 8, 16, 16, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24,
+ },
+ { /* Fourth byte table 32. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 8, 8, 16, 16, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24,
+ },
+ { /* Fourth byte table 33. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 8, 8, 8, 8,
+ 8, 16, 16, 16, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 32, 32, 40, 40,
+ 40, 40, 40, 40, 40, 40, 40, 40,
+ 40, 40, 40, 40, 40, 40, 40, 40,
+ 40, 40, 40, 40, 40, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48,
+ },
+ { /* Fourth byte table 34. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 8, 8, 16, 16,
+ 16, 24, 24, 24, 24, 24, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 40, 40, 40, 48, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 64, 72, 72, 72, 80,
+ 88, 88, 88, 96, 104, 112, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120,
+ },
+ { /* Fourth byte table 35. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 8, 16, 16, 16, 24,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 40, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 56, 56, 56, 56, 56,
+ 56, 64, 72, 72, 80, 80, 80, 80,
+ 80, 80, 80, 88, 96, 104, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112,
+ },
+ { /* Fourth byte table 36. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 9,
+ 9, 9, 9, 9, 18, 18, 27, 27,
+ 36, 36, 45, 45, 54, 54, 63, 63,
+ 72, 72, 81, 81, 90, 90, 99, 99,
+ 108, 108, 117, 117, 117, 126, 126, 135,
+ 135, 144, 144, 144, 144, 144, 144, 144,
+ 161, 161, 161, 178, 178, 178, 195, 195,
+ 195, 212, 212, 212, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229,
+ 229,
+ },
+ { /* Fourth byte table 37. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 18,
+ 18, 18, 18, 18, 27, 27, 36, 36,
+ 45, 45, 54, 54, 63, 63, 72, 72,
+ 81, 81, 90, 90, 99, 99, 108, 108,
+ 117, 117, 117, 117, 117, 117, 117, 117,
+ 117, 117, 117, 117, 117, 117, 117, 117,
+ 117, 117, 117, 117, 117, 117, 117, 117,
+ 117, 117, 117, 117, 117, 117, 117, 117,
+ 117, 117, 117, 117, 117, 117, 117, 117,
+ 117, 117, 117, 117, 117, 117, 117, 117,
+ 117, 117, 117, 117, 117, 117, 117, 117,
+ 117, 117, 117, 117, 117, 117, 117, 117,
+ 117,
+ },
+ { /* Fourth byte table 38. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 9, 9, 9, 18, 18, 27,
+ 27, 36, 36, 36, 36, 36, 36, 36,
+ 53, 53, 53, 70, 70, 70, 87, 87,
+ 87, 104, 104, 104, 121, 121, 121, 121,
+ 121, 121, 121, 121, 121, 121, 121, 121,
+ 121, 121, 121, 121, 121, 121, 121, 121,
+ 130, 139, 148, 157, 157, 157, 157, 157,
+ 157, 157, 157, 157, 157, 157, 166, 166,
+ 166, 166, 166, 166, 166, 166, 166, 166,
+ 166, 166, 166, 166, 166, 166, 166, 166,
+ 166, 166, 166, 166, 166, 166, 166, 166,
+ 166, 166, 166, 166, 166, 166, 166, 166,
+ 166, 166, 166, 166, 166, 166, 166, 166,
+ 166, 166, 166, 166, 166, 166, 166, 166,
+ 166, 166, 166, 166, 166, 166, 166, 166,
+ 166, 166, 166, 166, 166, 166, 166, 166,
+ 166,
+ },
+ { /* Fourth byte table 39. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ { /* Fourth byte table 40. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ },
+ {
+ { /* Fourth byte table 0. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21,
+ },
+ { /* Fourth byte table 1. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 29, 58, 58, 58, 58,
+ 58, 58, 58, 58, 58, 58, 58, 58,
+ 58, 58, 58, 73, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88,
+ },
+ { /* Fourth byte table 2. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 15, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 38, 46, 46, 46, 46,
+ 46, 54, 62, 62, 62, 62, 62, 62,
+ 62, 70, 78, 86, 94, 94, 94, 94,
+ 94, 94, 94, 94, 94, 94, 94, 94,
+ 94, 94, 94, 94, 94, 94, 94, 94,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102,
+ },
+ { /* Fourth byte table 3. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 36, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 108, 144, 144, 144, 144, 144, 144, 144,
+ 151, 151, 151, 151, 151, 151, 151, 151,
+ 151, 151, 151, 151, 151, 151, 151, 151,
+ 151, 151, 151, 151, 151, 151, 151, 151,
+ 151, 151, 151, 151, 151, 151, 151, 151,
+ 151, 151, 151, 151, 151, 151, 151, 151,
+ 151, 151, 151, 151, 151, 151, 151, 151,
+ 151, 151, 151, 151, 151, 151, 151, 151,
+ 151, 151, 151, 151, 151, 151, 151, 151,
+ 151, 151, 151, 151, 151, 151, 151, 151,
+ 151,
+ },
+ { /* Fourth byte table 4. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14,
+ },
+ { /* Fourth byte table 5. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 7,
+ 14, 22, 30, 30, 30, 30, 30, 37,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44,
+ },
+ { /* Fourth byte table 6. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7,
+ },
+ { /* Fourth byte table 7. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 15, 15, 15, 15, 70, 70,
+ 70, 70, 112, 133, 154, 154, 154, 162,
+ 162, 162, 162, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175,
+ },
+ { /* Fourth byte table 8. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 20, 20, 20, 27, 27, 46, 59,
+ 66, 91, 91, 98, 98, 98, 98, 105,
+ 105, 105, 105, 105, 130, 130, 130, 130,
+ 137, 137, 137, 137, 144, 144, 151, 151,
+ 151, 164, 164, 164, 171, 171, 190, 203,
+ 210, 235, 235, 242, 242, 242, 242, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249,
+ 249,
+ },
+ { /* Fourth byte table 9. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 25, 25, 25, 25,
+ 32, 32, 32, 32, 39, 39, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 53,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 53, 53, 53, 53, 53, 60, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67,
+ },
+ { /* Fourth byte table 10. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 7, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 21, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28,
+ },
+ { /* Fourth byte table 11. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19,
+ },
+ { /* Fourth byte table 12. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 7, 7, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14,
+ },
+ { /* Fourth byte table 13. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 14, 14, 14, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21,
+ },
+ { /* Fourth byte table 14. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 9, 9, 9, 9, 9, 9, 9,
+ 9, 18, 18, 18, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27,
+ },
+ { /* Fourth byte table 15. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17,
+ },
+ { /* Fourth byte table 16. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25,
+ },
+ { /* Fourth byte table 17. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9,
+ },
+ { /* Fourth byte table 18. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 17,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26,
+ },
+ { /* Fourth byte table 19. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9,
+ },
+ { /* Fourth byte table 20. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9,
+ },
+ { /* Fourth byte table 21. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 25,
+ 25, 25, 25, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34,
+ },
+ { /* Fourth byte table 22. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 17,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26,
+ },
+ { /* Fourth byte table 23. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 25, 25, 25, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34,
+ 34,
+ },
+ { /* Fourth byte table 24. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9,
+ },
+ { /* Fourth byte table 25. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 9, 9,
+ 18, 18, 27, 27, 36, 36, 45, 45,
+ 45, 45, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 63, 63, 72, 72, 81,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90,
+ },
+ { /* Fourth byte table 26. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9,
+ },
+ { /* Fourth byte table 27. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 8,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16,
+ },
+ { /* Fourth byte table 28. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 8, 16, 16, 16, 16,
+ 16, 16, 16, 24, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32,
+ },
+ { /* Fourth byte table 29. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 15, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 38, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46,
+ },
+ { /* Fourth byte table 30. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16,
+ },
+ { /* Fourth byte table 31. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30,
+ },
+ { /* Fourth byte table 32. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 16,
+ 16, 16, 16, 16, 16, 16, 16, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38,
+ },
+ { /* Fourth byte table 33. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 8, 8, 16, 16, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24,
+ },
+ { /* Fourth byte table 34. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 8, 8, 16, 16, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24,
+ },
+ { /* Fourth byte table 35. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 8, 8, 8, 8,
+ 8, 16, 16, 16, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 32, 32, 40, 40,
+ 40, 40, 40, 40, 40, 40, 40, 40,
+ 40, 40, 40, 40, 40, 40, 40, 40,
+ 40, 40, 40, 40, 40, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48,
+ },
+ { /* Fourth byte table 36. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 8, 8, 16, 16,
+ 16, 24, 24, 24, 24, 24, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 40, 40, 40, 48, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 64, 72, 72, 72, 80,
+ 88, 88, 88, 96, 104, 112, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120,
+ },
+ { /* Fourth byte table 37. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 8, 16, 16, 16, 24,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 40, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 56, 56, 56, 56, 56,
+ 56, 64, 72, 72, 80, 80, 80, 80,
+ 80, 80, 80, 88, 96, 104, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112,
+ },
+ { /* Fourth byte table 38. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 9,
+ 9, 9, 9, 9, 18, 18, 27, 27,
+ 36, 36, 45, 45, 54, 54, 63, 63,
+ 72, 72, 81, 81, 90, 90, 99, 99,
+ 108, 108, 117, 117, 117, 126, 126, 135,
+ 135, 144, 144, 144, 144, 144, 144, 144,
+ 161, 161, 161, 178, 178, 178, 195, 195,
+ 195, 212, 212, 212, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229,
+ 229,
+ },
+ { /* Fourth byte table 39. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 18,
+ 18, 18, 18, 18, 27, 27, 36, 36,
+ 45, 45, 54, 54, 63, 63, 72, 72,
+ 81, 81, 90, 90, 99, 99, 108, 108,
+ 117, 117, 117, 117, 117, 117, 117, 117,
+ 117, 117, 117, 117, 117, 117, 117, 117,
+ 117, 117, 117, 117, 117, 117, 117, 117,
+ 117, 117, 117, 117, 117, 117, 117, 117,
+ 117, 117, 117, 117, 117, 117, 117, 117,
+ 117, 117, 117, 117, 117, 117, 117, 117,
+ 117, 117, 117, 117, 117, 117, 117, 117,
+ 117, 117, 117, 117, 117, 117, 117, 117,
+ 117,
+ },
+ { /* Fourth byte table 40. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 9, 9, 9, 18, 18, 27,
+ 27, 36, 36, 36, 36, 36, 36, 36,
+ 53, 53, 53, 70, 70, 70, 87, 87,
+ 87, 104, 104, 104, 121, 121, 121, 121,
+ 121, 121, 121, 121, 121, 121, 121, 121,
+ 121, 121, 121, 121, 121, 121, 121, 121,
+ 130, 139, 148, 157, 157, 157, 157, 157,
+ 157, 157, 157, 157, 157, 157, 166, 166,
+ 166, 166, 166, 166, 166, 166, 166, 166,
+ 166, 166, 166, 166, 166, 166, 166, 166,
+ 166, 166, 166, 166, 166, 166, 166, 166,
+ 166, 166, 166, 166, 166, 166, 166, 166,
+ 166, 166, 166, 166, 166, 166, 166, 166,
+ 166, 166, 166, 166, 166, 166, 166, 166,
+ 166, 166, 166, 166, 166, 166, 166, 166,
+ 166, 166, 166, 166, 166, 166, 166, 166,
+ 166,
+ },
+ },
+};
+
+static const uint16_t u8_composition_b4_16bit_tbl[2][5][257] = {
+ {
+ { /* Fourth byte 16-bit table 0. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8, 16, 24,
+ 24, 24, 124, 146, 177, 219, 327, 335,
+ 379, 427, 521, 528, 562, 602, 624, 683,
+ 782, 797, 797, 849, 894, 941, 1061, 1076,
+ 1118, 1133, 1193, 1233, 1233, 1233, 1233, 1233,
+ 1233, 1233, 1333, 1355, 1386, 1428, 1536, 1544,
+ 1588, 1643, 1731, 1744, 1778, 1818, 1840, 1899,
+ 1998, 2013, 2013, 2065, 2110, 2164, 2284, 2299,
+ 2348, 2363, 2430, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470,
+ },
+ { /* Fourth byte 16-bit table 1. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 29, 29, 36, 43, 56,
+ 64, 64, 64, 93, 93, 93, 93, 93,
+ 101, 101, 101, 101, 101, 130, 151, 158,
+ 158, 165, 165, 165, 165, 190, 190, 190,
+ 190, 190, 190, 219, 219, 226, 233, 246,
+ 254, 254, 254, 283, 283, 283, 283, 283,
+ 291, 291, 291, 291, 291, 320, 341, 348,
+ 348, 355, 355, 355, 355, 380, 380, 380,
+ 380, 380, 380, 380, 380, 380, 380, 380,
+ 380, 380, 380, 380, 380, 380, 380, 380,
+ 380, 380, 380, 380, 380, 380, 380, 380,
+ 380, 380, 380, 380, 380, 380, 380, 380,
+ 380, 380, 380, 380, 380, 380, 380, 380,
+ 380, 380, 380, 380, 380, 380, 380, 380,
+ 380, 380, 380, 380, 380, 380, 380, 380,
+ 380, 380, 380, 380, 380, 380, 380, 380,
+ 380,
+ },
+ { /* Fourth byte 16-bit table 2. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 49, 49, 49, 49, 77, 77,
+ 112, 112, 160, 160, 160, 160, 160, 160,
+ 188, 188, 196, 196, 196, 196, 237, 237,
+ 237, 237, 272, 272, 272, 280, 280, 288,
+ 288, 288, 344, 344, 344, 344, 372, 372,
+ 414, 414, 469, 469, 469, 469, 469, 469,
+ 497, 497, 497, 497, 497, 497, 497, 497,
+ 497, 497, 497, 497, 497, 497, 497, 497,
+ 497, 497, 497, 497, 497, 497, 497, 497,
+ 497, 497, 497, 497, 497, 497, 497, 497,
+ 497, 497, 497, 497, 497, 497, 497, 497,
+ 497, 497, 497, 497, 497, 497, 497, 497,
+ 497, 497, 497, 497, 497, 497, 497, 497,
+ 497, 497, 497, 497, 497, 497, 497, 497,
+ 497,
+ },
+ { /* Fourth byte 16-bit table 3. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 29, 58, 66, 74, 82, 90, 98,
+ 106, 135, 164, 172, 180, 188, 196, 204,
+ 212, 227, 242, 242, 242, 242, 242, 242,
+ 242, 257, 272, 272, 272, 272, 272, 272,
+ 272, 301, 330, 338, 346, 354, 362, 370,
+ 378, 407, 436, 444, 452, 460, 468, 476,
+ 484, 506, 528, 528, 528, 528, 528, 528,
+ 528, 550, 572, 572, 572, 572, 572, 572,
+ 572, 572, 572, 572, 572, 572, 572, 572,
+ 572, 572, 572, 572, 572, 572, 572, 572,
+ 572, 572, 572, 572, 572, 572, 572, 572,
+ 572, 572, 572, 572, 572, 572, 572, 572,
+ 572, 572, 572, 572, 572, 572, 572, 572,
+ 572, 572, 572, 572, 572, 572, 572, 572,
+ 572, 572, 572, 572, 572, 572, 572, 572,
+ 572, 572, 572, 572, 572, 572, 572, 572,
+ 572,
+ },
+ { /* Fourth byte 16-bit table 4. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 15, 30, 30, 30, 30, 30, 30,
+ 30, 45, 60, 60, 60, 60, 60, 60,
+ 60, 82, 104, 104, 104, 104, 104, 104,
+ 104, 104, 126, 126, 126, 126, 126, 126,
+ 126, 155, 184, 192, 200, 208, 216, 224,
+ 232, 261, 290, 298, 306, 314, 322, 330,
+ 338, 346, 346, 346, 346, 354, 354, 354,
+ 354, 354, 354, 354, 354, 362, 362, 362,
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ 362,
+ },
+ },
+ {
+ { /* Fourth byte 16-bit table 0. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 8, 16, 24,
+ 24, 24, 124, 146, 177, 219, 327, 335,
+ 379, 427, 521, 528, 562, 602, 624, 683,
+ 782, 797, 797, 849, 894, 941, 1061, 1076,
+ 1118, 1133, 1193, 1233, 1233, 1233, 1233, 1233,
+ 1233, 1233, 1333, 1355, 1386, 1428, 1536, 1544,
+ 1588, 1643, 1731, 1744, 1778, 1818, 1840, 1899,
+ 1998, 2013, 2013, 2065, 2110, 2164, 2284, 2299,
+ 2348, 2363, 2430, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470, 2470, 2470, 2470, 2470, 2470, 2470, 2470,
+ 2470,
+ },
+ { /* Fourth byte 16-bit table 1. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 29, 29, 36, 43, 56,
+ 64, 64, 64, 93, 93, 93, 93, 93,
+ 101, 101, 101, 101, 101, 130, 151, 158,
+ 158, 165, 165, 165, 165, 190, 190, 190,
+ 190, 190, 190, 219, 219, 226, 233, 246,
+ 254, 254, 254, 283, 283, 283, 283, 283,
+ 291, 291, 291, 291, 291, 320, 341, 348,
+ 348, 355, 355, 355, 355, 380, 380, 380,
+ 380, 380, 380, 380, 380, 380, 380, 380,
+ 380, 380, 380, 380, 380, 380, 380, 380,
+ 380, 380, 380, 380, 380, 380, 380, 380,
+ 380, 380, 380, 380, 380, 380, 380, 380,
+ 380, 380, 380, 380, 380, 380, 380, 380,
+ 380, 380, 380, 380, 380, 380, 380, 380,
+ 380, 380, 380, 380, 380, 380, 380, 380,
+ 380, 380, 380, 380, 380, 380, 380, 380,
+ 380,
+ },
+ { /* Fourth byte 16-bit table 2. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 49, 49, 49, 49, 77, 77,
+ 112, 112, 160, 160, 160, 160, 160, 160,
+ 188, 188, 196, 196, 196, 196, 237, 237,
+ 237, 237, 272, 272, 272, 280, 280, 288,
+ 288, 288, 344, 344, 344, 344, 372, 372,
+ 414, 414, 469, 469, 469, 469, 469, 469,
+ 497, 497, 497, 497, 497, 497, 497, 497,
+ 497, 497, 497, 497, 497, 497, 497, 497,
+ 497, 497, 497, 497, 497, 497, 497, 497,
+ 497, 497, 497, 497, 497, 497, 497, 497,
+ 497, 497, 497, 497, 497, 497, 497, 497,
+ 497, 497, 497, 497, 497, 497, 497, 497,
+ 497, 497, 497, 497, 497, 497, 497, 497,
+ 497, 497, 497, 497, 497, 497, 497, 497,
+ 497,
+ },
+ { /* Fourth byte 16-bit table 3. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 29, 58, 66, 74, 82, 90, 98,
+ 106, 135, 164, 172, 180, 188, 196, 204,
+ 212, 227, 242, 242, 242, 242, 242, 242,
+ 242, 257, 272, 272, 272, 272, 272, 272,
+ 272, 301, 330, 338, 346, 354, 362, 370,
+ 378, 407, 436, 444, 452, 460, 468, 476,
+ 484, 506, 528, 528, 528, 528, 528, 528,
+ 528, 550, 572, 572, 572, 572, 572, 572,
+ 572, 572, 572, 572, 572, 572, 572, 572,
+ 572, 572, 572, 572, 572, 572, 572, 572,
+ 572, 572, 572, 572, 572, 572, 572, 572,
+ 572, 572, 572, 572, 572, 572, 572, 572,
+ 572, 572, 572, 572, 572, 572, 572, 572,
+ 572, 572, 572, 572, 572, 572, 572, 572,
+ 572, 572, 572, 572, 572, 572, 572, 572,
+ 572, 572, 572, 572, 572, 572, 572, 572,
+ 572,
+ },
+ { /* Fourth byte 16-bit table 4. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 15, 30, 30, 30, 30, 30, 30,
+ 30, 45, 60, 60, 60, 60, 60, 60,
+ 60, 82, 104, 104, 104, 104, 104, 104,
+ 104, 104, 126, 126, 126, 126, 126, 126,
+ 126, 155, 184, 192, 200, 208, 216, 224,
+ 232, 261, 290, 298, 306, 314, 322, 330,
+ 338, 346, 346, 346, 346, 354, 354, 354,
+ 354, 354, 354, 354, 354, 362, 362, 362,
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ 362,
+ },
+ },
+};
+
+static const uchar_t u8_composition_final_tbl[2][6623] = {
+ {
+ 0x01, 0xCC, 0xB8, FIL_, 0xE2, 0x89, 0xAE, FIL_,
+ 0x01, 0xCC, 0xB8, FIL_, 0xE2, 0x89, 0xA0, FIL_,
+ 0x01, 0xCC, 0xB8, FIL_, 0xE2, 0x89, 0xAF, FIL_,
+ 0x10, 0xCC, 0x86, FIL_, 0xC4, 0x82, FIL_, 0xCC,
+ 0x87, FIL_, 0xC8, 0xA6, FIL_, 0xCC, 0x8F, FIL_,
+ 0xC8, 0x80, FIL_, 0xCC, 0x82, FIL_, 0xC3, 0x82,
+ FIL_, 0xCC, 0x81, FIL_, 0xC3, 0x81, FIL_, 0xCC,
+ 0x80, FIL_, 0xC3, 0x80, FIL_, 0xCC, 0x83, FIL_,
+ 0xC3, 0x83, FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xBA,
+ 0xA0, FIL_, 0xCC, 0xA5, FIL_, 0xE1, 0xB8, 0x80,
+ FIL_, 0xCC, 0x91, FIL_, 0xC8, 0x82, FIL_, 0xCC,
+ 0x84, FIL_, 0xC4, 0x80, FIL_, 0xCC, 0x88, FIL_,
+ 0xC3, 0x84, FIL_, 0xCC, 0x8A, FIL_, 0xC3, 0x85,
+ FIL_, 0xCC, 0xA8, FIL_, 0xC4, 0x84, FIL_, 0xCC,
+ 0x89, FIL_, 0xE1, 0xBA, 0xA2, FIL_, 0xCC, 0x8C,
+ FIL_, 0xC7, 0x8D, FIL_, 0x03, 0xCC, 0x87, FIL_,
+ 0xE1, 0xB8, 0x82, FIL_, 0xCC, 0xB1, FIL_, 0xE1,
+ 0xB8, 0x86, FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xB8,
+ 0x84, FIL_, 0x05, 0xCC, 0xA7, FIL_, 0xC3, 0x87,
+ FIL_, 0xCC, 0x81, FIL_, 0xC4, 0x86, FIL_, 0xCC,
+ 0x8C, FIL_, 0xC4, 0x8C, FIL_, 0xCC, 0x87, FIL_,
+ 0xC4, 0x8A, FIL_, 0xCC, 0x82, FIL_, 0xC4, 0x88,
+ FIL_, 0x06, 0xCC, 0xB1, FIL_, 0xE1, 0xB8, 0x8E,
+ FIL_, 0xCC, 0xA7, FIL_, 0xE1, 0xB8, 0x90, FIL_,
+ 0xCC, 0xAD, FIL_, 0xE1, 0xB8, 0x92, FIL_, 0xCC,
+ 0x87, FIL_, 0xE1, 0xB8, 0x8A, FIL_, 0xCC, 0x8C,
+ FIL_, 0xC4, 0x8E, FIL_, 0xCC, 0xA3, FIL_, 0xE1,
+ 0xB8, 0x8C, FIL_, 0x11, 0xCC, 0x80, FIL_, 0xC3,
+ 0x88, FIL_, 0xCC, 0x81, FIL_, 0xC3, 0x89, FIL_,
+ 0xCC, 0x82, FIL_, 0xC3, 0x8A, FIL_, 0xCC, 0x88,
+ FIL_, 0xC3, 0x8B, FIL_, 0xCC, 0xA7, FIL_, 0xC8,
+ 0xA8, FIL_, 0xCC, 0x91, FIL_, 0xC8, 0x86, FIL_,
+ 0xCC, 0x8F, FIL_, 0xC8, 0x84, FIL_, 0xCC, 0x89,
+ FIL_, 0xE1, 0xBA, 0xBA, FIL_, 0xCC, 0xB0, FIL_,
+ 0xE1, 0xB8, 0x9A, FIL_, 0xCC, 0xAD, FIL_, 0xE1,
+ 0xB8, 0x98, FIL_, 0xCC, 0x83, FIL_, 0xE1, 0xBA,
+ 0xBC, FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xBA, 0xB8,
+ FIL_, 0xCC, 0x84, FIL_, 0xC4, 0x92, FIL_, 0xCC,
+ 0x86, FIL_, 0xC4, 0x94, FIL_, 0xCC, 0x87, FIL_,
+ 0xC4, 0x96, FIL_, 0xCC, 0xA8, FIL_, 0xC4, 0x98,
+ FIL_, 0xCC, 0x8C, FIL_, 0xC4, 0x9A, FIL_, 0x01,
+ 0xCC, 0x87, FIL_, 0xE1, 0xB8, 0x9E, FIL_, 0x07,
+ 0xCC, 0x8C, FIL_, 0xC7, 0xA6, FIL_, 0xCC, 0x87,
+ FIL_, 0xC4, 0xA0, FIL_, 0xCC, 0x84, FIL_, 0xE1,
+ 0xB8, 0xA0, FIL_, 0xCC, 0x82, FIL_, 0xC4, 0x9C,
+ FIL_, 0xCC, 0x81, FIL_, 0xC7, 0xB4, FIL_, 0xCC,
+ 0xA7, FIL_, 0xC4, 0xA2, FIL_, 0xCC, 0x86, FIL_,
+ 0xC4, 0x9E, FIL_, 0x07, 0xCC, 0xAE, FIL_, 0xE1,
+ 0xB8, 0xAA, FIL_, 0xCC, 0x87, FIL_, 0xE1, 0xB8,
+ 0xA2, FIL_, 0xCC, 0x88, FIL_, 0xE1, 0xB8, 0xA6,
+ FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xB8, 0xA4, FIL_,
+ 0xCC, 0xA7, FIL_, 0xE1, 0xB8, 0xA8, FIL_, 0xCC,
+ 0x8C, FIL_, 0xC8, 0x9E, FIL_, 0xCC, 0x82, FIL_,
+ 0xC4, 0xA4, FIL_, 0x0F, 0xCC, 0x84, FIL_, 0xC4,
+ 0xAA, FIL_, 0xCC, 0x80, FIL_, 0xC3, 0x8C, FIL_,
+ 0xCC, 0xA8, FIL_, 0xC4, 0xAE, FIL_, 0xCC, 0x83,
+ FIL_, 0xC4, 0xA8, FIL_, 0xCC, 0x88, FIL_, 0xC3,
+ 0x8F, FIL_, 0xCC, 0x81, FIL_, 0xC3, 0x8D, FIL_,
+ 0xCC, 0x8F, FIL_, 0xC8, 0x88, FIL_, 0xCC, 0x86,
+ FIL_, 0xC4, 0xAC, FIL_, 0xCC, 0x91, FIL_, 0xC8,
+ 0x8A, FIL_, 0xCC, 0x8C, FIL_, 0xC7, 0x8F, FIL_,
+ 0xCC, 0x89, FIL_, 0xE1, 0xBB, 0x88, FIL_, 0xCC,
+ 0x87, FIL_, 0xC4, 0xB0, FIL_, 0xCC, 0xA3, FIL_,
+ 0xE1, 0xBB, 0x8A, FIL_, 0xCC, 0xB0, FIL_, 0xE1,
+ 0xB8, 0xAC, FIL_, 0xCC, 0x82, FIL_, 0xC3, 0x8E,
+ FIL_, 0x01, 0xCC, 0x82, FIL_, 0xC4, 0xB4, FIL_,
+ 0x05, 0xCC, 0x8C, FIL_, 0xC7, 0xA8, FIL_, 0xCC,
+ 0xB1, FIL_, 0xE1, 0xB8, 0xB4, FIL_, 0xCC, 0x81,
+ FIL_, 0xE1, 0xB8, 0xB0, FIL_, 0xCC, 0xA7, FIL_,
+ 0xC4, 0xB6, FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xB8,
+ 0xB2, FIL_, 0x06, 0xCC, 0xA7, FIL_, 0xC4, 0xBB,
+ FIL_, 0xCC, 0x8C, FIL_, 0xC4, 0xBD, FIL_, 0xCC,
+ 0xB1, FIL_, 0xE1, 0xB8, 0xBA, FIL_, 0xCC, 0xA3,
+ FIL_, 0xE1, 0xB8, 0xB6, FIL_, 0xCC, 0xAD, FIL_,
+ 0xE1, 0xB8, 0xBC, FIL_, 0xCC, 0x81, FIL_, 0xC4,
+ 0xB9, FIL_, 0x03, 0xCC, 0x81, FIL_, 0xE1, 0xB8,
+ 0xBE, FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xB9, 0x82,
+ FIL_, 0xCC, 0x87, FIL_, 0xE1, 0xB9, 0x80, FIL_,
+ 0x09, 0xCC, 0x80, FIL_, 0xC7, 0xB8, FIL_, 0xCC,
+ 0xAD, FIL_, 0xE1, 0xB9, 0x8A, FIL_, 0xCC, 0x87,
+ FIL_, 0xE1, 0xB9, 0x84, FIL_, 0xCC, 0xB1, FIL_,
+ 0xE1, 0xB9, 0x88, FIL_, 0xCC, 0x83, FIL_, 0xC3,
+ 0x91, FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xB9, 0x86,
+ FIL_, 0xCC, 0x81, FIL_, 0xC5, 0x83, FIL_, 0xCC,
+ 0xA7, FIL_, 0xC5, 0x85, FIL_, 0xCC, 0x8C, FIL_,
+ 0xC5, 0x87, FIL_, 0x10, 0xCC, 0xA8, FIL_, 0xC7,
+ 0xAA, FIL_, 0xCC, 0x91, FIL_, 0xC8, 0x8E, FIL_,
+ 0xCC, 0x80, FIL_, 0xC3, 0x92, FIL_, 0xCC, 0x9B,
+ FIL_, 0xC6, 0xA0, FIL_, 0xCC, 0x8F, FIL_, 0xC8,
+ 0x8C, FIL_, 0xCC, 0x81, FIL_, 0xC3, 0x93, FIL_,
+ 0xCC, 0x87, FIL_, 0xC8, 0xAE, FIL_, 0xCC, 0x8C,
+ FIL_, 0xC7, 0x91, FIL_, 0xCC, 0xA3, FIL_, 0xE1,
+ 0xBB, 0x8C, FIL_, 0xCC, 0x82, FIL_, 0xC3, 0x94,
+ FIL_, 0xCC, 0x84, FIL_, 0xC5, 0x8C, FIL_, 0xCC,
+ 0x83, FIL_, 0xC3, 0x95, FIL_, 0xCC, 0x86, FIL_,
+ 0xC5, 0x8E, FIL_, 0xCC, 0x88, FIL_, 0xC3, 0x96,
+ FIL_, 0xCC, 0x8B, FIL_, 0xC5, 0x90, FIL_, 0xCC,
+ 0x89, FIL_, 0xE1, 0xBB, 0x8E, FIL_, 0x02, 0xCC,
+ 0x87, FIL_, 0xE1, 0xB9, 0x96, FIL_, 0xCC, 0x81,
+ FIL_, 0xE1, 0xB9, 0x94, FIL_, 0x08, 0xCC, 0x91,
+ FIL_, 0xC8, 0x92, FIL_, 0xCC, 0xA7, FIL_, 0xC5,
+ 0x96, FIL_, 0xCC, 0x8C, FIL_, 0xC5, 0x98, FIL_,
+ 0xCC, 0xB1, FIL_, 0xE1, 0xB9, 0x9E, FIL_, 0xCC,
+ 0xA3, FIL_, 0xE1, 0xB9, 0x9A, FIL_, 0xCC, 0x87,
+ FIL_, 0xE1, 0xB9, 0x98, FIL_, 0xCC, 0x81, FIL_,
+ 0xC5, 0x94, FIL_, 0xCC, 0x8F, FIL_, 0xC8, 0x90,
+ FIL_, 0x07, 0xCC, 0x81, FIL_, 0xC5, 0x9A, FIL_,
+ 0xCC, 0x82, FIL_, 0xC5, 0x9C, FIL_, 0xCC, 0xA7,
+ FIL_, 0xC5, 0x9E, FIL_, 0xCC, 0x8C, FIL_, 0xC5,
+ 0xA0, FIL_, 0xCC, 0xA6, FIL_, 0xC8, 0x98, FIL_,
+ 0xCC, 0x87, FIL_, 0xE1, 0xB9, 0xA0, FIL_, 0xCC,
+ 0xA3, FIL_, 0xE1, 0xB9, 0xA2, FIL_, 0x07, 0xCC,
+ 0x8C, FIL_, 0xC5, 0xA4, FIL_, 0xCC, 0xB1, FIL_,
+ 0xE1, 0xB9, 0xAE, FIL_, 0xCC, 0xA6, FIL_, 0xC8,
+ 0x9A, FIL_, 0xCC, 0xA7, FIL_, 0xC5, 0xA2, FIL_,
+ 0xCC, 0x87, FIL_, 0xE1, 0xB9, 0xAA, FIL_, 0xCC,
+ 0xAD, FIL_, 0xE1, 0xB9, 0xB0, FIL_, 0xCC, 0xA3,
+ FIL_, 0xE1, 0xB9, 0xAC, FIL_, 0x13, 0xCC, 0xA8,
+ FIL_, 0xC5, 0xB2, FIL_, 0xCC, 0x83, FIL_, 0xC5,
+ 0xA8, FIL_, 0xCC, 0x84, FIL_, 0xC5, 0xAA, FIL_,
+ 0xCC, 0x81, FIL_, 0xC3, 0x9A, FIL_, 0xCC, 0x86,
+ FIL_, 0xC5, 0xAC, FIL_, 0xCC, 0x8A, FIL_, 0xC5,
+ 0xAE, FIL_, 0xCC, 0x80, FIL_, 0xC3, 0x99, FIL_,
+ 0xCC, 0x91, FIL_, 0xC8, 0x96, FIL_, 0xCC, 0x8B,
+ FIL_, 0xC5, 0xB0, FIL_, 0xCC, 0xA4, FIL_, 0xE1,
+ 0xB9, 0xB2, FIL_, 0xCC, 0xB0, FIL_, 0xE1, 0xB9,
+ 0xB4, FIL_, 0xCC, 0x8F, FIL_, 0xC8, 0x94, FIL_,
+ 0xCC, 0xAD, FIL_, 0xE1, 0xB9, 0xB6, FIL_, 0xCC,
+ 0x9B, FIL_, 0xC6, 0xAF, FIL_, 0xCC, 0x82, FIL_,
+ 0xC3, 0x9B, FIL_, 0xCC, 0x88, FIL_, 0xC3, 0x9C,
+ FIL_, 0xCC, 0x8C, FIL_, 0xC7, 0x93, FIL_, 0xCC,
+ 0xA3, FIL_, 0xE1, 0xBB, 0xA4, FIL_, 0xCC, 0x89,
+ FIL_, 0xE1, 0xBB, 0xA6, FIL_, 0x02, 0xCC, 0x83,
+ FIL_, 0xE1, 0xB9, 0xBC, FIL_, 0xCC, 0xA3, FIL_,
+ 0xE1, 0xB9, 0xBE, FIL_, 0x06, 0xCC, 0x82, FIL_,
+ 0xC5, 0xB4, FIL_, 0xCC, 0x88, FIL_, 0xE1, 0xBA,
+ 0x84, FIL_, 0xCC, 0x87, FIL_, 0xE1, 0xBA, 0x86,
+ FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xBA, 0x88, FIL_,
+ 0xCC, 0x81, FIL_, 0xE1, 0xBA, 0x82, FIL_, 0xCC,
+ 0x80, FIL_, 0xE1, 0xBA, 0x80, FIL_, 0x02, 0xCC,
+ 0x87, FIL_, 0xE1, 0xBA, 0x8A, FIL_, 0xCC, 0x88,
+ FIL_, 0xE1, 0xBA, 0x8C, FIL_, 0x09, 0xCC, 0x89,
+ FIL_, 0xE1, 0xBB, 0xB6, FIL_, 0xCC, 0x87, FIL_,
+ 0xE1, 0xBA, 0x8E, FIL_, 0xCC, 0xA3, FIL_, 0xE1,
+ 0xBB, 0xB4, FIL_, 0xCC, 0x81, FIL_, 0xC3, 0x9D,
+ FIL_, 0xCC, 0x84, FIL_, 0xC8, 0xB2, FIL_, 0xCC,
+ 0x82, FIL_, 0xC5, 0xB6, FIL_, 0xCC, 0x88, FIL_,
+ 0xC5, 0xB8, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBB,
+ 0xB2, FIL_, 0xCC, 0x83, FIL_, 0xE1, 0xBB, 0xB8,
+ FIL_, 0x06, 0xCC, 0x87, FIL_, 0xC5, 0xBB, FIL_,
+ 0xCC, 0xA3, FIL_, 0xE1, 0xBA, 0x92, FIL_, 0xCC,
+ 0x8C, FIL_, 0xC5, 0xBD, FIL_, 0xCC, 0xB1, FIL_,
+ 0xE1, 0xBA, 0x94, FIL_, 0xCC, 0x82, FIL_, 0xE1,
+ 0xBA, 0x90, FIL_, 0xCC, 0x81, FIL_, 0xC5, 0xB9,
+ FIL_, 0x10, 0xCC, 0x8C, FIL_, 0xC7, 0x8E, FIL_,
+ 0xCC, 0x8F, FIL_, 0xC8, 0x81, FIL_, 0xCC, 0xA8,
+ FIL_, 0xC4, 0x85, FIL_, 0xCC, 0xA3, FIL_, 0xE1,
+ 0xBA, 0xA1, FIL_, 0xCC, 0x86, FIL_, 0xC4, 0x83,
+ FIL_, 0xCC, 0x89, FIL_, 0xE1, 0xBA, 0xA3, FIL_,
+ 0xCC, 0x84, FIL_, 0xC4, 0x81, FIL_, 0xCC, 0x91,
+ FIL_, 0xC8, 0x83, FIL_, 0xCC, 0x8A, FIL_, 0xC3,
+ 0xA5, FIL_, 0xCC, 0x88, FIL_, 0xC3, 0xA4, FIL_,
+ 0xCC, 0x83, FIL_, 0xC3, 0xA3, FIL_, 0xCC, 0x82,
+ FIL_, 0xC3, 0xA2, FIL_, 0xCC, 0x81, FIL_, 0xC3,
+ 0xA1, FIL_, 0xCC, 0x80, FIL_, 0xC3, 0xA0, FIL_,
+ 0xCC, 0x87, FIL_, 0xC8, 0xA7, FIL_, 0xCC, 0xA5,
+ FIL_, 0xE1, 0xB8, 0x81, FIL_, 0x03, 0xCC, 0xB1,
+ FIL_, 0xE1, 0xB8, 0x87, FIL_, 0xCC, 0xA3, FIL_,
+ 0xE1, 0xB8, 0x85, FIL_, 0xCC, 0x87, FIL_, 0xE1,
+ 0xB8, 0x83, FIL_, 0x05, 0xCC, 0x87, FIL_, 0xC4,
+ 0x8B, FIL_, 0xCC, 0xA7, FIL_, 0xC3, 0xA7, FIL_,
+ 0xCC, 0x82, FIL_, 0xC4, 0x89, FIL_, 0xCC, 0x8C,
+ FIL_, 0xC4, 0x8D, FIL_, 0xCC, 0x81, FIL_, 0xC4,
+ 0x87, FIL_, 0x06, 0xCC, 0xAD, FIL_, 0xE1, 0xB8,
+ 0x93, FIL_, 0xCC, 0x87, FIL_, 0xE1, 0xB8, 0x8B,
+ FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xB8, 0x8D, FIL_,
+ 0xCC, 0xB1, FIL_, 0xE1, 0xB8, 0x8F, FIL_, 0xCC,
+ 0xA7, FIL_, 0xE1, 0xB8, 0x91, FIL_, 0xCC, 0x8C,
+ FIL_, 0xC4, 0x8F, FIL_, 0x11, 0xCC, 0xA8, FIL_,
+ 0xC4, 0x99, FIL_, 0xCC, 0x8C, FIL_, 0xC4, 0x9B,
+ FIL_, 0xCC, 0x87, FIL_, 0xC4, 0x97, FIL_, 0xCC,
+ 0x88, FIL_, 0xC3, 0xAB, FIL_, 0xCC, 0xA3, FIL_,
+ 0xE1, 0xBA, 0xB9, FIL_, 0xCC, 0xB0, FIL_, 0xE1,
+ 0xB8, 0x9B, FIL_, 0xCC, 0x84, FIL_, 0xC4, 0x93,
+ FIL_, 0xCC, 0xAD, FIL_, 0xE1, 0xB8, 0x99, FIL_,
+ 0xCC, 0x83, FIL_, 0xE1, 0xBA, 0xBD, FIL_, 0xCC,
+ 0x86, FIL_, 0xC4, 0x95, FIL_, 0xCC, 0xA7, FIL_,
+ 0xC8, 0xA9, FIL_, 0xCC, 0x89, FIL_, 0xE1, 0xBA,
+ 0xBB, FIL_, 0xCC, 0x8F, FIL_, 0xC8, 0x85, FIL_,
+ 0xCC, 0x81, FIL_, 0xC3, 0xA9, FIL_, 0xCC, 0x91,
+ FIL_, 0xC8, 0x87, FIL_, 0xCC, 0x80, FIL_, 0xC3,
+ 0xA8, FIL_, 0xCC, 0x82, FIL_, 0xC3, 0xAA, FIL_,
+ 0x01, 0xCC, 0x87, FIL_, 0xE1, 0xB8, 0x9F, FIL_,
+ 0x07, 0xCC, 0x86, FIL_, 0xC4, 0x9F, FIL_, 0xCC,
+ 0xA7, FIL_, 0xC4, 0xA3, FIL_, 0xCC, 0x81, FIL_,
+ 0xC7, 0xB5, FIL_, 0xCC, 0x82, FIL_, 0xC4, 0x9D,
+ FIL_, 0xCC, 0x87, FIL_, 0xC4, 0xA1, FIL_, 0xCC,
+ 0x8C, FIL_, 0xC7, 0xA7, FIL_, 0xCC, 0x84, FIL_,
+ 0xE1, 0xB8, 0xA1, FIL_, 0x08, 0xCC, 0x8C, FIL_,
+ 0xC8, 0x9F, FIL_, 0xCC, 0x82, FIL_, 0xC4, 0xA5,
+ FIL_, 0xCC, 0x88, FIL_, 0xE1, 0xB8, 0xA7, FIL_,
+ 0xCC, 0x87, FIL_, 0xE1, 0xB8, 0xA3, FIL_, 0xCC,
+ 0xB1, FIL_, 0xE1, 0xBA, 0x96, FIL_, 0xCC, 0xA3,
+ FIL_, 0xE1, 0xB8, 0xA5, FIL_, 0xCC, 0xA7, FIL_,
+ 0xE1, 0xB8, 0xA9, FIL_, 0xCC, 0xAE, FIL_, 0xE1,
+ 0xB8, 0xAB, FIL_, 0x0E, 0xCC, 0x81, FIL_, 0xC3,
+ 0xAD, FIL_, 0xCC, 0x80, FIL_, 0xC3, 0xAC, FIL_,
+ 0xCC, 0xA3, FIL_, 0xE1, 0xBB, 0x8B, FIL_, 0xCC,
+ 0x8C, FIL_, 0xC7, 0x90, FIL_, 0xCC, 0x89, FIL_,
+ 0xE1, 0xBB, 0x89, FIL_, 0xCC, 0x91, FIL_, 0xC8,
+ 0x8B, FIL_, 0xCC, 0x8F, FIL_, 0xC8, 0x89, FIL_,
+ 0xCC, 0x82, FIL_, 0xC3, 0xAE, FIL_, 0xCC, 0xB0,
+ FIL_, 0xE1, 0xB8, 0xAD, FIL_, 0xCC, 0xA8, FIL_,
+ 0xC4, 0xAF, FIL_, 0xCC, 0x86, FIL_, 0xC4, 0xAD,
+ FIL_, 0xCC, 0x84, FIL_, 0xC4, 0xAB, FIL_, 0xCC,
+ 0x83, FIL_, 0xC4, 0xA9, FIL_, 0xCC, 0x88, FIL_,
+ 0xC3, 0xAF, FIL_, 0x02, 0xCC, 0x82, FIL_, 0xC4,
+ 0xB5, FIL_, 0xCC, 0x8C, FIL_, 0xC7, 0xB0, FIL_,
+ 0x05, 0xCC, 0xA3, FIL_, 0xE1, 0xB8, 0xB3, FIL_,
+ 0xCC, 0x81, FIL_, 0xE1, 0xB8, 0xB1, FIL_, 0xCC,
+ 0xA7, FIL_, 0xC4, 0xB7, FIL_, 0xCC, 0x8C, FIL_,
+ 0xC7, 0xA9, FIL_, 0xCC, 0xB1, FIL_, 0xE1, 0xB8,
+ 0xB5, FIL_, 0x06, 0xCC, 0xA3, FIL_, 0xE1, 0xB8,
+ 0xB7, FIL_, 0xCC, 0x81, FIL_, 0xC4, 0xBA, FIL_,
+ 0xCC, 0xA7, FIL_, 0xC4, 0xBC, FIL_, 0xCC, 0x8C,
+ FIL_, 0xC4, 0xBE, FIL_, 0xCC, 0xB1, FIL_, 0xE1,
+ 0xB8, 0xBB, FIL_, 0xCC, 0xAD, FIL_, 0xE1, 0xB8,
+ 0xBD, FIL_, 0x03, 0xCC, 0xA3, FIL_, 0xE1, 0xB9,
+ 0x83, FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xB8, 0xBF,
+ FIL_, 0xCC, 0x87, FIL_, 0xE1, 0xB9, 0x81, FIL_,
+ 0x09, 0xCC, 0xA3, FIL_, 0xE1, 0xB9, 0x87, FIL_,
+ 0xCC, 0x83, FIL_, 0xC3, 0xB1, FIL_, 0xCC, 0x87,
+ FIL_, 0xE1, 0xB9, 0x85, FIL_, 0xCC, 0xB1, FIL_,
+ 0xE1, 0xB9, 0x89, FIL_, 0xCC, 0x81, FIL_, 0xC5,
+ 0x84, FIL_, 0xCC, 0xA7, FIL_, 0xC5, 0x86, FIL_,
+ 0xCC, 0xAD, FIL_, 0xE1, 0xB9, 0x8B, FIL_, 0xCC,
+ 0x8C, FIL_, 0xC5, 0x88, FIL_, 0xCC, 0x80, FIL_,
+ 0xC7, 0xB9, FIL_, 0x10, 0xCC, 0x89, FIL_, 0xE1,
+ 0xBB, 0x8F, FIL_, 0xCC, 0x81, FIL_, 0xC3, 0xB3,
+ FIL_, 0xCC, 0x80, FIL_, 0xC3, 0xB2, FIL_, 0xCC,
+ 0x87, FIL_, 0xC8, 0xAF, FIL_, 0xCC, 0x8F, FIL_,
+ 0xC8, 0x8D, FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xBB,
+ 0x8D, FIL_, 0xCC, 0x84, FIL_, 0xC5, 0x8D, FIL_,
+ 0xCC, 0x8C, FIL_, 0xC7, 0x92, FIL_, 0xCC, 0x86,
+ FIL_, 0xC5, 0x8F, FIL_, 0xCC, 0x8B, FIL_, 0xC5,
+ 0x91, FIL_, 0xCC, 0x9B, FIL_, 0xC6, 0xA1, FIL_,
+ 0xCC, 0x91, FIL_, 0xC8, 0x8F, FIL_, 0xCC, 0xA8,
+ FIL_, 0xC7, 0xAB, FIL_, 0xCC, 0x88, FIL_, 0xC3,
+ 0xB6, FIL_, 0xCC, 0x83, FIL_, 0xC3, 0xB5, FIL_,
+ 0xCC, 0x82, FIL_, 0xC3, 0xB4, FIL_, 0x02, 0xCC,
+ 0x87, FIL_, 0xE1, 0xB9, 0x97, FIL_, 0xCC, 0x81,
+ FIL_, 0xE1, 0xB9, 0x95, FIL_, 0x08, 0xCC, 0xB1,
+ FIL_, 0xE1, 0xB9, 0x9F, FIL_, 0xCC, 0x87, FIL_,
+ 0xE1, 0xB9, 0x99, FIL_, 0xCC, 0x81, FIL_, 0xC5,
+ 0x95, FIL_, 0xCC, 0x8F, FIL_, 0xC8, 0x91, FIL_,
+ 0xCC, 0xA3, FIL_, 0xE1, 0xB9, 0x9B, FIL_, 0xCC,
+ 0x8C, FIL_, 0xC5, 0x99, FIL_, 0xCC, 0x91, FIL_,
+ 0xC8, 0x93, FIL_, 0xCC, 0xA7, FIL_, 0xC5, 0x97,
+ FIL_, 0x07, 0xCC, 0xA6, FIL_, 0xC8, 0x99, FIL_,
+ 0xCC, 0x8C, FIL_, 0xC5, 0xA1, FIL_, 0xCC, 0x81,
+ FIL_, 0xC5, 0x9B, FIL_, 0xCC, 0x87, FIL_, 0xE1,
+ 0xB9, 0xA1, FIL_, 0xCC, 0x82, FIL_, 0xC5, 0x9D,
+ FIL_, 0xCC, 0xA7, FIL_, 0xC5, 0x9F, FIL_, 0xCC,
+ 0xA3, FIL_, 0xE1, 0xB9, 0xA3, FIL_, 0x08, 0xCC,
+ 0x88, FIL_, 0xE1, 0xBA, 0x97, FIL_, 0xCC, 0xAD,
+ FIL_, 0xE1, 0xB9, 0xB1, FIL_, 0xCC, 0xB1, FIL_,
+ 0xE1, 0xB9, 0xAF, FIL_, 0xCC, 0xA3, FIL_, 0xE1,
+ 0xB9, 0xAD, FIL_, 0xCC, 0x8C, FIL_, 0xC5, 0xA5,
+ FIL_, 0xCC, 0xA7, FIL_, 0xC5, 0xA3, FIL_, 0xCC,
+ 0x87, FIL_, 0xE1, 0xB9, 0xAB, FIL_, 0xCC, 0xA6,
+ FIL_, 0xC8, 0x9B, FIL_, 0x13, 0xCC, 0x81, FIL_,
+ 0xC3, 0xBA, FIL_, 0xCC, 0x91, FIL_, 0xC8, 0x97,
+ FIL_, 0xCC, 0x83, FIL_, 0xC5, 0xA9, FIL_, 0xCC,
+ 0x8F, FIL_, 0xC8, 0x95, FIL_, 0xCC, 0xA8, FIL_,
+ 0xC5, 0xB3, FIL_, 0xCC, 0x82, FIL_, 0xC3, 0xBB,
+ FIL_, 0xCC, 0x88, FIL_, 0xC3, 0xBC, FIL_, 0xCC,
+ 0x80, FIL_, 0xC3, 0xB9, FIL_, 0xCC, 0xA3, FIL_,
+ 0xE1, 0xBB, 0xA5, FIL_, 0xCC, 0xA4, FIL_, 0xE1,
+ 0xB9, 0xB3, FIL_, 0xCC, 0x89, FIL_, 0xE1, 0xBB,
+ 0xA7, FIL_, 0xCC, 0xB0, FIL_, 0xE1, 0xB9, 0xB5,
+ FIL_, 0xCC, 0xAD, FIL_, 0xE1, 0xB9, 0xB7, FIL_,
+ 0xCC, 0x9B, FIL_, 0xC6, 0xB0, FIL_, 0xCC, 0x84,
+ FIL_, 0xC5, 0xAB, FIL_, 0xCC, 0x8B, FIL_, 0xC5,
+ 0xB1, FIL_, 0xCC, 0x86, FIL_, 0xC5, 0xAD, FIL_,
+ 0xCC, 0x8C, FIL_, 0xC7, 0x94, FIL_, 0xCC, 0x8A,
+ FIL_, 0xC5, 0xAF, FIL_, 0x02, 0xCC, 0x83, FIL_,
+ 0xE1, 0xB9, 0xBD, FIL_, 0xCC, 0xA3, FIL_, 0xE1,
+ 0xB9, 0xBF, FIL_, 0x07, 0xCC, 0x82, FIL_, 0xC5,
+ 0xB5, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBA, 0x81,
+ FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBA, 0x83, FIL_,
+ 0xCC, 0x88, FIL_, 0xE1, 0xBA, 0x85, FIL_, 0xCC,
+ 0xA3, FIL_, 0xE1, 0xBA, 0x89, FIL_, 0xCC, 0x87,
+ FIL_, 0xE1, 0xBA, 0x87, FIL_, 0xCC, 0x8A, FIL_,
+ 0xE1, 0xBA, 0x98, FIL_, 0x02, 0xCC, 0x87, FIL_,
+ 0xE1, 0xBA, 0x8B, FIL_, 0xCC, 0x88, FIL_, 0xE1,
+ 0xBA, 0x8D, FIL_, 0x0A, 0xCC, 0x87, FIL_, 0xE1,
+ 0xBA, 0x8F, FIL_, 0xCC, 0x83, FIL_, 0xE1, 0xBB,
+ 0xB9, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBB, 0xB3,
+ FIL_, 0xCC, 0x89, FIL_, 0xE1, 0xBB, 0xB7, FIL_,
+ 0xCC, 0xA3, FIL_, 0xE1, 0xBB, 0xB5, FIL_, 0xCC,
+ 0x82, FIL_, 0xC5, 0xB7, FIL_, 0xCC, 0x84, FIL_,
+ 0xC8, 0xB3, FIL_, 0xCC, 0x8A, FIL_, 0xE1, 0xBA,
+ 0x99, FIL_, 0xCC, 0x88, FIL_, 0xC3, 0xBF, FIL_,
+ 0xCC, 0x81, FIL_, 0xC3, 0xBD, FIL_, 0x06, 0xCC,
+ 0x8C, FIL_, 0xC5, 0xBE, FIL_, 0xCC, 0x87, FIL_,
+ 0xC5, 0xBC, FIL_, 0xCC, 0xB1, FIL_, 0xE1, 0xBA,
+ 0x95, FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xBA, 0x93,
+ FIL_, 0xCC, 0x81, FIL_, 0xC5, 0xBA, FIL_, 0xCC,
+ 0x82, FIL_, 0xE1, 0xBA, 0x91, FIL_, 0x03, 0xCC,
+ 0x80, FIL_, 0xE1, 0xBF, 0xAD, FIL_, 0xCD, 0x82,
+ FIL_, 0xE1, 0xBF, 0x81, FIL_, 0xCC, 0x81, FIL_,
+ 0xCE, 0x85, FIL_, 0x04, 0xCC, 0x89, FIL_, 0xE1,
+ 0xBA, 0xA8, FIL_, 0xCC, 0x83, FIL_, 0xE1, 0xBA,
+ 0xAA, FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBA, 0xA4,
+ FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBA, 0xA6, FIL_,
+ 0x01, 0xCC, 0x84, FIL_, 0xC7, 0x9E, FIL_, 0x01,
+ 0xCC, 0x81, FIL_, 0xC7, 0xBA, FIL_, 0x02, 0xCC,
+ 0x84, FIL_, 0xC7, 0xA2, FIL_, 0xCC, 0x81, FIL_,
+ 0xC7, 0xBC, FIL_, 0x01, 0xCC, 0x81, FIL_, 0xE1,
+ 0xB8, 0x88, FIL_, 0x04, 0xCC, 0x81, FIL_, 0xE1,
+ 0xBA, 0xBE, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBB,
+ 0x80, FIL_, 0xCC, 0x83, FIL_, 0xE1, 0xBB, 0x84,
+ FIL_, 0xCC, 0x89, FIL_, 0xE1, 0xBB, 0x82, FIL_,
+ 0x01, 0xCC, 0x81, FIL_, 0xE1, 0xB8, 0xAE, FIL_,
+ 0x04, 0xCC, 0x83, FIL_, 0xE1, 0xBB, 0x96, FIL_,
+ 0xCC, 0x81, FIL_, 0xE1, 0xBB, 0x90, FIL_, 0xCC,
+ 0x80, FIL_, 0xE1, 0xBB, 0x92, FIL_, 0xCC, 0x89,
+ FIL_, 0xE1, 0xBB, 0x94, FIL_, 0x03, 0xCC, 0x84,
+ FIL_, 0xC8, 0xAC, FIL_, 0xCC, 0x81, FIL_, 0xE1,
+ 0xB9, 0x8C, FIL_, 0xCC, 0x88, FIL_, 0xE1, 0xB9,
+ 0x8E, FIL_, 0x01, 0xCC, 0x84, FIL_, 0xC8, 0xAA,
+ FIL_, 0x01, 0xCC, 0x81, FIL_, 0xC7, 0xBE, FIL_,
+ 0x04, 0xCC, 0x80, FIL_, 0xC7, 0x9B, FIL_, 0xCC,
+ 0x84, FIL_, 0xC7, 0x95, FIL_, 0xCC, 0x8C, FIL_,
+ 0xC7, 0x99, FIL_, 0xCC, 0x81, FIL_, 0xC7, 0x97,
+ FIL_, 0x04, 0xCC, 0x89, FIL_, 0xE1, 0xBA, 0xA9,
+ FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBA, 0xA7, FIL_,
+ 0xCC, 0x81, FIL_, 0xE1, 0xBA, 0xA5, FIL_, 0xCC,
+ 0x83, FIL_, 0xE1, 0xBA, 0xAB, FIL_, 0x01, 0xCC,
+ 0x84, FIL_, 0xC7, 0x9F, FIL_, 0x01, 0xCC, 0x81,
+ FIL_, 0xC7, 0xBB, FIL_, 0x02, 0xCC, 0x84, FIL_,
+ 0xC7, 0xA3, FIL_, 0xCC, 0x81, FIL_, 0xC7, 0xBD,
+ FIL_, 0x01, 0xCC, 0x81, FIL_, 0xE1, 0xB8, 0x89,
+ FIL_, 0x04, 0xCC, 0x89, FIL_, 0xE1, 0xBB, 0x83,
+ FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBA, 0xBF, FIL_,
+ 0xCC, 0x80, FIL_, 0xE1, 0xBB, 0x81, FIL_, 0xCC,
+ 0x83, FIL_, 0xE1, 0xBB, 0x85, FIL_, 0x01, 0xCC,
+ 0x81, FIL_, 0xE1, 0xB8, 0xAF, FIL_, 0x04, 0xCC,
+ 0x83, FIL_, 0xE1, 0xBB, 0x97, FIL_, 0xCC, 0x89,
+ FIL_, 0xE1, 0xBB, 0x95, FIL_, 0xCC, 0x80, FIL_,
+ 0xE1, 0xBB, 0x93, FIL_, 0xCC, 0x81, FIL_, 0xE1,
+ 0xBB, 0x91, FIL_, 0x03, 0xCC, 0x81, FIL_, 0xE1,
+ 0xB9, 0x8D, FIL_, 0xCC, 0x84, FIL_, 0xC8, 0xAD,
+ FIL_, 0xCC, 0x88, FIL_, 0xE1, 0xB9, 0x8F, FIL_,
+ 0x01, 0xCC, 0x84, FIL_, 0xC8, 0xAB, FIL_, 0x01,
+ 0xCC, 0x81, FIL_, 0xC7, 0xBF, FIL_, 0x04, 0xCC,
+ 0x81, FIL_, 0xC7, 0x98, FIL_, 0xCC, 0x84, FIL_,
+ 0xC7, 0x96, FIL_, 0xCC, 0x8C, FIL_, 0xC7, 0x9A,
+ FIL_, 0xCC, 0x80, FIL_, 0xC7, 0x9C, FIL_, 0x04,
+ 0xCC, 0x80, FIL_, 0xE1, 0xBA, 0xB0, FIL_, 0xCC,
+ 0x81, FIL_, 0xE1, 0xBA, 0xAE, FIL_, 0xCC, 0x83,
+ FIL_, 0xE1, 0xBA, 0xB4, FIL_, 0xCC, 0x89, FIL_,
+ 0xE1, 0xBA, 0xB2, FIL_, 0x04, 0xCC, 0x80, FIL_,
+ 0xE1, 0xBA, 0xB1, FIL_, 0xCC, 0x83, FIL_, 0xE1,
+ 0xBA, 0xB5, FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBA,
+ 0xAF, FIL_, 0xCC, 0x89, FIL_, 0xE1, 0xBA, 0xB3,
+ FIL_, 0x02, 0xCC, 0x81, FIL_, 0xE1, 0xB8, 0x96,
+ FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xB8, 0x94, FIL_,
+ 0x02, 0xCC, 0x80, FIL_, 0xE1, 0xB8, 0x95, FIL_,
+ 0xCC, 0x81, FIL_, 0xE1, 0xB8, 0x97, FIL_, 0x02,
+ 0xCC, 0x80, FIL_, 0xE1, 0xB9, 0x90, FIL_, 0xCC,
+ 0x81, FIL_, 0xE1, 0xB9, 0x92, FIL_, 0x02, 0xCC,
+ 0x80, FIL_, 0xE1, 0xB9, 0x91, FIL_, 0xCC, 0x81,
+ FIL_, 0xE1, 0xB9, 0x93, FIL_, 0x01, 0xCC, 0x87,
+ FIL_, 0xE1, 0xB9, 0xA4, FIL_, 0x01, 0xCC, 0x87,
+ FIL_, 0xE1, 0xB9, 0xA5, FIL_, 0x01, 0xCC, 0x87,
+ FIL_, 0xE1, 0xB9, 0xA6, FIL_, 0x01, 0xCC, 0x87,
+ FIL_, 0xE1, 0xB9, 0xA7, FIL_, 0x01, 0xCC, 0x81,
+ FIL_, 0xE1, 0xB9, 0xB8, FIL_, 0x01, 0xCC, 0x81,
+ FIL_, 0xE1, 0xB9, 0xB9, FIL_, 0x01, 0xCC, 0x88,
+ FIL_, 0xE1, 0xB9, 0xBA, FIL_, 0x01, 0xCC, 0x88,
+ FIL_, 0xE1, 0xB9, 0xBB, FIL_, 0x01, 0xCC, 0x87,
+ FIL_, 0xE1, 0xBA, 0x9B, FIL_, 0x05, 0xCC, 0x80,
+ FIL_, 0xE1, 0xBB, 0x9C, FIL_, 0xCC, 0x81, FIL_,
+ 0xE1, 0xBB, 0x9A, FIL_, 0xCC, 0xA3, FIL_, 0xE1,
+ 0xBB, 0xA2, FIL_, 0xCC, 0x83, FIL_, 0xE1, 0xBB,
+ 0xA0, FIL_, 0xCC, 0x89, FIL_, 0xE1, 0xBB, 0x9E,
+ FIL_, 0x05, 0xCC, 0x83, FIL_, 0xE1, 0xBB, 0xA1,
+ FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBB, 0x9B, FIL_,
+ 0xCC, 0xA3, FIL_, 0xE1, 0xBB, 0xA3, FIL_, 0xCC,
+ 0x89, FIL_, 0xE1, 0xBB, 0x9F, FIL_, 0xCC, 0x80,
+ FIL_, 0xE1, 0xBB, 0x9D, FIL_, 0x05, 0xCC, 0x83,
+ FIL_, 0xE1, 0xBB, 0xAE, FIL_, 0xCC, 0xA3, FIL_,
+ 0xE1, 0xBB, 0xB0, FIL_, 0xCC, 0x89, FIL_, 0xE1,
+ 0xBB, 0xAC, FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBB,
+ 0xA8, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBB, 0xAA,
+ FIL_, 0x05, 0xCC, 0xA3, FIL_, 0xE1, 0xBB, 0xB1,
+ FIL_, 0xCC, 0x83, FIL_, 0xE1, 0xBB, 0xAF, FIL_,
+ 0xCC, 0x89, FIL_, 0xE1, 0xBB, 0xAD, FIL_, 0xCC,
+ 0x81, FIL_, 0xE1, 0xBB, 0xA9, FIL_, 0xCC, 0x80,
+ FIL_, 0xE1, 0xBB, 0xAB, FIL_, 0x01, 0xCC, 0x8C,
+ FIL_, 0xC7, 0xAE, FIL_, 0x01, 0xCC, 0x84, FIL_,
+ 0xC7, 0xAC, FIL_, 0x01, 0xCC, 0x84, FIL_, 0xC7,
+ 0xAD, FIL_, 0x01, 0xCC, 0x84, FIL_, 0xC7, 0xA0,
+ FIL_, 0x01, 0xCC, 0x84, FIL_, 0xC7, 0xA1, FIL_,
+ 0x01, 0xCC, 0x86, FIL_, 0xE1, 0xB8, 0x9C, FIL_,
+ 0x01, 0xCC, 0x86, FIL_, 0xE1, 0xB8, 0x9D, FIL_,
+ 0x01, 0xCC, 0x84, FIL_, 0xC8, 0xB0, FIL_, 0x01,
+ 0xCC, 0x84, FIL_, 0xC8, 0xB1, FIL_, 0x01, 0xCC,
+ 0x8C, FIL_, 0xC7, 0xAF, FIL_, 0x07, 0xCC, 0x93,
+ FIL_, 0xE1, 0xBC, 0x88, FIL_, 0xCC, 0x94, FIL_,
+ 0xE1, 0xBC, 0x89, FIL_, 0xCC, 0x81, FIL_, 0xCE,
+ 0x86, FIL_, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0xBC,
+ FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBE, 0xBA, FIL_,
+ 0xCC, 0x84, FIL_, 0xE1, 0xBE, 0xB9, FIL_, 0xCC,
+ 0x86, FIL_, 0xE1, 0xBE, 0xB8, FIL_, 0x04, 0xCC,
+ 0x81, FIL_, 0xCE, 0x88, FIL_, 0xCC, 0x94, FIL_,
+ 0xE1, 0xBC, 0x99, FIL_, 0xCC, 0x93, FIL_, 0xE1,
+ 0xBC, 0x98, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBF,
+ 0x88, FIL_, 0x05, 0xCC, 0x94, FIL_, 0xE1, 0xBC,
+ 0xA9, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBF, 0x8A,
+ FIL_, 0xCC, 0x81, FIL_, 0xCE, 0x89, FIL_, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBF, 0x8C, FIL_, 0xCC, 0x93,
+ FIL_, 0xE1, 0xBC, 0xA8, FIL_, 0x07, 0xCC, 0x81,
+ FIL_, 0xCE, 0x8A, FIL_, 0xCC, 0x88, FIL_, 0xCE,
+ 0xAA, FIL_, 0xCC, 0x86, FIL_, 0xE1, 0xBF, 0x98,
+ FIL_, 0xCC, 0x84, FIL_, 0xE1, 0xBF, 0x99, FIL_,
+ 0xCC, 0x93, FIL_, 0xE1, 0xBC, 0xB8, FIL_, 0xCC,
+ 0x94, FIL_, 0xE1, 0xBC, 0xB9, FIL_, 0xCC, 0x80,
+ FIL_, 0xE1, 0xBF, 0x9A, FIL_, 0x04, 0xCC, 0x94,
+ FIL_, 0xE1, 0xBD, 0x89, FIL_, 0xCC, 0x80, FIL_,
+ 0xE1, 0xBF, 0xB8, FIL_, 0xCC, 0x81, FIL_, 0xCE,
+ 0x8C, FIL_, 0xCC, 0x93, FIL_, 0xE1, 0xBD, 0x88,
+ FIL_, 0x01, 0xCC, 0x94, FIL_, 0xE1, 0xBF, 0xAC,
+ FIL_, 0x06, 0xCC, 0x81, FIL_, 0xCE, 0x8E, FIL_,
+ 0xCC, 0x86, FIL_, 0xE1, 0xBF, 0xA8, FIL_, 0xCC,
+ 0x94, FIL_, 0xE1, 0xBD, 0x99, FIL_, 0xCC, 0x80,
+ FIL_, 0xE1, 0xBF, 0xAA, FIL_, 0xCC, 0x84, FIL_,
+ 0xE1, 0xBF, 0xA9, FIL_, 0xCC, 0x88, FIL_, 0xCE,
+ 0xAB, FIL_, 0x05, 0xCC, 0x80, FIL_, 0xE1, 0xBF,
+ 0xBA, FIL_, 0xCC, 0x81, FIL_, 0xCE, 0x8F, FIL_,
+ 0xCD, 0x85, FIL_, 0xE1, 0xBF, 0xBC, FIL_, 0xCC,
+ 0x94, FIL_, 0xE1, 0xBD, 0xA9, FIL_, 0xCC, 0x93,
+ FIL_, 0xE1, 0xBD, 0xA8, FIL_, 0x01, 0xCD, 0x85,
+ FIL_, 0xE1, 0xBE, 0xB4, FIL_, 0x01, 0xCD, 0x85,
+ FIL_, 0xE1, 0xBF, 0x84, FIL_, 0x08, 0xCC, 0x81,
+ FIL_, 0xCE, 0xAC, FIL_, 0xCC, 0x80, FIL_, 0xE1,
+ 0xBD, 0xB0, FIL_, 0xCC, 0x93, FIL_, 0xE1, 0xBC,
+ 0x80, FIL_, 0xCC, 0x94, FIL_, 0xE1, 0xBC, 0x81,
+ FIL_, 0xCD, 0x82, FIL_, 0xE1, 0xBE, 0xB6, FIL_,
+ 0xCC, 0x86, FIL_, 0xE1, 0xBE, 0xB0, FIL_, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBE, 0xB3, FIL_, 0xCC, 0x84,
+ FIL_, 0xE1, 0xBE, 0xB1, FIL_, 0x04, 0xCC, 0x81,
+ FIL_, 0xCE, 0xAD, FIL_, 0xCC, 0x94, FIL_, 0xE1,
+ 0xBC, 0x91, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBD,
+ 0xB2, FIL_, 0xCC, 0x93, FIL_, 0xE1, 0xBC, 0x90,
+ FIL_, 0x06, 0xCC, 0x81, FIL_, 0xCE, 0xAE, FIL_,
+ 0xCC, 0x80, FIL_, 0xE1, 0xBD, 0xB4, FIL_, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBF, 0x83, FIL_, 0xCD, 0x82,
+ FIL_, 0xE1, 0xBF, 0x86, FIL_, 0xCC, 0x94, FIL_,
+ 0xE1, 0xBC, 0xA1, FIL_, 0xCC, 0x93, FIL_, 0xE1,
+ 0xBC, 0xA0, FIL_, 0x08, 0xCD, 0x82, FIL_, 0xE1,
+ 0xBF, 0x96, FIL_, 0xCC, 0x86, FIL_, 0xE1, 0xBF,
+ 0x90, FIL_, 0xCC, 0x93, FIL_, 0xE1, 0xBC, 0xB0,
+ FIL_, 0xCC, 0x81, FIL_, 0xCE, 0xAF, FIL_, 0xCC,
+ 0x94, FIL_, 0xE1, 0xBC, 0xB1, FIL_, 0xCC, 0x84,
+ FIL_, 0xE1, 0xBF, 0x91, FIL_, 0xCC, 0x88, FIL_,
+ 0xCF, 0x8A, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBD,
+ 0xB6, FIL_, 0x04, 0xCC, 0x81, FIL_, 0xCF, 0x8C,
+ FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBD, 0xB8, FIL_,
+ 0xCC, 0x93, FIL_, 0xE1, 0xBD, 0x80, FIL_, 0xCC,
+ 0x94, FIL_, 0xE1, 0xBD, 0x81, FIL_, 0x02, 0xCC,
+ 0x93, FIL_, 0xE1, 0xBF, 0xA4, FIL_, 0xCC, 0x94,
+ FIL_, 0xE1, 0xBF, 0xA5, FIL_, 0x08, 0xCC, 0x93,
+ FIL_, 0xE1, 0xBD, 0x90, FIL_, 0xCC, 0x94, FIL_,
+ 0xE1, 0xBD, 0x91, FIL_, 0xCC, 0x86, FIL_, 0xE1,
+ 0xBF, 0xA0, FIL_, 0xCD, 0x82, FIL_, 0xE1, 0xBF,
+ 0xA6, FIL_, 0xCC, 0x84, FIL_, 0xE1, 0xBF, 0xA1,
+ FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBD, 0xBA, FIL_,
+ 0xCC, 0x81, FIL_, 0xCF, 0x8D, FIL_, 0xCC, 0x88,
+ FIL_, 0xCF, 0x8B, FIL_, 0x06, 0xCC, 0x94, FIL_,
+ 0xE1, 0xBD, 0xA1, FIL_, 0xCD, 0x85, FIL_, 0xE1,
+ 0xBF, 0xB3, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBD,
+ 0xBC, FIL_, 0xCD, 0x82, FIL_, 0xE1, 0xBF, 0xB6,
+ FIL_, 0xCC, 0x93, FIL_, 0xE1, 0xBD, 0xA0, FIL_,
+ 0xCC, 0x81, FIL_, 0xCF, 0x8E, FIL_, 0x03, 0xCD,
+ 0x82, FIL_, 0xE1, 0xBF, 0x97, FIL_, 0xCC, 0x80,
+ FIL_, 0xE1, 0xBF, 0x92, FIL_, 0xCC, 0x81, FIL_,
+ 0xCE, 0x90, FIL_, 0x03, 0xCC, 0x80, FIL_, 0xE1,
+ 0xBF, 0xA2, FIL_, 0xCC, 0x81, FIL_, 0xCE, 0xB0,
+ FIL_, 0xCD, 0x82, FIL_, 0xE1, 0xBF, 0xA7, FIL_,
+ 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBF, 0xB4, FIL_,
+ 0x02, 0xCC, 0x88, FIL_, 0xCF, 0x94, FIL_, 0xCC,
+ 0x81, FIL_, 0xCF, 0x93, FIL_, 0x01, 0xCC, 0x88,
+ FIL_, 0xD0, 0x87, FIL_, 0x02, 0xCC, 0x86, FIL_,
+ 0xD3, 0x90, FIL_, 0xCC, 0x88, FIL_, 0xD3, 0x92,
+ FIL_, 0x01, 0xCC, 0x81, FIL_, 0xD0, 0x83, FIL_,
+ 0x03, 0xCC, 0x86, FIL_, 0xD3, 0x96, FIL_, 0xCC,
+ 0x80, FIL_, 0xD0, 0x80, FIL_, 0xCC, 0x88, FIL_,
+ 0xD0, 0x81, FIL_, 0x02, 0xCC, 0x88, FIL_, 0xD3,
+ 0x9C, FIL_, 0xCC, 0x86, FIL_, 0xD3, 0x81, FIL_,
+ 0x01, 0xCC, 0x88, FIL_, 0xD3, 0x9E, FIL_, 0x04,
+ 0xCC, 0x80, FIL_, 0xD0, 0x8D, FIL_, 0xCC, 0x88,
+ FIL_, 0xD3, 0xA4, FIL_, 0xCC, 0x86, FIL_, 0xD0,
+ 0x99, FIL_, 0xCC, 0x84, FIL_, 0xD3, 0xA2, FIL_,
+ 0x01, 0xCC, 0x81, FIL_, 0xD0, 0x8C, FIL_, 0x01,
+ 0xCC, 0x88, FIL_, 0xD3, 0xA6, FIL_, 0x04, 0xCC,
+ 0x86, FIL_, 0xD0, 0x8E, FIL_, 0xCC, 0x8B, FIL_,
+ 0xD3, 0xB2, FIL_, 0xCC, 0x88, FIL_, 0xD3, 0xB0,
+ FIL_, 0xCC, 0x84, FIL_, 0xD3, 0xAE, FIL_, 0x01,
+ 0xCC, 0x88, FIL_, 0xD3, 0xB4, FIL_, 0x01, 0xCC,
+ 0x88, FIL_, 0xD3, 0xB8, FIL_, 0x01, 0xCC, 0x88,
+ FIL_, 0xD3, 0xAC, FIL_, 0x02, 0xCC, 0x86, FIL_,
+ 0xD3, 0x91, FIL_, 0xCC, 0x88, FIL_, 0xD3, 0x93,
+ FIL_, 0x01, 0xCC, 0x81, FIL_, 0xD1, 0x93, FIL_,
+ 0x03, 0xCC, 0x80, FIL_, 0xD1, 0x90, FIL_, 0xCC,
+ 0x88, FIL_, 0xD1, 0x91, FIL_, 0xCC, 0x86, FIL_,
+ 0xD3, 0x97, FIL_, 0x02, 0xCC, 0x88, FIL_, 0xD3,
+ 0x9D, FIL_, 0xCC, 0x86, FIL_, 0xD3, 0x82, FIL_,
+ 0x01, 0xCC, 0x88, FIL_, 0xD3, 0x9F, FIL_, 0x04,
+ 0xCC, 0x88, FIL_, 0xD3, 0xA5, FIL_, 0xCC, 0x86,
+ FIL_, 0xD0, 0xB9, FIL_, 0xCC, 0x80, FIL_, 0xD1,
+ 0x9D, FIL_, 0xCC, 0x84, FIL_, 0xD3, 0xA3, FIL_,
+ 0x01, 0xCC, 0x81, FIL_, 0xD1, 0x9C, FIL_, 0x01,
+ 0xCC, 0x88, FIL_, 0xD3, 0xA7, FIL_, 0x04, 0xCC,
+ 0x84, FIL_, 0xD3, 0xAF, FIL_, 0xCC, 0x86, FIL_,
+ 0xD1, 0x9E, FIL_, 0xCC, 0x8B, FIL_, 0xD3, 0xB3,
+ FIL_, 0xCC, 0x88, FIL_, 0xD3, 0xB1, FIL_, 0x01,
+ 0xCC, 0x88, FIL_, 0xD3, 0xB5, FIL_, 0x01, 0xCC,
+ 0x88, FIL_, 0xD3, 0xB9, FIL_, 0x01, 0xCC, 0x88,
+ FIL_, 0xD3, 0xAD, FIL_, 0x01, 0xCC, 0x88, FIL_,
+ 0xD1, 0x97, FIL_, 0x01, 0xCC, 0x8F, FIL_, 0xD1,
+ 0xB6, FIL_, 0x01, 0xCC, 0x8F, FIL_, 0xD1, 0xB7,
+ FIL_, 0x01, 0xCC, 0x88, FIL_, 0xD3, 0x9A, FIL_,
+ 0x01, 0xCC, 0x88, FIL_, 0xD3, 0x9B, FIL_, 0x01,
+ 0xCC, 0x88, FIL_, 0xD3, 0xAA, FIL_, 0x01, 0xCC,
+ 0x88, FIL_, 0xD3, 0xAB, FIL_, 0x03, 0xD9, 0x94,
+ FIL_, 0xD8, 0xA3, FIL_, 0xD9, 0x93, FIL_, 0xD8,
+ 0xA2, FIL_, 0xD9, 0x95, FIL_, 0xD8, 0xA5, FIL_,
+ 0x01, 0xD9, 0x94, FIL_, 0xD8, 0xA4, FIL_, 0x01,
+ 0xD9, 0x94, FIL_, 0xD8, 0xA6, FIL_, 0x01, 0xD9,
+ 0x94, FIL_, 0xDB, 0x82, FIL_, 0x01, 0xD9, 0x94,
+ FIL_, 0xDB, 0x93, FIL_, 0x01, 0xD9, 0x94, FIL_,
+ 0xDB, 0x80, FIL_, 0x01, 0xE0, 0xA4, 0xBC, FIL_,
+ 0xE0, 0xA4, 0xA9, FIL_, 0x01, 0xE0, 0xA4, 0xBC,
+ FIL_, 0xE0, 0xA4, 0xB1, FIL_, 0x01, 0xE0, 0xA4,
+ 0xBC, FIL_, 0xE0, 0xA4, 0xB4, FIL_, 0x02, 0xE0,
+ 0xA6, 0xBE, FIL_, 0xE0, 0xA7, 0x8B, FIL_, 0xE0,
+ 0xA7, 0x97, FIL_, 0xE0, 0xA7, 0x8C, FIL_, 0x03,
+ 0xE0, 0xAD, 0x97, FIL_, 0xE0, 0xAD, 0x8C, FIL_,
+ 0xE0, 0xAC, 0xBE, FIL_, 0xE0, 0xAD, 0x8B, FIL_,
+ 0xE0, 0xAD, 0x96, FIL_, 0xE0, 0xAD, 0x88, FIL_,
+ 0x01, 0xE0, 0xAF, 0x97, FIL_, 0xE0, 0xAE, 0x94,
+ FIL_, 0x02, 0xE0, 0xAE, 0xBE, FIL_, 0xE0, 0xAF,
+ 0x8A, FIL_, 0xE0, 0xAF, 0x97, FIL_, 0xE0, 0xAF,
+ 0x8C, FIL_, 0x01, 0xE0, 0xAE, 0xBE, FIL_, 0xE0,
+ 0xAF, 0x8B, FIL_, 0x01, 0xE0, 0xB1, 0x96, FIL_,
+ 0xE0, 0xB1, 0x88, FIL_, 0x01, 0xE0, 0xB3, 0x95,
+ FIL_, 0xE0, 0xB3, 0x80, FIL_, 0x03, 0xE0, 0xB3,
+ 0x95, FIL_, 0xE0, 0xB3, 0x87, FIL_, 0xE0, 0xB3,
+ 0x82, FIL_, 0xE0, 0xB3, 0x8A, FIL_, 0xE0, 0xB3,
+ 0x96, FIL_, 0xE0, 0xB3, 0x88, FIL_, 0x01, 0xE0,
+ 0xB3, 0x95, FIL_, 0xE0, 0xB3, 0x8B, FIL_, 0x02,
+ 0xE0, 0xB4, 0xBE, FIL_, 0xE0, 0xB5, 0x8A, FIL_,
+ 0xE0, 0xB5, 0x97, FIL_, 0xE0, 0xB5, 0x8C, FIL_,
+ 0x01, 0xE0, 0xB4, 0xBE, FIL_, 0xE0, 0xB5, 0x8B,
+ FIL_, 0x03, 0xE0, 0xB7, 0x8F, FIL_, 0xE0, 0xB7,
+ 0x9C, FIL_, 0xE0, 0xB7, 0x8A, FIL_, 0xE0, 0xB7,
+ 0x9A, FIL_, 0xE0, 0xB7, 0x9F, FIL_, 0xE0, 0xB7,
+ 0x9E, FIL_, 0x01, 0xE0, 0xB7, 0x8A, FIL_, 0xE0,
+ 0xB7, 0x9D, FIL_, 0x01, 0xE1, 0x80, 0xAE, FIL_,
+ 0xE1, 0x80, 0xA6, FIL_, 0x01, 0xCC, 0x84, FIL_,
+ 0xE1, 0xB8, 0xB8, FIL_, 0x01, 0xCC, 0x84, FIL_,
+ 0xE1, 0xB8, 0xB9, FIL_, 0x01, 0xCC, 0x84, FIL_,
+ 0xE1, 0xB9, 0x9C, FIL_, 0x01, 0xCC, 0x84, FIL_,
+ 0xE1, 0xB9, 0x9D, FIL_, 0x01, 0xCC, 0x87, FIL_,
+ 0xE1, 0xB9, 0xA8, FIL_, 0x01, 0xCC, 0x87, FIL_,
+ 0xE1, 0xB9, 0xA9, FIL_, 0x02, 0xCC, 0x86, FIL_,
+ 0xE1, 0xBA, 0xB6, FIL_, 0xCC, 0x82, FIL_, 0xE1,
+ 0xBA, 0xAC, FIL_, 0x02, 0xCC, 0x86, FIL_, 0xE1,
+ 0xBA, 0xB7, FIL_, 0xCC, 0x82, FIL_, 0xE1, 0xBA,
+ 0xAD, FIL_, 0x01, 0xCC, 0x82, FIL_, 0xE1, 0xBB,
+ 0x86, FIL_, 0x01, 0xCC, 0x82, FIL_, 0xE1, 0xBB,
+ 0x87, FIL_, 0x01, 0xCC, 0x82, FIL_, 0xE1, 0xBB,
+ 0x98, FIL_, 0x01, 0xCC, 0x82, FIL_, 0xE1, 0xBB,
+ 0x99, FIL_, 0x04, 0xCC, 0x80, FIL_, 0xE1, 0xBC,
+ 0x82, FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBC, 0x84,
+ FIL_, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x80, FIL_,
+ 0xCD, 0x82, FIL_, 0xE1, 0xBC, 0x86, FIL_, 0x04,
+ 0xCD, 0x82, FIL_, 0xE1, 0xBC, 0x87, FIL_, 0xCC,
+ 0x80, FIL_, 0xE1, 0xBC, 0x83, FIL_, 0xCC, 0x81,
+ FIL_, 0xE1, 0xBC, 0x85, FIL_, 0xCD, 0x85, FIL_,
+ 0xE1, 0xBE, 0x81, FIL_, 0x01, 0xCD, 0x85, FIL_,
+ 0xE1, 0xBE, 0x82, FIL_, 0x01, 0xCD, 0x85, FIL_,
+ 0xE1, 0xBE, 0x83, FIL_, 0x01, 0xCD, 0x85, FIL_,
+ 0xE1, 0xBE, 0x84, FIL_, 0x01, 0xCD, 0x85, FIL_,
+ 0xE1, 0xBE, 0x85, FIL_, 0x01, 0xCD, 0x85, FIL_,
+ 0xE1, 0xBE, 0x86, FIL_, 0x01, 0xCD, 0x85, FIL_,
+ 0xE1, 0xBE, 0x87, FIL_, 0x04, 0xCD, 0x85, FIL_,
+ 0xE1, 0xBE, 0x88, FIL_, 0xCC, 0x80, FIL_, 0xE1,
+ 0xBC, 0x8A, FIL_, 0xCD, 0x82, FIL_, 0xE1, 0xBC,
+ 0x8E, FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBC, 0x8C,
+ FIL_, 0x04, 0xCC, 0x81, FIL_, 0xE1, 0xBC, 0x8D,
+ FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBC, 0x8B, FIL_,
+ 0xCD, 0x82, FIL_, 0xE1, 0xBC, 0x8F, FIL_, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBE, 0x89, FIL_, 0x01, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBE, 0x8A, FIL_, 0x01, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBE, 0x8B, FIL_, 0x01, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBE, 0x8C, FIL_, 0x01, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBE, 0x8D, FIL_, 0x01, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBE, 0x8E, FIL_, 0x01, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBE, 0x8F, FIL_, 0x02, 0xCC,
+ 0x80, FIL_, 0xE1, 0xBC, 0x92, FIL_, 0xCC, 0x81,
+ FIL_, 0xE1, 0xBC, 0x94, FIL_, 0x02, 0xCC, 0x80,
+ FIL_, 0xE1, 0xBC, 0x93, FIL_, 0xCC, 0x81, FIL_,
+ 0xE1, 0xBC, 0x95, FIL_, 0x02, 0xCC, 0x80, FIL_,
+ 0xE1, 0xBC, 0x9A, FIL_, 0xCC, 0x81, FIL_, 0xE1,
+ 0xBC, 0x9C, FIL_, 0x02, 0xCC, 0x80, FIL_, 0xE1,
+ 0xBC, 0x9B, FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBC,
+ 0x9D, FIL_, 0x04, 0xCD, 0x82, FIL_, 0xE1, 0xBC,
+ 0xA6, FIL_, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x90,
+ FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBC, 0xA4, FIL_,
+ 0xCC, 0x80, FIL_, 0xE1, 0xBC, 0xA2, FIL_, 0x04,
+ 0xCC, 0x80, FIL_, 0xE1, 0xBC, 0xA3, FIL_, 0xCC,
+ 0x81, FIL_, 0xE1, 0xBC, 0xA5, FIL_, 0xCD, 0x82,
+ FIL_, 0xE1, 0xBC, 0xA7, FIL_, 0xCD, 0x85, FIL_,
+ 0xE1, 0xBE, 0x91, FIL_, 0x01, 0xCD, 0x85, FIL_,
+ 0xE1, 0xBE, 0x92, FIL_, 0x01, 0xCD, 0x85, FIL_,
+ 0xE1, 0xBE, 0x93, FIL_, 0x01, 0xCD, 0x85, FIL_,
+ 0xE1, 0xBE, 0x94, FIL_, 0x01, 0xCD, 0x85, FIL_,
+ 0xE1, 0xBE, 0x95, FIL_, 0x01, 0xCD, 0x85, FIL_,
+ 0xE1, 0xBE, 0x96, FIL_, 0x01, 0xCD, 0x85, FIL_,
+ 0xE1, 0xBE, 0x97, FIL_, 0x04, 0xCD, 0x82, FIL_,
+ 0xE1, 0xBC, 0xAE, FIL_, 0xCC, 0x81, FIL_, 0xE1,
+ 0xBC, 0xAC, FIL_, 0xCD, 0x85, FIL_, 0xE1, 0xBE,
+ 0x98, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBC, 0xAA,
+ FIL_, 0x04, 0xCD, 0x82, FIL_, 0xE1, 0xBC, 0xAF,
+ FIL_, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x99, FIL_,
+ 0xCC, 0x81, FIL_, 0xE1, 0xBC, 0xAD, FIL_, 0xCC,
+ 0x80, FIL_, 0xE1, 0xBC, 0xAB, FIL_, 0x01, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBE, 0x9A, FIL_, 0x01, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBE, 0x9B, FIL_, 0x01, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBE, 0x9C, FIL_, 0x01, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBE, 0x9D, FIL_, 0x01, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBE, 0x9E, FIL_, 0x01, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBE, 0x9F, FIL_, 0x03, 0xCC,
+ 0x81, FIL_, 0xE1, 0xBC, 0xB4, FIL_, 0xCD, 0x82,
+ FIL_, 0xE1, 0xBC, 0xB6, FIL_, 0xCC, 0x80, FIL_,
+ 0xE1, 0xBC, 0xB2, FIL_, 0x03, 0xCC, 0x81, FIL_,
+ 0xE1, 0xBC, 0xB5, FIL_, 0xCD, 0x82, FIL_, 0xE1,
+ 0xBC, 0xB7, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBC,
+ 0xB3, FIL_, 0x03, 0xCC, 0x81, FIL_, 0xE1, 0xBC,
+ 0xBC, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBC, 0xBA,
+ FIL_, 0xCD, 0x82, FIL_, 0xE1, 0xBC, 0xBE, FIL_,
+ 0x03, 0xCC, 0x80, FIL_, 0xE1, 0xBC, 0xBB, FIL_,
+ 0xCD, 0x82, FIL_, 0xE1, 0xBC, 0xBF, FIL_, 0xCC,
+ 0x81, FIL_, 0xE1, 0xBC, 0xBD, FIL_, 0x02, 0xCC,
+ 0x80, FIL_, 0xE1, 0xBD, 0x82, FIL_, 0xCC, 0x81,
+ FIL_, 0xE1, 0xBD, 0x84, FIL_, 0x02, 0xCC, 0x80,
+ FIL_, 0xE1, 0xBD, 0x83, FIL_, 0xCC, 0x81, FIL_,
+ 0xE1, 0xBD, 0x85, FIL_, 0x02, 0xCC, 0x81, FIL_,
+ 0xE1, 0xBD, 0x8C, FIL_, 0xCC, 0x80, FIL_, 0xE1,
+ 0xBD, 0x8A, FIL_, 0x02, 0xCC, 0x81, FIL_, 0xE1,
+ 0xBD, 0x8D, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBD,
+ 0x8B, FIL_, 0x03, 0xCC, 0x81, FIL_, 0xE1, 0xBD,
+ 0x94, FIL_, 0xCD, 0x82, FIL_, 0xE1, 0xBD, 0x96,
+ FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBD, 0x92, FIL_,
+ 0x03, 0xCD, 0x82, FIL_, 0xE1, 0xBD, 0x97, FIL_,
+ 0xCC, 0x81, FIL_, 0xE1, 0xBD, 0x95, FIL_, 0xCC,
+ 0x80, FIL_, 0xE1, 0xBD, 0x93, FIL_, 0x03, 0xCC,
+ 0x81, FIL_, 0xE1, 0xBD, 0x9D, FIL_, 0xCD, 0x82,
+ FIL_, 0xE1, 0xBD, 0x9F, FIL_, 0xCC, 0x80, FIL_,
+ 0xE1, 0xBD, 0x9B, FIL_, 0x04, 0xCC, 0x81, FIL_,
+ 0xE1, 0xBD, 0xA4, FIL_, 0xCC, 0x80, FIL_, 0xE1,
+ 0xBD, 0xA2, FIL_, 0xCD, 0x82, FIL_, 0xE1, 0xBD,
+ 0xA6, FIL_, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0xA0,
+ FIL_, 0x04, 0xCD, 0x82, FIL_, 0xE1, 0xBD, 0xA7,
+ FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBD, 0xA5, FIL_,
+ 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0xA1, FIL_, 0xCC,
+ 0x80, FIL_, 0xE1, 0xBD, 0xA3, FIL_, 0x01, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBE, 0xA2, FIL_, 0x01, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBE, 0xA3, FIL_, 0x01, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBE, 0xA4, FIL_, 0x01, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBE, 0xA5, FIL_, 0x01, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBE, 0xA6, FIL_, 0x01, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBE, 0xA7, FIL_, 0x04, 0xCC,
+ 0x81, FIL_, 0xE1, 0xBD, 0xAC, FIL_, 0xCC, 0x80,
+ FIL_, 0xE1, 0xBD, 0xAA, FIL_, 0xCD, 0x82, FIL_,
+ 0xE1, 0xBD, 0xAE, FIL_, 0xCD, 0x85, FIL_, 0xE1,
+ 0xBE, 0xA8, FIL_, 0x04, 0xCC, 0x81, FIL_, 0xE1,
+ 0xBD, 0xAD, FIL_, 0xCD, 0x85, FIL_, 0xE1, 0xBE,
+ 0xA9, FIL_, 0xCD, 0x82, FIL_, 0xE1, 0xBD, 0xAF,
+ FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBD, 0xAB, FIL_,
+ 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0xAA, FIL_,
+ 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0xAB, FIL_,
+ 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0xAC, FIL_,
+ 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0xAD, FIL_,
+ 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0xAE, FIL_,
+ 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0xAF, FIL_,
+ 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0xB2, FIL_,
+ 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBF, 0x82, FIL_,
+ 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBF, 0xB2, FIL_,
+ 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0xB7, FIL_,
+ 0x03, 0xCD, 0x82, FIL_, 0xE1, 0xBF, 0x8F, FIL_,
+ 0xCC, 0x80, FIL_, 0xE1, 0xBF, 0x8D, FIL_, 0xCC,
+ 0x81, FIL_, 0xE1, 0xBF, 0x8E, FIL_, 0x01, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBF, 0x87, FIL_, 0x01, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBF, 0xB7, FIL_, 0x03, 0xCC,
+ 0x80, FIL_, 0xE1, 0xBF, 0x9D, FIL_, 0xCD, 0x82,
+ FIL_, 0xE1, 0xBF, 0x9F, FIL_, 0xCC, 0x81, FIL_,
+ 0xE1, 0xBF, 0x9E, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x86, 0x9A, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x86, 0x9B, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x86, 0xAE, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x87, 0x8D, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x87, 0x8F, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x87, 0x8E, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x88, 0x84, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x88, 0x89, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x88, 0x8C, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x88, 0xA4, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x88, 0xA6, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x89, 0x81, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x89, 0x84, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x89, 0x87, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x89, 0x89, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x89, 0xAD, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x89, 0xA2, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x89, 0xB0, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x89, 0xB1, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x89, 0xB4, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x89, 0xB5, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x89, 0xB8, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x89, 0xB9, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x8A, 0x80, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x8A, 0x81, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x8B, 0xA0, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x8B, 0xA1, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x8A, 0x84, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x8A, 0x85, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x8A, 0x88, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x8A, 0x89, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x8B, 0xA2, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x8B, 0xA3, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x8A, 0xAC, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x8A, 0xAD, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x8A, 0xAE, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x8A, 0xAF, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x8B, 0xAA, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x8B, 0xAB, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x8B, 0xAC, FIL_, 0x01, 0xCC, 0xB8, FIL_,
+ 0xE2, 0x8B, 0xAD, FIL_, 0x01, 0xE3, 0x82, 0x99,
+ FIL_, 0xE3, 0x82, 0x94, FIL_, 0x01, 0xE3, 0x82,
+ 0x99, FIL_, 0xE3, 0x81, 0x8C, FIL_, 0x01, 0xE3,
+ 0x82, 0x99, FIL_, 0xE3, 0x81, 0x8E, FIL_, 0x01,
+ 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x81, 0x90, FIL_,
+ 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x81, 0x92,
+ FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x81,
+ 0x94, FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3,
+ 0x81, 0x96, FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_,
+ 0xE3, 0x81, 0x98, FIL_, 0x01, 0xE3, 0x82, 0x99,
+ FIL_, 0xE3, 0x81, 0x9A, FIL_, 0x01, 0xE3, 0x82,
+ 0x99, FIL_, 0xE3, 0x81, 0x9C, FIL_, 0x01, 0xE3,
+ 0x82, 0x99, FIL_, 0xE3, 0x81, 0x9E, FIL_, 0x01,
+ 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x81, 0xA0, FIL_,
+ 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x81, 0xA2,
+ FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x81,
+ 0xA5, FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3,
+ 0x81, 0xA7, FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_,
+ 0xE3, 0x81, 0xA9, FIL_, 0x02, 0xE3, 0x82, 0x9A,
+ FIL_, 0xE3, 0x81, 0xB1, FIL_, 0xE3, 0x82, 0x99,
+ FIL_, 0xE3, 0x81, 0xB0, FIL_, 0x02, 0xE3, 0x82,
+ 0x9A, FIL_, 0xE3, 0x81, 0xB4, FIL_, 0xE3, 0x82,
+ 0x99, FIL_, 0xE3, 0x81, 0xB3, FIL_, 0x02, 0xE3,
+ 0x82, 0x9A, FIL_, 0xE3, 0x81, 0xB7, FIL_, 0xE3,
+ 0x82, 0x99, FIL_, 0xE3, 0x81, 0xB6, FIL_, 0x02,
+ 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x81, 0xB9, FIL_,
+ 0xE3, 0x82, 0x9A, FIL_, 0xE3, 0x81, 0xBA, FIL_,
+ 0x02, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x81, 0xBC,
+ FIL_, 0xE3, 0x82, 0x9A, FIL_, 0xE3, 0x81, 0xBD,
+ FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x82,
+ 0x9E, FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3,
+ 0x83, 0xB4, FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_,
+ 0xE3, 0x82, 0xAC, FIL_, 0x01, 0xE3, 0x82, 0x99,
+ FIL_, 0xE3, 0x82, 0xAE, FIL_, 0x01, 0xE3, 0x82,
+ 0x99, FIL_, 0xE3, 0x82, 0xB0, FIL_, 0x01, 0xE3,
+ 0x82, 0x99, FIL_, 0xE3, 0x82, 0xB2, FIL_, 0x01,
+ 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x82, 0xB4, FIL_,
+ 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x82, 0xB6,
+ FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x82,
+ 0xB8, FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3,
+ 0x82, 0xBA, FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_,
+ 0xE3, 0x82, 0xBC, FIL_, 0x01, 0xE3, 0x82, 0x99,
+ FIL_, 0xE3, 0x82, 0xBE, FIL_, 0x01, 0xE3, 0x82,
+ 0x99, FIL_, 0xE3, 0x83, 0x80, FIL_, 0x01, 0xE3,
+ 0x82, 0x99, FIL_, 0xE3, 0x83, 0x82, FIL_, 0x01,
+ 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x83, 0x85, FIL_,
+ 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x83, 0x87,
+ FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x83,
+ 0x89, FIL_, 0x02, 0xE3, 0x82, 0x99, FIL_, 0xE3,
+ 0x83, 0x90, FIL_, 0xE3, 0x82, 0x9A, FIL_, 0xE3,
+ 0x83, 0x91, FIL_, 0x02, 0xE3, 0x82, 0x99, FIL_,
+ 0xE3, 0x83, 0x93, FIL_, 0xE3, 0x82, 0x9A, FIL_,
+ 0xE3, 0x83, 0x94, FIL_, 0x02, 0xE3, 0x82, 0x99,
+ FIL_, 0xE3, 0x83, 0x96, FIL_, 0xE3, 0x82, 0x9A,
+ FIL_, 0xE3, 0x83, 0x97, FIL_, 0x02, 0xE3, 0x82,
+ 0x9A, FIL_, 0xE3, 0x83, 0x9A, FIL_, 0xE3, 0x82,
+ 0x99, FIL_, 0xE3, 0x83, 0x99, FIL_, 0x02, 0xE3,
+ 0x82, 0x9A, FIL_, 0xE3, 0x83, 0x9D, FIL_, 0xE3,
+ 0x82, 0x99, FIL_, 0xE3, 0x83, 0x9C, FIL_, 0x01,
+ 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x83, 0xB7, FIL_,
+ 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x83, 0xB8,
+ FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x83,
+ 0xB9, FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3,
+ 0x83, 0xBA, FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_,
+ 0xE3, 0x83, 0xBE, FIL_, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0,
+ },
+ {
+ 0x01, 0xCC, 0xB8, FIL_, 0xE2, 0x89, 0xAE, FIL_,
+ 0x01, 0xCC, 0xB8, FIL_, 0xE2, 0x89, 0xA0, FIL_,
+ 0x01, 0xCC, 0xB8, FIL_, 0xE2, 0x89, 0xAF, FIL_,
+ 0x10, 0xCC, 0xA5, FIL_, 0xE1, 0xB8, 0x80, FIL_,
+ 0xCC, 0x87, FIL_, 0xC8, 0xA6, FIL_, 0xCC, 0x83,
+ FIL_, 0xC3, 0x83, FIL_, 0xCC, 0x91, FIL_, 0xC8,
+ 0x82, FIL_, 0xCC, 0x8F, FIL_, 0xC8, 0x80, FIL_,
+ 0xCC, 0x8A, FIL_, 0xC3, 0x85, FIL_, 0xCC, 0x88,
+ FIL_, 0xC3, 0x84, FIL_, 0xCC, 0x89, FIL_, 0xE1,
+ 0xBA, 0xA2, FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xBA,
+ 0xA0, FIL_, 0xCC, 0x8C, FIL_, 0xC7, 0x8D, FIL_,
+ 0xCC, 0x80, FIL_, 0xC3, 0x80, FIL_, 0xCC, 0x81,
+ FIL_, 0xC3, 0x81, FIL_, 0xCC, 0x82, FIL_, 0xC3,
+ 0x82, FIL_, 0xCC, 0xA8, FIL_, 0xC4, 0x84, FIL_,
+ 0xCC, 0x86, FIL_, 0xC4, 0x82, FIL_, 0xCC, 0x84,
+ FIL_, 0xC4, 0x80, FIL_, 0x03, 0xCC, 0xB1, FIL_,
+ 0xE1, 0xB8, 0x86, FIL_, 0xCC, 0x87, FIL_, 0xE1,
+ 0xB8, 0x82, FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xB8,
+ 0x84, FIL_, 0x05, 0xCC, 0xA7, FIL_, 0xC3, 0x87,
+ FIL_, 0xCC, 0x8C, FIL_, 0xC4, 0x8C, FIL_, 0xCC,
+ 0x81, FIL_, 0xC4, 0x86, FIL_, 0xCC, 0x82, FIL_,
+ 0xC4, 0x88, FIL_, 0xCC, 0x87, FIL_, 0xC4, 0x8A,
+ FIL_, 0x06, 0xCC, 0xA7, FIL_, 0xE1, 0xB8, 0x90,
+ FIL_, 0xCC, 0x8C, FIL_, 0xC4, 0x8E, FIL_, 0xCC,
+ 0xB1, FIL_, 0xE1, 0xB8, 0x8E, FIL_, 0xCC, 0xAD,
+ FIL_, 0xE1, 0xB8, 0x92, FIL_, 0xCC, 0xA3, FIL_,
+ 0xE1, 0xB8, 0x8C, FIL_, 0xCC, 0x87, FIL_, 0xE1,
+ 0xB8, 0x8A, FIL_, 0x11, 0xCC, 0x84, FIL_, 0xC4,
+ 0x92, FIL_, 0xCC, 0x86, FIL_, 0xC4, 0x94, FIL_,
+ 0xCC, 0xA3, FIL_, 0xE1, 0xBA, 0xB8, FIL_, 0xCC,
+ 0x91, FIL_, 0xC8, 0x86, FIL_, 0xCC, 0x82, FIL_,
+ 0xC3, 0x8A, FIL_, 0xCC, 0x8F, FIL_, 0xC8, 0x84,
+ FIL_, 0xCC, 0xAD, FIL_, 0xE1, 0xB8, 0x98, FIL_,
+ 0xCC, 0x89, FIL_, 0xE1, 0xBA, 0xBA, FIL_, 0xCC,
+ 0xA7, FIL_, 0xC8, 0xA8, FIL_, 0xCC, 0x8C, FIL_,
+ 0xC4, 0x9A, FIL_, 0xCC, 0x80, FIL_, 0xC3, 0x88,
+ FIL_, 0xCC, 0xA8, FIL_, 0xC4, 0x98, FIL_, 0xCC,
+ 0x83, FIL_, 0xE1, 0xBA, 0xBC, FIL_, 0xCC, 0x87,
+ FIL_, 0xC4, 0x96, FIL_, 0xCC, 0x81, FIL_, 0xC3,
+ 0x89, FIL_, 0xCC, 0x88, FIL_, 0xC3, 0x8B, FIL_,
+ 0xCC, 0xB0, FIL_, 0xE1, 0xB8, 0x9A, FIL_, 0x01,
+ 0xCC, 0x87, FIL_, 0xE1, 0xB8, 0x9E, FIL_, 0x07,
+ 0xCC, 0x8C, FIL_, 0xC7, 0xA6, FIL_, 0xCC, 0x86,
+ FIL_, 0xC4, 0x9E, FIL_, 0xCC, 0x82, FIL_, 0xC4,
+ 0x9C, FIL_, 0xCC, 0xA7, FIL_, 0xC4, 0xA2, FIL_,
+ 0xCC, 0x84, FIL_, 0xE1, 0xB8, 0xA0, FIL_, 0xCC,
+ 0x81, FIL_, 0xC7, 0xB4, FIL_, 0xCC, 0x87, FIL_,
+ 0xC4, 0xA0, FIL_, 0x07, 0xCC, 0x87, FIL_, 0xE1,
+ 0xB8, 0xA2, FIL_, 0xCC, 0xA7, FIL_, 0xE1, 0xB8,
+ 0xA8, FIL_, 0xCC, 0x82, FIL_, 0xC4, 0xA4, FIL_,
+ 0xCC, 0x88, FIL_, 0xE1, 0xB8, 0xA6, FIL_, 0xCC,
+ 0x8C, FIL_, 0xC8, 0x9E, FIL_, 0xCC, 0xAE, FIL_,
+ 0xE1, 0xB8, 0xAA, FIL_, 0xCC, 0xA3, FIL_, 0xE1,
+ 0xB8, 0xA4, FIL_, 0x0F, 0xCC, 0xB0, FIL_, 0xE1,
+ 0xB8, 0xAC, FIL_, 0xCC, 0x8C, FIL_, 0xC7, 0x8F,
+ FIL_, 0xCC, 0x80, FIL_, 0xC3, 0x8C, FIL_, 0xCC,
+ 0x89, FIL_, 0xE1, 0xBB, 0x88, FIL_, 0xCC, 0xA3,
+ FIL_, 0xE1, 0xBB, 0x8A, FIL_, 0xCC, 0x91, FIL_,
+ 0xC8, 0x8A, FIL_, 0xCC, 0x88, FIL_, 0xC3, 0x8F,
+ FIL_, 0xCC, 0x82, FIL_, 0xC3, 0x8E, FIL_, 0xCC,
+ 0x81, FIL_, 0xC3, 0x8D, FIL_, 0xCC, 0x83, FIL_,
+ 0xC4, 0xA8, FIL_, 0xCC, 0x87, FIL_, 0xC4, 0xB0,
+ FIL_, 0xCC, 0x8F, FIL_, 0xC8, 0x88, FIL_, 0xCC,
+ 0xA8, FIL_, 0xC4, 0xAE, FIL_, 0xCC, 0x86, FIL_,
+ 0xC4, 0xAC, FIL_, 0xCC, 0x84, FIL_, 0xC4, 0xAA,
+ FIL_, 0x01, 0xCC, 0x82, FIL_, 0xC4, 0xB4, FIL_,
+ 0x05, 0xCC, 0x81, FIL_, 0xE1, 0xB8, 0xB0, FIL_,
+ 0xCC, 0x8C, FIL_, 0xC7, 0xA8, FIL_, 0xCC, 0xB1,
+ FIL_, 0xE1, 0xB8, 0xB4, FIL_, 0xCC, 0xA7, FIL_,
+ 0xC4, 0xB6, FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xB8,
+ 0xB2, FIL_, 0x06, 0xCC, 0xA3, FIL_, 0xE1, 0xB8,
+ 0xB6, FIL_, 0xCC, 0x8C, FIL_, 0xC4, 0xBD, FIL_,
+ 0xCC, 0xAD, FIL_, 0xE1, 0xB8, 0xBC, FIL_, 0xCC,
+ 0xB1, FIL_, 0xE1, 0xB8, 0xBA, FIL_, 0xCC, 0xA7,
+ FIL_, 0xC4, 0xBB, FIL_, 0xCC, 0x81, FIL_, 0xC4,
+ 0xB9, FIL_, 0x03, 0xCC, 0x81, FIL_, 0xE1, 0xB8,
+ 0xBE, FIL_, 0xCC, 0x87, FIL_, 0xE1, 0xB9, 0x80,
+ FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xB9, 0x82, FIL_,
+ 0x09, 0xCC, 0x83, FIL_, 0xC3, 0x91, FIL_, 0xCC,
+ 0x81, FIL_, 0xC5, 0x83, FIL_, 0xCC, 0xA7, FIL_,
+ 0xC5, 0x85, FIL_, 0xCC, 0x8C, FIL_, 0xC5, 0x87,
+ FIL_, 0xCC, 0x87, FIL_, 0xE1, 0xB9, 0x84, FIL_,
+ 0xCC, 0xA3, FIL_, 0xE1, 0xB9, 0x86, FIL_, 0xCC,
+ 0xB1, FIL_, 0xE1, 0xB9, 0x88, FIL_, 0xCC, 0xAD,
+ FIL_, 0xE1, 0xB9, 0x8A, FIL_, 0xCC, 0x80, FIL_,
+ 0xC7, 0xB8, FIL_, 0x10, 0xCC, 0x89, FIL_, 0xE1,
+ 0xBB, 0x8E, FIL_, 0xCC, 0x84, FIL_, 0xC5, 0x8C,
+ FIL_, 0xCC, 0x82, FIL_, 0xC3, 0x94, FIL_, 0xCC,
+ 0x86, FIL_, 0xC5, 0x8E, FIL_, 0xCC, 0x83, FIL_,
+ 0xC3, 0x95, FIL_, 0xCC, 0x8B, FIL_, 0xC5, 0x90,
+ FIL_, 0xCC, 0x88, FIL_, 0xC3, 0x96, FIL_, 0xCC,
+ 0x9B, FIL_, 0xC6, 0xA0, FIL_, 0xCC, 0x91, FIL_,
+ 0xC8, 0x8E, FIL_, 0xCC, 0x8C, FIL_, 0xC7, 0x91,
+ FIL_, 0xCC, 0x8F, FIL_, 0xC8, 0x8C, FIL_, 0xCC,
+ 0xA3, FIL_, 0xE1, 0xBB, 0x8C, FIL_, 0xCC, 0x80,
+ FIL_, 0xC3, 0x92, FIL_, 0xCC, 0xA8, FIL_, 0xC7,
+ 0xAA, FIL_, 0xCC, 0x87, FIL_, 0xC8, 0xAE, FIL_,
+ 0xCC, 0x81, FIL_, 0xC3, 0x93, FIL_, 0x02, 0xCC,
+ 0x87, FIL_, 0xE1, 0xB9, 0x96, FIL_, 0xCC, 0x81,
+ FIL_, 0xE1, 0xB9, 0x94, FIL_, 0x08, 0xCC, 0xA7,
+ FIL_, 0xC5, 0x96, FIL_, 0xCC, 0x8C, FIL_, 0xC5,
+ 0x98, FIL_, 0xCC, 0x91, FIL_, 0xC8, 0x92, FIL_,
+ 0xCC, 0x8F, FIL_, 0xC8, 0x90, FIL_, 0xCC, 0x81,
+ FIL_, 0xC5, 0x94, FIL_, 0xCC, 0x87, FIL_, 0xE1,
+ 0xB9, 0x98, FIL_, 0xCC, 0xB1, FIL_, 0xE1, 0xB9,
+ 0x9E, FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xB9, 0x9A,
+ FIL_, 0x07, 0xCC, 0xA6, FIL_, 0xC8, 0x98, FIL_,
+ 0xCC, 0x81, FIL_, 0xC5, 0x9A, FIL_, 0xCC, 0x82,
+ FIL_, 0xC5, 0x9C, FIL_, 0xCC, 0xA7, FIL_, 0xC5,
+ 0x9E, FIL_, 0xCC, 0x8C, FIL_, 0xC5, 0xA0, FIL_,
+ 0xCC, 0x87, FIL_, 0xE1, 0xB9, 0xA0, FIL_, 0xCC,
+ 0xA3, FIL_, 0xE1, 0xB9, 0xA2, FIL_, 0x07, 0xCC,
+ 0xA6, FIL_, 0xC8, 0x9A, FIL_, 0xCC, 0x87, FIL_,
+ 0xE1, 0xB9, 0xAA, FIL_, 0xCC, 0xA3, FIL_, 0xE1,
+ 0xB9, 0xAC, FIL_, 0xCC, 0xB1, FIL_, 0xE1, 0xB9,
+ 0xAE, FIL_, 0xCC, 0xAD, FIL_, 0xE1, 0xB9, 0xB0,
+ FIL_, 0xCC, 0xA7, FIL_, 0xC5, 0xA2, FIL_, 0xCC,
+ 0x8C, FIL_, 0xC5, 0xA4, FIL_, 0x13, 0xCC, 0x8A,
+ FIL_, 0xC5, 0xAE, FIL_, 0xCC, 0x88, FIL_, 0xC3,
+ 0x9C, FIL_, 0xCC, 0x8B, FIL_, 0xC5, 0xB0, FIL_,
+ 0xCC, 0xAD, FIL_, 0xE1, 0xB9, 0xB6, FIL_, 0xCC,
+ 0xA8, FIL_, 0xC5, 0xB2, FIL_, 0xCC, 0x8C, FIL_,
+ 0xC7, 0x93, FIL_, 0xCC, 0x80, FIL_, 0xC3, 0x99,
+ FIL_, 0xCC, 0x8F, FIL_, 0xC8, 0x94, FIL_, 0xCC,
+ 0xA3, FIL_, 0xE1, 0xBB, 0xA4, FIL_, 0xCC, 0xA4,
+ FIL_, 0xE1, 0xB9, 0xB2, FIL_, 0xCC, 0x81, FIL_,
+ 0xC3, 0x9A, FIL_, 0xCC, 0x82, FIL_, 0xC3, 0x9B,
+ FIL_, 0xCC, 0xB0, FIL_, 0xE1, 0xB9, 0xB4, FIL_,
+ 0xCC, 0x83, FIL_, 0xC5, 0xA8, FIL_, 0xCC, 0x89,
+ FIL_, 0xE1, 0xBB, 0xA6, FIL_, 0xCC, 0x84, FIL_,
+ 0xC5, 0xAA, FIL_, 0xCC, 0x91, FIL_, 0xC8, 0x96,
+ FIL_, 0xCC, 0x86, FIL_, 0xC5, 0xAC, FIL_, 0xCC,
+ 0x9B, FIL_, 0xC6, 0xAF, FIL_, 0x02, 0xCC, 0xA3,
+ FIL_, 0xE1, 0xB9, 0xBE, FIL_, 0xCC, 0x83, FIL_,
+ 0xE1, 0xB9, 0xBC, FIL_, 0x06, 0xCC, 0x88, FIL_,
+ 0xE1, 0xBA, 0x84, FIL_, 0xCC, 0x81, FIL_, 0xE1,
+ 0xBA, 0x82, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBA,
+ 0x80, FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xBA, 0x88,
+ FIL_, 0xCC, 0x82, FIL_, 0xC5, 0xB4, FIL_, 0xCC,
+ 0x87, FIL_, 0xE1, 0xBA, 0x86, FIL_, 0x02, 0xCC,
+ 0x88, FIL_, 0xE1, 0xBA, 0x8C, FIL_, 0xCC, 0x87,
+ FIL_, 0xE1, 0xBA, 0x8A, FIL_, 0x09, 0xCC, 0x89,
+ FIL_, 0xE1, 0xBB, 0xB6, FIL_, 0xCC, 0xA3, FIL_,
+ 0xE1, 0xBB, 0xB4, FIL_, 0xCC, 0x80, FIL_, 0xE1,
+ 0xBB, 0xB2, FIL_, 0xCC, 0x88, FIL_, 0xC5, 0xB8,
+ FIL_, 0xCC, 0x81, FIL_, 0xC3, 0x9D, FIL_, 0xCC,
+ 0x83, FIL_, 0xE1, 0xBB, 0xB8, FIL_, 0xCC, 0x87,
+ FIL_, 0xE1, 0xBA, 0x8E, FIL_, 0xCC, 0x84, FIL_,
+ 0xC8, 0xB2, FIL_, 0xCC, 0x82, FIL_, 0xC5, 0xB6,
+ FIL_, 0x06, 0xCC, 0x82, FIL_, 0xE1, 0xBA, 0x90,
+ FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xBA, 0x92, FIL_,
+ 0xCC, 0xB1, FIL_, 0xE1, 0xBA, 0x94, FIL_, 0xCC,
+ 0x8C, FIL_, 0xC5, 0xBD, FIL_, 0xCC, 0x87, FIL_,
+ 0xC5, 0xBB, FIL_, 0xCC, 0x81, FIL_, 0xC5, 0xB9,
+ FIL_, 0x10, 0xCC, 0xA3, FIL_, 0xE1, 0xBA, 0xA1,
+ FIL_, 0xCC, 0xA8, FIL_, 0xC4, 0x85, FIL_, 0xCC,
+ 0x81, FIL_, 0xC3, 0xA1, FIL_, 0xCC, 0x82, FIL_,
+ 0xC3, 0xA2, FIL_, 0xCC, 0x89, FIL_, 0xE1, 0xBA,
+ 0xA3, FIL_, 0xCC, 0x83, FIL_, 0xC3, 0xA3, FIL_,
+ 0xCC, 0x8C, FIL_, 0xC7, 0x8E, FIL_, 0xCC, 0x8A,
+ FIL_, 0xC3, 0xA5, FIL_, 0xCC, 0x88, FIL_, 0xC3,
+ 0xA4, FIL_, 0xCC, 0x87, FIL_, 0xC8, 0xA7, FIL_,
+ 0xCC, 0x91, FIL_, 0xC8, 0x83, FIL_, 0xCC, 0xA5,
+ FIL_, 0xE1, 0xB8, 0x81, FIL_, 0xCC, 0x84, FIL_,
+ 0xC4, 0x81, FIL_, 0xCC, 0x8F, FIL_, 0xC8, 0x81,
+ FIL_, 0xCC, 0x86, FIL_, 0xC4, 0x83, FIL_, 0xCC,
+ 0x80, FIL_, 0xC3, 0xA0, FIL_, 0x03, 0xCC, 0xA3,
+ FIL_, 0xE1, 0xB8, 0x85, FIL_, 0xCC, 0x87, FIL_,
+ 0xE1, 0xB8, 0x83, FIL_, 0xCC, 0xB1, FIL_, 0xE1,
+ 0xB8, 0x87, FIL_, 0x05, 0xCC, 0x87, FIL_, 0xC4,
+ 0x8B, FIL_, 0xCC, 0x8C, FIL_, 0xC4, 0x8D, FIL_,
+ 0xCC, 0x82, FIL_, 0xC4, 0x89, FIL_, 0xCC, 0x81,
+ FIL_, 0xC4, 0x87, FIL_, 0xCC, 0xA7, FIL_, 0xC3,
+ 0xA7, FIL_, 0x06, 0xCC, 0x87, FIL_, 0xE1, 0xB8,
+ 0x8B, FIL_, 0xCC, 0xA7, FIL_, 0xE1, 0xB8, 0x91,
+ FIL_, 0xCC, 0xB1, FIL_, 0xE1, 0xB8, 0x8F, FIL_,
+ 0xCC, 0xA3, FIL_, 0xE1, 0xB8, 0x8D, FIL_, 0xCC,
+ 0x8C, FIL_, 0xC4, 0x8F, FIL_, 0xCC, 0xAD, FIL_,
+ 0xE1, 0xB8, 0x93, FIL_, 0x11, 0xCC, 0x80, FIL_,
+ 0xC3, 0xA8, FIL_, 0xCC, 0x81, FIL_, 0xC3, 0xA9,
+ FIL_, 0xCC, 0x82, FIL_, 0xC3, 0xAA, FIL_, 0xCC,
+ 0x88, FIL_, 0xC3, 0xAB, FIL_, 0xCC, 0x84, FIL_,
+ 0xC4, 0x93, FIL_, 0xCC, 0x86, FIL_, 0xC4, 0x95,
+ FIL_, 0xCC, 0x87, FIL_, 0xC4, 0x97, FIL_, 0xCC,
+ 0xA8, FIL_, 0xC4, 0x99, FIL_, 0xCC, 0x8C, FIL_,
+ 0xC4, 0x9B, FIL_, 0xCC, 0x8F, FIL_, 0xC8, 0x85,
+ FIL_, 0xCC, 0x91, FIL_, 0xC8, 0x87, FIL_, 0xCC,
+ 0xA3, FIL_, 0xE1, 0xBA, 0xB9, FIL_, 0xCC, 0xA7,
+ FIL_, 0xC8, 0xA9, FIL_, 0xCC, 0x83, FIL_, 0xE1,
+ 0xBA, 0xBD, FIL_, 0xCC, 0x89, FIL_, 0xE1, 0xBA,
+ 0xBB, FIL_, 0xCC, 0xAD, FIL_, 0xE1, 0xB8, 0x99,
+ FIL_, 0xCC, 0xB0, FIL_, 0xE1, 0xB8, 0x9B, FIL_,
+ 0x01, 0xCC, 0x87, FIL_, 0xE1, 0xB8, 0x9F, FIL_,
+ 0x07, 0xCC, 0x86, FIL_, 0xC4, 0x9F, FIL_, 0xCC,
+ 0x87, FIL_, 0xC4, 0xA1, FIL_, 0xCC, 0x82, FIL_,
+ 0xC4, 0x9D, FIL_, 0xCC, 0x84, FIL_, 0xE1, 0xB8,
+ 0xA1, FIL_, 0xCC, 0x8C, FIL_, 0xC7, 0xA7, FIL_,
+ 0xCC, 0xA7, FIL_, 0xC4, 0xA3, FIL_, 0xCC, 0x81,
+ FIL_, 0xC7, 0xB5, FIL_, 0x08, 0xCC, 0xA7, FIL_,
+ 0xE1, 0xB8, 0xA9, FIL_, 0xCC, 0xB1, FIL_, 0xE1,
+ 0xBA, 0x96, FIL_, 0xCC, 0x8C, FIL_, 0xC8, 0x9F,
+ FIL_, 0xCC, 0xAE, FIL_, 0xE1, 0xB8, 0xAB, FIL_,
+ 0xCC, 0x88, FIL_, 0xE1, 0xB8, 0xA7, FIL_, 0xCC,
+ 0xA3, FIL_, 0xE1, 0xB8, 0xA5, FIL_, 0xCC, 0x87,
+ FIL_, 0xE1, 0xB8, 0xA3, FIL_, 0xCC, 0x82, FIL_,
+ 0xC4, 0xA5, FIL_, 0x0E, 0xCC, 0x88, FIL_, 0xC3,
+ 0xAF, FIL_, 0xCC, 0x89, FIL_, 0xE1, 0xBB, 0x89,
+ FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xBB, 0x8B, FIL_,
+ 0xCC, 0x82, FIL_, 0xC3, 0xAE, FIL_, 0xCC, 0x81,
+ FIL_, 0xC3, 0xAD, FIL_, 0xCC, 0x80, FIL_, 0xC3,
+ 0xAC, FIL_, 0xCC, 0x83, FIL_, 0xC4, 0xA9, FIL_,
+ 0xCC, 0x84, FIL_, 0xC4, 0xAB, FIL_, 0xCC, 0x86,
+ FIL_, 0xC4, 0xAD, FIL_, 0xCC, 0xA8, FIL_, 0xC4,
+ 0xAF, FIL_, 0xCC, 0xB0, FIL_, 0xE1, 0xB8, 0xAD,
+ FIL_, 0xCC, 0x8C, FIL_, 0xC7, 0x90, FIL_, 0xCC,
+ 0x91, FIL_, 0xC8, 0x8B, FIL_, 0xCC, 0x8F, FIL_,
+ 0xC8, 0x89, FIL_, 0x02, 0xCC, 0x8C, FIL_, 0xC7,
+ 0xB0, FIL_, 0xCC, 0x82, FIL_, 0xC4, 0xB5, FIL_,
+ 0x05, 0xCC, 0xB1, FIL_, 0xE1, 0xB8, 0xB5, FIL_,
+ 0xCC, 0xA7, FIL_, 0xC4, 0xB7, FIL_, 0xCC, 0x8C,
+ FIL_, 0xC7, 0xA9, FIL_, 0xCC, 0x81, FIL_, 0xE1,
+ 0xB8, 0xB1, FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xB8,
+ 0xB3, FIL_, 0x06, 0xCC, 0xA3, FIL_, 0xE1, 0xB8,
+ 0xB7, FIL_, 0xCC, 0xAD, FIL_, 0xE1, 0xB8, 0xBD,
+ FIL_, 0xCC, 0xB1, FIL_, 0xE1, 0xB8, 0xBB, FIL_,
+ 0xCC, 0xA7, FIL_, 0xC4, 0xBC, FIL_, 0xCC, 0x81,
+ FIL_, 0xC4, 0xBA, FIL_, 0xCC, 0x8C, FIL_, 0xC4,
+ 0xBE, FIL_, 0x03, 0xCC, 0x87, FIL_, 0xE1, 0xB9,
+ 0x81, FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xB9, 0x83,
+ FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xB8, 0xBF, FIL_,
+ 0x09, 0xCC, 0x80, FIL_, 0xC7, 0xB9, FIL_, 0xCC,
+ 0xAD, FIL_, 0xE1, 0xB9, 0x8B, FIL_, 0xCC, 0x83,
+ FIL_, 0xC3, 0xB1, FIL_, 0xCC, 0x81, FIL_, 0xC5,
+ 0x84, FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xB9, 0x87,
+ FIL_, 0xCC, 0xB1, FIL_, 0xE1, 0xB9, 0x89, FIL_,
+ 0xCC, 0x87, FIL_, 0xE1, 0xB9, 0x85, FIL_, 0xCC,
+ 0xA7, FIL_, 0xC5, 0x86, FIL_, 0xCC, 0x8C, FIL_,
+ 0xC5, 0x88, FIL_, 0x10, 0xCC, 0xA3, FIL_, 0xE1,
+ 0xBB, 0x8D, FIL_, 0xCC, 0x87, FIL_, 0xC8, 0xAF,
+ FIL_, 0xCC, 0x80, FIL_, 0xC3, 0xB2, FIL_, 0xCC,
+ 0x91, FIL_, 0xC8, 0x8F, FIL_, 0xCC, 0x89, FIL_,
+ 0xE1, 0xBB, 0x8F, FIL_, 0xCC, 0x88, FIL_, 0xC3,
+ 0xB6, FIL_, 0xCC, 0x83, FIL_, 0xC3, 0xB5, FIL_,
+ 0xCC, 0x81, FIL_, 0xC3, 0xB3, FIL_, 0xCC, 0x8C,
+ FIL_, 0xC7, 0x92, FIL_, 0xCC, 0xA8, FIL_, 0xC7,
+ 0xAB, FIL_, 0xCC, 0x9B, FIL_, 0xC6, 0xA1, FIL_,
+ 0xCC, 0x84, FIL_, 0xC5, 0x8D, FIL_, 0xCC, 0x86,
+ FIL_, 0xC5, 0x8F, FIL_, 0xCC, 0x8B, FIL_, 0xC5,
+ 0x91, FIL_, 0xCC, 0x82, FIL_, 0xC3, 0xB4, FIL_,
+ 0xCC, 0x8F, FIL_, 0xC8, 0x8D, FIL_, 0x02, 0xCC,
+ 0x87, FIL_, 0xE1, 0xB9, 0x97, FIL_, 0xCC, 0x81,
+ FIL_, 0xE1, 0xB9, 0x95, FIL_, 0x08, 0xCC, 0x8C,
+ FIL_, 0xC5, 0x99, FIL_, 0xCC, 0xA3, FIL_, 0xE1,
+ 0xB9, 0x9B, FIL_, 0xCC, 0x81, FIL_, 0xC5, 0x95,
+ FIL_, 0xCC, 0xA7, FIL_, 0xC5, 0x97, FIL_, 0xCC,
+ 0xB1, FIL_, 0xE1, 0xB9, 0x9F, FIL_, 0xCC, 0x87,
+ FIL_, 0xE1, 0xB9, 0x99, FIL_, 0xCC, 0x91, FIL_,
+ 0xC8, 0x93, FIL_, 0xCC, 0x8F, FIL_, 0xC8, 0x91,
+ FIL_, 0x07, 0xCC, 0xA7, FIL_, 0xC5, 0x9F, FIL_,
+ 0xCC, 0x82, FIL_, 0xC5, 0x9D, FIL_, 0xCC, 0x87,
+ FIL_, 0xE1, 0xB9, 0xA1, FIL_, 0xCC, 0xA6, FIL_,
+ 0xC8, 0x99, FIL_, 0xCC, 0x81, FIL_, 0xC5, 0x9B,
+ FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xB9, 0xA3, FIL_,
+ 0xCC, 0x8C, FIL_, 0xC5, 0xA1, FIL_, 0x08, 0xCC,
+ 0xA6, FIL_, 0xC8, 0x9B, FIL_, 0xCC, 0xAD, FIL_,
+ 0xE1, 0xB9, 0xB1, FIL_, 0xCC, 0xB1, FIL_, 0xE1,
+ 0xB9, 0xAF, FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xB9,
+ 0xAD, FIL_, 0xCC, 0x87, FIL_, 0xE1, 0xB9, 0xAB,
+ FIL_, 0xCC, 0x8C, FIL_, 0xC5, 0xA5, FIL_, 0xCC,
+ 0xA7, FIL_, 0xC5, 0xA3, FIL_, 0xCC, 0x88, FIL_,
+ 0xE1, 0xBA, 0x97, FIL_, 0x13, 0xCC, 0x8A, FIL_,
+ 0xC5, 0xAF, FIL_, 0xCC, 0x8F, FIL_, 0xC8, 0x95,
+ FIL_, 0xCC, 0x8C, FIL_, 0xC7, 0x94, FIL_, 0xCC,
+ 0x80, FIL_, 0xC3, 0xB9, FIL_, 0xCC, 0x9B, FIL_,
+ 0xC6, 0xB0, FIL_, 0xCC, 0x82, FIL_, 0xC3, 0xBB,
+ FIL_, 0xCC, 0x81, FIL_, 0xC3, 0xBA, FIL_, 0xCC,
+ 0x88, FIL_, 0xC3, 0xBC, FIL_, 0xCC, 0x83, FIL_,
+ 0xC5, 0xA9, FIL_, 0xCC, 0x89, FIL_, 0xE1, 0xBB,
+ 0xA7, FIL_, 0xCC, 0x84, FIL_, 0xC5, 0xAB, FIL_,
+ 0xCC, 0x86, FIL_, 0xC5, 0xAD, FIL_, 0xCC, 0xAD,
+ FIL_, 0xE1, 0xB9, 0xB7, FIL_, 0xCC, 0x8B, FIL_,
+ 0xC5, 0xB1, FIL_, 0xCC, 0xA8, FIL_, 0xC5, 0xB3,
+ FIL_, 0xCC, 0x91, FIL_, 0xC8, 0x97, FIL_, 0xCC,
+ 0xA4, FIL_, 0xE1, 0xB9, 0xB3, FIL_, 0xCC, 0xA3,
+ FIL_, 0xE1, 0xBB, 0xA5, FIL_, 0xCC, 0xB0, FIL_,
+ 0xE1, 0xB9, 0xB5, FIL_, 0x02, 0xCC, 0x83, FIL_,
+ 0xE1, 0xB9, 0xBD, FIL_, 0xCC, 0xA3, FIL_, 0xE1,
+ 0xB9, 0xBF, FIL_, 0x07, 0xCC, 0x8A, FIL_, 0xE1,
+ 0xBA, 0x98, FIL_, 0xCC, 0x87, FIL_, 0xE1, 0xBA,
+ 0x87, FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBA, 0x83,
+ FIL_, 0xCC, 0x82, FIL_, 0xC5, 0xB5, FIL_, 0xCC,
+ 0x80, FIL_, 0xE1, 0xBA, 0x81, FIL_, 0xCC, 0xA3,
+ FIL_, 0xE1, 0xBA, 0x89, FIL_, 0xCC, 0x88, FIL_,
+ 0xE1, 0xBA, 0x85, FIL_, 0x02, 0xCC, 0x87, FIL_,
+ 0xE1, 0xBA, 0x8B, FIL_, 0xCC, 0x88, FIL_, 0xE1,
+ 0xBA, 0x8D, FIL_, 0x0A, 0xCC, 0x87, FIL_, 0xE1,
+ 0xBA, 0x8F, FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xBB,
+ 0xB5, FIL_, 0xCC, 0x89, FIL_, 0xE1, 0xBB, 0xB7,
+ FIL_, 0xCC, 0x8A, FIL_, 0xE1, 0xBA, 0x99, FIL_,
+ 0xCC, 0x80, FIL_, 0xE1, 0xBB, 0xB3, FIL_, 0xCC,
+ 0x83, FIL_, 0xE1, 0xBB, 0xB9, FIL_, 0xCC, 0x88,
+ FIL_, 0xC3, 0xBF, FIL_, 0xCC, 0x81, FIL_, 0xC3,
+ 0xBD, FIL_, 0xCC, 0x84, FIL_, 0xC8, 0xB3, FIL_,
+ 0xCC, 0x82, FIL_, 0xC5, 0xB7, FIL_, 0x06, 0xCC,
+ 0xB1, FIL_, 0xE1, 0xBA, 0x95, FIL_, 0xCC, 0xA3,
+ FIL_, 0xE1, 0xBA, 0x93, FIL_, 0xCC, 0x82, FIL_,
+ 0xE1, 0xBA, 0x91, FIL_, 0xCC, 0x81, FIL_, 0xC5,
+ 0xBA, FIL_, 0xCC, 0x87, FIL_, 0xC5, 0xBC, FIL_,
+ 0xCC, 0x8C, FIL_, 0xC5, 0xBE, FIL_, 0x03, 0xCC,
+ 0x80, FIL_, 0xE1, 0xBF, 0xAD, FIL_, 0xCD, 0x82,
+ FIL_, 0xE1, 0xBF, 0x81, FIL_, 0xCC, 0x81, FIL_,
+ 0xCE, 0x85, FIL_, 0x04, 0xCC, 0x83, FIL_, 0xE1,
+ 0xBA, 0xAA, FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBA,
+ 0xA4, FIL_, 0xCC, 0x89, FIL_, 0xE1, 0xBA, 0xA8,
+ FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBA, 0xA6, FIL_,
+ 0x01, 0xCC, 0x84, FIL_, 0xC7, 0x9E, FIL_, 0x01,
+ 0xCC, 0x81, FIL_, 0xC7, 0xBA, FIL_, 0x02, 0xCC,
+ 0x84, FIL_, 0xC7, 0xA2, FIL_, 0xCC, 0x81, FIL_,
+ 0xC7, 0xBC, FIL_, 0x01, 0xCC, 0x81, FIL_, 0xE1,
+ 0xB8, 0x88, FIL_, 0x04, 0xCC, 0x83, FIL_, 0xE1,
+ 0xBB, 0x84, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBB,
+ 0x80, FIL_, 0xCC, 0x89, FIL_, 0xE1, 0xBB, 0x82,
+ FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBA, 0xBE, FIL_,
+ 0x01, 0xCC, 0x81, FIL_, 0xE1, 0xB8, 0xAE, FIL_,
+ 0x04, 0xCC, 0x81, FIL_, 0xE1, 0xBB, 0x90, FIL_,
+ 0xCC, 0x80, FIL_, 0xE1, 0xBB, 0x92, FIL_, 0xCC,
+ 0x89, FIL_, 0xE1, 0xBB, 0x94, FIL_, 0xCC, 0x83,
+ FIL_, 0xE1, 0xBB, 0x96, FIL_, 0x03, 0xCC, 0x84,
+ FIL_, 0xC8, 0xAC, FIL_, 0xCC, 0x88, FIL_, 0xE1,
+ 0xB9, 0x8E, FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xB9,
+ 0x8C, FIL_, 0x01, 0xCC, 0x84, FIL_, 0xC8, 0xAA,
+ FIL_, 0x01, 0xCC, 0x81, FIL_, 0xC7, 0xBE, FIL_,
+ 0x04, 0xCC, 0x80, FIL_, 0xC7, 0x9B, FIL_, 0xCC,
+ 0x84, FIL_, 0xC7, 0x95, FIL_, 0xCC, 0x8C, FIL_,
+ 0xC7, 0x99, FIL_, 0xCC, 0x81, FIL_, 0xC7, 0x97,
+ FIL_, 0x04, 0xCC, 0x81, FIL_, 0xE1, 0xBA, 0xA5,
+ FIL_, 0xCC, 0x83, FIL_, 0xE1, 0xBA, 0xAB, FIL_,
+ 0xCC, 0x89, FIL_, 0xE1, 0xBA, 0xA9, FIL_, 0xCC,
+ 0x80, FIL_, 0xE1, 0xBA, 0xA7, FIL_, 0x01, 0xCC,
+ 0x84, FIL_, 0xC7, 0x9F, FIL_, 0x01, 0xCC, 0x81,
+ FIL_, 0xC7, 0xBB, FIL_, 0x02, 0xCC, 0x81, FIL_,
+ 0xC7, 0xBD, FIL_, 0xCC, 0x84, FIL_, 0xC7, 0xA3,
+ FIL_, 0x01, 0xCC, 0x81, FIL_, 0xE1, 0xB8, 0x89,
+ FIL_, 0x04, 0xCC, 0x89, FIL_, 0xE1, 0xBB, 0x83,
+ FIL_, 0xCC, 0x83, FIL_, 0xE1, 0xBB, 0x85, FIL_,
+ 0xCC, 0x80, FIL_, 0xE1, 0xBB, 0x81, FIL_, 0xCC,
+ 0x81, FIL_, 0xE1, 0xBA, 0xBF, FIL_, 0x01, 0xCC,
+ 0x81, FIL_, 0xE1, 0xB8, 0xAF, FIL_, 0x04, 0xCC,
+ 0x80, FIL_, 0xE1, 0xBB, 0x93, FIL_, 0xCC, 0x81,
+ FIL_, 0xE1, 0xBB, 0x91, FIL_, 0xCC, 0x83, FIL_,
+ 0xE1, 0xBB, 0x97, FIL_, 0xCC, 0x89, FIL_, 0xE1,
+ 0xBB, 0x95, FIL_, 0x03, 0xCC, 0x81, FIL_, 0xE1,
+ 0xB9, 0x8D, FIL_, 0xCC, 0x88, FIL_, 0xE1, 0xB9,
+ 0x8F, FIL_, 0xCC, 0x84, FIL_, 0xC8, 0xAD, FIL_,
+ 0x01, 0xCC, 0x84, FIL_, 0xC8, 0xAB, FIL_, 0x01,
+ 0xCC, 0x81, FIL_, 0xC7, 0xBF, FIL_, 0x04, 0xCC,
+ 0x8C, FIL_, 0xC7, 0x9A, FIL_, 0xCC, 0x84, FIL_,
+ 0xC7, 0x96, FIL_, 0xCC, 0x80, FIL_, 0xC7, 0x9C,
+ FIL_, 0xCC, 0x81, FIL_, 0xC7, 0x98, FIL_, 0x04,
+ 0xCC, 0x81, FIL_, 0xE1, 0xBA, 0xAE, FIL_, 0xCC,
+ 0x83, FIL_, 0xE1, 0xBA, 0xB4, FIL_, 0xCC, 0x89,
+ FIL_, 0xE1, 0xBA, 0xB2, FIL_, 0xCC, 0x80, FIL_,
+ 0xE1, 0xBA, 0xB0, FIL_, 0x04, 0xCC, 0x83, FIL_,
+ 0xE1, 0xBA, 0xB5, FIL_, 0xCC, 0x80, FIL_, 0xE1,
+ 0xBA, 0xB1, FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBA,
+ 0xAF, FIL_, 0xCC, 0x89, FIL_, 0xE1, 0xBA, 0xB3,
+ FIL_, 0x02, 0xCC, 0x81, FIL_, 0xE1, 0xB8, 0x96,
+ FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xB8, 0x94, FIL_,
+ 0x02, 0xCC, 0x80, FIL_, 0xE1, 0xB8, 0x95, FIL_,
+ 0xCC, 0x81, FIL_, 0xE1, 0xB8, 0x97, FIL_, 0x02,
+ 0xCC, 0x80, FIL_, 0xE1, 0xB9, 0x90, FIL_, 0xCC,
+ 0x81, FIL_, 0xE1, 0xB9, 0x92, FIL_, 0x02, 0xCC,
+ 0x81, FIL_, 0xE1, 0xB9, 0x93, FIL_, 0xCC, 0x80,
+ FIL_, 0xE1, 0xB9, 0x91, FIL_, 0x01, 0xCC, 0x87,
+ FIL_, 0xE1, 0xB9, 0xA4, FIL_, 0x01, 0xCC, 0x87,
+ FIL_, 0xE1, 0xB9, 0xA5, FIL_, 0x01, 0xCC, 0x87,
+ FIL_, 0xE1, 0xB9, 0xA6, FIL_, 0x01, 0xCC, 0x87,
+ FIL_, 0xE1, 0xB9, 0xA7, FIL_, 0x01, 0xCC, 0x81,
+ FIL_, 0xE1, 0xB9, 0xB8, FIL_, 0x01, 0xCC, 0x81,
+ FIL_, 0xE1, 0xB9, 0xB9, FIL_, 0x01, 0xCC, 0x88,
+ FIL_, 0xE1, 0xB9, 0xBA, FIL_, 0x01, 0xCC, 0x88,
+ FIL_, 0xE1, 0xB9, 0xBB, FIL_, 0x01, 0xCC, 0x87,
+ FIL_, 0xE1, 0xBA, 0x9B, FIL_, 0x05, 0xCC, 0x80,
+ FIL_, 0xE1, 0xBB, 0x9C, FIL_, 0xCC, 0x89, FIL_,
+ 0xE1, 0xBB, 0x9E, FIL_, 0xCC, 0x83, FIL_, 0xE1,
+ 0xBB, 0xA0, FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBB,
+ 0x9A, FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xBB, 0xA2,
+ FIL_, 0x05, 0xCC, 0x83, FIL_, 0xE1, 0xBB, 0xA1,
+ FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xBB, 0xA3, FIL_,
+ 0xCC, 0x81, FIL_, 0xE1, 0xBB, 0x9B, FIL_, 0xCC,
+ 0x80, FIL_, 0xE1, 0xBB, 0x9D, FIL_, 0xCC, 0x89,
+ FIL_, 0xE1, 0xBB, 0x9F, FIL_, 0x05, 0xCC, 0x81,
+ FIL_, 0xE1, 0xBB, 0xA8, FIL_, 0xCC, 0x80, FIL_,
+ 0xE1, 0xBB, 0xAA, FIL_, 0xCC, 0x89, FIL_, 0xE1,
+ 0xBB, 0xAC, FIL_, 0xCC, 0x83, FIL_, 0xE1, 0xBB,
+ 0xAE, FIL_, 0xCC, 0xA3, FIL_, 0xE1, 0xBB, 0xB0,
+ FIL_, 0x05, 0xCC, 0x80, FIL_, 0xE1, 0xBB, 0xAB,
+ FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBB, 0xA9, FIL_,
+ 0xCC, 0x83, FIL_, 0xE1, 0xBB, 0xAF, FIL_, 0xCC,
+ 0xA3, FIL_, 0xE1, 0xBB, 0xB1, FIL_, 0xCC, 0x89,
+ FIL_, 0xE1, 0xBB, 0xAD, FIL_, 0x01, 0xCC, 0x8C,
+ FIL_, 0xC7, 0xAE, FIL_, 0x01, 0xCC, 0x84, FIL_,
+ 0xC7, 0xAC, FIL_, 0x01, 0xCC, 0x84, FIL_, 0xC7,
+ 0xAD, FIL_, 0x01, 0xCC, 0x84, FIL_, 0xC7, 0xA0,
+ FIL_, 0x01, 0xCC, 0x84, FIL_, 0xC7, 0xA1, FIL_,
+ 0x01, 0xCC, 0x86, FIL_, 0xE1, 0xB8, 0x9C, FIL_,
+ 0x01, 0xCC, 0x86, FIL_, 0xE1, 0xB8, 0x9D, FIL_,
+ 0x01, 0xCC, 0x84, FIL_, 0xC8, 0xB0, FIL_, 0x01,
+ 0xCC, 0x84, FIL_, 0xC8, 0xB1, FIL_, 0x01, 0xCC,
+ 0x8C, FIL_, 0xC7, 0xAF, FIL_, 0x07, 0xCC, 0x93,
+ FIL_, 0xE1, 0xBC, 0x88, FIL_, 0xCC, 0x81, FIL_,
+ 0xCE, 0x86, FIL_, 0xCC, 0x86, FIL_, 0xE1, 0xBE,
+ 0xB8, FIL_, 0xCC, 0x84, FIL_, 0xE1, 0xBE, 0xB9,
+ FIL_, 0xCC, 0x94, FIL_, 0xE1, 0xBC, 0x89, FIL_,
+ 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0xBC, FIL_, 0xCC,
+ 0x80, FIL_, 0xE1, 0xBE, 0xBA, FIL_, 0x04, 0xCC,
+ 0x94, FIL_, 0xE1, 0xBC, 0x99, FIL_, 0xCC, 0x80,
+ FIL_, 0xE1, 0xBF, 0x88, FIL_, 0xCC, 0x81, FIL_,
+ 0xCE, 0x88, FIL_, 0xCC, 0x93, FIL_, 0xE1, 0xBC,
+ 0x98, FIL_, 0x05, 0xCD, 0x85, FIL_, 0xE1, 0xBF,
+ 0x8C, FIL_, 0xCC, 0x81, FIL_, 0xCE, 0x89, FIL_,
+ 0xCC, 0x80, FIL_, 0xE1, 0xBF, 0x8A, FIL_, 0xCC,
+ 0x93, FIL_, 0xE1, 0xBC, 0xA8, FIL_, 0xCC, 0x94,
+ FIL_, 0xE1, 0xBC, 0xA9, FIL_, 0x07, 0xCC, 0x80,
+ FIL_, 0xE1, 0xBF, 0x9A, FIL_, 0xCC, 0x84, FIL_,
+ 0xE1, 0xBF, 0x99, FIL_, 0xCC, 0x93, FIL_, 0xE1,
+ 0xBC, 0xB8, FIL_, 0xCC, 0x94, FIL_, 0xE1, 0xBC,
+ 0xB9, FIL_, 0xCC, 0x86, FIL_, 0xE1, 0xBF, 0x98,
+ FIL_, 0xCC, 0x81, FIL_, 0xCE, 0x8A, FIL_, 0xCC,
+ 0x88, FIL_, 0xCE, 0xAA, FIL_, 0x04, 0xCC, 0x81,
+ FIL_, 0xCE, 0x8C, FIL_, 0xCC, 0x94, FIL_, 0xE1,
+ 0xBD, 0x89, FIL_, 0xCC, 0x93, FIL_, 0xE1, 0xBD,
+ 0x88, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBF, 0xB8,
+ FIL_, 0x01, 0xCC, 0x94, FIL_, 0xE1, 0xBF, 0xAC,
+ FIL_, 0x06, 0xCC, 0x94, FIL_, 0xE1, 0xBD, 0x99,
+ FIL_, 0xCC, 0x86, FIL_, 0xE1, 0xBF, 0xA8, FIL_,
+ 0xCC, 0x88, FIL_, 0xCE, 0xAB, FIL_, 0xCC, 0x84,
+ FIL_, 0xE1, 0xBF, 0xA9, FIL_, 0xCC, 0x81, FIL_,
+ 0xCE, 0x8E, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBF,
+ 0xAA, FIL_, 0x05, 0xCC, 0x93, FIL_, 0xE1, 0xBD,
+ 0xA8, FIL_, 0xCD, 0x85, FIL_, 0xE1, 0xBF, 0xBC,
+ FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBF, 0xBA, FIL_,
+ 0xCC, 0x94, FIL_, 0xE1, 0xBD, 0xA9, FIL_, 0xCC,
+ 0x81, FIL_, 0xCE, 0x8F, FIL_, 0x01, 0xCD, 0x85,
+ FIL_, 0xE1, 0xBE, 0xB4, FIL_, 0x01, 0xCD, 0x85,
+ FIL_, 0xE1, 0xBF, 0x84, FIL_, 0x08, 0xCD, 0x85,
+ FIL_, 0xE1, 0xBE, 0xB3, FIL_, 0xCC, 0x84, FIL_,
+ 0xE1, 0xBE, 0xB1, FIL_, 0xCC, 0x86, FIL_, 0xE1,
+ 0xBE, 0xB0, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBD,
+ 0xB0, FIL_, 0xCC, 0x81, FIL_, 0xCE, 0xAC, FIL_,
+ 0xCC, 0x94, FIL_, 0xE1, 0xBC, 0x81, FIL_, 0xCC,
+ 0x93, FIL_, 0xE1, 0xBC, 0x80, FIL_, 0xCD, 0x82,
+ FIL_, 0xE1, 0xBE, 0xB6, FIL_, 0x04, 0xCC, 0x93,
+ FIL_, 0xE1, 0xBC, 0x90, FIL_, 0xCC, 0x80, FIL_,
+ 0xE1, 0xBD, 0xB2, FIL_, 0xCC, 0x94, FIL_, 0xE1,
+ 0xBC, 0x91, FIL_, 0xCC, 0x81, FIL_, 0xCE, 0xAD,
+ FIL_, 0x06, 0xCC, 0x94, FIL_, 0xE1, 0xBC, 0xA1,
+ FIL_, 0xCC, 0x81, FIL_, 0xCE, 0xAE, FIL_, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBF, 0x83, FIL_, 0xCD, 0x82,
+ FIL_, 0xE1, 0xBF, 0x86, FIL_, 0xCC, 0x93, FIL_,
+ 0xE1, 0xBC, 0xA0, FIL_, 0xCC, 0x80, FIL_, 0xE1,
+ 0xBD, 0xB4, FIL_, 0x08, 0xCC, 0x88, FIL_, 0xCF,
+ 0x8A, FIL_, 0xCC, 0x81, FIL_, 0xCE, 0xAF, FIL_,
+ 0xCC, 0x93, FIL_, 0xE1, 0xBC, 0xB0, FIL_, 0xCC,
+ 0x94, FIL_, 0xE1, 0xBC, 0xB1, FIL_, 0xCC, 0x80,
+ FIL_, 0xE1, 0xBD, 0xB6, FIL_, 0xCC, 0x86, FIL_,
+ 0xE1, 0xBF, 0x90, FIL_, 0xCC, 0x84, FIL_, 0xE1,
+ 0xBF, 0x91, FIL_, 0xCD, 0x82, FIL_, 0xE1, 0xBF,
+ 0x96, FIL_, 0x04, 0xCC, 0x93, FIL_, 0xE1, 0xBD,
+ 0x80, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBD, 0xB8,
+ FIL_, 0xCC, 0x94, FIL_, 0xE1, 0xBD, 0x81, FIL_,
+ 0xCC, 0x81, FIL_, 0xCF, 0x8C, FIL_, 0x02, 0xCC,
+ 0x93, FIL_, 0xE1, 0xBF, 0xA4, FIL_, 0xCC, 0x94,
+ FIL_, 0xE1, 0xBF, 0xA5, FIL_, 0x08, 0xCC, 0x81,
+ FIL_, 0xCF, 0x8D, FIL_, 0xCC, 0x94, FIL_, 0xE1,
+ 0xBD, 0x91, FIL_, 0xCD, 0x82, FIL_, 0xE1, 0xBF,
+ 0xA6, FIL_, 0xCC, 0x88, FIL_, 0xCF, 0x8B, FIL_,
+ 0xCC, 0x84, FIL_, 0xE1, 0xBF, 0xA1, FIL_, 0xCC,
+ 0x80, FIL_, 0xE1, 0xBD, 0xBA, FIL_, 0xCC, 0x93,
+ FIL_, 0xE1, 0xBD, 0x90, FIL_, 0xCC, 0x86, FIL_,
+ 0xE1, 0xBF, 0xA0, FIL_, 0x06, 0xCC, 0x80, FIL_,
+ 0xE1, 0xBD, 0xBC, FIL_, 0xCC, 0x94, FIL_, 0xE1,
+ 0xBD, 0xA1, FIL_, 0xCC, 0x93, FIL_, 0xE1, 0xBD,
+ 0xA0, FIL_, 0xCC, 0x81, FIL_, 0xCF, 0x8E, FIL_,
+ 0xCD, 0x85, FIL_, 0xE1, 0xBF, 0xB3, FIL_, 0xCD,
+ 0x82, FIL_, 0xE1, 0xBF, 0xB6, FIL_, 0x03, 0xCC,
+ 0x80, FIL_, 0xE1, 0xBF, 0x92, FIL_, 0xCD, 0x82,
+ FIL_, 0xE1, 0xBF, 0x97, FIL_, 0xCC, 0x81, FIL_,
+ 0xCE, 0x90, FIL_, 0x03, 0xCD, 0x82, FIL_, 0xE1,
+ 0xBF, 0xA7, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBF,
+ 0xA2, FIL_, 0xCC, 0x81, FIL_, 0xCE, 0xB0, FIL_,
+ 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBF, 0xB4, FIL_,
+ 0x02, 0xCC, 0x88, FIL_, 0xCF, 0x94, FIL_, 0xCC,
+ 0x81, FIL_, 0xCF, 0x93, FIL_, 0x01, 0xCC, 0x88,
+ FIL_, 0xD0, 0x87, FIL_, 0x02, 0xCC, 0x88, FIL_,
+ 0xD3, 0x92, FIL_, 0xCC, 0x86, FIL_, 0xD3, 0x90,
+ FIL_, 0x01, 0xCC, 0x81, FIL_, 0xD0, 0x83, FIL_,
+ 0x03, 0xCC, 0x88, FIL_, 0xD0, 0x81, FIL_, 0xCC,
+ 0x80, FIL_, 0xD0, 0x80, FIL_, 0xCC, 0x86, FIL_,
+ 0xD3, 0x96, FIL_, 0x02, 0xCC, 0x86, FIL_, 0xD3,
+ 0x81, FIL_, 0xCC, 0x88, FIL_, 0xD3, 0x9C, FIL_,
+ 0x01, 0xCC, 0x88, FIL_, 0xD3, 0x9E, FIL_, 0x04,
+ 0xCC, 0x84, FIL_, 0xD3, 0xA2, FIL_, 0xCC, 0x88,
+ FIL_, 0xD3, 0xA4, FIL_, 0xCC, 0x86, FIL_, 0xD0,
+ 0x99, FIL_, 0xCC, 0x80, FIL_, 0xD0, 0x8D, FIL_,
+ 0x01, 0xCC, 0x81, FIL_, 0xD0, 0x8C, FIL_, 0x01,
+ 0xCC, 0x88, FIL_, 0xD3, 0xA6, FIL_, 0x04, 0xCC,
+ 0x8B, FIL_, 0xD3, 0xB2, FIL_, 0xCC, 0x88, FIL_,
+ 0xD3, 0xB0, FIL_, 0xCC, 0x86, FIL_, 0xD0, 0x8E,
+ FIL_, 0xCC, 0x84, FIL_, 0xD3, 0xAE, FIL_, 0x01,
+ 0xCC, 0x88, FIL_, 0xD3, 0xB4, FIL_, 0x01, 0xCC,
+ 0x88, FIL_, 0xD3, 0xB8, FIL_, 0x01, 0xCC, 0x88,
+ FIL_, 0xD3, 0xAC, FIL_, 0x02, 0xCC, 0x86, FIL_,
+ 0xD3, 0x91, FIL_, 0xCC, 0x88, FIL_, 0xD3, 0x93,
+ FIL_, 0x01, 0xCC, 0x81, FIL_, 0xD1, 0x93, FIL_,
+ 0x03, 0xCC, 0x80, FIL_, 0xD1, 0x90, FIL_, 0xCC,
+ 0x86, FIL_, 0xD3, 0x97, FIL_, 0xCC, 0x88, FIL_,
+ 0xD1, 0x91, FIL_, 0x02, 0xCC, 0x86, FIL_, 0xD3,
+ 0x82, FIL_, 0xCC, 0x88, FIL_, 0xD3, 0x9D, FIL_,
+ 0x01, 0xCC, 0x88, FIL_, 0xD3, 0x9F, FIL_, 0x04,
+ 0xCC, 0x86, FIL_, 0xD0, 0xB9, FIL_, 0xCC, 0x88,
+ FIL_, 0xD3, 0xA5, FIL_, 0xCC, 0x84, FIL_, 0xD3,
+ 0xA3, FIL_, 0xCC, 0x80, FIL_, 0xD1, 0x9D, FIL_,
+ 0x01, 0xCC, 0x81, FIL_, 0xD1, 0x9C, FIL_, 0x01,
+ 0xCC, 0x88, FIL_, 0xD3, 0xA7, FIL_, 0x04, 0xCC,
+ 0x8B, FIL_, 0xD3, 0xB3, FIL_, 0xCC, 0x84, FIL_,
+ 0xD3, 0xAF, FIL_, 0xCC, 0x86, FIL_, 0xD1, 0x9E,
+ FIL_, 0xCC, 0x88, FIL_, 0xD3, 0xB1, FIL_, 0x01,
+ 0xCC, 0x88, FIL_, 0xD3, 0xB5, FIL_, 0x01, 0xCC,
+ 0x88, FIL_, 0xD3, 0xB9, FIL_, 0x01, 0xCC, 0x88,
+ FIL_, 0xD3, 0xAD, FIL_, 0x01, 0xCC, 0x88, FIL_,
+ 0xD1, 0x97, FIL_, 0x01, 0xCC, 0x8F, FIL_, 0xD1,
+ 0xB6, FIL_, 0x01, 0xCC, 0x8F, FIL_, 0xD1, 0xB7,
+ FIL_, 0x01, 0xCC, 0x88, FIL_, 0xD3, 0x9A, FIL_,
+ 0x01, 0xCC, 0x88, FIL_, 0xD3, 0x9B, FIL_, 0x01,
+ 0xCC, 0x88, FIL_, 0xD3, 0xAA, FIL_, 0x01, 0xCC,
+ 0x88, FIL_, 0xD3, 0xAB, FIL_, 0x03, 0xD9, 0x94,
+ FIL_, 0xD8, 0xA3, FIL_, 0xD9, 0x95, FIL_, 0xD8,
+ 0xA5, FIL_, 0xD9, 0x93, FIL_, 0xD8, 0xA2, FIL_,
+ 0x01, 0xD9, 0x94, FIL_, 0xD8, 0xA4, FIL_, 0x01,
+ 0xD9, 0x94, FIL_, 0xD8, 0xA6, FIL_, 0x01, 0xD9,
+ 0x94, FIL_, 0xDB, 0x82, FIL_, 0x01, 0xD9, 0x94,
+ FIL_, 0xDB, 0x93, FIL_, 0x01, 0xD9, 0x94, FIL_,
+ 0xDB, 0x80, FIL_, 0x01, 0xE0, 0xA4, 0xBC, FIL_,
+ 0xE0, 0xA4, 0xA9, FIL_, 0x01, 0xE0, 0xA4, 0xBC,
+ FIL_, 0xE0, 0xA4, 0xB1, FIL_, 0x01, 0xE0, 0xA4,
+ 0xBC, FIL_, 0xE0, 0xA4, 0xB4, FIL_, 0x02, 0xE0,
+ 0xA6, 0xBE, FIL_, 0xE0, 0xA7, 0x8B, FIL_, 0xE0,
+ 0xA7, 0x97, FIL_, 0xE0, 0xA7, 0x8C, FIL_, 0x03,
+ 0xE0, 0xAD, 0x96, FIL_, 0xE0, 0xAD, 0x88, FIL_,
+ 0xE0, 0xAC, 0xBE, FIL_, 0xE0, 0xAD, 0x8B, FIL_,
+ 0xE0, 0xAD, 0x97, FIL_, 0xE0, 0xAD, 0x8C, FIL_,
+ 0x01, 0xE0, 0xAF, 0x97, FIL_, 0xE0, 0xAE, 0x94,
+ FIL_, 0x02, 0xE0, 0xAF, 0x97, FIL_, 0xE0, 0xAF,
+ 0x8C, FIL_, 0xE0, 0xAE, 0xBE, FIL_, 0xE0, 0xAF,
+ 0x8A, FIL_, 0x01, 0xE0, 0xAE, 0xBE, FIL_, 0xE0,
+ 0xAF, 0x8B, FIL_, 0x01, 0xE0, 0xB1, 0x96, FIL_,
+ 0xE0, 0xB1, 0x88, FIL_, 0x01, 0xE0, 0xB3, 0x95,
+ FIL_, 0xE0, 0xB3, 0x80, FIL_, 0x03, 0xE0, 0xB3,
+ 0x82, FIL_, 0xE0, 0xB3, 0x8A, FIL_, 0xE0, 0xB3,
+ 0x96, FIL_, 0xE0, 0xB3, 0x88, FIL_, 0xE0, 0xB3,
+ 0x95, FIL_, 0xE0, 0xB3, 0x87, FIL_, 0x01, 0xE0,
+ 0xB3, 0x95, FIL_, 0xE0, 0xB3, 0x8B, FIL_, 0x02,
+ 0xE0, 0xB4, 0xBE, FIL_, 0xE0, 0xB5, 0x8A, FIL_,
+ 0xE0, 0xB5, 0x97, FIL_, 0xE0, 0xB5, 0x8C, FIL_,
+ 0x01, 0xE0, 0xB4, 0xBE, FIL_, 0xE0, 0xB5, 0x8B,
+ FIL_, 0x03, 0xE0, 0xB7, 0x9F, FIL_, 0xE0, 0xB7,
+ 0x9E, FIL_, 0xE0, 0xB7, 0x8A, FIL_, 0xE0, 0xB7,
+ 0x9A, FIL_, 0xE0, 0xB7, 0x8F, FIL_, 0xE0, 0xB7,
+ 0x9C, FIL_, 0x01, 0xE0, 0xB7, 0x8A, FIL_, 0xE0,
+ 0xB7, 0x9D, FIL_, 0x01, 0xE1, 0x80, 0xAE, FIL_,
+ 0xE1, 0x80, 0xA6, FIL_, 0x01, 0xE1, 0xAC, 0xB5,
+ FIL_, 0xE1, 0xAC, 0x86, FIL_, 0x01, 0xE1, 0xAC,
+ 0xB5, FIL_, 0xE1, 0xAC, 0x88, FIL_, 0x01, 0xE1,
+ 0xAC, 0xB5, FIL_, 0xE1, 0xAC, 0x8A, FIL_, 0x01,
+ 0xE1, 0xAC, 0xB5, FIL_, 0xE1, 0xAC, 0x8C, FIL_,
+ 0x01, 0xE1, 0xAC, 0xB5, FIL_, 0xE1, 0xAC, 0x8E,
+ FIL_, 0x01, 0xE1, 0xAC, 0xB5, FIL_, 0xE1, 0xAC,
+ 0x92, FIL_, 0x01, 0xE1, 0xAC, 0xB5, FIL_, 0xE1,
+ 0xAC, 0xBB, FIL_, 0x01, 0xE1, 0xAC, 0xB5, FIL_,
+ 0xE1, 0xAC, 0xBD, FIL_, 0x01, 0xE1, 0xAC, 0xB5,
+ FIL_, 0xE1, 0xAD, 0x80, FIL_, 0x01, 0xE1, 0xAC,
+ 0xB5, FIL_, 0xE1, 0xAD, 0x81, FIL_, 0x01, 0xE1,
+ 0xAC, 0xB5, FIL_, 0xE1, 0xAD, 0x83, FIL_, 0x01,
+ 0xCC, 0x84, FIL_, 0xE1, 0xB8, 0xB8, FIL_, 0x01,
+ 0xCC, 0x84, FIL_, 0xE1, 0xB8, 0xB9, FIL_, 0x01,
+ 0xCC, 0x84, FIL_, 0xE1, 0xB9, 0x9C, FIL_, 0x01,
+ 0xCC, 0x84, FIL_, 0xE1, 0xB9, 0x9D, FIL_, 0x01,
+ 0xCC, 0x87, FIL_, 0xE1, 0xB9, 0xA8, FIL_, 0x01,
+ 0xCC, 0x87, FIL_, 0xE1, 0xB9, 0xA9, FIL_, 0x02,
+ 0xCC, 0x86, FIL_, 0xE1, 0xBA, 0xB6, FIL_, 0xCC,
+ 0x82, FIL_, 0xE1, 0xBA, 0xAC, FIL_, 0x02, 0xCC,
+ 0x82, FIL_, 0xE1, 0xBA, 0xAD, FIL_, 0xCC, 0x86,
+ FIL_, 0xE1, 0xBA, 0xB7, FIL_, 0x01, 0xCC, 0x82,
+ FIL_, 0xE1, 0xBB, 0x86, FIL_, 0x01, 0xCC, 0x82,
+ FIL_, 0xE1, 0xBB, 0x87, FIL_, 0x01, 0xCC, 0x82,
+ FIL_, 0xE1, 0xBB, 0x98, FIL_, 0x01, 0xCC, 0x82,
+ FIL_, 0xE1, 0xBB, 0x99, FIL_, 0x04, 0xCD, 0x85,
+ FIL_, 0xE1, 0xBE, 0x80, FIL_, 0xCD, 0x82, FIL_,
+ 0xE1, 0xBC, 0x86, FIL_, 0xCC, 0x80, FIL_, 0xE1,
+ 0xBC, 0x82, FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBC,
+ 0x84, FIL_, 0x04, 0xCD, 0x82, FIL_, 0xE1, 0xBC,
+ 0x87, FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBC, 0x85,
+ FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBC, 0x83, FIL_,
+ 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x81, FIL_, 0x01,
+ 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x82, FIL_, 0x01,
+ 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x83, FIL_, 0x01,
+ 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x84, FIL_, 0x01,
+ 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x85, FIL_, 0x01,
+ 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x86, FIL_, 0x01,
+ 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x87, FIL_, 0x04,
+ 0xCC, 0x81, FIL_, 0xE1, 0xBC, 0x8C, FIL_, 0xCC,
+ 0x80, FIL_, 0xE1, 0xBC, 0x8A, FIL_, 0xCD, 0x85,
+ FIL_, 0xE1, 0xBE, 0x88, FIL_, 0xCD, 0x82, FIL_,
+ 0xE1, 0xBC, 0x8E, FIL_, 0x04, 0xCC, 0x80, FIL_,
+ 0xE1, 0xBC, 0x8B, FIL_, 0xCD, 0x82, FIL_, 0xE1,
+ 0xBC, 0x8F, FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBC,
+ 0x8D, FIL_, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x89,
+ FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x8A,
+ FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x8B,
+ FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x8C,
+ FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x8D,
+ FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x8E,
+ FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x8F,
+ FIL_, 0x02, 0xCC, 0x80, FIL_, 0xE1, 0xBC, 0x92,
+ FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBC, 0x94, FIL_,
+ 0x02, 0xCC, 0x80, FIL_, 0xE1, 0xBC, 0x93, FIL_,
+ 0xCC, 0x81, FIL_, 0xE1, 0xBC, 0x95, FIL_, 0x02,
+ 0xCC, 0x80, FIL_, 0xE1, 0xBC, 0x9A, FIL_, 0xCC,
+ 0x81, FIL_, 0xE1, 0xBC, 0x9C, FIL_, 0x02, 0xCC,
+ 0x80, FIL_, 0xE1, 0xBC, 0x9B, FIL_, 0xCC, 0x81,
+ FIL_, 0xE1, 0xBC, 0x9D, FIL_, 0x04, 0xCC, 0x80,
+ FIL_, 0xE1, 0xBC, 0xA2, FIL_, 0xCC, 0x81, FIL_,
+ 0xE1, 0xBC, 0xA4, FIL_, 0xCD, 0x82, FIL_, 0xE1,
+ 0xBC, 0xA6, FIL_, 0xCD, 0x85, FIL_, 0xE1, 0xBE,
+ 0x90, FIL_, 0x04, 0xCD, 0x85, FIL_, 0xE1, 0xBE,
+ 0x91, FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBC, 0xA5,
+ FIL_, 0xCD, 0x82, FIL_, 0xE1, 0xBC, 0xA7, FIL_,
+ 0xCC, 0x80, FIL_, 0xE1, 0xBC, 0xA3, FIL_, 0x01,
+ 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x92, FIL_, 0x01,
+ 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x93, FIL_, 0x01,
+ 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x94, FIL_, 0x01,
+ 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x95, FIL_, 0x01,
+ 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x96, FIL_, 0x01,
+ 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x97, FIL_, 0x04,
+ 0xCC, 0x81, FIL_, 0xE1, 0xBC, 0xAC, FIL_, 0xCC,
+ 0x80, FIL_, 0xE1, 0xBC, 0xAA, FIL_, 0xCD, 0x85,
+ FIL_, 0xE1, 0xBE, 0x98, FIL_, 0xCD, 0x82, FIL_,
+ 0xE1, 0xBC, 0xAE, FIL_, 0x04, 0xCD, 0x82, FIL_,
+ 0xE1, 0xBC, 0xAF, FIL_, 0xCD, 0x85, FIL_, 0xE1,
+ 0xBE, 0x99, FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBC,
+ 0xAD, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBC, 0xAB,
+ FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x9A,
+ FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x9B,
+ FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x9C,
+ FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x9D,
+ FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x9E,
+ FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0x9F,
+ FIL_, 0x03, 0xCC, 0x81, FIL_, 0xE1, 0xBC, 0xB4,
+ FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBC, 0xB2, FIL_,
+ 0xCD, 0x82, FIL_, 0xE1, 0xBC, 0xB6, FIL_, 0x03,
+ 0xCC, 0x80, FIL_, 0xE1, 0xBC, 0xB3, FIL_, 0xCD,
+ 0x82, FIL_, 0xE1, 0xBC, 0xB7, FIL_, 0xCC, 0x81,
+ FIL_, 0xE1, 0xBC, 0xB5, FIL_, 0x03, 0xCC, 0x81,
+ FIL_, 0xE1, 0xBC, 0xBC, FIL_, 0xCC, 0x80, FIL_,
+ 0xE1, 0xBC, 0xBA, FIL_, 0xCD, 0x82, FIL_, 0xE1,
+ 0xBC, 0xBE, FIL_, 0x03, 0xCC, 0x80, FIL_, 0xE1,
+ 0xBC, 0xBB, FIL_, 0xCD, 0x82, FIL_, 0xE1, 0xBC,
+ 0xBF, FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBC, 0xBD,
+ FIL_, 0x02, 0xCC, 0x80, FIL_, 0xE1, 0xBD, 0x82,
+ FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBD, 0x84, FIL_,
+ 0x02, 0xCC, 0x81, FIL_, 0xE1, 0xBD, 0x85, FIL_,
+ 0xCC, 0x80, FIL_, 0xE1, 0xBD, 0x83, FIL_, 0x02,
+ 0xCC, 0x80, FIL_, 0xE1, 0xBD, 0x8A, FIL_, 0xCC,
+ 0x81, FIL_, 0xE1, 0xBD, 0x8C, FIL_, 0x02, 0xCC,
+ 0x80, FIL_, 0xE1, 0xBD, 0x8B, FIL_, 0xCC, 0x81,
+ FIL_, 0xE1, 0xBD, 0x8D, FIL_, 0x03, 0xCD, 0x82,
+ FIL_, 0xE1, 0xBD, 0x96, FIL_, 0xCC, 0x80, FIL_,
+ 0xE1, 0xBD, 0x92, FIL_, 0xCC, 0x81, FIL_, 0xE1,
+ 0xBD, 0x94, FIL_, 0x03, 0xCC, 0x80, FIL_, 0xE1,
+ 0xBD, 0x93, FIL_, 0xCD, 0x82, FIL_, 0xE1, 0xBD,
+ 0x97, FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBD, 0x95,
+ FIL_, 0x03, 0xCC, 0x80, FIL_, 0xE1, 0xBD, 0x9B,
+ FIL_, 0xCD, 0x82, FIL_, 0xE1, 0xBD, 0x9F, FIL_,
+ 0xCC, 0x81, FIL_, 0xE1, 0xBD, 0x9D, FIL_, 0x04,
+ 0xCD, 0x82, FIL_, 0xE1, 0xBD, 0xA6, FIL_, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBE, 0xA0, FIL_, 0xCC, 0x80,
+ FIL_, 0xE1, 0xBD, 0xA2, FIL_, 0xCC, 0x81, FIL_,
+ 0xE1, 0xBD, 0xA4, FIL_, 0x04, 0xCD, 0x85, FIL_,
+ 0xE1, 0xBE, 0xA1, FIL_, 0xCD, 0x82, FIL_, 0xE1,
+ 0xBD, 0xA7, FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBD,
+ 0xA5, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBD, 0xA3,
+ FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0xA2,
+ FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0xA3,
+ FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0xA4,
+ FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0xA5,
+ FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0xA6,
+ FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBE, 0xA7,
+ FIL_, 0x04, 0xCC, 0x80, FIL_, 0xE1, 0xBD, 0xAA,
+ FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBD, 0xAC, FIL_,
+ 0xCD, 0x82, FIL_, 0xE1, 0xBD, 0xAE, FIL_, 0xCD,
+ 0x85, FIL_, 0xE1, 0xBE, 0xA8, FIL_, 0x04, 0xCD,
+ 0x82, FIL_, 0xE1, 0xBD, 0xAF, FIL_, 0xCC, 0x80,
+ FIL_, 0xE1, 0xBD, 0xAB, FIL_, 0xCD, 0x85, FIL_,
+ 0xE1, 0xBE, 0xA9, FIL_, 0xCC, 0x81, FIL_, 0xE1,
+ 0xBD, 0xAD, FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1,
+ 0xBE, 0xAA, FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1,
+ 0xBE, 0xAB, FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1,
+ 0xBE, 0xAC, FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1,
+ 0xBE, 0xAD, FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1,
+ 0xBE, 0xAE, FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1,
+ 0xBE, 0xAF, FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1,
+ 0xBE, 0xB2, FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1,
+ 0xBF, 0x82, FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1,
+ 0xBF, 0xB2, FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1,
+ 0xBE, 0xB7, FIL_, 0x03, 0xCC, 0x81, FIL_, 0xE1,
+ 0xBF, 0x8E, FIL_, 0xCC, 0x80, FIL_, 0xE1, 0xBF,
+ 0x8D, FIL_, 0xCD, 0x82, FIL_, 0xE1, 0xBF, 0x8F,
+ FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBF, 0x87,
+ FIL_, 0x01, 0xCD, 0x85, FIL_, 0xE1, 0xBF, 0xB7,
+ FIL_, 0x03, 0xCC, 0x80, FIL_, 0xE1, 0xBF, 0x9D,
+ FIL_, 0xCC, 0x81, FIL_, 0xE1, 0xBF, 0x9E, FIL_,
+ 0xCD, 0x82, FIL_, 0xE1, 0xBF, 0x9F, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x86, 0x9A, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x86, 0x9B, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x86, 0xAE, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x87, 0x8D, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x87, 0x8F, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x87, 0x8E, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x88, 0x84, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x88, 0x89, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x88, 0x8C, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x88, 0xA4, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x88, 0xA6, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x89, 0x81, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x89, 0x84, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x89, 0x87, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x89, 0x89, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x89, 0xAD, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x89, 0xA2, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x89, 0xB0, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x89, 0xB1, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x89, 0xB4, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x89, 0xB5, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x89, 0xB8, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x89, 0xB9, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x8A, 0x80, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x8A, 0x81, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x8B, 0xA0, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x8B, 0xA1, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x8A, 0x84, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x8A, 0x85, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x8A, 0x88, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x8A, 0x89, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x8B, 0xA2, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x8B, 0xA3, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x8A, 0xAC, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x8A, 0xAD, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x8A, 0xAE, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x8A, 0xAF, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x8B, 0xAA, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x8B, 0xAB, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x8B, 0xAC, FIL_, 0x01,
+ 0xCC, 0xB8, FIL_, 0xE2, 0x8B, 0xAD, FIL_, 0x01,
+ 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x82, 0x94, FIL_,
+ 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x81, 0x8C,
+ FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x81,
+ 0x8E, FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3,
+ 0x81, 0x90, FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_,
+ 0xE3, 0x81, 0x92, FIL_, 0x01, 0xE3, 0x82, 0x99,
+ FIL_, 0xE3, 0x81, 0x94, FIL_, 0x01, 0xE3, 0x82,
+ 0x99, FIL_, 0xE3, 0x81, 0x96, FIL_, 0x01, 0xE3,
+ 0x82, 0x99, FIL_, 0xE3, 0x81, 0x98, FIL_, 0x01,
+ 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x81, 0x9A, FIL_,
+ 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x81, 0x9C,
+ FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x81,
+ 0x9E, FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3,
+ 0x81, 0xA0, FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_,
+ 0xE3, 0x81, 0xA2, FIL_, 0x01, 0xE3, 0x82, 0x99,
+ FIL_, 0xE3, 0x81, 0xA5, FIL_, 0x01, 0xE3, 0x82,
+ 0x99, FIL_, 0xE3, 0x81, 0xA7, FIL_, 0x01, 0xE3,
+ 0x82, 0x99, FIL_, 0xE3, 0x81, 0xA9, FIL_, 0x02,
+ 0xE3, 0x82, 0x9A, FIL_, 0xE3, 0x81, 0xB1, FIL_,
+ 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x81, 0xB0, FIL_,
+ 0x02, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x81, 0xB3,
+ FIL_, 0xE3, 0x82, 0x9A, FIL_, 0xE3, 0x81, 0xB4,
+ FIL_, 0x02, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x81,
+ 0xB6, FIL_, 0xE3, 0x82, 0x9A, FIL_, 0xE3, 0x81,
+ 0xB7, FIL_, 0x02, 0xE3, 0x82, 0x9A, FIL_, 0xE3,
+ 0x81, 0xBA, FIL_, 0xE3, 0x82, 0x99, FIL_, 0xE3,
+ 0x81, 0xB9, FIL_, 0x02, 0xE3, 0x82, 0x9A, FIL_,
+ 0xE3, 0x81, 0xBD, FIL_, 0xE3, 0x82, 0x99, FIL_,
+ 0xE3, 0x81, 0xBC, FIL_, 0x01, 0xE3, 0x82, 0x99,
+ FIL_, 0xE3, 0x82, 0x9E, FIL_, 0x01, 0xE3, 0x82,
+ 0x99, FIL_, 0xE3, 0x83, 0xB4, FIL_, 0x01, 0xE3,
+ 0x82, 0x99, FIL_, 0xE3, 0x82, 0xAC, FIL_, 0x01,
+ 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x82, 0xAE, FIL_,
+ 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x82, 0xB0,
+ FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x82,
+ 0xB2, FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3,
+ 0x82, 0xB4, FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_,
+ 0xE3, 0x82, 0xB6, FIL_, 0x01, 0xE3, 0x82, 0x99,
+ FIL_, 0xE3, 0x82, 0xB8, FIL_, 0x01, 0xE3, 0x82,
+ 0x99, FIL_, 0xE3, 0x82, 0xBA, FIL_, 0x01, 0xE3,
+ 0x82, 0x99, FIL_, 0xE3, 0x82, 0xBC, FIL_, 0x01,
+ 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x82, 0xBE, FIL_,
+ 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x83, 0x80,
+ FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x83,
+ 0x82, FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3,
+ 0x83, 0x85, FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_,
+ 0xE3, 0x83, 0x87, FIL_, 0x01, 0xE3, 0x82, 0x99,
+ FIL_, 0xE3, 0x83, 0x89, FIL_, 0x02, 0xE3, 0x82,
+ 0x99, FIL_, 0xE3, 0x83, 0x90, FIL_, 0xE3, 0x82,
+ 0x9A, FIL_, 0xE3, 0x83, 0x91, FIL_, 0x02, 0xE3,
+ 0x82, 0x99, FIL_, 0xE3, 0x83, 0x93, FIL_, 0xE3,
+ 0x82, 0x9A, FIL_, 0xE3, 0x83, 0x94, FIL_, 0x02,
+ 0xE3, 0x82, 0x9A, FIL_, 0xE3, 0x83, 0x97, FIL_,
+ 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x83, 0x96, FIL_,
+ 0x02, 0xE3, 0x82, 0x9A, FIL_, 0xE3, 0x83, 0x9A,
+ FIL_, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x83, 0x99,
+ FIL_, 0x02, 0xE3, 0x82, 0x99, FIL_, 0xE3, 0x83,
+ 0x9C, FIL_, 0xE3, 0x82, 0x9A, FIL_, 0xE3, 0x83,
+ 0x9D, FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_, 0xE3,
+ 0x83, 0xB7, FIL_, 0x01, 0xE3, 0x82, 0x99, FIL_,
+ 0xE3, 0x83, 0xB8, FIL_, 0x01, 0xE3, 0x82, 0x99,
+ FIL_, 0xE3, 0x83, 0xB9, FIL_, 0x01, 0xE3, 0x82,
+ 0x99, FIL_, 0xE3, 0x83, 0xBA, FIL_, 0x01, 0xE3,
+ 0x82, 0x99, FIL_, 0xE3, 0x83, 0xBE, FIL_,
+ },
+};
+
+static const uchar_t u8_decomp_b2_tbl[2][2][256] = {
+ {
+ {
+ 0, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ 1, 2, 3, 4, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, 5,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ {
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, 6, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, 7,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+
+ },
+ {
+ {
+ 0, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ 1, 2, 3, 4, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, 5,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ {
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, 6, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, 7,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+
+ },
+
+};
+
+static const u8_displacement_t u8_decomp_b3_tbl[2][8][256] = {
+ {
+ { /* Third byte table 0. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 0, 0 },
+ { 1, 35 }, { 2, 247 }, { 3, 474 },
+ { 4, 693 }, { 5, 709 }, { 6, 951 },
+ { N_, 0 }, { 7, 1139 }, { 8, 1152 },
+ { N_, 0 }, { 9, 1177 }, { 10, 1199 },
+ { 11, 1295 }, { 12, 1360 }, { 13, 1405 },
+ { N_, 0 }, { 14, 1450 }, { N_, 0 },
+ { N_, 0 }, { 15, 1620 }, { N_, 0 },
+ { 16, 1624 }, { 17, 1649 }, { N_, 0 },
+ { 18, 1665 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ { /* Third byte table 1. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 19, 1680 },
+ { 20, 1701 }, { N_, 0 }, { 21, 1757 },
+ { 22, 1792 }, { 23, 1806 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 24, 1834 },
+ { 25, 1869 }, { 26, 1876 }, { N_, 0 },
+ { 27, 1897 }, { N_, 0 }, { 28, 1904 },
+ { N_, 0 }, { 29, 1942 }, { N_, 0 },
+ { 30, 1963 }, { 31, 1994 }, { N_, 0 },
+ { 32, 2000 }, { 33, 2006 }, { 34, 2018 },
+ { 35, 2021 }, { 36, 2109 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ { /* Third byte table 2. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 37, 2158 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { 0x8000, 2165 }, { 0x8001, 2445 },
+ { 0x8002, 2741 }, { 0x8003, 3029 }, { 0x8004, 3337 },
+ { 0x8005, 3725 }, { 0x8006, 4053 }, { 0x8007, 4536 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ { /* Third byte table 3. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 38, 4895 },
+ { 39, 4964 }, { 40, 4999 }, { N_, 0 },
+ { 41, 5018 }, { 42, 5098 }, { 43, 5230 },
+ { 44, 5248 }, { 45, 5266 }, { 46, 5326 },
+ { 47, 5410 }, { 48, 5470 }, { 49, 5518 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { 50, 5526 }, { 51, 5596 },
+ { 52, 5767 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 53, 5810 }, { 54, 5822 }, { N_, 0 },
+ { 55, 5830 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 56, 5836 }, { 57, 5839 }, { 58, 5842 },
+ { 59, 6034 }, { 60, 6226 }, { 61, 6418 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ { /* Third byte table 4. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 62, 6484 },
+ { 63, 6497 }, { 64, 6672 }, { 65, 6770 },
+ { 66, 6923 }, { 67, 6968 }, { 68, 7160 },
+ { N_, 0 }, { 0x8008, 7247 }, { 69, 7597 },
+ { 70, 7773 }, { 71, 7950 }, { 0x8009, 8142 },
+ { 0x800A, 8919 }, { 72, 9351 }, { 73, 9522 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ { /* Third byte table 5. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 0x800B, 9743 },
+ { 0x800C, 9999 }, { 0x800D, 10255 }, { 0x800E, 10511 },
+ { 74, 10767 }, { 75, 10967 }, { N_, 0 },
+ { N_, 0 }, { 76, 11139 }, { 77, 11303 },
+ { 78, 11468 }, { 79, 11576 }, { 0x800F, 11740 },
+ { 0x8010, 12006 }, { 0x8011, 12280 }, { 0x8012, 12546 },
+ { 80, 12812 }, { 0x8013, 13060 }, { 0x8014, 13348 },
+ { 81, 13720 }, { 82, 13898 }, { 83, 13933 },
+ { 84, 14045 }, { 85, 14197 }, { 86, 14347 },
+ { 87, 14410 }, { 88, 14540 }, { 89, 14729 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ { /* Third byte table 6. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { 90, 14829 }, { 91, 14912 },
+ { 92, 14969 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 93, 14982 }, { 94, 15046 }, { 95, 15109 },
+ { 96, 15163 }, { 97, 15225 }, { 98, 15282 },
+ { 99, 15341 }, { 100, 15405 }, { 101, 15469 },
+ { 102, 15533 }, { 103, 15597 }, { 104, 15681 },
+ { 105, 15812 }, { 106, 15942 }, { 107, 16072 },
+ { 108, 16202 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ { /* Third byte table 7. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { 0x8015, 16273 }, { 0x8016, 16536 },
+ { 0x8017, 16799 }, { 0x8018, 17064 }, { 0x8019, 17329 },
+ { 0x801A, 17601 }, { 0x801B, 17878 }, { 0x801C, 18147 },
+ { 109, 18419 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ },
+ {
+ { /* Third byte table 0. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 0, 0 },
+ { 1, 35 }, { 2, 247 }, { 3, 474 },
+ { 4, 693 }, { 5, 709 }, { 6, 951 },
+ { N_, 0 }, { 7, 1139 }, { 8, 1152 },
+ { N_, 0 }, { 9, 1177 }, { 10, 1199 },
+ { 11, 1295 }, { 12, 1362 }, { 13, 1407 },
+ { N_, 0 }, { 14, 1452 }, { N_, 0 },
+ { N_, 0 }, { 15, 1622 }, { N_, 0 },
+ { 16, 1626 }, { 17, 1651 }, { N_, 0 },
+ { 18, 1667 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ { /* Third byte table 1. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 19, 1682 },
+ { 20, 1703 }, { N_, 0 }, { 21, 1759 },
+ { 22, 1794 }, { 23, 1808 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 24, 1836 },
+ { 25, 1871 }, { 26, 1878 }, { N_, 0 },
+ { 27, 1899 }, { N_, 0 }, { 28, 1906 },
+ { N_, 0 }, { 29, 1944 }, { N_, 0 },
+ { 30, 1965 }, { 31, 1996 }, { N_, 0 },
+ { 32, 2002 }, { 33, 2008 }, { 34, 2020 },
+ { 35, 2023 }, { 36, 2111 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ { /* Third byte table 2. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 37, 2160 },
+ { N_, 0 }, { N_, 0 }, { 38, 2167 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { 39, 2170 }, { 40, 2226 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 41, 2247 }, { 42, 2268 }, { 43, 2340 },
+ { N_, 0 }, { 0x8000, 2414 }, { 0x8001, 2694 },
+ { 0x8002, 2990 }, { 0x8003, 3278 }, { 0x8004, 3586 },
+ { 0x8005, 3974 }, { 0x8006, 4302 }, { 0x8007, 4785 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ { /* Third byte table 3. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 44, 5144 },
+ { 45, 5213 }, { 46, 5248 }, { N_, 0 },
+ { 47, 5273 }, { 48, 5358 }, { 49, 5490 },
+ { 50, 5508 }, { 51, 5526 }, { 52, 5586 },
+ { 53, 5670 }, { 54, 5730 }, { 55, 5778 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { 56, 5786 }, { 57, 5856 },
+ { 58, 6027 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 59, 6070 }, { 60, 6082 }, { N_, 0 },
+ { 61, 6090 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { 62, 6096 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 63, 6099 }, { 64, 6102 }, { 65, 6105 },
+ { 66, 6297 }, { 67, 6489 }, { 68, 6681 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ { /* Third byte table 4. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 69, 6747 },
+ { 70, 6760 }, { 71, 6935 }, { 72, 7033 },
+ { 73, 7186 }, { 74, 7231 }, { 75, 7423 },
+ { N_, 0 }, { 0x8008, 7510 }, { 76, 7891 },
+ { 77, 8103 }, { 78, 8280 }, { 0x8009, 8482 },
+ { 0x800A, 9259 }, { 79, 9701 }, { 80, 9872 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ { /* Third byte table 5. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 0x800B, 10106 },
+ { 0x800C, 10362 }, { 0x800D, 10618 }, { 0x800E, 10874 },
+ { 81, 11130 }, { 82, 11330 }, { 0x800F, 11566 },
+ { 83, 11822 }, { 84, 11932 }, { 85, 12096 },
+ { 86, 12261 }, { 87, 12369 }, { 0x8010, 12533 },
+ { 0x8011, 12799 }, { 0x8012, 13073 }, { 0x8013, 13339 },
+ { 88, 13605 }, { 0x8014, 13853 }, { 0x8015, 14141 },
+ { 89, 14513 }, { 90, 14691 }, { 91, 14746 },
+ { 92, 14860 }, { 93, 15012 }, { 94, 15162 },
+ { 95, 15225 }, { 96, 15355 }, { 97, 15544 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ { /* Third byte table 6. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { 98, 15644 }, { 99, 15727 },
+ { 100, 15784 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 101, 15797 }, { 102, 15861 }, { 103, 15924 },
+ { 104, 15978 }, { 105, 16041 }, { 106, 16098 },
+ { 107, 16157 }, { 108, 16221 }, { 109, 16285 },
+ { 110, 16349 }, { 111, 16413 }, { 112, 16501 },
+ { 113, 16632 }, { 114, 16762 }, { 115, 16892 },
+ { 116, 17022 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ { /* Third byte table 7. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { 0x8016, 17097 }, { 0x8017, 17360 },
+ { 0x8018, 17623 }, { 0x8019, 17888 }, { 0x801A, 18153 },
+ { 0x801B, 18425 }, { 0x801C, 18702 }, { 0x801D, 18971 },
+ { 117, 19243 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 },
+ },
+ },
+};
+
+static const uchar_t u8_decomp_b4_tbl[2][118][257] = {
+ {
+ { /* Fourth byte table 0. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1,
+ 1, 4, 4, 5, 5, 5, 5, 5,
+ 8, 8, 8, 9, 10, 13, 15, 15,
+ 15, 18, 19, 20, 20, 25, 30, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35,
+ },
+ { /* Fourth byte table 1. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 24,
+ 28, 32, 36, 40, 44, 48, 52, 56,
+ 60, 60, 64, 68, 72, 76, 80, 84,
+ 84, 84, 88, 92, 96, 100, 104, 104,
+ 104, 108, 112, 116, 120, 124, 128, 128,
+ 132, 136, 140, 144, 148, 152, 156, 160,
+ 164, 164, 168, 172, 176, 180, 184, 188,
+ 188, 188, 192, 196, 200, 204, 208, 208,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212,
+ },
+ { /* Fourth byte table 2. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 64, 64, 68, 72, 76, 80, 84,
+ 88, 92, 96, 100, 104, 108, 112, 116,
+ 120, 124, 128, 132, 136, 140, 144, 144,
+ 144, 148, 152, 156, 160, 164, 168, 172,
+ 176, 180, 180, 182, 184, 188, 192, 196,
+ 200, 200, 204, 208, 212, 216, 220, 224,
+ 227, 227, 227, 227, 227, 227, 227, 227,
+ 227, 227, 227, 227, 227, 227, 227, 227,
+ 227, 227, 227, 227, 227, 227, 227, 227,
+ 227, 227, 227, 227, 227, 227, 227, 227,
+ 227, 227, 227, 227, 227, 227, 227, 227,
+ 227, 227, 227, 227, 227, 227, 227, 227,
+ 227, 227, 227, 227, 227, 227, 227, 227,
+ 227, 227, 227, 227, 227, 227, 227, 227,
+ 227,
+ },
+ { /* Fourth byte table 3. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 3, 7, 11, 15, 19,
+ 23, 27, 30, 30, 30, 34, 38, 42,
+ 46, 50, 54, 54, 54, 58, 62, 66,
+ 70, 74, 78, 82, 86, 90, 94, 98,
+ 102, 106, 110, 114, 118, 122, 126, 126,
+ 126, 130, 134, 138, 142, 146, 150, 154,
+ 158, 162, 166, 170, 174, 178, 182, 186,
+ 190, 194, 198, 202, 206, 210, 214, 218,
+ 219, 219, 219, 219, 219, 219, 219, 219,
+ 219, 219, 219, 219, 219, 219, 219, 219,
+ 219, 219, 219, 219, 219, 219, 219, 219,
+ 219, 219, 219, 219, 219, 219, 219, 219,
+ 219, 219, 219, 219, 219, 219, 219, 219,
+ 219, 219, 219, 219, 219, 219, 219, 219,
+ 219, 219, 219, 219, 219, 219, 219, 219,
+ 219, 219, 219, 219, 219, 219, 219, 219,
+ 219,
+ },
+ { /* Fourth byte table 4. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 12, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16,
+ },
+ { /* Fourth byte table 5. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 4, 8, 12,
+ 14, 16, 18, 20, 22, 24, 28, 32,
+ 36, 40, 44, 48, 52, 56, 62, 68,
+ 74, 80, 86, 92, 98, 104, 104, 110,
+ 116, 122, 128, 133, 138, 138, 138, 142,
+ 146, 150, 154, 158, 162, 168, 174, 179,
+ 184, 188, 190, 192, 194, 198, 202, 202,
+ 202, 206, 210, 216, 222, 227, 232, 237,
+ 242, 242, 242, 242, 242, 242, 242, 242,
+ 242, 242, 242, 242, 242, 242, 242, 242,
+ 242, 242, 242, 242, 242, 242, 242, 242,
+ 242, 242, 242, 242, 242, 242, 242, 242,
+ 242, 242, 242, 242, 242, 242, 242, 242,
+ 242, 242, 242, 242, 242, 242, 242, 242,
+ 242, 242, 242, 242, 242, 242, 242, 242,
+ 242, 242, 242, 242, 242, 242, 242, 242,
+ 242,
+ },
+ { /* Fourth byte table 6. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 112, 112, 116,
+ 120, 120, 120, 120, 120, 120, 120, 124,
+ 128, 132, 136, 142, 148, 154, 160, 164,
+ 168, 174, 180, 184, 188, 188, 188, 188,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 188,
+ },
+ { /* Fourth byte table 7. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 3, 4, 5, 7, 9, 11,
+ 12, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13,
+ },
+ { /* Fourth byte table 8. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 18,
+ 18, 20, 21, 22, 23, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25,
+ },
+ { /* Fourth byte table 9. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 6, 9, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 17, 17, 17,
+ 17, 17, 17, 20, 20, 20, 20, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22,
+ },
+ { /* Fourth byte table 10. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3, 14, 19,
+ 22, 27, 32, 37, 37, 42, 42, 47,
+ 52, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 64, 69, 74, 79, 84,
+ 89, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96,
+ },
+ { /* Fourth byte table 11. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 5, 10, 15, 20, 25,
+ 25, 27, 29, 31, 41, 51, 53, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55,
+ 55, 57, 59, 61, 61, 63, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65,
+ 65,
+ },
+ { /* Fourth byte table 12. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5, 10, 10, 15, 15, 15, 15,
+ 20, 20, 20, 20, 20, 25, 30, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 40, 40, 40, 40, 40, 40,
+ 40, 40, 40, 40, 40, 40, 40, 40,
+ 40, 40, 40, 40, 40, 40, 40, 40,
+ 40, 40, 40, 40, 40, 40, 40, 40,
+ 40, 40, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45,
+ },
+ { /* Fourth byte table 13. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5, 10, 10, 15, 15, 15, 15,
+ 20, 20, 20, 20, 20, 25, 30, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 40,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45,
+ },
+ { /* Fourth byte table 14. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 5, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 15, 20, 25, 30, 30, 30, 35,
+ 40, 40, 40, 45, 50, 55, 60, 65,
+ 70, 70, 70, 75, 80, 85, 90, 95,
+ 100, 100, 100, 105, 110, 115, 120, 125,
+ 130, 135, 140, 145, 150, 155, 160, 160,
+ 160, 165, 170, 170, 170, 170, 170, 170,
+ 170, 170, 170, 170, 170, 170, 170, 170,
+ 170, 170, 170, 170, 170, 170, 170, 170,
+ 170, 170, 170, 170, 170, 170, 170, 170,
+ 170, 170, 170, 170, 170, 170, 170, 170,
+ 170, 170, 170, 170, 170, 170, 170, 170,
+ 170, 170, 170, 170, 170, 170, 170, 170,
+ 170, 170, 170, 170, 170, 170, 170, 170,
+ 170, 170, 170, 170, 170, 170, 170, 170,
+ 170,
+ },
+ { /* Fourth byte table 15. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4,
+ },
+ { /* Fourth byte table 16. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 5, 10, 15, 20, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25,
+ },
+ { /* Fourth byte table 17. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 4, 8,
+ 12, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16,
+ },
+ { /* Fourth byte table 18. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5, 5, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15,
+ },
+ { /* Fourth byte table 19. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 7, 7, 7, 7, 7, 7,
+ 7, 7, 14, 14, 14, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21,
+ },
+ { /* Fourth byte table 20. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 7, 14, 21, 28, 35, 42, 49,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56,
+ },
+ { /* Fourth byte table 21. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 21, 28, 28,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35,
+ },
+ { /* Fourth byte table 22. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 7, 7, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14,
+ },
+ { /* Fourth byte table 23. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 7, 14, 21, 21, 21, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28,
+ },
+ { /* Fourth byte table 24. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 7, 7, 7, 14, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 28, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35,
+ },
+ { /* Fourth byte table 25. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7,
+ },
+ { /* Fourth byte table 26. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 14, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21,
+ },
+ { /* Fourth byte table 27. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7,
+ },
+ { /* Fourth byte table 28. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 7, 7, 7, 7, 7, 7, 7,
+ 14, 21, 21, 28, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38,
+ },
+ { /* Fourth byte table 29. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 14, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21,
+ },
+ { /* Fourth byte table 30. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 7, 14, 24, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31,
+ },
+ { /* Fourth byte table 31. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6,
+ },
+ { /* Fourth byte table 32. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6,
+ },
+ { /* Fourth byte table 33. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 6, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12,
+ },
+ { /* Fourth byte table 34. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3,
+ },
+ { /* Fourth byte table 35. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 14, 14,
+ 14, 14, 14, 21, 21, 21, 21, 21,
+ 28, 28, 28, 28, 28, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 49, 49, 56, 63,
+ 72, 79, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88,
+ },
+ { /* Fourth byte table 36. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 21, 21,
+ 21, 21, 21, 28, 28, 28, 28, 28,
+ 35, 35, 35, 35, 35, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49,
+ },
+ { /* Fourth byte table 37. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7,
+ },
+ { /* Fourth byte table 38. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 6, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 21, 21, 21, 21,
+ 21, 21, 24, 24, 24, 24, 24, 24,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 28, 30, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33,
+ 34, 34, 34, 34, 40, 49, 49, 55,
+ 64, 64, 64, 64, 64, 66, 66, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 69,
+ },
+ { /* Fourth byte table 39. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 4, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 20, 21, 21, 21, 22, 23, 24,
+ 25, 26, 27, 28, 31, 32, 33, 34,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35,
+ },
+ { /* Fourth byte table 40. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 14, 15, 16, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19,
+ },
+ { /* Fourth byte table 41. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 7, 10, 10, 13, 16,
+ 18, 18, 21, 22, 23, 24, 25, 26,
+ 28, 29, 30, 31, 32, 32, 33, 35,
+ 35, 35, 36, 37, 38, 39, 40, 40,
+ 40, 42, 45, 47, 47, 48, 48, 51,
+ 51, 52, 52, 54, 58, 59, 60, 60,
+ 61, 62, 63, 63, 64, 65, 67, 69,
+ 71, 73, 74, 74, 74, 74, 76, 78,
+ 80, 80, 80, 80, 80, 80, 80, 80,
+ 80, 80, 80, 80, 80, 80, 80, 80,
+ 80, 80, 80, 80, 80, 80, 80, 80,
+ 80, 80, 80, 80, 80, 80, 80, 80,
+ 80, 80, 80, 80, 80, 80, 80, 80,
+ 80, 80, 80, 80, 80, 80, 80, 80,
+ 80, 80, 80, 80, 80, 80, 80, 80,
+ 80, 80, 80, 80, 80, 80, 80, 80,
+ 80,
+ },
+ { /* Fourth byte table 42. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 3, 3, 3, 4, 5,
+ 6, 7, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 13, 18, 23, 28,
+ 33, 38, 43, 48, 53, 58, 63, 68,
+ 72, 73, 75, 78, 80, 81, 83, 86,
+ 90, 92, 93, 95, 98, 99, 100, 101,
+ 102, 103, 105, 108, 110, 111, 113, 116,
+ 120, 122, 123, 125, 128, 129, 130, 131,
+ 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132,
+ 132,
+ },
+ { /* Fourth byte table 43. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 6, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18,
+ },
+ { /* Fourth byte table 44. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 6, 12,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18,
+ },
+ { /* Fourth byte table 45. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 6, 6, 6,
+ 6, 6, 12, 12, 12, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 24, 24, 30,
+ 30, 30, 30, 30, 30, 36, 45, 45,
+ 51, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60,
+ },
+ { /* Fourth byte table 46. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 6, 6, 6, 12, 12, 12,
+ 18, 18, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 28, 28, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 40, 44,
+ 48, 54, 60, 60, 60, 66, 72, 72,
+ 72, 78, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84,
+ },
+ { /* Fourth byte table 47. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 6, 12, 12, 12, 18, 24, 24,
+ 24, 30, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 42, 48, 54,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60,
+ },
+ { /* Fourth byte table 48. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 6, 12, 18, 24, 24, 24, 24,
+ 24, 24, 24, 30, 36, 42, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48,
+ },
+ { /* Fourth byte table 49. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 4, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8,
+ },
+ { /* Fourth byte table 50. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 11, 13, 15, 17, 19, 21,
+ 23, 25, 27, 29, 31, 34, 37, 40,
+ 43, 46, 49, 52, 55, 58, 62, 66,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70,
+ },
+ { /* Fourth byte table 51. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 48, 50, 53, 56, 59, 62, 65, 68,
+ 71, 74, 77, 80, 83, 86, 89, 92,
+ 95, 98, 101, 104, 107, 110, 113, 116,
+ 119, 122, 125, 128, 131, 134, 137, 140,
+ 143, 146, 149, 152, 155, 158, 161, 162,
+ 163, 164, 165, 166, 167, 168, 169, 170,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171,
+ },
+ { /* Fourth byte table 52. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 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, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43,
+ },
+ { /* Fourth byte table 53. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12,
+ },
+ { /* Fourth byte table 54. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3, 5, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8,
+ },
+ { /* Fourth byte table 55. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6,
+ },
+ { /* Fourth byte table 56. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3,
+ },
+ { /* Fourth byte table 57. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3,
+ },
+ { /* Fourth byte table 58. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 60, 63, 66, 69,
+ 72, 75, 78, 81, 84, 87, 90, 93,
+ 96, 99, 102, 105, 108, 111, 114, 117,
+ 120, 123, 126, 129, 132, 135, 138, 141,
+ 144, 147, 150, 153, 156, 159, 162, 165,
+ 168, 171, 174, 177, 180, 183, 186, 189,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192,
+ },
+ { /* Fourth byte table 59. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 60, 63, 66, 69,
+ 72, 75, 78, 81, 84, 87, 90, 93,
+ 96, 99, 102, 105, 108, 111, 114, 117,
+ 120, 123, 126, 129, 132, 135, 138, 141,
+ 144, 147, 150, 153, 156, 159, 162, 165,
+ 168, 171, 174, 177, 180, 183, 186, 189,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192,
+ },
+ { /* Fourth byte table 60. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 60, 63, 66, 69,
+ 72, 75, 78, 81, 84, 87, 90, 93,
+ 96, 99, 102, 105, 108, 111, 114, 117,
+ 120, 123, 126, 129, 132, 135, 138, 141,
+ 144, 147, 150, 153, 156, 159, 162, 165,
+ 168, 171, 174, 177, 180, 183, 186, 189,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192,
+ },
+ { /* Fourth byte table 61. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 60, 63, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66,
+ },
+ { /* Fourth byte table 62. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 4,
+ 4, 7, 10, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13,
+ },
+ { /* Fourth byte table 63. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7, 7, 14,
+ 14, 21, 21, 28, 28, 35, 35, 42,
+ 42, 49, 49, 56, 56, 63, 63, 70,
+ 70, 77, 77, 84, 84, 84, 91, 91,
+ 98, 98, 105, 105, 105, 105, 105, 105,
+ 105, 112, 119, 119, 126, 133, 133, 140,
+ 147, 147, 154, 161, 161, 168, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175,
+ },
+ { /* Fourth byte table 64. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7, 7, 7,
+ 7, 7, 7, 7, 11, 15, 15, 22,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 35, 35, 42,
+ 42, 49, 49, 56, 56, 63, 63, 70,
+ 70, 77, 77, 84, 84, 91, 91, 98,
+ 98, 98, 98, 98, 98, 98, 98, 98,
+ 98, 98, 98, 98, 98, 98, 98, 98,
+ 98, 98, 98, 98, 98, 98, 98, 98,
+ 98, 98, 98, 98, 98, 98, 98, 98,
+ 98, 98, 98, 98, 98, 98, 98, 98,
+ 98, 98, 98, 98, 98, 98, 98, 98,
+ 98, 98, 98, 98, 98, 98, 98, 98,
+ 98, 98, 98, 98, 98, 98, 98, 98,
+ 98,
+ },
+ { /* Fourth byte table 65. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 7, 7, 14, 14, 14, 21, 21,
+ 28, 28, 35, 35, 35, 35, 35, 35,
+ 35, 42, 49, 49, 56, 63, 63, 70,
+ 77, 77, 84, 91, 91, 98, 105, 105,
+ 105, 105, 105, 105, 105, 105, 105, 105,
+ 105, 105, 105, 105, 105, 105, 105, 105,
+ 105, 105, 105, 105, 105, 112, 112, 112,
+ 119, 126, 133, 140, 140, 140, 140, 147,
+ 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153,
+ 153,
+ },
+ { /* Fourth byte table 66. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 6, 9, 12, 15, 18,
+ 21, 24, 27, 30, 33, 36, 39, 42,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45,
+ },
+ { /* Fourth byte table 67. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 60, 63, 66, 69,
+ 72, 75, 78, 81, 84, 87, 90, 93,
+ 96, 99, 102, 105, 108, 111, 114, 117,
+ 120, 123, 126, 129, 132, 135, 138, 141,
+ 144, 147, 150, 153, 156, 159, 162, 165,
+ 168, 171, 174, 177, 180, 183, 186, 189,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192,
+ },
+ { /* Fourth byte table 68. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 45, 45, 45, 48, 51, 54, 57, 60,
+ 63, 66, 69, 72, 75, 78, 81, 84,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87,
+ },
+ { /* Fourth byte table 69. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5, 10, 15, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 22, 24, 26, 28, 30, 32,
+ 34, 36, 38, 40, 42, 44, 46, 48,
+ 50, 53, 56, 59, 62, 65, 68, 71,
+ 74, 77, 80, 83, 86, 89, 92, 98,
+ 104, 110, 116, 122, 128, 134, 140, 146,
+ 152, 158, 164, 170, 176, 176, 176, 176,
+ 176, 176, 176, 176, 176, 176, 176, 176,
+ 176, 176, 176, 176, 176, 176, 176, 176,
+ 176, 176, 176, 176, 176, 176, 176, 176,
+ 176, 176, 176, 176, 176, 176, 176, 176,
+ 176, 176, 176, 176, 176, 176, 176, 176,
+ 176, 176, 176, 176, 176, 176, 176, 176,
+ 176, 176, 176, 176, 176, 176, 176, 176,
+ 176, 176, 176, 176, 176, 176, 176, 176,
+ 176,
+ },
+ { /* Fourth byte table 70. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 60, 63, 66, 69,
+ 72, 75, 78, 81, 84, 87, 90, 93,
+ 96, 99, 102, 105, 108, 111, 114, 117,
+ 120, 123, 126, 129, 132, 135, 138, 141,
+ 144, 147, 149, 151, 153, 155, 157, 159,
+ 161, 163, 165, 167, 169, 171, 173, 175,
+ 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177,
+ 177,
+ },
+ { /* Fourth byte table 71. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 41, 46, 51, 51, 51, 51,
+ 51, 54, 57, 60, 63, 66, 69, 72,
+ 75, 78, 81, 84, 87, 90, 93, 96,
+ 99, 102, 105, 108, 111, 114, 117, 120,
+ 123, 126, 129, 132, 135, 138, 141, 144,
+ 147, 150, 153, 156, 159, 162, 165, 168,
+ 171, 174, 177, 180, 183, 186, 189, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192,
+ },
+ { /* Fourth byte table 72. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 7, 9, 11, 13, 15,
+ 17, 20, 24, 26, 28, 31, 34, 36,
+ 38, 40, 43, 46, 49, 52, 55, 57,
+ 59, 61, 63, 65, 68, 70, 72, 74,
+ 77, 80, 82, 85, 88, 91, 93, 96,
+ 101, 107, 109, 112, 115, 118, 121, 128,
+ 136, 138, 140, 143, 145, 147, 149, 152,
+ 154, 156, 158, 160, 162, 165, 167, 169,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171,
+ },
+ { /* Fourth byte table 73. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 10, 12, 14, 16, 22,
+ 25, 27, 29, 31, 33, 35, 37, 39,
+ 41, 43, 45, 48, 50, 52, 55, 58,
+ 60, 64, 67, 69, 71, 73, 75, 75,
+ 75, 79, 83, 87, 91, 95, 99, 103,
+ 107, 111, 116, 121, 126, 131, 136, 141,
+ 146, 151, 156, 161, 166, 171, 176, 181,
+ 186, 191, 196, 201, 206, 211, 216, 221,
+ 221, 221, 221, 221, 221, 221, 221, 221,
+ 221, 221, 221, 221, 221, 221, 221, 221,
+ 221, 221, 221, 221, 221, 221, 221, 221,
+ 221, 221, 221, 221, 221, 221, 221, 221,
+ 221, 221, 221, 221, 221, 221, 221, 221,
+ 221, 221, 221, 221, 221, 221, 221, 221,
+ 221, 221, 221, 221, 221, 221, 221, 221,
+ 221, 221, 221, 221, 221, 221, 221, 221,
+ 221,
+ },
+ { /* Fourth byte table 74. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 56,
+ 56, 60, 60, 64, 64, 64, 68, 72,
+ 76, 80, 84, 88, 92, 96, 100, 104,
+ 104, 108, 108, 112, 112, 112, 116, 120,
+ 120, 120, 120, 124, 128, 132, 136, 136,
+ 136, 140, 144, 148, 152, 156, 160, 164,
+ 168, 172, 176, 180, 184, 188, 192, 196,
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ 200,
+ },
+ { /* Fourth byte table 75. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 116, 120, 124,
+ 128, 132, 136, 140, 144, 148, 152, 156,
+ 160, 164, 168, 172, 172, 172, 172, 172,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 172,
+ },
+ { /* Fourth byte table 76. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 9, 12, 14, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 20, 24, 28, 32,
+ 36, 36, 36, 36, 36, 36, 41, 41,
+ 46, 48, 50, 52, 54, 56, 58, 60,
+ 62, 64, 65, 70, 75, 82, 89, 94,
+ 99, 104, 109, 114, 119, 124, 129, 134,
+ 134, 139, 144, 149, 154, 159, 159, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164,
+ },
+ { /* Fourth byte table 77. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5, 10, 10, 15, 20, 20, 25,
+ 30, 35, 40, 45, 50, 55, 60, 65,
+ 69, 71, 73, 75, 77, 79, 81, 83,
+ 85, 87, 89, 91, 93, 95, 97, 99,
+ 101, 103, 105, 107, 109, 111, 113, 115,
+ 117, 119, 121, 123, 125, 127, 129, 131,
+ 133, 135, 137, 139, 141, 143, 145, 147,
+ 149, 151, 153, 155, 157, 159, 161, 163,
+ 165, 165, 165, 165, 165, 165, 165, 165,
+ 165, 165, 165, 165, 165, 165, 165, 165,
+ 165, 165, 165, 165, 165, 165, 165, 165,
+ 165, 165, 165, 165, 165, 165, 165, 165,
+ 165, 165, 165, 165, 165, 165, 165, 165,
+ 165, 165, 165, 165, 165, 165, 165, 165,
+ 165, 165, 165, 165, 165, 165, 165, 165,
+ 165, 165, 165, 165, 165, 165, 165, 165,
+ 165,
+ },
+ { /* Fourth byte table 78. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 48, 50, 52, 54, 56, 58, 60, 62,
+ 64, 66, 68, 70, 72, 76, 80, 82,
+ 84, 86, 88, 90, 92, 94, 96, 98,
+ 100, 104, 108, 108, 108, 108, 108, 108,
+ 108, 108, 108, 108, 108, 108, 108, 108,
+ 108, 108, 108, 108, 108, 108, 108, 108,
+ 108, 108, 108, 108, 108, 108, 108, 108,
+ 108, 108, 108, 108, 108, 108, 108, 108,
+ 108, 108, 108, 108, 108, 108, 108, 108,
+ 108, 108, 108, 108, 108, 108, 108, 108,
+ 108, 108, 108, 108, 108, 108, 108, 108,
+ 108, 108, 108, 108, 108, 108, 108, 108,
+ 108, 108, 108, 108, 108, 108, 108, 108,
+ 108,
+ },
+ { /* Fourth byte table 79. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 4, 6, 8,
+ 10, 12, 14, 16, 18, 20, 24, 26,
+ 28, 30, 32, 34, 36, 38, 40, 42,
+ 44, 46, 48, 54, 60, 66, 72, 78,
+ 84, 90, 96, 102, 108, 114, 120, 126,
+ 132, 138, 144, 150, 156, 158, 160, 162,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164,
+ },
+ { /* Fourth byte table 80. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 116, 120, 124,
+ 128, 132, 136, 140, 144, 148, 152, 156,
+ 160, 164, 168, 172, 176, 180, 184, 188,
+ 192, 196, 200, 204, 208, 212, 216, 220,
+ 224, 228, 232, 236, 240, 244, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248,
+ 248,
+ },
+ { /* Fourth byte table 81. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 6, 12, 18, 24, 30, 36, 42,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 54, 60, 68, 76, 84, 92, 100,
+ 108, 116, 122, 155, 170, 178, 178, 178,
+ 178, 178, 178, 178, 178, 178, 178, 178,
+ 178, 178, 178, 178, 178, 178, 178, 178,
+ 178, 178, 178, 178, 178, 178, 178, 178,
+ 178, 178, 178, 178, 178, 178, 178, 178,
+ 178, 178, 178, 178, 178, 178, 178, 178,
+ 178, 178, 178, 178, 178, 178, 178, 178,
+ 178, 178, 178, 178, 178, 178, 178, 178,
+ 178, 178, 178, 178, 178, 178, 178, 178,
+ 178,
+ },
+ { /* Fourth byte table 82. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 5, 8, 9, 10, 11, 12,
+ 13, 14, 17, 20, 23, 26, 29, 32,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35,
+ },
+ { /* Fourth byte table 83. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 15, 15,
+ 15, 15, 18, 21, 24, 27, 28, 29,
+ 30, 31, 34, 35, 35, 36, 37, 38,
+ 39, 42, 43, 44, 45, 46, 49, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60,
+ 60, 61, 62, 63, 64, 64, 64, 64,
+ 64, 67, 71, 74, 74, 77, 77, 80,
+ 84, 87, 91, 94, 98, 101, 105, 108,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112,
+ },
+ { /* Fourth byte table 84. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 6, 10, 14, 18, 22, 26,
+ 30, 34, 38, 42, 46, 50, 52, 54,
+ 56, 58, 60, 62, 64, 66, 68, 70,
+ 72, 74, 76, 78, 80, 82, 84, 86,
+ 88, 90, 92, 94, 96, 98, 100, 102,
+ 104, 106, 108, 110, 112, 114, 116, 118,
+ 120, 122, 124, 126, 128, 130, 132, 134,
+ 136, 138, 140, 142, 144, 146, 148, 150,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152,
+ },
+ { /* Fourth byte table 85. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 48, 50, 52, 54, 56, 58, 60, 62,
+ 64, 66, 68, 70, 72, 74, 76, 78,
+ 80, 82, 84, 86, 88, 90, 92, 94,
+ 96, 98, 100, 102, 104, 106, 112, 118,
+ 124, 130, 136, 142, 146, 150, 150, 150,
+ 150, 150, 150, 150, 150, 150, 150, 150,
+ 150, 150, 150, 150, 150, 150, 150, 150,
+ 150, 150, 150, 150, 150, 150, 150, 150,
+ 150, 150, 150, 150, 150, 150, 150, 150,
+ 150, 150, 150, 150, 150, 150, 150, 150,
+ 150, 150, 150, 150, 150, 150, 150, 150,
+ 150, 150, 150, 150, 150, 150, 150, 150,
+ 150, 150, 150, 150, 150, 150, 150, 150,
+ 150,
+ },
+ { /* Fourth byte table 86. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 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, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63,
+ },
+ { /* Fourth byte table 87. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 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,
+ 34, 37, 40, 43, 46, 49, 52, 55,
+ 58, 61, 64, 67, 70, 73, 76, 79,
+ 82, 85, 88, 91, 94, 97, 100, 103,
+ 106, 109, 112, 115, 118, 121, 124, 127,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130,
+ },
+ { /* Fourth byte table 88. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 60, 63, 66, 69,
+ 72, 75, 78, 81, 84, 87, 90, 93,
+ 96, 99, 102, 105, 108, 111, 114, 117,
+ 120, 123, 126, 129, 132, 135, 138, 141,
+ 144, 147, 150, 153, 156, 159, 162, 165,
+ 168, 171, 174, 177, 180, 183, 186, 189,
+ 189, 189, 189, 189, 189, 189, 189, 189,
+ 189, 189, 189, 189, 189, 189, 189, 189,
+ 189, 189, 189, 189, 189, 189, 189, 189,
+ 189, 189, 189, 189, 189, 189, 189, 189,
+ 189, 189, 189, 189, 189, 189, 189, 189,
+ 189, 189, 189, 189, 189, 189, 189, 189,
+ 189, 189, 189, 189, 189, 189, 189, 189,
+ 189, 189, 189, 189, 189, 189, 189, 189,
+ 189,
+ },
+ { /* Fourth byte table 89. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 3, 6, 9, 12, 15,
+ 18, 18, 18, 21, 24, 27, 30, 33,
+ 36, 36, 36, 39, 42, 45, 48, 51,
+ 54, 54, 54, 57, 60, 63, 63, 63,
+ 63, 65, 67, 69, 72, 74, 76, 79,
+ 79, 82, 85, 88, 91, 94, 97, 100,
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100,
+ },
+ { /* Fourth byte table 90. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 9,
+ 18, 31, 44, 57, 70, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83,
+ },
+ { /* Fourth byte table 91. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 9, 18, 31, 44,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57,
+ },
+ { /* Fourth byte table 92. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13,
+ },
+ { /* Fourth byte table 93. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 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,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64,
+ },
+ { /* Fourth byte table 94. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 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, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63,
+ },
+ { /* Fourth byte table 95. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 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, 29, 30,
+ 31, 31, 31, 32, 32, 32, 33, 34,
+ 34, 34, 35, 36, 37, 38, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 50, 51, 51, 52, 53,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54,
+ },
+ { /* Fourth byte table 96. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 2, 3, 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, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62,
+ },
+ { /* Fourth byte table 97. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 3, 4, 5, 6, 6,
+ 7, 8, 9, 10, 10, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 18, 19,
+ 20, 21, 22, 23, 24, 25, 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, 53, 54, 55, 56, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57,
+ },
+ { /* Fourth byte table 98. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 3, 4, 5, 5, 6,
+ 6, 6, 6, 7, 8, 9, 10, 11,
+ 12, 13, 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, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59,
+ 59,
+ },
+ { /* Fourth byte table 99. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 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,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64,
+ },
+ { /* Fourth byte table 100. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 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,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64,
+ },
+ { /* Fourth byte table 101. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 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,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64,
+ },
+ { /* Fourth byte table 102. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 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,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64,
+ },
+ { /* Fourth byte table 103. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 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, 36, 36, 36,
+ 36, 38, 40, 42, 44, 46, 48, 50,
+ 52, 54, 56, 58, 60, 62, 64, 66,
+ 68, 70, 72, 74, 76, 78, 80, 82,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84,
+ },
+ { /* Fourth byte table 104. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 5, 7, 9, 11, 13, 15,
+ 17, 19, 21, 23, 25, 27, 29, 31,
+ 33, 35, 37, 39, 41, 43, 45, 47,
+ 49, 51, 53, 55, 58, 60, 62, 64,
+ 66, 68, 70, 72, 74, 76, 78, 80,
+ 82, 84, 86, 88, 90, 92, 94, 96,
+ 98, 100, 102, 104, 106, 108, 110, 112,
+ 114, 116, 118, 120, 123, 125, 127, 129,
+ 131, 131, 131, 131, 131, 131, 131, 131,
+ 131, 131, 131, 131, 131, 131, 131, 131,
+ 131, 131, 131, 131, 131, 131, 131, 131,
+ 131, 131, 131, 131, 131, 131, 131, 131,
+ 131, 131, 131, 131, 131, 131, 131, 131,
+ 131, 131, 131, 131, 131, 131, 131, 131,
+ 131, 131, 131, 131, 131, 131, 131, 131,
+ 131, 131, 131, 131, 131, 131, 131, 131,
+ 131,
+ },
+ { /* Fourth byte table 105. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 45, 47,
+ 49, 51, 53, 55, 57, 59, 61, 63,
+ 65, 67, 69, 71, 73, 75, 77, 79,
+ 81, 83, 85, 87, 89, 91, 93, 95,
+ 97, 99, 101, 103, 105, 107, 110, 112,
+ 114, 116, 118, 120, 122, 124, 126, 128,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130,
+ },
+ { /* Fourth byte table 106. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 33, 35, 37, 39, 41, 43, 45, 47,
+ 49, 51, 53, 55, 57, 59, 61, 63,
+ 65, 67, 69, 71, 73, 75, 77, 79,
+ 81, 83, 85, 87, 89, 91, 93, 95,
+ 98, 100, 102, 104, 106, 108, 110, 112,
+ 114, 116, 118, 120, 122, 124, 126, 128,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130,
+ },
+ { /* Fourth byte table 107. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 21, 23, 25, 27, 29, 31,
+ 33, 35, 37, 39, 41, 43, 45, 47,
+ 49, 51, 53, 55, 57, 59, 61, 63,
+ 65, 67, 69, 71, 73, 75, 77, 79,
+ 81, 83, 86, 88, 90, 92, 94, 96,
+ 98, 100, 102, 104, 106, 108, 110, 112,
+ 114, 116, 118, 120, 122, 124, 126, 128,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130,
+ },
+ { /* Fourth byte table 108. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 9, 11, 13, 15,
+ 17, 19, 21, 21, 21, 21, 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, 64, 65, 66, 67, 68, 69, 70,
+ 71, 71, 71, 71, 71, 71, 71, 71,
+ 71, 71, 71, 71, 71, 71, 71, 71,
+ 71, 71, 71, 71, 71, 71, 71, 71,
+ 71, 71, 71, 71, 71, 71, 71, 71,
+ 71, 71, 71, 71, 71, 71, 71, 71,
+ 71, 71, 71, 71, 71, 71, 71, 71,
+ 71, 71, 71, 71, 71, 71, 71, 71,
+ 71, 71, 71, 71, 71, 71, 71, 71,
+ 71,
+ },
+ { /* Fourth byte table 109. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 9, 13, 17, 21, 25, 29,
+ 33, 37, 42, 46, 50, 54, 58, 62,
+ 66, 71, 75, 80, 85, 90, 94, 98,
+ 102, 106, 110, 114, 118, 122, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127,
+ },
+ { /* Fourth byte table 110. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ { /* Fourth byte table 111. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ { /* Fourth byte table 112. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ { /* Fourth byte table 113. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ { /* Fourth byte table 114. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ { /* Fourth byte table 115. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ { /* Fourth byte table 116. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ { /* Fourth byte table 117. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ },
+ {
+ { /* Fourth byte table 0. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1,
+ 1, 4, 4, 5, 5, 5, 5, 5,
+ 8, 8, 8, 9, 10, 13, 15, 15,
+ 15, 18, 19, 20, 20, 25, 30, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35,
+ },
+ { /* Fourth byte table 1. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 24,
+ 28, 32, 36, 40, 44, 48, 52, 56,
+ 60, 60, 64, 68, 72, 76, 80, 84,
+ 84, 84, 88, 92, 96, 100, 104, 104,
+ 104, 108, 112, 116, 120, 124, 128, 128,
+ 132, 136, 140, 144, 148, 152, 156, 160,
+ 164, 164, 168, 172, 176, 180, 184, 188,
+ 188, 188, 192, 196, 200, 204, 208, 208,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212,
+ },
+ { /* Fourth byte table 2. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 64, 64, 68, 72, 76, 80, 84,
+ 88, 92, 96, 100, 104, 108, 112, 116,
+ 120, 124, 128, 132, 136, 140, 144, 144,
+ 144, 148, 152, 156, 160, 164, 168, 172,
+ 176, 180, 180, 182, 184, 188, 192, 196,
+ 200, 200, 204, 208, 212, 216, 220, 224,
+ 227, 227, 227, 227, 227, 227, 227, 227,
+ 227, 227, 227, 227, 227, 227, 227, 227,
+ 227, 227, 227, 227, 227, 227, 227, 227,
+ 227, 227, 227, 227, 227, 227, 227, 227,
+ 227, 227, 227, 227, 227, 227, 227, 227,
+ 227, 227, 227, 227, 227, 227, 227, 227,
+ 227, 227, 227, 227, 227, 227, 227, 227,
+ 227, 227, 227, 227, 227, 227, 227, 227,
+ 227,
+ },
+ { /* Fourth byte table 3. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 3, 7, 11, 15, 19,
+ 23, 27, 30, 30, 30, 34, 38, 42,
+ 46, 50, 54, 54, 54, 58, 62, 66,
+ 70, 74, 78, 82, 86, 90, 94, 98,
+ 102, 106, 110, 114, 118, 122, 126, 126,
+ 126, 130, 134, 138, 142, 146, 150, 154,
+ 158, 162, 166, 170, 174, 178, 182, 186,
+ 190, 194, 198, 202, 206, 210, 214, 218,
+ 219, 219, 219, 219, 219, 219, 219, 219,
+ 219, 219, 219, 219, 219, 219, 219, 219,
+ 219, 219, 219, 219, 219, 219, 219, 219,
+ 219, 219, 219, 219, 219, 219, 219, 219,
+ 219, 219, 219, 219, 219, 219, 219, 219,
+ 219, 219, 219, 219, 219, 219, 219, 219,
+ 219, 219, 219, 219, 219, 219, 219, 219,
+ 219, 219, 219, 219, 219, 219, 219, 219,
+ 219,
+ },
+ { /* Fourth byte table 4. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 12, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16,
+ },
+ { /* Fourth byte table 5. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 4, 8, 12,
+ 14, 16, 18, 20, 22, 24, 28, 32,
+ 36, 40, 44, 48, 52, 56, 62, 68,
+ 74, 80, 86, 92, 98, 104, 104, 110,
+ 116, 122, 128, 133, 138, 138, 138, 142,
+ 146, 150, 154, 158, 162, 168, 174, 179,
+ 184, 188, 190, 192, 194, 198, 202, 202,
+ 202, 206, 210, 216, 222, 227, 232, 237,
+ 242, 242, 242, 242, 242, 242, 242, 242,
+ 242, 242, 242, 242, 242, 242, 242, 242,
+ 242, 242, 242, 242, 242, 242, 242, 242,
+ 242, 242, 242, 242, 242, 242, 242, 242,
+ 242, 242, 242, 242, 242, 242, 242, 242,
+ 242, 242, 242, 242, 242, 242, 242, 242,
+ 242, 242, 242, 242, 242, 242, 242, 242,
+ 242, 242, 242, 242, 242, 242, 242, 242,
+ 242,
+ },
+ { /* Fourth byte table 6. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 112, 112, 116,
+ 120, 120, 120, 120, 120, 120, 120, 124,
+ 128, 132, 136, 142, 148, 154, 160, 164,
+ 168, 174, 180, 184, 188, 188, 188, 188,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 188,
+ },
+ { /* Fourth byte table 7. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 3, 4, 5, 7, 9, 11,
+ 12, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13,
+ },
+ { /* Fourth byte table 8. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 18,
+ 18, 20, 21, 22, 23, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25,
+ },
+ { /* Fourth byte table 9. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 6, 9, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 17, 17, 17,
+ 17, 17, 17, 20, 20, 20, 20, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22,
+ },
+ { /* Fourth byte table 10. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3, 14, 19,
+ 22, 27, 32, 37, 37, 42, 42, 47,
+ 52, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 64, 69, 74, 79, 84,
+ 89, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96,
+ },
+ { /* Fourth byte table 11. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 5, 10, 15, 20, 25,
+ 25, 27, 29, 31, 41, 51, 53, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55,
+ 55, 57, 59, 61, 61, 63, 65, 65,
+ 65, 65, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 67,
+ },
+ { /* Fourth byte table 12. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5, 10, 10, 15, 15, 15, 15,
+ 20, 20, 20, 20, 20, 25, 30, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 40, 40, 40, 40, 40, 40,
+ 40, 40, 40, 40, 40, 40, 40, 40,
+ 40, 40, 40, 40, 40, 40, 40, 40,
+ 40, 40, 40, 40, 40, 40, 40, 40,
+ 40, 40, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45,
+ },
+ { /* Fourth byte table 13. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5, 10, 10, 15, 15, 15, 15,
+ 20, 20, 20, 20, 20, 25, 30, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 40,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45,
+ },
+ { /* Fourth byte table 14. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 5, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 15, 20, 25, 30, 30, 30, 35,
+ 40, 40, 40, 45, 50, 55, 60, 65,
+ 70, 70, 70, 75, 80, 85, 90, 95,
+ 100, 100, 100, 105, 110, 115, 120, 125,
+ 130, 135, 140, 145, 150, 155, 160, 160,
+ 160, 165, 170, 170, 170, 170, 170, 170,
+ 170, 170, 170, 170, 170, 170, 170, 170,
+ 170, 170, 170, 170, 170, 170, 170, 170,
+ 170, 170, 170, 170, 170, 170, 170, 170,
+ 170, 170, 170, 170, 170, 170, 170, 170,
+ 170, 170, 170, 170, 170, 170, 170, 170,
+ 170, 170, 170, 170, 170, 170, 170, 170,
+ 170, 170, 170, 170, 170, 170, 170, 170,
+ 170, 170, 170, 170, 170, 170, 170, 170,
+ 170,
+ },
+ { /* Fourth byte table 15. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4,
+ },
+ { /* Fourth byte table 16. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 5, 10, 15, 20, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25,
+ },
+ { /* Fourth byte table 17. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 4, 8,
+ 12, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16,
+ },
+ { /* Fourth byte table 18. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5, 5, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15,
+ },
+ { /* Fourth byte table 19. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 7, 7, 7, 7, 7, 7,
+ 7, 7, 14, 14, 14, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21,
+ },
+ { /* Fourth byte table 20. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 7, 14, 21, 28, 35, 42, 49,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56,
+ },
+ { /* Fourth byte table 21. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 21, 28, 28,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35,
+ },
+ { /* Fourth byte table 22. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 7, 7, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14,
+ },
+ { /* Fourth byte table 23. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 7, 14, 21, 21, 21, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28,
+ },
+ { /* Fourth byte table 24. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 7, 7, 7, 14, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 28, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35,
+ },
+ { /* Fourth byte table 25. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7,
+ },
+ { /* Fourth byte table 26. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 14, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21,
+ },
+ { /* Fourth byte table 27. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7,
+ },
+ { /* Fourth byte table 28. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 7, 7, 7, 7, 7, 7, 7,
+ 14, 21, 21, 28, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38,
+ },
+ { /* Fourth byte table 29. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 14, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21,
+ },
+ { /* Fourth byte table 30. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 7, 14, 24, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31,
+ },
+ { /* Fourth byte table 31. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6,
+ },
+ { /* Fourth byte table 32. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6,
+ },
+ { /* Fourth byte table 33. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 6, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12,
+ },
+ { /* Fourth byte table 34. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3,
+ },
+ { /* Fourth byte table 35. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 14, 14,
+ 14, 14, 14, 21, 21, 21, 21, 21,
+ 28, 28, 28, 28, 28, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 49, 49, 56, 63,
+ 72, 79, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88,
+ },
+ { /* Fourth byte table 36. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 21, 21,
+ 21, 21, 21, 28, 28, 28, 28, 28,
+ 35, 35, 35, 35, 35, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49,
+ },
+ { /* Fourth byte table 37. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7,
+ },
+ { /* Fourth byte table 38. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3,
+ },
+ { /* Fourth byte table 39. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 7,
+ 7, 14, 14, 21, 21, 28, 28, 35,
+ 35, 35, 35, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 49, 49, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56,
+ },
+ { /* Fourth byte table 40. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 7, 14, 14, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21,
+ },
+ { /* Fourth byte table 41. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 3, 4,
+ 4, 5, 6, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 16, 17, 19, 20,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21,
+ },
+ { /* Fourth byte table 42. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 3, 4, 6, 8, 11,
+ 12, 13, 14, 16, 18, 20, 21, 21,
+ 22, 23, 25, 26, 28, 31, 34, 35,
+ 36, 37, 40, 42, 43, 46, 48, 50,
+ 52, 54, 56, 57, 58, 59, 60, 62,
+ 64, 66, 68, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72,
+ },
+ { /* Fourth byte table 43. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 3, 5, 7,
+ 9, 10, 12, 14, 16, 18, 20, 22,
+ 25, 27, 29, 32, 34, 36, 38, 40,
+ 42, 44, 46, 48, 50, 52, 54, 56,
+ 58, 61, 63, 65, 66, 68, 70, 72,
+ 74, 74, 74, 74, 74, 74, 74, 74,
+ 74, 74, 74, 74, 74, 74, 74, 74,
+ 74, 74, 74, 74, 74, 74, 74, 74,
+ 74, 74, 74, 74, 74, 74, 74, 74,
+ 74, 74, 74, 74, 74, 74, 74, 74,
+ 74, 74, 74, 74, 74, 74, 74, 74,
+ 74, 74, 74, 74, 74, 74, 74, 74,
+ 74, 74, 74, 74, 74, 74, 74, 74,
+ 74,
+ },
+ { /* Fourth byte table 44. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 6, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 21, 21, 21, 21,
+ 21, 21, 24, 24, 24, 24, 24, 24,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 28, 30, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33,
+ 34, 34, 34, 34, 40, 49, 49, 55,
+ 64, 64, 64, 64, 64, 66, 66, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 69,
+ },
+ { /* Fourth byte table 45. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 4, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 20, 21, 21, 21, 22, 23, 24,
+ 25, 26, 27, 28, 31, 32, 33, 34,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ 35,
+ },
+ { /* Fourth byte table 46. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 14, 15, 16, 17,
+ 17, 18, 19, 20, 21, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25,
+ },
+ { /* Fourth byte table 47. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 7, 10, 10, 13, 16,
+ 18, 18, 21, 22, 23, 24, 25, 26,
+ 28, 29, 30, 31, 32, 32, 33, 35,
+ 35, 35, 36, 37, 38, 39, 40, 40,
+ 40, 42, 45, 47, 47, 48, 48, 51,
+ 51, 52, 52, 54, 58, 59, 60, 60,
+ 61, 62, 63, 63, 64, 65, 67, 69,
+ 71, 73, 74, 74, 77, 79, 81, 83,
+ 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85,
+ 85,
+ },
+ { /* Fourth byte table 48. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 3, 3, 3, 4, 5,
+ 6, 7, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 13, 18, 23, 28,
+ 33, 38, 43, 48, 53, 58, 63, 68,
+ 72, 73, 75, 78, 80, 81, 83, 86,
+ 90, 92, 93, 95, 98, 99, 100, 101,
+ 102, 103, 105, 108, 110, 111, 113, 116,
+ 120, 122, 123, 125, 128, 129, 130, 131,
+ 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132,
+ 132,
+ },
+ { /* Fourth byte table 49. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 6, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18,
+ },
+ { /* Fourth byte table 50. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 6, 12,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18,
+ },
+ { /* Fourth byte table 51. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 6, 6, 6,
+ 6, 6, 12, 12, 12, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 24, 24, 30,
+ 30, 30, 30, 30, 30, 36, 45, 45,
+ 51, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60,
+ },
+ { /* Fourth byte table 52. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 6, 6, 6, 12, 12, 12,
+ 18, 18, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 28, 28, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 40, 44,
+ 48, 54, 60, 60, 60, 66, 72, 72,
+ 72, 78, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84,
+ },
+ { /* Fourth byte table 53. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 6, 12, 12, 12, 18, 24, 24,
+ 24, 30, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 42, 48, 54,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60,
+ },
+ { /* Fourth byte table 54. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 6, 12, 18, 24, 24, 24, 24,
+ 24, 24, 24, 30, 36, 42, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48,
+ },
+ { /* Fourth byte table 55. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 4, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8,
+ },
+ { /* Fourth byte table 56. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 11, 13, 15, 17, 19, 21,
+ 23, 25, 27, 29, 31, 34, 37, 40,
+ 43, 46, 49, 52, 55, 58, 62, 66,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70,
+ },
+ { /* Fourth byte table 57. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 48, 50, 53, 56, 59, 62, 65, 68,
+ 71, 74, 77, 80, 83, 86, 89, 92,
+ 95, 98, 101, 104, 107, 110, 113, 116,
+ 119, 122, 125, 128, 131, 134, 137, 140,
+ 143, 146, 149, 152, 155, 158, 161, 162,
+ 163, 164, 165, 166, 167, 168, 169, 170,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171,
+ },
+ { /* Fourth byte table 58. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 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, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43,
+ },
+ { /* Fourth byte table 59. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12,
+ },
+ { /* Fourth byte table 60. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3, 5, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8,
+ },
+ { /* Fourth byte table 61. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6,
+ },
+ { /* Fourth byte table 62. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3,
+ },
+ { /* Fourth byte table 63. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3,
+ },
+ { /* Fourth byte table 64. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3,
+ },
+ { /* Fourth byte table 65. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 60, 63, 66, 69,
+ 72, 75, 78, 81, 84, 87, 90, 93,
+ 96, 99, 102, 105, 108, 111, 114, 117,
+ 120, 123, 126, 129, 132, 135, 138, 141,
+ 144, 147, 150, 153, 156, 159, 162, 165,
+ 168, 171, 174, 177, 180, 183, 186, 189,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192,
+ },
+ { /* Fourth byte table 66. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 60, 63, 66, 69,
+ 72, 75, 78, 81, 84, 87, 90, 93,
+ 96, 99, 102, 105, 108, 111, 114, 117,
+ 120, 123, 126, 129, 132, 135, 138, 141,
+ 144, 147, 150, 153, 156, 159, 162, 165,
+ 168, 171, 174, 177, 180, 183, 186, 189,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192,
+ },
+ { /* Fourth byte table 67. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 60, 63, 66, 69,
+ 72, 75, 78, 81, 84, 87, 90, 93,
+ 96, 99, 102, 105, 108, 111, 114, 117,
+ 120, 123, 126, 129, 132, 135, 138, 141,
+ 144, 147, 150, 153, 156, 159, 162, 165,
+ 168, 171, 174, 177, 180, 183, 186, 189,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192,
+ },
+ { /* Fourth byte table 68. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 60, 63, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66,
+ },
+ { /* Fourth byte table 69. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 4,
+ 4, 7, 10, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13,
+ },
+ { /* Fourth byte table 70. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7, 7, 14,
+ 14, 21, 21, 28, 28, 35, 35, 42,
+ 42, 49, 49, 56, 56, 63, 63, 70,
+ 70, 77, 77, 84, 84, 84, 91, 91,
+ 98, 98, 105, 105, 105, 105, 105, 105,
+ 105, 112, 119, 119, 126, 133, 133, 140,
+ 147, 147, 154, 161, 161, 168, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175,
+ },
+ { /* Fourth byte table 71. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7, 7, 7,
+ 7, 7, 7, 7, 11, 15, 15, 22,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 35, 35, 42,
+ 42, 49, 49, 56, 56, 63, 63, 70,
+ 70, 77, 77, 84, 84, 91, 91, 98,
+ 98, 98, 98, 98, 98, 98, 98, 98,
+ 98, 98, 98, 98, 98, 98, 98, 98,
+ 98, 98, 98, 98, 98, 98, 98, 98,
+ 98, 98, 98, 98, 98, 98, 98, 98,
+ 98, 98, 98, 98, 98, 98, 98, 98,
+ 98, 98, 98, 98, 98, 98, 98, 98,
+ 98, 98, 98, 98, 98, 98, 98, 98,
+ 98, 98, 98, 98, 98, 98, 98, 98,
+ 98,
+ },
+ { /* Fourth byte table 72. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 7, 7, 14, 14, 14, 21, 21,
+ 28, 28, 35, 35, 35, 35, 35, 35,
+ 35, 42, 49, 49, 56, 63, 63, 70,
+ 77, 77, 84, 91, 91, 98, 105, 105,
+ 105, 105, 105, 105, 105, 105, 105, 105,
+ 105, 105, 105, 105, 105, 105, 105, 105,
+ 105, 105, 105, 105, 105, 112, 112, 112,
+ 119, 126, 133, 140, 140, 140, 140, 147,
+ 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153,
+ 153,
+ },
+ { /* Fourth byte table 73. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 6, 9, 12, 15, 18,
+ 21, 24, 27, 30, 33, 36, 39, 42,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45,
+ },
+ { /* Fourth byte table 74. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 60, 63, 66, 69,
+ 72, 75, 78, 81, 84, 87, 90, 93,
+ 96, 99, 102, 105, 108, 111, 114, 117,
+ 120, 123, 126, 129, 132, 135, 138, 141,
+ 144, 147, 150, 153, 156, 159, 162, 165,
+ 168, 171, 174, 177, 180, 183, 186, 189,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192,
+ 192,
+ },
+ { /* Fourth byte table 75. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 45, 45, 45, 48, 51, 54, 57, 60,
+ 63, 66, 69, 72, 75, 78, 81, 84,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87,
+ },
+ { /* Fourth byte table 76. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5, 10, 15, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 23, 25, 27, 29, 31, 33, 35,
+ 37, 39, 41, 43, 45, 47, 49, 51,
+ 53, 56, 59, 62, 65, 68, 71, 74,
+ 77, 80, 83, 86, 89, 92, 95, 101,
+ 107, 113, 119, 125, 131, 137, 143, 149,
+ 155, 161, 167, 173, 179, 194, 206, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212,
+ 212,
+ },
+ { /* Fourth byte table 77. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 60, 63, 66, 69,
+ 72, 75, 78, 81, 84, 87, 90, 93,
+ 96, 99, 102, 105, 108, 111, 114, 117,
+ 120, 123, 126, 129, 132, 135, 138, 141,
+ 144, 147, 149, 151, 153, 155, 157, 159,
+ 161, 163, 165, 167, 169, 171, 173, 175,
+ 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177,
+ 177,
+ },
+ { /* Fourth byte table 78. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 41, 46, 51, 53, 56, 58,
+ 61, 64, 67, 70, 73, 76, 79, 82,
+ 85, 88, 91, 94, 97, 100, 103, 106,
+ 109, 112, 115, 118, 121, 124, 127, 130,
+ 133, 136, 139, 142, 145, 148, 151, 154,
+ 157, 160, 163, 166, 169, 172, 175, 178,
+ 181, 184, 187, 190, 193, 196, 199, 202,
+ 202, 202, 202, 202, 202, 202, 202, 202,
+ 202, 202, 202, 202, 202, 202, 202, 202,
+ 202, 202, 202, 202, 202, 202, 202, 202,
+ 202, 202, 202, 202, 202, 202, 202, 202,
+ 202, 202, 202, 202, 202, 202, 202, 202,
+ 202, 202, 202, 202, 202, 202, 202, 202,
+ 202, 202, 202, 202, 202, 202, 202, 202,
+ 202, 202, 202, 202, 202, 202, 202, 202,
+ 202,
+ },
+ { /* Fourth byte table 79. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 7, 9, 11, 13, 15,
+ 17, 20, 24, 26, 28, 31, 34, 36,
+ 38, 40, 43, 46, 49, 52, 55, 57,
+ 59, 61, 63, 65, 68, 70, 72, 74,
+ 77, 80, 82, 85, 88, 91, 93, 96,
+ 101, 107, 109, 112, 115, 118, 121, 128,
+ 136, 138, 140, 143, 145, 147, 149, 152,
+ 154, 156, 158, 160, 162, 165, 167, 169,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 171,
+ },
+ { /* Fourth byte table 80. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 10, 12, 14, 16, 22,
+ 25, 27, 29, 31, 33, 35, 37, 39,
+ 41, 43, 45, 48, 50, 52, 55, 58,
+ 60, 64, 67, 69, 71, 73, 75, 80,
+ 85, 89, 93, 97, 101, 105, 109, 113,
+ 117, 121, 126, 131, 136, 141, 146, 151,
+ 156, 161, 166, 171, 176, 181, 186, 191,
+ 196, 201, 206, 211, 216, 221, 226, 231,
+ 234, 234, 234, 234, 234, 234, 234, 234,
+ 234, 234, 234, 234, 234, 234, 234, 234,
+ 234, 234, 234, 234, 234, 234, 234, 234,
+ 234, 234, 234, 234, 234, 234, 234, 234,
+ 234, 234, 234, 234, 234, 234, 234, 234,
+ 234, 234, 234, 234, 234, 234, 234, 234,
+ 234, 234, 234, 234, 234, 234, 234, 234,
+ 234, 234, 234, 234, 234, 234, 234, 234,
+ 234,
+ },
+ { /* Fourth byte table 81. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 56,
+ 56, 60, 60, 64, 64, 64, 68, 72,
+ 76, 80, 84, 88, 92, 96, 100, 104,
+ 104, 108, 108, 112, 112, 112, 116, 120,
+ 120, 120, 120, 124, 128, 132, 136, 136,
+ 136, 140, 144, 148, 152, 156, 160, 164,
+ 168, 172, 176, 180, 184, 188, 192, 196,
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ 200,
+ },
+ { /* Fourth byte table 82. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 116, 120, 124,
+ 128, 132, 136, 140, 144, 148, 152, 156,
+ 160, 164, 168, 172, 172, 172, 172, 172,
+ 172, 176, 180, 184, 188, 192, 196, 200,
+ 204, 208, 212, 216, 220, 224, 228, 232,
+ 236, 236, 236, 236, 236, 236, 236, 236,
+ 236, 236, 236, 236, 236, 236, 236, 236,
+ 236, 236, 236, 236, 236, 236, 236, 236,
+ 236, 236, 236, 236, 236, 236, 236, 236,
+ 236, 236, 236, 236, 236, 236, 236, 236,
+ 236, 236, 236, 236, 236, 236, 236, 236,
+ 236, 236, 236, 236, 236, 236, 236, 236,
+ 236, 236, 236, 236, 236, 236, 236, 236,
+ 236,
+ },
+ { /* Fourth byte table 83. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 65, 70, 75, 79, 83, 87, 92, 97,
+ 102, 106, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 110, 110, 110, 110, 110,
+ 110,
+ },
+ { /* Fourth byte table 84. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 9, 12, 14, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 20, 24, 28, 32,
+ 36, 36, 36, 36, 36, 36, 41, 41,
+ 46, 48, 50, 52, 54, 56, 58, 60,
+ 62, 64, 65, 70, 75, 82, 89, 94,
+ 99, 104, 109, 114, 119, 124, 129, 134,
+ 134, 139, 144, 149, 154, 159, 159, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164,
+ },
+ { /* Fourth byte table 85. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5, 10, 10, 15, 20, 20, 25,
+ 30, 35, 40, 45, 50, 55, 60, 65,
+ 69, 71, 73, 75, 77, 79, 81, 83,
+ 85, 87, 89, 91, 93, 95, 97, 99,
+ 101, 103, 105, 107, 109, 111, 113, 115,
+ 117, 119, 121, 123, 125, 127, 129, 131,
+ 133, 135, 137, 139, 141, 143, 145, 147,
+ 149, 151, 153, 155, 157, 159, 161, 163,
+ 165, 165, 165, 165, 165, 165, 165, 165,
+ 165, 165, 165, 165, 165, 165, 165, 165,
+ 165, 165, 165, 165, 165, 165, 165, 165,
+ 165, 165, 165, 165, 165, 165, 165, 165,
+ 165, 165, 165, 165, 165, 165, 165, 165,
+ 165, 165, 165, 165, 165, 165, 165, 165,
+ 165, 165, 165, 165, 165, 165, 165, 165,
+ 165, 165, 165, 165, 165, 165, 165, 165,
+ 165,
+ },
+ { /* Fourth byte table 86. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 48, 50, 52, 54, 56, 58, 60, 62,
+ 64, 66, 68, 70, 72, 76, 80, 82,
+ 84, 86, 88, 90, 92, 94, 96, 98,
+ 100, 104, 108, 108, 108, 108, 108, 108,
+ 108, 108, 108, 108, 108, 108, 108, 108,
+ 108, 108, 108, 108, 108, 108, 108, 108,
+ 108, 108, 108, 108, 108, 108, 108, 108,
+ 108, 108, 108, 108, 108, 108, 108, 108,
+ 108, 108, 108, 108, 108, 108, 108, 108,
+ 108, 108, 108, 108, 108, 108, 108, 108,
+ 108, 108, 108, 108, 108, 108, 108, 108,
+ 108, 108, 108, 108, 108, 108, 108, 108,
+ 108, 108, 108, 108, 108, 108, 108, 108,
+ 108,
+ },
+ { /* Fourth byte table 87. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 4, 6, 8,
+ 10, 12, 14, 16, 18, 20, 24, 26,
+ 28, 30, 32, 34, 36, 38, 40, 42,
+ 44, 46, 48, 54, 60, 66, 72, 78,
+ 84, 90, 96, 102, 108, 114, 120, 126,
+ 132, 138, 144, 150, 156, 158, 160, 162,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164, 164, 164, 164, 164, 164, 164, 164,
+ 164,
+ },
+ { /* Fourth byte table 88. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 116, 120, 124,
+ 128, 132, 136, 140, 144, 148, 152, 156,
+ 160, 164, 168, 172, 176, 180, 184, 188,
+ 192, 196, 200, 204, 208, 212, 216, 220,
+ 224, 228, 232, 236, 240, 244, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248,
+ 248, 248, 248, 248, 248, 248, 248, 248,
+ 248,
+ },
+ { /* Fourth byte table 89. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 6, 12, 18, 24, 30, 36, 42,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 54, 60, 68, 76, 84, 92, 100,
+ 108, 116, 122, 155, 170, 178, 178, 178,
+ 178, 178, 178, 178, 178, 178, 178, 178,
+ 178, 178, 178, 178, 178, 178, 178, 178,
+ 178, 178, 178, 178, 178, 178, 178, 178,
+ 178, 178, 178, 178, 178, 178, 178, 178,
+ 178, 178, 178, 178, 178, 178, 178, 178,
+ 178, 178, 178, 178, 178, 178, 178, 178,
+ 178, 178, 178, 178, 178, 178, 178, 178,
+ 178, 178, 178, 178, 178, 178, 178, 178,
+ 178,
+ },
+ { /* Fourth byte table 90. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 4, 7, 8, 9, 10, 11,
+ 14, 17, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 22, 25, 28, 29, 30, 31, 32,
+ 33, 34, 37, 40, 43, 46, 49, 52,
+ 55, 55, 55, 55, 55, 55, 55, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55,
+ 55,
+ },
+ { /* Fourth byte table 91. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 15, 15,
+ 16, 17, 20, 23, 26, 29, 30, 31,
+ 32, 33, 36, 37, 37, 38, 39, 40,
+ 41, 44, 45, 46, 47, 48, 51, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62,
+ 62, 63, 64, 65, 66, 66, 66, 66,
+ 66, 69, 73, 76, 76, 79, 79, 82,
+ 86, 89, 93, 96, 100, 103, 107, 110,
+ 114, 114, 114, 114, 114, 114, 114, 114,
+ 114, 114, 114, 114, 114, 114, 114, 114,
+ 114, 114, 114, 114, 114, 114, 114, 114,
+ 114, 114, 114, 114, 114, 114, 114, 114,
+ 114, 114, 114, 114, 114, 114, 114, 114,
+ 114, 114, 114, 114, 114, 114, 114, 114,
+ 114, 114, 114, 114, 114, 114, 114, 114,
+ 114, 114, 114, 114, 114, 114, 114, 114,
+ 114,
+ },
+ { /* Fourth byte table 92. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 6, 10, 14, 18, 22, 26,
+ 30, 34, 38, 42, 46, 50, 52, 54,
+ 56, 58, 60, 62, 64, 66, 68, 70,
+ 72, 74, 76, 78, 80, 82, 84, 86,
+ 88, 90, 92, 94, 96, 98, 100, 102,
+ 104, 106, 108, 110, 112, 114, 116, 118,
+ 120, 122, 124, 126, 128, 130, 132, 134,
+ 136, 138, 140, 142, 144, 146, 148, 150,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152,
+ },
+ { /* Fourth byte table 93. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 48, 50, 52, 54, 56, 58, 60, 62,
+ 64, 66, 68, 70, 72, 74, 76, 78,
+ 80, 82, 84, 86, 88, 90, 92, 94,
+ 96, 98, 100, 102, 104, 106, 112, 118,
+ 124, 130, 136, 142, 146, 150, 150, 150,
+ 150, 150, 150, 150, 150, 150, 150, 150,
+ 150, 150, 150, 150, 150, 150, 150, 150,
+ 150, 150, 150, 150, 150, 150, 150, 150,
+ 150, 150, 150, 150, 150, 150, 150, 150,
+ 150, 150, 150, 150, 150, 150, 150, 150,
+ 150, 150, 150, 150, 150, 150, 150, 150,
+ 150, 150, 150, 150, 150, 150, 150, 150,
+ 150, 150, 150, 150, 150, 150, 150, 150,
+ 150,
+ },
+ { /* Fourth byte table 94. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 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, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63,
+ },
+ { /* Fourth byte table 95. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 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,
+ 34, 37, 40, 43, 46, 49, 52, 55,
+ 58, 61, 64, 67, 70, 73, 76, 79,
+ 82, 85, 88, 91, 94, 97, 100, 103,
+ 106, 109, 112, 115, 118, 121, 124, 127,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130,
+ },
+ { /* Fourth byte table 96. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 60, 63, 66, 69,
+ 72, 75, 78, 81, 84, 87, 90, 93,
+ 96, 99, 102, 105, 108, 111, 114, 117,
+ 120, 123, 126, 129, 132, 135, 138, 141,
+ 144, 147, 150, 153, 156, 159, 162, 165,
+ 168, 171, 174, 177, 180, 183, 186, 189,
+ 189, 189, 189, 189, 189, 189, 189, 189,
+ 189, 189, 189, 189, 189, 189, 189, 189,
+ 189, 189, 189, 189, 189, 189, 189, 189,
+ 189, 189, 189, 189, 189, 189, 189, 189,
+ 189, 189, 189, 189, 189, 189, 189, 189,
+ 189, 189, 189, 189, 189, 189, 189, 189,
+ 189, 189, 189, 189, 189, 189, 189, 189,
+ 189, 189, 189, 189, 189, 189, 189, 189,
+ 189,
+ },
+ { /* Fourth byte table 97. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 3, 6, 9, 12, 15,
+ 18, 18, 18, 21, 24, 27, 30, 33,
+ 36, 36, 36, 39, 42, 45, 48, 51,
+ 54, 54, 54, 57, 60, 63, 63, 63,
+ 63, 65, 67, 69, 72, 74, 76, 79,
+ 79, 82, 85, 88, 91, 94, 97, 100,
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100,
+ },
+ { /* Fourth byte table 98. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 9,
+ 18, 31, 44, 57, 70, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83,
+ },
+ { /* Fourth byte table 99. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 9, 18, 31, 44,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57,
+ },
+ { /* Fourth byte table 100. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 13,
+ },
+ { /* Fourth byte table 101. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 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,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64,
+ },
+ { /* Fourth byte table 102. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 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, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63,
+ },
+ { /* Fourth byte table 103. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 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, 29, 30,
+ 31, 31, 31, 32, 32, 32, 33, 34,
+ 34, 34, 35, 36, 37, 38, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 50, 51, 51, 52, 53,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54,
+ },
+ { /* Fourth byte table 104. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 3, 4, 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, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63,
+ },
+ { /* Fourth byte table 105. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 3, 4, 5, 6, 6,
+ 7, 8, 9, 10, 10, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 18, 19,
+ 20, 21, 22, 23, 24, 25, 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, 53, 54, 55, 56, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57,
+ },
+ { /* Fourth byte table 106. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 3, 4, 5, 5, 6,
+ 6, 6, 6, 7, 8, 9, 10, 11,
+ 12, 13, 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, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59,
+ 59,
+ },
+ { /* Fourth byte table 107. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 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,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64,
+ },
+ { /* Fourth byte table 108. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 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,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64,
+ },
+ { /* Fourth byte table 109. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 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,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64,
+ },
+ { /* Fourth byte table 110. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 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,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64,
+ },
+ { /* Fourth byte table 111. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 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, 38, 40, 40,
+ 40, 42, 44, 46, 48, 50, 52, 54,
+ 56, 58, 60, 62, 64, 66, 68, 70,
+ 72, 74, 76, 78, 80, 82, 84, 86,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88,
+ 88,
+ },
+ { /* Fourth byte table 112. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 5, 7, 9, 11, 13, 15,
+ 17, 19, 21, 23, 25, 27, 29, 31,
+ 33, 35, 37, 39, 41, 43, 45, 47,
+ 49, 51, 53, 55, 58, 60, 62, 64,
+ 66, 68, 70, 72, 74, 76, 78, 80,
+ 82, 84, 86, 88, 90, 92, 94, 96,
+ 98, 100, 102, 104, 106, 108, 110, 112,
+ 114, 116, 118, 120, 123, 125, 127, 129,
+ 131, 131, 131, 131, 131, 131, 131, 131,
+ 131, 131, 131, 131, 131, 131, 131, 131,
+ 131, 131, 131, 131, 131, 131, 131, 131,
+ 131, 131, 131, 131, 131, 131, 131, 131,
+ 131, 131, 131, 131, 131, 131, 131, 131,
+ 131, 131, 131, 131, 131, 131, 131, 131,
+ 131, 131, 131, 131, 131, 131, 131, 131,
+ 131, 131, 131, 131, 131, 131, 131, 131,
+ 131,
+ },
+ { /* Fourth byte table 113. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 45, 47,
+ 49, 51, 53, 55, 57, 59, 61, 63,
+ 65, 67, 69, 71, 73, 75, 77, 79,
+ 81, 83, 85, 87, 89, 91, 93, 95,
+ 97, 99, 101, 103, 105, 107, 110, 112,
+ 114, 116, 118, 120, 122, 124, 126, 128,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130,
+ },
+ { /* Fourth byte table 114. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 33, 35, 37, 39, 41, 43, 45, 47,
+ 49, 51, 53, 55, 57, 59, 61, 63,
+ 65, 67, 69, 71, 73, 75, 77, 79,
+ 81, 83, 85, 87, 89, 91, 93, 95,
+ 98, 100, 102, 104, 106, 108, 110, 112,
+ 114, 116, 118, 120, 122, 124, 126, 128,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130,
+ },
+ { /* Fourth byte table 115. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 21, 23, 25, 27, 29, 31,
+ 33, 35, 37, 39, 41, 43, 45, 47,
+ 49, 51, 53, 55, 57, 59, 61, 63,
+ 65, 67, 69, 71, 73, 75, 77, 79,
+ 81, 83, 86, 88, 90, 92, 94, 96,
+ 98, 100, 102, 104, 106, 108, 110, 112,
+ 114, 116, 118, 120, 122, 124, 126, 128,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 130, 130, 130, 130, 130, 130,
+ 130,
+ },
+ { /* Fourth byte table 116. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 9, 11, 13, 15,
+ 17, 19, 21, 23, 25, 25, 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, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 75, 75, 75, 75, 75, 75, 75,
+ 75, 75, 75, 75, 75, 75, 75, 75,
+ 75, 75, 75, 75, 75, 75, 75, 75,
+ 75, 75, 75, 75, 75, 75, 75, 75,
+ 75, 75, 75, 75, 75, 75, 75, 75,
+ 75, 75, 75, 75, 75, 75, 75, 75,
+ 75, 75, 75, 75, 75, 75, 75, 75,
+ 75, 75, 75, 75, 75, 75, 75, 75,
+ 75,
+ },
+ { /* Fourth byte table 117. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 9, 13, 17, 21, 25, 29,
+ 33, 37, 42, 46, 50, 54, 58, 62,
+ 66, 71, 75, 80, 85, 90, 94, 98,
+ 102, 106, 110, 114, 118, 122, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127,
+ 127,
+ },
+ },
+};
+
+static const uint16_t u8_decomp_b4_16bit_tbl[2][30][257] = {
+ {
+ { /* Fourth byte 16-bit table 0. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 38, 44, 48, 52, 56, 60, 64,
+ 68, 72, 76, 80, 84, 90, 96, 102,
+ 108, 112, 116, 120, 124, 130, 136, 140,
+ 144, 148, 152, 156, 160, 164, 168, 172,
+ 176, 180, 184, 188, 192, 196, 200, 206,
+ 212, 216, 220, 224, 228, 232, 236, 240,
+ 244, 250, 256, 260, 264, 268, 272, 276,
+ 280, 280, 280, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 280, 280, 280,
+ 280,
+ },
+ { /* Fourth byte 16-bit table 1. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 54, 60, 66,
+ 72, 78, 84, 90, 96, 100, 104, 108,
+ 112, 116, 120, 124, 128, 134, 140, 144,
+ 148, 152, 156, 160, 164, 170, 176, 182,
+ 188, 194, 200, 204, 208, 212, 216, 220,
+ 224, 228, 232, 236, 240, 244, 248, 252,
+ 256, 262, 268, 274, 280, 284, 288, 292,
+ 296, 296, 296, 296, 296, 296, 296, 296,
+ 296, 296, 296, 296, 296, 296, 296, 296,
+ 296, 296, 296, 296, 296, 296, 296, 296,
+ 296, 296, 296, 296, 296, 296, 296, 296,
+ 296, 296, 296, 296, 296, 296, 296, 296,
+ 296, 296, 296, 296, 296, 296, 296, 296,
+ 296, 296, 296, 296, 296, 296, 296, 296,
+ 296, 296, 296, 296, 296, 296, 296, 296,
+ 296,
+ },
+ { /* Fourth byte 16-bit table 2. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 107, 116, 116, 116, 116,
+ 116, 120, 124, 128, 132, 138, 144, 150,
+ 156, 162, 168, 174, 180, 186, 192, 198,
+ 204, 210, 216, 222, 228, 234, 240, 246,
+ 252, 256, 260, 264, 268, 272, 276, 282,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288,
+ },
+ { /* Fourth byte 16-bit table 3. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 6, 12, 18, 24, 30, 36, 42,
+ 48, 52, 56, 60, 64, 68, 72, 76,
+ 80, 86, 92, 98, 104, 110, 116, 122,
+ 128, 134, 140, 146, 152, 158, 164, 170,
+ 176, 182, 188, 194, 200, 204, 208, 212,
+ 216, 222, 228, 234, 240, 246, 252, 258,
+ 264, 270, 276, 280, 284, 288, 292, 296,
+ 300, 304, 308, 308, 308, 308, 308, 308,
+ 308, 308, 308, 308, 308, 308, 308, 308,
+ 308, 308, 308, 308, 308, 308, 308, 308,
+ 308, 308, 308, 308, 308, 308, 308, 308,
+ 308, 308, 308, 308, 308, 308, 308, 308,
+ 308, 308, 308, 308, 308, 308, 308, 308,
+ 308, 308, 308, 308, 308, 308, 308, 308,
+ 308, 308, 308, 308, 308, 308, 308, 308,
+ 308, 308, 308, 308, 308, 308, 308, 308,
+ 308,
+ },
+ { /* Fourth byte 16-bit table 4. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5, 10, 17, 24, 31, 38, 45,
+ 52, 57, 62, 69, 76, 83, 90, 97,
+ 104, 109, 114, 121, 128, 135, 142, 142,
+ 142, 147, 152, 159, 166, 173, 180, 180,
+ 180, 185, 190, 197, 204, 211, 218, 225,
+ 232, 237, 242, 249, 256, 263, 270, 277,
+ 284, 289, 294, 301, 308, 315, 322, 329,
+ 336, 341, 346, 353, 360, 367, 374, 381,
+ 388, 388, 388, 388, 388, 388, 388, 388,
+ 388, 388, 388, 388, 388, 388, 388, 388,
+ 388, 388, 388, 388, 388, 388, 388, 388,
+ 388, 388, 388, 388, 388, 388, 388, 388,
+ 388, 388, 388, 388, 388, 388, 388, 388,
+ 388, 388, 388, 388, 388, 388, 388, 388,
+ 388, 388, 388, 388, 388, 388, 388, 388,
+ 388, 388, 388, 388, 388, 388, 388, 388,
+ 388,
+ },
+ { /* Fourth byte 16-bit table 5. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5, 10, 17, 24, 31, 38, 38,
+ 38, 43, 48, 55, 62, 69, 76, 76,
+ 76, 81, 86, 93, 100, 107, 114, 121,
+ 128, 128, 133, 133, 140, 140, 147, 147,
+ 154, 159, 164, 171, 178, 185, 192, 199,
+ 206, 211, 216, 223, 230, 237, 244, 251,
+ 258, 263, 268, 273, 278, 283, 288, 293,
+ 298, 303, 308, 313, 318, 323, 328, 328,
+ 328, 328, 328, 328, 328, 328, 328, 328,
+ 328, 328, 328, 328, 328, 328, 328, 328,
+ 328, 328, 328, 328, 328, 328, 328, 328,
+ 328, 328, 328, 328, 328, 328, 328, 328,
+ 328, 328, 328, 328, 328, 328, 328, 328,
+ 328, 328, 328, 328, 328, 328, 328, 328,
+ 328, 328, 328, 328, 328, 328, 328, 328,
+ 328, 328, 328, 328, 328, 328, 328, 328,
+ 328,
+ },
+ { /* Fourth byte 16-bit table 6. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 7, 14, 23, 32, 41, 50, 59,
+ 68, 75, 82, 91, 100, 109, 118, 127,
+ 136, 143, 150, 159, 168, 177, 186, 195,
+ 204, 211, 218, 227, 236, 245, 254, 263,
+ 272, 279, 286, 295, 304, 313, 322, 331,
+ 340, 347, 354, 363, 372, 381, 390, 399,
+ 408, 413, 418, 425, 430, 437, 437, 442,
+ 449, 454, 459, 464, 469, 474, 477, 480,
+ 483, 483, 483, 483, 483, 483, 483, 483,
+ 483, 483, 483, 483, 483, 483, 483, 483,
+ 483, 483, 483, 483, 483, 483, 483, 483,
+ 483, 483, 483, 483, 483, 483, 483, 483,
+ 483, 483, 483, 483, 483, 483, 483, 483,
+ 483, 483, 483, 483, 483, 483, 483, 483,
+ 483, 483, 483, 483, 483, 483, 483, 483,
+ 483, 483, 483, 483, 483, 483, 483, 483,
+ 483,
+ },
+ { /* Fourth byte 16-bit table 7. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 14, 21, 26, 33, 33, 38,
+ 45, 50, 55, 60, 65, 70, 82, 94,
+ 106, 111, 116, 123, 130, 130, 130, 135,
+ 142, 147, 152, 157, 162, 162, 174, 186,
+ 198, 203, 208, 215, 222, 227, 232, 237,
+ 244, 249, 254, 259, 264, 269, 280, 291,
+ 293, 293, 293, 300, 305, 312, 312, 317,
+ 324, 329, 334, 339, 344, 349, 356, 359,
+ 359, 359, 359, 359, 359, 359, 359, 359,
+ 359, 359, 359, 359, 359, 359, 359, 359,
+ 359, 359, 359, 359, 359, 359, 359, 359,
+ 359, 359, 359, 359, 359, 359, 359, 359,
+ 359, 359, 359, 359, 359, 359, 359, 359,
+ 359, 359, 359, 359, 359, 359, 359, 359,
+ 359, 359, 359, 359, 359, 359, 359, 359,
+ 359, 359, 359, 359, 359, 359, 359, 359,
+ 359,
+ },
+ { /* Fourth byte 16-bit table 8. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5, 10, 15, 20, 25, 30, 35,
+ 40, 45, 50, 55, 60, 65, 70, 78,
+ 86, 94, 102, 110, 118, 126, 134, 142,
+ 150, 158, 166, 174, 182, 190, 190, 190,
+ 190, 195, 200, 205, 210, 215, 220, 225,
+ 230, 235, 240, 245, 250, 255, 260, 265,
+ 270, 275, 280, 285, 290, 295, 300, 305,
+ 310, 315, 320, 325, 330, 335, 340, 345,
+ 350, 350, 350, 350, 350, 350, 350, 350,
+ 350, 350, 350, 350, 350, 350, 350, 350,
+ 350, 350, 350, 350, 350, 350, 350, 350,
+ 350, 350, 350, 350, 350, 350, 350, 350,
+ 350, 350, 350, 350, 350, 350, 350, 350,
+ 350, 350, 350, 350, 350, 350, 350, 350,
+ 350, 350, 350, 350, 350, 350, 350, 350,
+ 350, 350, 350, 350, 350, 350, 350, 350,
+ 350,
+ },
+ { /* Fourth byte 16-bit table 9. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 15, 27, 42, 51, 66, 75, 84,
+ 102, 114, 123, 132, 141, 153, 165, 177,
+ 189, 201, 213, 225, 243, 249, 267, 285,
+ 300, 312, 330, 348, 360, 369, 378, 390,
+ 402, 417, 432, 441, 450, 462, 471, 480,
+ 486, 492, 501, 510, 528, 540, 555, 573,
+ 585, 594, 603, 621, 633, 651, 660, 675,
+ 684, 696, 705, 717, 732, 744, 759, 771,
+ 777, 777, 777, 777, 777, 777, 777, 777,
+ 777, 777, 777, 777, 777, 777, 777, 777,
+ 777, 777, 777, 777, 777, 777, 777, 777,
+ 777, 777, 777, 777, 777, 777, 777, 777,
+ 777, 777, 777, 777, 777, 777, 777, 777,
+ 777, 777, 777, 777, 777, 777, 777, 777,
+ 777, 777, 777, 777, 777, 777, 777, 777,
+ 777, 777, 777, 777, 777, 777, 777, 777,
+ 777,
+ },
+ { /* Fourth byte 16-bit table 10. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 15, 24, 33, 45, 54, 63, 72,
+ 87, 99, 105, 123, 132, 147, 159, 171,
+ 180, 189, 201, 207, 219, 234, 240, 258,
+ 267, 271, 275, 279, 283, 287, 291, 295,
+ 299, 303, 307, 312, 317, 322, 327, 332,
+ 337, 342, 347, 352, 357, 362, 367, 372,
+ 377, 382, 385, 387, 389, 392, 394, 396,
+ 396, 396, 396, 396, 402, 408, 414, 420,
+ 432, 432, 432, 432, 432, 432, 432, 432,
+ 432, 432, 432, 432, 432, 432, 432, 432,
+ 432, 432, 432, 432, 432, 432, 432, 432,
+ 432, 432, 432, 432, 432, 432, 432, 432,
+ 432, 432, 432, 432, 432, 432, 432, 432,
+ 432, 432, 432, 432, 432, 432, 432, 432,
+ 432, 432, 432, 432, 432, 432, 432, 432,
+ 432, 432, 432, 432, 432, 432, 432, 432,
+ 432,
+ },
+ { /* Fourth byte 16-bit table 11. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 116, 120, 124,
+ 128, 132, 136, 140, 144, 148, 152, 156,
+ 160, 164, 168, 172, 176, 180, 184, 188,
+ 192, 196, 200, 204, 208, 212, 216, 220,
+ 224, 228, 232, 236, 240, 244, 248, 252,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256,
+ },
+ { /* Fourth byte 16-bit table 12. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 116, 120, 124,
+ 128, 132, 136, 140, 144, 148, 152, 156,
+ 160, 164, 168, 172, 176, 180, 184, 188,
+ 192, 196, 200, 204, 208, 212, 216, 220,
+ 224, 228, 232, 236, 240, 244, 248, 252,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256,
+ },
+ { /* Fourth byte 16-bit table 13. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 116, 120, 124,
+ 128, 132, 136, 140, 144, 148, 152, 156,
+ 160, 164, 168, 172, 176, 180, 184, 188,
+ 192, 196, 200, 204, 208, 212, 216, 220,
+ 224, 228, 232, 236, 240, 244, 248, 252,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256,
+ },
+ { /* Fourth byte 16-bit table 14. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 116, 120, 124,
+ 128, 132, 136, 140, 144, 148, 152, 156,
+ 160, 164, 168, 172, 176, 180, 184, 188,
+ 192, 196, 200, 204, 208, 212, 216, 220,
+ 224, 228, 232, 236, 240, 244, 248, 252,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256,
+ },
+ { /* Fourth byte 16-bit table 15. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 6, 12, 18, 24, 30, 34, 38,
+ 42, 46, 50, 54, 58, 62, 66, 70,
+ 74, 78, 82, 86, 90, 94, 98, 102,
+ 106, 110, 114, 118, 122, 126, 130, 134,
+ 138, 142, 146, 150, 154, 158, 162, 166,
+ 170, 174, 178, 182, 186, 190, 194, 198,
+ 202, 206, 210, 214, 218, 222, 226, 230,
+ 234, 238, 242, 246, 250, 254, 258, 262,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266,
+ },
+ { /* Fourth byte 16-bit table 16. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 116, 120, 125,
+ 130, 135, 140, 145, 150, 156, 162, 168,
+ 174, 180, 186, 190, 194, 198, 202, 206,
+ 210, 214, 218, 222, 226, 230, 234, 238,
+ 242, 246, 250, 254, 258, 262, 266, 270,
+ 274, 274, 274, 274, 274, 274, 274, 274,
+ 274, 274, 274, 274, 274, 274, 274, 274,
+ 274, 274, 274, 274, 274, 274, 274, 274,
+ 274, 274, 274, 274, 274, 274, 274, 274,
+ 274, 274, 274, 274, 274, 274, 274, 274,
+ 274, 274, 274, 274, 274, 274, 274, 274,
+ 274, 274, 274, 274, 274, 274, 274, 274,
+ 274, 274, 274, 274, 274, 274, 274, 274,
+ 274,
+ },
+ { /* Fourth byte 16-bit table 17. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 98, 104, 110, 116, 122, 126, 130, 134,
+ 138, 142, 146, 150, 154, 158, 162, 166,
+ 170, 174, 178, 182, 186, 190, 194, 198,
+ 202, 206, 210, 214, 218, 222, 226, 230,
+ 234, 238, 242, 246, 250, 254, 258, 262,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266,
+ },
+ { /* Fourth byte 16-bit table 18. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 116, 120, 124,
+ 130, 136, 140, 144, 148, 152, 156, 160,
+ 164, 168, 172, 176, 180, 184, 188, 192,
+ 196, 200, 204, 210, 216, 222, 226, 230,
+ 234, 238, 242, 246, 250, 254, 258, 262,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266,
+ },
+ { /* Fourth byte 16-bit table 19. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 6, 12, 18, 24, 30, 36, 42,
+ 48, 54, 60, 66, 72, 78, 84, 90,
+ 96, 102, 108, 114, 120, 126, 132, 138,
+ 144, 150, 156, 162, 168, 174, 180, 186,
+ 192, 198, 204, 210, 216, 222, 228, 234,
+ 240, 246, 252, 258, 264, 270, 276, 282,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288,
+ },
+ { /* Fourth byte 16-bit table 20. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 6, 12, 18, 24, 30, 36, 42,
+ 48, 54, 60, 66, 72, 78, 84, 90,
+ 96, 96, 96, 102, 108, 114, 120, 126,
+ 132, 138, 144, 150, 156, 162, 168, 174,
+ 180, 186, 192, 198, 204, 210, 216, 222,
+ 228, 234, 240, 246, 252, 258, 264, 270,
+ 276, 282, 288, 294, 300, 306, 312, 318,
+ 324, 330, 336, 342, 348, 354, 360, 366,
+ 372, 372, 372, 372, 372, 372, 372, 372,
+ 372, 372, 372, 372, 372, 372, 372, 372,
+ 372, 372, 372, 372, 372, 372, 372, 372,
+ 372, 372, 372, 372, 372, 372, 372, 372,
+ 372, 372, 372, 372, 372, 372, 372, 372,
+ 372, 372, 372, 372, 372, 372, 372, 372,
+ 372, 372, 372, 372, 372, 372, 372, 372,
+ 372, 372, 372, 372, 372, 372, 372, 372,
+ 372,
+ },
+ { /* Fourth byte 16-bit table 21. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 17, 21, 25, 29,
+ 33, 37, 41, 45, 49, 53, 58, 62,
+ 66, 70, 74, 79, 83, 87, 91, 96,
+ 100, 104, 108, 112, 116, 121, 125, 129,
+ 133, 137, 141, 145, 149, 153, 157, 161,
+ 165, 169, 173, 177, 181, 185, 189, 193,
+ 197, 201, 205, 209, 213, 218, 222, 226,
+ 230, 235, 239, 243, 247, 251, 255, 259,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263,
+ },
+ { /* Fourth byte 16-bit table 22. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 105, 109, 113, 117, 121, 125,
+ 129, 134, 139, 143, 147, 151, 155, 159,
+ 163, 167, 171, 175, 179, 184, 188, 192,
+ 196, 200, 205, 209, 213, 217, 221, 225,
+ 229, 233, 237, 241, 246, 250, 255, 259,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263,
+ },
+ { /* Fourth byte 16-bit table 23. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 41, 45, 49, 53, 57, 61,
+ 66, 70, 75, 80, 84, 88, 92, 96,
+ 101, 106, 110, 114, 118, 122, 126, 130,
+ 134, 138, 142, 146, 150, 155, 159, 163,
+ 167, 171, 175, 179, 183, 187, 191, 195,
+ 199, 203, 207, 211, 215, 219, 223, 227,
+ 231, 236, 240, 244, 248, 252, 256, 261,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265,
+ },
+ { /* Fourth byte 16-bit table 24. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 45, 49, 53, 57, 61,
+ 65, 69, 73, 77, 81, 85, 89, 93,
+ 97, 101, 105, 109, 113, 117, 122, 126,
+ 130, 134, 138, 142, 147, 151, 155, 159,
+ 163, 167, 171, 175, 179, 184, 188, 192,
+ 196, 201, 205, 209, 213, 217, 221, 225,
+ 230, 235, 240, 244, 249, 253, 257, 261,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265,
+ },
+ { /* Fourth byte 16-bit table 25. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 29,
+ 33, 37, 41, 45, 49, 53, 58, 62,
+ 66, 71, 76, 80, 84, 88, 92, 96,
+ 100, 104, 108, 112, 117, 121, 126, 130,
+ 135, 139, 143, 147, 152, 156, 160, 165,
+ 170, 174, 178, 182, 186, 190, 194, 198,
+ 202, 206, 210, 214, 218, 222, 227, 231,
+ 236, 240, 245, 249, 254, 259, 264, 268,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272,
+ },
+ { /* Fourth byte 16-bit table 26. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 9, 14, 19, 24, 28, 32,
+ 36, 40, 44, 48, 52, 56, 61, 65,
+ 69, 73, 77, 82, 86, 91, 96, 100,
+ 104, 108, 112, 116, 120, 125, 130, 135,
+ 139, 143, 148, 152, 156, 160, 165, 169,
+ 173, 177, 181, 185, 190, 194, 198, 202,
+ 206, 210, 214, 219, 224, 228, 233, 237,
+ 242, 246, 250, 254, 259, 264, 268, 273,
+ 277, 277, 277, 277, 277, 277, 277, 277,
+ 277, 277, 277, 277, 277, 277, 277, 277,
+ 277, 277, 277, 277, 277, 277, 277, 277,
+ 277, 277, 277, 277, 277, 277, 277, 277,
+ 277, 277, 277, 277, 277, 277, 277, 277,
+ 277, 277, 277, 277, 277, 277, 277, 277,
+ 277, 277, 277, 277, 277, 277, 277, 277,
+ 277, 277, 277, 277, 277, 277, 277, 277,
+ 277,
+ },
+ { /* Fourth byte 16-bit table 27. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5, 9, 13, 17, 21, 25, 29,
+ 34, 39, 44, 49, 53, 57, 61, 65,
+ 69, 73, 77, 81, 85, 89, 93, 97,
+ 102, 106, 110, 114, 118, 122, 126, 130,
+ 134, 138, 142, 146, 150, 155, 160, 165,
+ 169, 173, 177, 181, 186, 190, 195, 199,
+ 203, 208, 213, 217, 221, 225, 229, 233,
+ 237, 241, 245, 249, 253, 257, 261, 265,
+ 269, 269, 269, 269, 269, 269, 269, 269,
+ 269, 269, 269, 269, 269, 269, 269, 269,
+ 269, 269, 269, 269, 269, 269, 269, 269,
+ 269, 269, 269, 269, 269, 269, 269, 269,
+ 269, 269, 269, 269, 269, 269, 269, 269,
+ 269, 269, 269, 269, 269, 269, 269, 269,
+ 269, 269, 269, 269, 269, 269, 269, 269,
+ 269, 269, 269, 269, 269, 269, 269, 269,
+ 269,
+ },
+ { /* Fourth byte 16-bit table 28. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 25, 29,
+ 33, 37, 41, 45, 50, 55, 59, 63,
+ 67, 71, 75, 79, 84, 88, 92, 96,
+ 100, 105, 110, 114, 118, 122, 127, 131,
+ 135, 140, 145, 149, 153, 157, 162, 166,
+ 170, 174, 178, 182, 186, 190, 195, 199,
+ 203, 207, 212, 216, 220, 224, 228, 233,
+ 238, 242, 246, 250, 255, 259, 264, 268,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272,
+ },
+ { /* Fourth byte 16-bit table 29. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ },
+ {
+ { /* Fourth byte 16-bit table 0. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 38, 44, 48, 52, 56, 60, 64,
+ 68, 72, 76, 80, 84, 90, 96, 102,
+ 108, 112, 116, 120, 124, 130, 136, 140,
+ 144, 148, 152, 156, 160, 164, 168, 172,
+ 176, 180, 184, 188, 192, 196, 200, 206,
+ 212, 216, 220, 224, 228, 232, 236, 240,
+ 244, 250, 256, 260, 264, 268, 272, 276,
+ 280, 280, 280, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 280, 280, 280,
+ 280,
+ },
+ { /* Fourth byte 16-bit table 1. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 54, 60, 66,
+ 72, 78, 84, 90, 96, 100, 104, 108,
+ 112, 116, 120, 124, 128, 134, 140, 144,
+ 148, 152, 156, 160, 164, 170, 176, 182,
+ 188, 194, 200, 204, 208, 212, 216, 220,
+ 224, 228, 232, 236, 240, 244, 248, 252,
+ 256, 262, 268, 274, 280, 284, 288, 292,
+ 296, 296, 296, 296, 296, 296, 296, 296,
+ 296, 296, 296, 296, 296, 296, 296, 296,
+ 296, 296, 296, 296, 296, 296, 296, 296,
+ 296, 296, 296, 296, 296, 296, 296, 296,
+ 296, 296, 296, 296, 296, 296, 296, 296,
+ 296, 296, 296, 296, 296, 296, 296, 296,
+ 296, 296, 296, 296, 296, 296, 296, 296,
+ 296, 296, 296, 296, 296, 296, 296, 296,
+ 296,
+ },
+ { /* Fourth byte 16-bit table 2. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 107, 116, 116, 116, 116,
+ 116, 120, 124, 128, 132, 138, 144, 150,
+ 156, 162, 168, 174, 180, 186, 192, 198,
+ 204, 210, 216, 222, 228, 234, 240, 246,
+ 252, 256, 260, 264, 268, 272, 276, 282,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288,
+ },
+ { /* Fourth byte 16-bit table 3. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 6, 12, 18, 24, 30, 36, 42,
+ 48, 52, 56, 60, 64, 68, 72, 76,
+ 80, 86, 92, 98, 104, 110, 116, 122,
+ 128, 134, 140, 146, 152, 158, 164, 170,
+ 176, 182, 188, 194, 200, 204, 208, 212,
+ 216, 222, 228, 234, 240, 246, 252, 258,
+ 264, 270, 276, 280, 284, 288, 292, 296,
+ 300, 304, 308, 308, 308, 308, 308, 308,
+ 308, 308, 308, 308, 308, 308, 308, 308,
+ 308, 308, 308, 308, 308, 308, 308, 308,
+ 308, 308, 308, 308, 308, 308, 308, 308,
+ 308, 308, 308, 308, 308, 308, 308, 308,
+ 308, 308, 308, 308, 308, 308, 308, 308,
+ 308, 308, 308, 308, 308, 308, 308, 308,
+ 308, 308, 308, 308, 308, 308, 308, 308,
+ 308, 308, 308, 308, 308, 308, 308, 308,
+ 308,
+ },
+ { /* Fourth byte 16-bit table 4. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5, 10, 17, 24, 31, 38, 45,
+ 52, 57, 62, 69, 76, 83, 90, 97,
+ 104, 109, 114, 121, 128, 135, 142, 142,
+ 142, 147, 152, 159, 166, 173, 180, 180,
+ 180, 185, 190, 197, 204, 211, 218, 225,
+ 232, 237, 242, 249, 256, 263, 270, 277,
+ 284, 289, 294, 301, 308, 315, 322, 329,
+ 336, 341, 346, 353, 360, 367, 374, 381,
+ 388, 388, 388, 388, 388, 388, 388, 388,
+ 388, 388, 388, 388, 388, 388, 388, 388,
+ 388, 388, 388, 388, 388, 388, 388, 388,
+ 388, 388, 388, 388, 388, 388, 388, 388,
+ 388, 388, 388, 388, 388, 388, 388, 388,
+ 388, 388, 388, 388, 388, 388, 388, 388,
+ 388, 388, 388, 388, 388, 388, 388, 388,
+ 388, 388, 388, 388, 388, 388, 388, 388,
+ 388,
+ },
+ { /* Fourth byte 16-bit table 5. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5, 10, 17, 24, 31, 38, 38,
+ 38, 43, 48, 55, 62, 69, 76, 76,
+ 76, 81, 86, 93, 100, 107, 114, 121,
+ 128, 128, 133, 133, 140, 140, 147, 147,
+ 154, 159, 164, 171, 178, 185, 192, 199,
+ 206, 211, 216, 223, 230, 237, 244, 251,
+ 258, 263, 268, 273, 278, 283, 288, 293,
+ 298, 303, 308, 313, 318, 323, 328, 328,
+ 328, 328, 328, 328, 328, 328, 328, 328,
+ 328, 328, 328, 328, 328, 328, 328, 328,
+ 328, 328, 328, 328, 328, 328, 328, 328,
+ 328, 328, 328, 328, 328, 328, 328, 328,
+ 328, 328, 328, 328, 328, 328, 328, 328,
+ 328, 328, 328, 328, 328, 328, 328, 328,
+ 328, 328, 328, 328, 328, 328, 328, 328,
+ 328, 328, 328, 328, 328, 328, 328, 328,
+ 328,
+ },
+ { /* Fourth byte 16-bit table 6. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 7, 14, 23, 32, 41, 50, 59,
+ 68, 75, 82, 91, 100, 109, 118, 127,
+ 136, 143, 150, 159, 168, 177, 186, 195,
+ 204, 211, 218, 227, 236, 245, 254, 263,
+ 272, 279, 286, 295, 304, 313, 322, 331,
+ 340, 347, 354, 363, 372, 381, 390, 399,
+ 408, 413, 418, 425, 430, 437, 437, 442,
+ 449, 454, 459, 464, 469, 474, 477, 480,
+ 483, 483, 483, 483, 483, 483, 483, 483,
+ 483, 483, 483, 483, 483, 483, 483, 483,
+ 483, 483, 483, 483, 483, 483, 483, 483,
+ 483, 483, 483, 483, 483, 483, 483, 483,
+ 483, 483, 483, 483, 483, 483, 483, 483,
+ 483, 483, 483, 483, 483, 483, 483, 483,
+ 483, 483, 483, 483, 483, 483, 483, 483,
+ 483, 483, 483, 483, 483, 483, 483, 483,
+ 483,
+ },
+ { /* Fourth byte 16-bit table 7. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 14, 21, 26, 33, 33, 38,
+ 45, 50, 55, 60, 65, 70, 82, 94,
+ 106, 111, 116, 123, 130, 130, 130, 135,
+ 142, 147, 152, 157, 162, 162, 174, 186,
+ 198, 203, 208, 215, 222, 227, 232, 237,
+ 244, 249, 254, 259, 264, 269, 280, 291,
+ 293, 293, 293, 300, 305, 312, 312, 317,
+ 324, 329, 334, 339, 344, 349, 356, 359,
+ 359, 359, 359, 359, 359, 359, 359, 359,
+ 359, 359, 359, 359, 359, 359, 359, 359,
+ 359, 359, 359, 359, 359, 359, 359, 359,
+ 359, 359, 359, 359, 359, 359, 359, 359,
+ 359, 359, 359, 359, 359, 359, 359, 359,
+ 359, 359, 359, 359, 359, 359, 359, 359,
+ 359, 359, 359, 359, 359, 359, 359, 359,
+ 359, 359, 359, 359, 359, 359, 359, 359,
+ 359,
+ },
+ { /* Fourth byte 16-bit table 8. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5, 10, 15, 20, 25, 30, 35,
+ 40, 45, 50, 55, 60, 65, 70, 78,
+ 86, 94, 102, 110, 118, 126, 134, 142,
+ 150, 158, 166, 174, 182, 190, 207, 221,
+ 221, 226, 231, 236, 241, 246, 251, 256,
+ 261, 266, 271, 276, 281, 286, 291, 296,
+ 301, 306, 311, 316, 321, 326, 331, 336,
+ 341, 346, 351, 356, 361, 366, 371, 376,
+ 381, 381, 381, 381, 381, 381, 381, 381,
+ 381, 381, 381, 381, 381, 381, 381, 381,
+ 381, 381, 381, 381, 381, 381, 381, 381,
+ 381, 381, 381, 381, 381, 381, 381, 381,
+ 381, 381, 381, 381, 381, 381, 381, 381,
+ 381, 381, 381, 381, 381, 381, 381, 381,
+ 381, 381, 381, 381, 381, 381, 381, 381,
+ 381, 381, 381, 381, 381, 381, 381, 381,
+ 381,
+ },
+ { /* Fourth byte 16-bit table 9. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 15, 27, 42, 51, 66, 75, 84,
+ 102, 114, 123, 132, 141, 153, 165, 177,
+ 189, 201, 213, 225, 243, 249, 267, 285,
+ 300, 312, 330, 348, 360, 369, 378, 390,
+ 402, 417, 432, 441, 450, 462, 471, 480,
+ 486, 492, 501, 510, 528, 540, 555, 573,
+ 585, 594, 603, 621, 633, 651, 660, 675,
+ 684, 696, 705, 717, 732, 744, 759, 771,
+ 777, 777, 777, 777, 777, 777, 777, 777,
+ 777, 777, 777, 777, 777, 777, 777, 777,
+ 777, 777, 777, 777, 777, 777, 777, 777,
+ 777, 777, 777, 777, 777, 777, 777, 777,
+ 777, 777, 777, 777, 777, 777, 777, 777,
+ 777, 777, 777, 777, 777, 777, 777, 777,
+ 777, 777, 777, 777, 777, 777, 777, 777,
+ 777, 777, 777, 777, 777, 777, 777, 777,
+ 777,
+ },
+ { /* Fourth byte 16-bit table 10. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 15, 24, 33, 45, 54, 63, 72,
+ 87, 99, 105, 123, 132, 147, 159, 171,
+ 180, 189, 201, 207, 219, 234, 240, 258,
+ 267, 271, 275, 279, 283, 287, 291, 295,
+ 299, 303, 307, 312, 317, 322, 327, 332,
+ 337, 342, 347, 352, 357, 362, 367, 372,
+ 377, 382, 385, 387, 389, 392, 394, 396,
+ 398, 401, 404, 406, 412, 418, 424, 430,
+ 442, 442, 442, 442, 442, 442, 442, 442,
+ 442, 442, 442, 442, 442, 442, 442, 442,
+ 442, 442, 442, 442, 442, 442, 442, 442,
+ 442, 442, 442, 442, 442, 442, 442, 442,
+ 442, 442, 442, 442, 442, 442, 442, 442,
+ 442, 442, 442, 442, 442, 442, 442, 442,
+ 442, 442, 442, 442, 442, 442, 442, 442,
+ 442, 442, 442, 442, 442, 442, 442, 442,
+ 442,
+ },
+ { /* Fourth byte 16-bit table 11. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 116, 120, 124,
+ 128, 132, 136, 140, 144, 148, 152, 156,
+ 160, 164, 168, 172, 176, 180, 184, 188,
+ 192, 196, 200, 204, 208, 212, 216, 220,
+ 224, 228, 232, 236, 240, 244, 248, 252,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256,
+ },
+ { /* Fourth byte 16-bit table 12. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 116, 120, 124,
+ 128, 132, 136, 140, 144, 148, 152, 156,
+ 160, 164, 168, 172, 176, 180, 184, 188,
+ 192, 196, 200, 204, 208, 212, 216, 220,
+ 224, 228, 232, 236, 240, 244, 248, 252,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256,
+ },
+ { /* Fourth byte 16-bit table 13. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 116, 120, 124,
+ 128, 132, 136, 140, 144, 148, 152, 156,
+ 160, 164, 168, 172, 176, 180, 184, 188,
+ 192, 196, 200, 204, 208, 212, 216, 220,
+ 224, 228, 232, 236, 240, 244, 248, 252,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256,
+ },
+ { /* Fourth byte 16-bit table 14. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 116, 120, 124,
+ 128, 132, 136, 140, 144, 148, 152, 156,
+ 160, 164, 168, 172, 176, 180, 184, 188,
+ 192, 196, 200, 204, 208, 212, 216, 220,
+ 224, 228, 232, 236, 240, 244, 248, 252,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256,
+ },
+ { /* Fourth byte 16-bit table 15. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 116, 120, 124,
+ 128, 132, 136, 140, 144, 148, 152, 156,
+ 160, 164, 168, 172, 176, 180, 184, 188,
+ 192, 196, 200, 204, 208, 212, 216, 220,
+ 224, 228, 232, 236, 240, 244, 248, 252,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256,
+ 256,
+ },
+ { /* Fourth byte 16-bit table 16. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 6, 12, 18, 24, 30, 34, 38,
+ 42, 46, 50, 54, 58, 62, 66, 70,
+ 74, 78, 82, 86, 90, 94, 98, 102,
+ 106, 110, 114, 118, 122, 126, 130, 134,
+ 138, 142, 146, 150, 154, 158, 162, 166,
+ 170, 174, 178, 182, 186, 190, 194, 198,
+ 202, 206, 210, 214, 218, 222, 226, 230,
+ 234, 238, 242, 246, 250, 254, 258, 262,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266,
+ },
+ { /* Fourth byte 16-bit table 17. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 116, 120, 125,
+ 130, 135, 140, 145, 150, 156, 162, 168,
+ 174, 180, 186, 190, 194, 198, 202, 206,
+ 210, 214, 218, 222, 226, 230, 234, 238,
+ 242, 246, 250, 254, 258, 262, 266, 270,
+ 274, 274, 274, 274, 274, 274, 274, 274,
+ 274, 274, 274, 274, 274, 274, 274, 274,
+ 274, 274, 274, 274, 274, 274, 274, 274,
+ 274, 274, 274, 274, 274, 274, 274, 274,
+ 274, 274, 274, 274, 274, 274, 274, 274,
+ 274, 274, 274, 274, 274, 274, 274, 274,
+ 274, 274, 274, 274, 274, 274, 274, 274,
+ 274, 274, 274, 274, 274, 274, 274, 274,
+ 274,
+ },
+ { /* Fourth byte 16-bit table 18. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 98, 104, 110, 116, 122, 126, 130, 134,
+ 138, 142, 146, 150, 154, 158, 162, 166,
+ 170, 174, 178, 182, 186, 190, 194, 198,
+ 202, 206, 210, 214, 218, 222, 226, 230,
+ 234, 238, 242, 246, 250, 254, 258, 262,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266,
+ },
+ { /* Fourth byte 16-bit table 19. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 116, 120, 124,
+ 130, 136, 140, 144, 148, 152, 156, 160,
+ 164, 168, 172, 176, 180, 184, 188, 192,
+ 196, 200, 204, 210, 216, 222, 226, 230,
+ 234, 238, 242, 246, 250, 254, 258, 262,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266,
+ },
+ { /* Fourth byte 16-bit table 20. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 6, 12, 18, 24, 30, 36, 42,
+ 48, 54, 60, 66, 72, 78, 84, 90,
+ 96, 102, 108, 114, 120, 126, 132, 138,
+ 144, 150, 156, 162, 168, 174, 180, 186,
+ 192, 198, 204, 210, 216, 222, 228, 234,
+ 240, 246, 252, 258, 264, 270, 276, 282,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288, 288, 288, 288, 288, 288, 288, 288,
+ 288,
+ },
+ { /* Fourth byte 16-bit table 21. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 6, 12, 18, 24, 30, 36, 42,
+ 48, 54, 60, 66, 72, 78, 84, 90,
+ 96, 96, 96, 102, 108, 114, 120, 126,
+ 132, 138, 144, 150, 156, 162, 168, 174,
+ 180, 186, 192, 198, 204, 210, 216, 222,
+ 228, 234, 240, 246, 252, 258, 264, 270,
+ 276, 282, 288, 294, 300, 306, 312, 318,
+ 324, 330, 336, 342, 348, 354, 360, 366,
+ 372, 372, 372, 372, 372, 372, 372, 372,
+ 372, 372, 372, 372, 372, 372, 372, 372,
+ 372, 372, 372, 372, 372, 372, 372, 372,
+ 372, 372, 372, 372, 372, 372, 372, 372,
+ 372, 372, 372, 372, 372, 372, 372, 372,
+ 372, 372, 372, 372, 372, 372, 372, 372,
+ 372, 372, 372, 372, 372, 372, 372, 372,
+ 372, 372, 372, 372, 372, 372, 372, 372,
+ 372,
+ },
+ { /* Fourth byte 16-bit table 22. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 17, 21, 25, 29,
+ 33, 37, 41, 45, 49, 53, 58, 62,
+ 66, 70, 74, 79, 83, 87, 91, 96,
+ 100, 104, 108, 112, 116, 121, 125, 129,
+ 133, 137, 141, 145, 149, 153, 157, 161,
+ 165, 169, 173, 177, 181, 185, 189, 193,
+ 197, 201, 205, 209, 213, 218, 222, 226,
+ 230, 235, 239, 243, 247, 251, 255, 259,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263,
+ },
+ { /* Fourth byte 16-bit table 23. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 105, 109, 113, 117, 121, 125,
+ 129, 134, 139, 143, 147, 151, 155, 159,
+ 163, 167, 171, 175, 179, 184, 188, 192,
+ 196, 200, 205, 209, 213, 217, 221, 225,
+ 229, 233, 237, 241, 246, 250, 255, 259,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 263,
+ 263,
+ },
+ { /* Fourth byte 16-bit table 24. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 41, 45, 49, 53, 57, 61,
+ 66, 70, 75, 80, 84, 88, 92, 96,
+ 101, 106, 110, 114, 118, 122, 126, 130,
+ 134, 138, 142, 146, 150, 155, 159, 163,
+ 167, 171, 175, 179, 183, 187, 191, 195,
+ 199, 203, 207, 211, 215, 219, 223, 227,
+ 231, 236, 240, 244, 248, 252, 256, 261,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265,
+ },
+ { /* Fourth byte 16-bit table 25. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 45, 49, 53, 57, 61,
+ 65, 69, 73, 77, 81, 85, 89, 93,
+ 97, 101, 105, 109, 113, 117, 122, 126,
+ 130, 134, 138, 142, 147, 151, 155, 159,
+ 163, 167, 171, 175, 179, 184, 188, 192,
+ 196, 201, 205, 209, 213, 217, 221, 225,
+ 230, 235, 240, 244, 249, 253, 257, 261,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 265,
+ 265,
+ },
+ { /* Fourth byte 16-bit table 26. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 29,
+ 33, 37, 41, 45, 49, 53, 58, 62,
+ 66, 71, 76, 80, 84, 88, 92, 96,
+ 100, 104, 108, 112, 117, 121, 126, 130,
+ 135, 139, 143, 147, 152, 156, 160, 165,
+ 170, 174, 178, 182, 186, 190, 194, 198,
+ 202, 206, 210, 214, 218, 222, 227, 231,
+ 236, 240, 245, 249, 254, 259, 264, 268,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272,
+ },
+ { /* Fourth byte 16-bit table 27. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 9, 14, 19, 24, 28, 32,
+ 36, 40, 44, 48, 52, 56, 61, 65,
+ 69, 73, 77, 82, 86, 91, 96, 100,
+ 104, 108, 112, 116, 120, 125, 130, 135,
+ 139, 143, 148, 152, 156, 160, 165, 169,
+ 173, 177, 181, 185, 190, 194, 198, 202,
+ 206, 210, 214, 219, 224, 228, 233, 237,
+ 242, 246, 250, 254, 259, 264, 268, 273,
+ 277, 277, 277, 277, 277, 277, 277, 277,
+ 277, 277, 277, 277, 277, 277, 277, 277,
+ 277, 277, 277, 277, 277, 277, 277, 277,
+ 277, 277, 277, 277, 277, 277, 277, 277,
+ 277, 277, 277, 277, 277, 277, 277, 277,
+ 277, 277, 277, 277, 277, 277, 277, 277,
+ 277, 277, 277, 277, 277, 277, 277, 277,
+ 277, 277, 277, 277, 277, 277, 277, 277,
+ 277,
+ },
+ { /* Fourth byte 16-bit table 28. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5, 9, 13, 17, 21, 25, 29,
+ 34, 39, 44, 49, 53, 57, 61, 65,
+ 69, 73, 77, 81, 85, 89, 93, 97,
+ 102, 106, 110, 114, 118, 122, 126, 130,
+ 134, 138, 142, 146, 150, 155, 160, 165,
+ 169, 173, 177, 181, 186, 190, 195, 199,
+ 203, 208, 213, 217, 221, 225, 229, 233,
+ 237, 241, 245, 249, 253, 257, 261, 265,
+ 269, 269, 269, 269, 269, 269, 269, 269,
+ 269, 269, 269, 269, 269, 269, 269, 269,
+ 269, 269, 269, 269, 269, 269, 269, 269,
+ 269, 269, 269, 269, 269, 269, 269, 269,
+ 269, 269, 269, 269, 269, 269, 269, 269,
+ 269, 269, 269, 269, 269, 269, 269, 269,
+ 269, 269, 269, 269, 269, 269, 269, 269,
+ 269, 269, 269, 269, 269, 269, 269, 269,
+ 269,
+ },
+ { /* Fourth byte 16-bit table 29. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 25, 29,
+ 33, 37, 41, 45, 50, 55, 59, 63,
+ 67, 71, 75, 79, 84, 88, 92, 96,
+ 100, 105, 110, 114, 118, 122, 127, 131,
+ 135, 140, 145, 149, 153, 157, 162, 166,
+ 170, 174, 178, 182, 186, 190, 195, 199,
+ 203, 207, 212, 216, 220, 224, 228, 233,
+ 238, 242, 246, 250, 255, 259, 264, 268,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272,
+ 272,
+ },
+ },
+};
+
+static const uchar_t u8_decomp_final_tbl[2][19370] = {
+ {
+ 0x20, 0x20, 0xCC, 0x88, 0x61, 0x20, 0xCC, 0x84,
+ 0x32, 0x33, 0x20, 0xCC, 0x81, 0xCE, 0xBC, 0x20,
+ 0xCC, 0xA7, 0x31, 0x6F, 0x31, 0xE2, 0x81, 0x84,
+ 0x34, 0x31, 0xE2, 0x81, 0x84, 0x32, 0x33, 0xE2,
+ 0x81, 0x84, 0x34, 0xF6, 0x41, 0xCC, 0x80, 0xF6,
+ 0x41, 0xCC, 0x81, 0xF6, 0x41, 0xCC, 0x82, 0xF6,
+ 0x41, 0xCC, 0x83, 0xF6, 0x41, 0xCC, 0x88, 0xF6,
+ 0x41, 0xCC, 0x8A, 0xF6, 0x43, 0xCC, 0xA7, 0xF6,
+ 0x45, 0xCC, 0x80, 0xF6, 0x45, 0xCC, 0x81, 0xF6,
+ 0x45, 0xCC, 0x82, 0xF6, 0x45, 0xCC, 0x88, 0xF6,
+ 0x49, 0xCC, 0x80, 0xF6, 0x49, 0xCC, 0x81, 0xF6,
+ 0x49, 0xCC, 0x82, 0xF6, 0x49, 0xCC, 0x88, 0xF6,
+ 0x4E, 0xCC, 0x83, 0xF6, 0x4F, 0xCC, 0x80, 0xF6,
+ 0x4F, 0xCC, 0x81, 0xF6, 0x4F, 0xCC, 0x82, 0xF6,
+ 0x4F, 0xCC, 0x83, 0xF6, 0x4F, 0xCC, 0x88, 0xF6,
+ 0x55, 0xCC, 0x80, 0xF6, 0x55, 0xCC, 0x81, 0xF6,
+ 0x55, 0xCC, 0x82, 0xF6, 0x55, 0xCC, 0x88, 0xF6,
+ 0x59, 0xCC, 0x81, 0xF6, 0x61, 0xCC, 0x80, 0xF6,
+ 0x61, 0xCC, 0x81, 0xF6, 0x61, 0xCC, 0x82, 0xF6,
+ 0x61, 0xCC, 0x83, 0xF6, 0x61, 0xCC, 0x88, 0xF6,
+ 0x61, 0xCC, 0x8A, 0xF6, 0x63, 0xCC, 0xA7, 0xF6,
+ 0x65, 0xCC, 0x80, 0xF6, 0x65, 0xCC, 0x81, 0xF6,
+ 0x65, 0xCC, 0x82, 0xF6, 0x65, 0xCC, 0x88, 0xF6,
+ 0x69, 0xCC, 0x80, 0xF6, 0x69, 0xCC, 0x81, 0xF6,
+ 0x69, 0xCC, 0x82, 0xF6, 0x69, 0xCC, 0x88, 0xF6,
+ 0x6E, 0xCC, 0x83, 0xF6, 0x6F, 0xCC, 0x80, 0xF6,
+ 0x6F, 0xCC, 0x81, 0xF6, 0x6F, 0xCC, 0x82, 0xF6,
+ 0x6F, 0xCC, 0x83, 0xF6, 0x6F, 0xCC, 0x88, 0xF6,
+ 0x75, 0xCC, 0x80, 0xF6, 0x75, 0xCC, 0x81, 0xF6,
+ 0x75, 0xCC, 0x82, 0xF6, 0x75, 0xCC, 0x88, 0xF6,
+ 0x79, 0xCC, 0x81, 0xF6, 0x79, 0xCC, 0x88, 0xF6,
+ 0x41, 0xCC, 0x84, 0xF6, 0x61, 0xCC, 0x84, 0xF6,
+ 0x41, 0xCC, 0x86, 0xF6, 0x61, 0xCC, 0x86, 0xF6,
+ 0x41, 0xCC, 0xA8, 0xF6, 0x61, 0xCC, 0xA8, 0xF6,
+ 0x43, 0xCC, 0x81, 0xF6, 0x63, 0xCC, 0x81, 0xF6,
+ 0x43, 0xCC, 0x82, 0xF6, 0x63, 0xCC, 0x82, 0xF6,
+ 0x43, 0xCC, 0x87, 0xF6, 0x63, 0xCC, 0x87, 0xF6,
+ 0x43, 0xCC, 0x8C, 0xF6, 0x63, 0xCC, 0x8C, 0xF6,
+ 0x44, 0xCC, 0x8C, 0xF6, 0x64, 0xCC, 0x8C, 0xF6,
+ 0x45, 0xCC, 0x84, 0xF6, 0x65, 0xCC, 0x84, 0xF6,
+ 0x45, 0xCC, 0x86, 0xF6, 0x65, 0xCC, 0x86, 0xF6,
+ 0x45, 0xCC, 0x87, 0xF6, 0x65, 0xCC, 0x87, 0xF6,
+ 0x45, 0xCC, 0xA8, 0xF6, 0x65, 0xCC, 0xA8, 0xF6,
+ 0x45, 0xCC, 0x8C, 0xF6, 0x65, 0xCC, 0x8C, 0xF6,
+ 0x47, 0xCC, 0x82, 0xF6, 0x67, 0xCC, 0x82, 0xF6,
+ 0x47, 0xCC, 0x86, 0xF6, 0x67, 0xCC, 0x86, 0xF6,
+ 0x47, 0xCC, 0x87, 0xF6, 0x67, 0xCC, 0x87, 0xF6,
+ 0x47, 0xCC, 0xA7, 0xF6, 0x67, 0xCC, 0xA7, 0xF6,
+ 0x48, 0xCC, 0x82, 0xF6, 0x68, 0xCC, 0x82, 0xF6,
+ 0x49, 0xCC, 0x83, 0xF6, 0x69, 0xCC, 0x83, 0xF6,
+ 0x49, 0xCC, 0x84, 0xF6, 0x69, 0xCC, 0x84, 0xF6,
+ 0x49, 0xCC, 0x86, 0xF6, 0x69, 0xCC, 0x86, 0xF6,
+ 0x49, 0xCC, 0xA8, 0xF6, 0x69, 0xCC, 0xA8, 0xF6,
+ 0x49, 0xCC, 0x87, 0x49, 0x4A, 0x69, 0x6A, 0xF6,
+ 0x4A, 0xCC, 0x82, 0xF6, 0x6A, 0xCC, 0x82, 0xF6,
+ 0x4B, 0xCC, 0xA7, 0xF6, 0x6B, 0xCC, 0xA7, 0xF6,
+ 0x4C, 0xCC, 0x81, 0xF6, 0x6C, 0xCC, 0x81, 0xF6,
+ 0x4C, 0xCC, 0xA7, 0xF6, 0x6C, 0xCC, 0xA7, 0xF6,
+ 0x4C, 0xCC, 0x8C, 0xF6, 0x6C, 0xCC, 0x8C, 0x4C,
+ 0xC2, 0xB7, 0x6C, 0xC2, 0xB7, 0xF6, 0x4E, 0xCC,
+ 0x81, 0xF6, 0x6E, 0xCC, 0x81, 0xF6, 0x4E, 0xCC,
+ 0xA7, 0xF6, 0x6E, 0xCC, 0xA7, 0xF6, 0x4E, 0xCC,
+ 0x8C, 0xF6, 0x6E, 0xCC, 0x8C, 0xCA, 0xBC, 0x6E,
+ 0xF6, 0x4F, 0xCC, 0x84, 0xF6, 0x6F, 0xCC, 0x84,
+ 0xF6, 0x4F, 0xCC, 0x86, 0xF6, 0x6F, 0xCC, 0x86,
+ 0xF6, 0x4F, 0xCC, 0x8B, 0xF6, 0x6F, 0xCC, 0x8B,
+ 0xF6, 0x52, 0xCC, 0x81, 0xF6, 0x72, 0xCC, 0x81,
+ 0xF6, 0x52, 0xCC, 0xA7, 0xF6, 0x72, 0xCC, 0xA7,
+ 0xF6, 0x52, 0xCC, 0x8C, 0xF6, 0x72, 0xCC, 0x8C,
+ 0xF6, 0x53, 0xCC, 0x81, 0xF6, 0x73, 0xCC, 0x81,
+ 0xF6, 0x53, 0xCC, 0x82, 0xF6, 0x73, 0xCC, 0x82,
+ 0xF6, 0x53, 0xCC, 0xA7, 0xF6, 0x73, 0xCC, 0xA7,
+ 0xF6, 0x53, 0xCC, 0x8C, 0xF6, 0x73, 0xCC, 0x8C,
+ 0xF6, 0x54, 0xCC, 0xA7, 0xF6, 0x74, 0xCC, 0xA7,
+ 0xF6, 0x54, 0xCC, 0x8C, 0xF6, 0x74, 0xCC, 0x8C,
+ 0xF6, 0x55, 0xCC, 0x83, 0xF6, 0x75, 0xCC, 0x83,
+ 0xF6, 0x55, 0xCC, 0x84, 0xF6, 0x75, 0xCC, 0x84,
+ 0xF6, 0x55, 0xCC, 0x86, 0xF6, 0x75, 0xCC, 0x86,
+ 0xF6, 0x55, 0xCC, 0x8A, 0xF6, 0x75, 0xCC, 0x8A,
+ 0xF6, 0x55, 0xCC, 0x8B, 0xF6, 0x75, 0xCC, 0x8B,
+ 0xF6, 0x55, 0xCC, 0xA8, 0xF6, 0x75, 0xCC, 0xA8,
+ 0xF6, 0x57, 0xCC, 0x82, 0xF6, 0x77, 0xCC, 0x82,
+ 0xF6, 0x59, 0xCC, 0x82, 0xF6, 0x79, 0xCC, 0x82,
+ 0xF6, 0x59, 0xCC, 0x88, 0xF6, 0x5A, 0xCC, 0x81,
+ 0xF6, 0x7A, 0xCC, 0x81, 0xF6, 0x5A, 0xCC, 0x87,
+ 0xF6, 0x7A, 0xCC, 0x87, 0xF6, 0x5A, 0xCC, 0x8C,
+ 0xF6, 0x7A, 0xCC, 0x8C, 0x73, 0xF6, 0x4F, 0xCC,
+ 0x9B, 0xF6, 0x6F, 0xCC, 0x9B, 0xF6, 0x55, 0xCC,
+ 0x9B, 0xF6, 0x75, 0xCC, 0x9B, 0x44, 0x5A, 0xCC,
+ 0x8C, 0x44, 0x7A, 0xCC, 0x8C, 0x64, 0x7A, 0xCC,
+ 0x8C, 0x4C, 0x4A, 0x4C, 0x6A, 0x6C, 0x6A, 0x4E,
+ 0x4A, 0x4E, 0x6A, 0x6E, 0x6A, 0xF6, 0x41, 0xCC,
+ 0x8C, 0xF6, 0x61, 0xCC, 0x8C, 0xF6, 0x49, 0xCC,
+ 0x8C, 0xF6, 0x69, 0xCC, 0x8C, 0xF6, 0x4F, 0xCC,
+ 0x8C, 0xF6, 0x6F, 0xCC, 0x8C, 0xF6, 0x55, 0xCC,
+ 0x8C, 0xF6, 0x75, 0xCC, 0x8C, 0xF6, 0x55, 0xCC,
+ 0x88, 0xCC, 0x84, 0xF6, 0x75, 0xCC, 0x88, 0xCC,
+ 0x84, 0xF6, 0x55, 0xCC, 0x88, 0xCC, 0x81, 0xF6,
+ 0x75, 0xCC, 0x88, 0xCC, 0x81, 0xF6, 0x55, 0xCC,
+ 0x88, 0xCC, 0x8C, 0xF6, 0x75, 0xCC, 0x88, 0xCC,
+ 0x8C, 0xF6, 0x55, 0xCC, 0x88, 0xCC, 0x80, 0xF6,
+ 0x75, 0xCC, 0x88, 0xCC, 0x80, 0xF6, 0x41, 0xCC,
+ 0x88, 0xCC, 0x84, 0xF6, 0x61, 0xCC, 0x88, 0xCC,
+ 0x84, 0xF6, 0x41, 0xCC, 0x87, 0xCC, 0x84, 0xF6,
+ 0x61, 0xCC, 0x87, 0xCC, 0x84, 0xF6, 0xC3, 0x86,
+ 0xCC, 0x84, 0xF6, 0xC3, 0xA6, 0xCC, 0x84, 0xF6,
+ 0x47, 0xCC, 0x8C, 0xF6, 0x67, 0xCC, 0x8C, 0xF6,
+ 0x4B, 0xCC, 0x8C, 0xF6, 0x6B, 0xCC, 0x8C, 0xF6,
+ 0x4F, 0xCC, 0xA8, 0xF6, 0x6F, 0xCC, 0xA8, 0xF6,
+ 0x4F, 0xCC, 0xA8, 0xCC, 0x84, 0xF6, 0x6F, 0xCC,
+ 0xA8, 0xCC, 0x84, 0xF6, 0xC6, 0xB7, 0xCC, 0x8C,
+ 0xF6, 0xCA, 0x92, 0xCC, 0x8C, 0xF6, 0x6A, 0xCC,
+ 0x8C, 0x44, 0x5A, 0x44, 0x7A, 0x64, 0x7A, 0xF6,
+ 0x47, 0xCC, 0x81, 0xF6, 0x67, 0xCC, 0x81, 0xF6,
+ 0x4E, 0xCC, 0x80, 0xF6, 0x6E, 0xCC, 0x80, 0xF6,
+ 0x41, 0xCC, 0x8A, 0xCC, 0x81, 0xF6, 0x61, 0xCC,
+ 0x8A, 0xCC, 0x81, 0xF6, 0xC3, 0x86, 0xCC, 0x81,
+ 0xF6, 0xC3, 0xA6, 0xCC, 0x81, 0xF6, 0xC3, 0x98,
+ 0xCC, 0x81, 0xF6, 0xC3, 0xB8, 0xCC, 0x81, 0xF6,
+ 0x41, 0xCC, 0x8F, 0xF6, 0x61, 0xCC, 0x8F, 0xF6,
+ 0x41, 0xCC, 0x91, 0xF6, 0x61, 0xCC, 0x91, 0xF6,
+ 0x45, 0xCC, 0x8F, 0xF6, 0x65, 0xCC, 0x8F, 0xF6,
+ 0x45, 0xCC, 0x91, 0xF6, 0x65, 0xCC, 0x91, 0xF6,
+ 0x49, 0xCC, 0x8F, 0xF6, 0x69, 0xCC, 0x8F, 0xF6,
+ 0x49, 0xCC, 0x91, 0xF6, 0x69, 0xCC, 0x91, 0xF6,
+ 0x4F, 0xCC, 0x8F, 0xF6, 0x6F, 0xCC, 0x8F, 0xF6,
+ 0x4F, 0xCC, 0x91, 0xF6, 0x6F, 0xCC, 0x91, 0xF6,
+ 0x52, 0xCC, 0x8F, 0xF6, 0x72, 0xCC, 0x8F, 0xF6,
+ 0x52, 0xCC, 0x91, 0xF6, 0x72, 0xCC, 0x91, 0xF6,
+ 0x55, 0xCC, 0x8F, 0xF6, 0x75, 0xCC, 0x8F, 0xF6,
+ 0x55, 0xCC, 0x91, 0xF6, 0x75, 0xCC, 0x91, 0xF6,
+ 0x53, 0xCC, 0xA6, 0xF6, 0x73, 0xCC, 0xA6, 0xF6,
+ 0x54, 0xCC, 0xA6, 0xF6, 0x74, 0xCC, 0xA6, 0xF6,
+ 0x48, 0xCC, 0x8C, 0xF6, 0x68, 0xCC, 0x8C, 0xF6,
+ 0x41, 0xCC, 0x87, 0xF6, 0x61, 0xCC, 0x87, 0xF6,
+ 0x45, 0xCC, 0xA7, 0xF6, 0x65, 0xCC, 0xA7, 0xF6,
+ 0x4F, 0xCC, 0x88, 0xCC, 0x84, 0xF6, 0x6F, 0xCC,
+ 0x88, 0xCC, 0x84, 0xF6, 0x4F, 0xCC, 0x83, 0xCC,
+ 0x84, 0xF6, 0x6F, 0xCC, 0x83, 0xCC, 0x84, 0xF6,
+ 0x4F, 0xCC, 0x87, 0xF6, 0x6F, 0xCC, 0x87, 0xF6,
+ 0x4F, 0xCC, 0x87, 0xCC, 0x84, 0xF6, 0x6F, 0xCC,
+ 0x87, 0xCC, 0x84, 0xF6, 0x59, 0xCC, 0x84, 0xF6,
+ 0x79, 0xCC, 0x84, 0x68, 0xC9, 0xA6, 0x6A, 0x72,
+ 0xC9, 0xB9, 0xC9, 0xBB, 0xCA, 0x81, 0x77, 0x79,
+ 0x20, 0xCC, 0x86, 0x20, 0xCC, 0x87, 0x20, 0xCC,
+ 0x8A, 0x20, 0xCC, 0xA8, 0x20, 0xCC, 0x83, 0x20,
+ 0xCC, 0x8B, 0xC9, 0xA3, 0x6C, 0x73, 0x78, 0xCA,
+ 0x95, 0xF6, 0xCC, 0x80, 0xF6, 0xCC, 0x81, 0xF6,
+ 0xCC, 0x93, 0xF6, 0xCC, 0x88, 0xCC, 0x81, 0xF6,
+ 0xCA, 0xB9, 0x20, 0xCD, 0x85, 0xF6, 0x3B, 0x20,
+ 0xCC, 0x81, 0xF5, 0x05, 0xC2, 0xA8, 0xCC, 0x81,
+ 0x20, 0xCC, 0x88, 0xCC, 0x81, 0xF6, 0xCE, 0x91,
+ 0xCC, 0x81, 0xF6, 0xC2, 0xB7, 0xF6, 0xCE, 0x95,
+ 0xCC, 0x81, 0xF6, 0xCE, 0x97, 0xCC, 0x81, 0xF6,
+ 0xCE, 0x99, 0xCC, 0x81, 0xF6, 0xCE, 0x9F, 0xCC,
+ 0x81, 0xF6, 0xCE, 0xA5, 0xCC, 0x81, 0xF6, 0xCE,
+ 0xA9, 0xCC, 0x81, 0xF6, 0xCE, 0xB9, 0xCC, 0x88,
+ 0xCC, 0x81, 0xF6, 0xCE, 0x99, 0xCC, 0x88, 0xF6,
+ 0xCE, 0xA5, 0xCC, 0x88, 0xF6, 0xCE, 0xB1, 0xCC,
+ 0x81, 0xF6, 0xCE, 0xB5, 0xCC, 0x81, 0xF6, 0xCE,
+ 0xB7, 0xCC, 0x81, 0xF6, 0xCE, 0xB9, 0xCC, 0x81,
+ 0xF6, 0xCF, 0x85, 0xCC, 0x88, 0xCC, 0x81, 0xF6,
+ 0xCE, 0xB9, 0xCC, 0x88, 0xF6, 0xCF, 0x85, 0xCC,
+ 0x88, 0xF6, 0xCE, 0xBF, 0xCC, 0x81, 0xF6, 0xCF,
+ 0x85, 0xCC, 0x81, 0xF6, 0xCF, 0x89, 0xCC, 0x81,
+ 0xCE, 0xB2, 0xCE, 0xB8, 0xCE, 0xA5, 0xF5, 0x05,
+ 0xCF, 0x92, 0xCC, 0x81, 0xCE, 0xA5, 0xCC, 0x81,
+ 0xF5, 0x05, 0xCF, 0x92, 0xCC, 0x88, 0xCE, 0xA5,
+ 0xCC, 0x88, 0xCF, 0x86, 0xCF, 0x80, 0xCE, 0xBA,
+ 0xCF, 0x81, 0xCF, 0x82, 0xCE, 0x98, 0xCE, 0xB5,
+ 0xF6, 0xD0, 0x95, 0xCC, 0x80, 0xF6, 0xD0, 0x95,
+ 0xCC, 0x88, 0xF6, 0xD0, 0x93, 0xCC, 0x81, 0xF6,
+ 0xD0, 0x86, 0xCC, 0x88, 0xF6, 0xD0, 0x9A, 0xCC,
+ 0x81, 0xF6, 0xD0, 0x98, 0xCC, 0x80, 0xF6, 0xD0,
+ 0xA3, 0xCC, 0x86, 0xF6, 0xD0, 0x98, 0xCC, 0x86,
+ 0xF6, 0xD0, 0xB8, 0xCC, 0x86, 0xF6, 0xD0, 0xB5,
+ 0xCC, 0x80, 0xF6, 0xD0, 0xB5, 0xCC, 0x88, 0xF6,
+ 0xD0, 0xB3, 0xCC, 0x81, 0xF6, 0xD1, 0x96, 0xCC,
+ 0x88, 0xF6, 0xD0, 0xBA, 0xCC, 0x81, 0xF6, 0xD0,
+ 0xB8, 0xCC, 0x80, 0xF6, 0xD1, 0x83, 0xCC, 0x86,
+ 0xF6, 0xD1, 0xB4, 0xCC, 0x8F, 0xF6, 0xD1, 0xB5,
+ 0xCC, 0x8F, 0xF6, 0xD0, 0x96, 0xCC, 0x86, 0xF6,
+ 0xD0, 0xB6, 0xCC, 0x86, 0xF6, 0xD0, 0x90, 0xCC,
+ 0x86, 0xF6, 0xD0, 0xB0, 0xCC, 0x86, 0xF6, 0xD0,
+ 0x90, 0xCC, 0x88, 0xF6, 0xD0, 0xB0, 0xCC, 0x88,
+ 0xF6, 0xD0, 0x95, 0xCC, 0x86, 0xF6, 0xD0, 0xB5,
+ 0xCC, 0x86, 0xF6, 0xD3, 0x98, 0xCC, 0x88, 0xF6,
+ 0xD3, 0x99, 0xCC, 0x88, 0xF6, 0xD0, 0x96, 0xCC,
+ 0x88, 0xF6, 0xD0, 0xB6, 0xCC, 0x88, 0xF6, 0xD0,
+ 0x97, 0xCC, 0x88, 0xF6, 0xD0, 0xB7, 0xCC, 0x88,
+ 0xF6, 0xD0, 0x98, 0xCC, 0x84, 0xF6, 0xD0, 0xB8,
+ 0xCC, 0x84, 0xF6, 0xD0, 0x98, 0xCC, 0x88, 0xF6,
+ 0xD0, 0xB8, 0xCC, 0x88, 0xF6, 0xD0, 0x9E, 0xCC,
+ 0x88, 0xF6, 0xD0, 0xBE, 0xCC, 0x88, 0xF6, 0xD3,
+ 0xA8, 0xCC, 0x88, 0xF6, 0xD3, 0xA9, 0xCC, 0x88,
+ 0xF6, 0xD0, 0xAD, 0xCC, 0x88, 0xF6, 0xD1, 0x8D,
+ 0xCC, 0x88, 0xF6, 0xD0, 0xA3, 0xCC, 0x84, 0xF6,
+ 0xD1, 0x83, 0xCC, 0x84, 0xF6, 0xD0, 0xA3, 0xCC,
+ 0x88, 0xF6, 0xD1, 0x83, 0xCC, 0x88, 0xF6, 0xD0,
+ 0xA3, 0xCC, 0x8B, 0xF6, 0xD1, 0x83, 0xCC, 0x8B,
+ 0xF6, 0xD0, 0xA7, 0xCC, 0x88, 0xF6, 0xD1, 0x87,
+ 0xCC, 0x88, 0xF6, 0xD0, 0xAB, 0xCC, 0x88, 0xF6,
+ 0xD1, 0x8B, 0xCC, 0x88, 0xD5, 0xA5, 0xD6, 0x82,
+ 0xF6, 0xD8, 0xA7, 0xD9, 0x93, 0xF6, 0xD8, 0xA7,
+ 0xD9, 0x94, 0xF6, 0xD9, 0x88, 0xD9, 0x94, 0xF6,
+ 0xD8, 0xA7, 0xD9, 0x95, 0xF6, 0xD9, 0x8A, 0xD9,
+ 0x94, 0xD8, 0xA7, 0xD9, 0xB4, 0xD9, 0x88, 0xD9,
+ 0xB4, 0xDB, 0x87, 0xD9, 0xB4, 0xD9, 0x8A, 0xD9,
+ 0xB4, 0xF6, 0xDB, 0x95, 0xD9, 0x94, 0xF6, 0xDB,
+ 0x81, 0xD9, 0x94, 0xF6, 0xDB, 0x92, 0xD9, 0x94,
+ 0xF6, 0xE0, 0xA4, 0xA8, 0xE0, 0xA4, 0xBC, 0xF6,
+ 0xE0, 0xA4, 0xB0, 0xE0, 0xA4, 0xBC, 0xF6, 0xE0,
+ 0xA4, 0xB3, 0xE0, 0xA4, 0xBC, 0xF6, 0xE0, 0xA4,
+ 0x95, 0xE0, 0xA4, 0xBC, 0xF6, 0xE0, 0xA4, 0x96,
+ 0xE0, 0xA4, 0xBC, 0xF6, 0xE0, 0xA4, 0x97, 0xE0,
+ 0xA4, 0xBC, 0xF6, 0xE0, 0xA4, 0x9C, 0xE0, 0xA4,
+ 0xBC, 0xF6, 0xE0, 0xA4, 0xA1, 0xE0, 0xA4, 0xBC,
+ 0xF6, 0xE0, 0xA4, 0xA2, 0xE0, 0xA4, 0xBC, 0xF6,
+ 0xE0, 0xA4, 0xAB, 0xE0, 0xA4, 0xBC, 0xF6, 0xE0,
+ 0xA4, 0xAF, 0xE0, 0xA4, 0xBC, 0xF6, 0xE0, 0xA7,
+ 0x87, 0xE0, 0xA6, 0xBE, 0xF6, 0xE0, 0xA7, 0x87,
+ 0xE0, 0xA7, 0x97, 0xF6, 0xE0, 0xA6, 0xA1, 0xE0,
+ 0xA6, 0xBC, 0xF6, 0xE0, 0xA6, 0xA2, 0xE0, 0xA6,
+ 0xBC, 0xF6, 0xE0, 0xA6, 0xAF, 0xE0, 0xA6, 0xBC,
+ 0xF6, 0xE0, 0xA8, 0xB2, 0xE0, 0xA8, 0xBC, 0xF6,
+ 0xE0, 0xA8, 0xB8, 0xE0, 0xA8, 0xBC, 0xF6, 0xE0,
+ 0xA8, 0x96, 0xE0, 0xA8, 0xBC, 0xF6, 0xE0, 0xA8,
+ 0x97, 0xE0, 0xA8, 0xBC, 0xF6, 0xE0, 0xA8, 0x9C,
+ 0xE0, 0xA8, 0xBC, 0xF6, 0xE0, 0xA8, 0xAB, 0xE0,
+ 0xA8, 0xBC, 0xF6, 0xE0, 0xAD, 0x87, 0xE0, 0xAD,
+ 0x96, 0xF6, 0xE0, 0xAD, 0x87, 0xE0, 0xAC, 0xBE,
+ 0xF6, 0xE0, 0xAD, 0x87, 0xE0, 0xAD, 0x97, 0xF6,
+ 0xE0, 0xAC, 0xA1, 0xE0, 0xAC, 0xBC, 0xF6, 0xE0,
+ 0xAC, 0xA2, 0xE0, 0xAC, 0xBC, 0xF6, 0xE0, 0xAE,
+ 0x92, 0xE0, 0xAF, 0x97, 0xF6, 0xE0, 0xAF, 0x86,
+ 0xE0, 0xAE, 0xBE, 0xF6, 0xE0, 0xAF, 0x87, 0xE0,
+ 0xAE, 0xBE, 0xF6, 0xE0, 0xAF, 0x86, 0xE0, 0xAF,
+ 0x97, 0xF6, 0xE0, 0xB1, 0x86, 0xE0, 0xB1, 0x96,
+ 0xF6, 0xE0, 0xB2, 0xBF, 0xE0, 0xB3, 0x95, 0xF6,
+ 0xE0, 0xB3, 0x86, 0xE0, 0xB3, 0x95, 0xF6, 0xE0,
+ 0xB3, 0x86, 0xE0, 0xB3, 0x96, 0xF6, 0xE0, 0xB3,
+ 0x86, 0xE0, 0xB3, 0x82, 0xF6, 0xE0, 0xB3, 0x86,
+ 0xE0, 0xB3, 0x82, 0xE0, 0xB3, 0x95, 0xF6, 0xE0,
+ 0xB5, 0x86, 0xE0, 0xB4, 0xBE, 0xF6, 0xE0, 0xB5,
+ 0x87, 0xE0, 0xB4, 0xBE, 0xF6, 0xE0, 0xB5, 0x86,
+ 0xE0, 0xB5, 0x97, 0xF6, 0xE0, 0xB7, 0x99, 0xE0,
+ 0xB7, 0x8A, 0xF6, 0xE0, 0xB7, 0x99, 0xE0, 0xB7,
+ 0x8F, 0xF6, 0xE0, 0xB7, 0x99, 0xE0, 0xB7, 0x8F,
+ 0xE0, 0xB7, 0x8A, 0xF6, 0xE0, 0xB7, 0x99, 0xE0,
+ 0xB7, 0x9F, 0xE0, 0xB9, 0x8D, 0xE0, 0xB8, 0xB2,
+ 0xE0, 0xBB, 0x8D, 0xE0, 0xBA, 0xB2, 0xE0, 0xBA,
+ 0xAB, 0xE0, 0xBA, 0x99, 0xE0, 0xBA, 0xAB, 0xE0,
+ 0xBA, 0xA1, 0xE0, 0xBC, 0x8B, 0xF6, 0xE0, 0xBD,
+ 0x82, 0xE0, 0xBE, 0xB7, 0xF6, 0xE0, 0xBD, 0x8C,
+ 0xE0, 0xBE, 0xB7, 0xF6, 0xE0, 0xBD, 0x91, 0xE0,
+ 0xBE, 0xB7, 0xF6, 0xE0, 0xBD, 0x96, 0xE0, 0xBE,
+ 0xB7, 0xF6, 0xE0, 0xBD, 0x9B, 0xE0, 0xBE, 0xB7,
+ 0xF6, 0xE0, 0xBD, 0x80, 0xE0, 0xBE, 0xB5, 0xF6,
+ 0xE0, 0xBD, 0xB1, 0xE0, 0xBD, 0xB2, 0xF6, 0xE0,
+ 0xBD, 0xB1, 0xE0, 0xBD, 0xB4, 0xF6, 0xE0, 0xBE,
+ 0xB2, 0xE0, 0xBE, 0x80, 0xE0, 0xBE, 0xB2, 0xE0,
+ 0xBD, 0xB1, 0xE0, 0xBE, 0x80, 0xF6, 0xE0, 0xBE,
+ 0xB3, 0xE0, 0xBE, 0x80, 0xE0, 0xBE, 0xB3, 0xE0,
+ 0xBD, 0xB1, 0xE0, 0xBE, 0x80, 0xF6, 0xE0, 0xBD,
+ 0xB1, 0xE0, 0xBE, 0x80, 0xF6, 0xE0, 0xBE, 0x92,
+ 0xE0, 0xBE, 0xB7, 0xF6, 0xE0, 0xBE, 0x9C, 0xE0,
+ 0xBE, 0xB7, 0xF6, 0xE0, 0xBE, 0xA1, 0xE0, 0xBE,
+ 0xB7, 0xF6, 0xE0, 0xBE, 0xA6, 0xE0, 0xBE, 0xB7,
+ 0xF6, 0xE0, 0xBE, 0xAB, 0xE0, 0xBE, 0xB7, 0xF6,
+ 0xE0, 0xBE, 0x90, 0xE0, 0xBE, 0xB5, 0xF6, 0xE1,
+ 0x80, 0xA5, 0xE1, 0x80, 0xAE, 0xF6, 0x41, 0xCC,
+ 0xA5, 0xF6, 0x61, 0xCC, 0xA5, 0xF6, 0x42, 0xCC,
+ 0x87, 0xF6, 0x62, 0xCC, 0x87, 0xF6, 0x42, 0xCC,
+ 0xA3, 0xF6, 0x62, 0xCC, 0xA3, 0xF6, 0x42, 0xCC,
+ 0xB1, 0xF6, 0x62, 0xCC, 0xB1, 0xF6, 0x43, 0xCC,
+ 0xA7, 0xCC, 0x81, 0xF6, 0x63, 0xCC, 0xA7, 0xCC,
+ 0x81, 0xF6, 0x44, 0xCC, 0x87, 0xF6, 0x64, 0xCC,
+ 0x87, 0xF6, 0x44, 0xCC, 0xA3, 0xF6, 0x64, 0xCC,
+ 0xA3, 0xF6, 0x44, 0xCC, 0xB1, 0xF6, 0x64, 0xCC,
+ 0xB1, 0xF6, 0x44, 0xCC, 0xA7, 0xF6, 0x64, 0xCC,
+ 0xA7, 0xF6, 0x44, 0xCC, 0xAD, 0xF6, 0x64, 0xCC,
+ 0xAD, 0xF6, 0x45, 0xCC, 0x84, 0xCC, 0x80, 0xF6,
+ 0x65, 0xCC, 0x84, 0xCC, 0x80, 0xF6, 0x45, 0xCC,
+ 0x84, 0xCC, 0x81, 0xF6, 0x65, 0xCC, 0x84, 0xCC,
+ 0x81, 0xF6, 0x45, 0xCC, 0xAD, 0xF6, 0x65, 0xCC,
+ 0xAD, 0xF6, 0x45, 0xCC, 0xB0, 0xF6, 0x65, 0xCC,
+ 0xB0, 0xF6, 0x45, 0xCC, 0xA7, 0xCC, 0x86, 0xF6,
+ 0x65, 0xCC, 0xA7, 0xCC, 0x86, 0xF6, 0x46, 0xCC,
+ 0x87, 0xF6, 0x66, 0xCC, 0x87, 0xF6, 0x47, 0xCC,
+ 0x84, 0xF6, 0x67, 0xCC, 0x84, 0xF6, 0x48, 0xCC,
+ 0x87, 0xF6, 0x68, 0xCC, 0x87, 0xF6, 0x48, 0xCC,
+ 0xA3, 0xF6, 0x68, 0xCC, 0xA3, 0xF6, 0x48, 0xCC,
+ 0x88, 0xF6, 0x68, 0xCC, 0x88, 0xF6, 0x48, 0xCC,
+ 0xA7, 0xF6, 0x68, 0xCC, 0xA7, 0xF6, 0x48, 0xCC,
+ 0xAE, 0xF6, 0x68, 0xCC, 0xAE, 0xF6, 0x49, 0xCC,
+ 0xB0, 0xF6, 0x69, 0xCC, 0xB0, 0xF6, 0x49, 0xCC,
+ 0x88, 0xCC, 0x81, 0xF6, 0x69, 0xCC, 0x88, 0xCC,
+ 0x81, 0xF6, 0x4B, 0xCC, 0x81, 0xF6, 0x6B, 0xCC,
+ 0x81, 0xF6, 0x4B, 0xCC, 0xA3, 0xF6, 0x6B, 0xCC,
+ 0xA3, 0xF6, 0x4B, 0xCC, 0xB1, 0xF6, 0x6B, 0xCC,
+ 0xB1, 0xF6, 0x4C, 0xCC, 0xA3, 0xF6, 0x6C, 0xCC,
+ 0xA3, 0xF6, 0x4C, 0xCC, 0xA3, 0xCC, 0x84, 0xF6,
+ 0x6C, 0xCC, 0xA3, 0xCC, 0x84, 0xF6, 0x4C, 0xCC,
+ 0xB1, 0xF6, 0x6C, 0xCC, 0xB1, 0xF6, 0x4C, 0xCC,
+ 0xAD, 0xF6, 0x6C, 0xCC, 0xAD, 0xF6, 0x4D, 0xCC,
+ 0x81, 0xF6, 0x6D, 0xCC, 0x81, 0xF6, 0x4D, 0xCC,
+ 0x87, 0xF6, 0x6D, 0xCC, 0x87, 0xF6, 0x4D, 0xCC,
+ 0xA3, 0xF6, 0x6D, 0xCC, 0xA3, 0xF6, 0x4E, 0xCC,
+ 0x87, 0xF6, 0x6E, 0xCC, 0x87, 0xF6, 0x4E, 0xCC,
+ 0xA3, 0xF6, 0x6E, 0xCC, 0xA3, 0xF6, 0x4E, 0xCC,
+ 0xB1, 0xF6, 0x6E, 0xCC, 0xB1, 0xF6, 0x4E, 0xCC,
+ 0xAD, 0xF6, 0x6E, 0xCC, 0xAD, 0xF6, 0x4F, 0xCC,
+ 0x83, 0xCC, 0x81, 0xF6, 0x6F, 0xCC, 0x83, 0xCC,
+ 0x81, 0xF6, 0x4F, 0xCC, 0x83, 0xCC, 0x88, 0xF6,
+ 0x6F, 0xCC, 0x83, 0xCC, 0x88, 0xF6, 0x4F, 0xCC,
+ 0x84, 0xCC, 0x80, 0xF6, 0x6F, 0xCC, 0x84, 0xCC,
+ 0x80, 0xF6, 0x4F, 0xCC, 0x84, 0xCC, 0x81, 0xF6,
+ 0x6F, 0xCC, 0x84, 0xCC, 0x81, 0xF6, 0x50, 0xCC,
+ 0x81, 0xF6, 0x70, 0xCC, 0x81, 0xF6, 0x50, 0xCC,
+ 0x87, 0xF6, 0x70, 0xCC, 0x87, 0xF6, 0x52, 0xCC,
+ 0x87, 0xF6, 0x72, 0xCC, 0x87, 0xF6, 0x52, 0xCC,
+ 0xA3, 0xF6, 0x72, 0xCC, 0xA3, 0xF6, 0x52, 0xCC,
+ 0xA3, 0xCC, 0x84, 0xF6, 0x72, 0xCC, 0xA3, 0xCC,
+ 0x84, 0xF6, 0x52, 0xCC, 0xB1, 0xF6, 0x72, 0xCC,
+ 0xB1, 0xF6, 0x53, 0xCC, 0x87, 0xF6, 0x73, 0xCC,
+ 0x87, 0xF6, 0x53, 0xCC, 0xA3, 0xF6, 0x73, 0xCC,
+ 0xA3, 0xF6, 0x53, 0xCC, 0x81, 0xCC, 0x87, 0xF6,
+ 0x73, 0xCC, 0x81, 0xCC, 0x87, 0xF6, 0x53, 0xCC,
+ 0x8C, 0xCC, 0x87, 0xF6, 0x73, 0xCC, 0x8C, 0xCC,
+ 0x87, 0xF6, 0x53, 0xCC, 0xA3, 0xCC, 0x87, 0xF6,
+ 0x73, 0xCC, 0xA3, 0xCC, 0x87, 0xF6, 0x54, 0xCC,
+ 0x87, 0xF6, 0x74, 0xCC, 0x87, 0xF6, 0x54, 0xCC,
+ 0xA3, 0xF6, 0x74, 0xCC, 0xA3, 0xF6, 0x54, 0xCC,
+ 0xB1, 0xF6, 0x74, 0xCC, 0xB1, 0xF6, 0x54, 0xCC,
+ 0xAD, 0xF6, 0x74, 0xCC, 0xAD, 0xF6, 0x55, 0xCC,
+ 0xA4, 0xF6, 0x75, 0xCC, 0xA4, 0xF6, 0x55, 0xCC,
+ 0xB0, 0xF6, 0x75, 0xCC, 0xB0, 0xF6, 0x55, 0xCC,
+ 0xAD, 0xF6, 0x75, 0xCC, 0xAD, 0xF6, 0x55, 0xCC,
+ 0x83, 0xCC, 0x81, 0xF6, 0x75, 0xCC, 0x83, 0xCC,
+ 0x81, 0xF6, 0x55, 0xCC, 0x84, 0xCC, 0x88, 0xF6,
+ 0x75, 0xCC, 0x84, 0xCC, 0x88, 0xF6, 0x56, 0xCC,
+ 0x83, 0xF6, 0x76, 0xCC, 0x83, 0xF6, 0x56, 0xCC,
+ 0xA3, 0xF6, 0x76, 0xCC, 0xA3, 0xF6, 0x57, 0xCC,
+ 0x80, 0xF6, 0x77, 0xCC, 0x80, 0xF6, 0x57, 0xCC,
+ 0x81, 0xF6, 0x77, 0xCC, 0x81, 0xF6, 0x57, 0xCC,
+ 0x88, 0xF6, 0x77, 0xCC, 0x88, 0xF6, 0x57, 0xCC,
+ 0x87, 0xF6, 0x77, 0xCC, 0x87, 0xF6, 0x57, 0xCC,
+ 0xA3, 0xF6, 0x77, 0xCC, 0xA3, 0xF6, 0x58, 0xCC,
+ 0x87, 0xF6, 0x78, 0xCC, 0x87, 0xF6, 0x58, 0xCC,
+ 0x88, 0xF6, 0x78, 0xCC, 0x88, 0xF6, 0x59, 0xCC,
+ 0x87, 0xF6, 0x79, 0xCC, 0x87, 0xF6, 0x5A, 0xCC,
+ 0x82, 0xF6, 0x7A, 0xCC, 0x82, 0xF6, 0x5A, 0xCC,
+ 0xA3, 0xF6, 0x7A, 0xCC, 0xA3, 0xF6, 0x5A, 0xCC,
+ 0xB1, 0xF6, 0x7A, 0xCC, 0xB1, 0xF6, 0x68, 0xCC,
+ 0xB1, 0xF6, 0x74, 0xCC, 0x88, 0xF6, 0x77, 0xCC,
+ 0x8A, 0xF6, 0x79, 0xCC, 0x8A, 0x61, 0xCA, 0xBE,
+ 0xF5, 0x05, 0xC5, 0xBF, 0xCC, 0x87, 0x73, 0xCC,
+ 0x87, 0xF6, 0x41, 0xCC, 0xA3, 0xF6, 0x61, 0xCC,
+ 0xA3, 0xF6, 0x41, 0xCC, 0x89, 0xF6, 0x61, 0xCC,
+ 0x89, 0xF6, 0x41, 0xCC, 0x82, 0xCC, 0x81, 0xF6,
+ 0x61, 0xCC, 0x82, 0xCC, 0x81, 0xF6, 0x41, 0xCC,
+ 0x82, 0xCC, 0x80, 0xF6, 0x61, 0xCC, 0x82, 0xCC,
+ 0x80, 0xF6, 0x41, 0xCC, 0x82, 0xCC, 0x89, 0xF6,
+ 0x61, 0xCC, 0x82, 0xCC, 0x89, 0xF6, 0x41, 0xCC,
+ 0x82, 0xCC, 0x83, 0xF6, 0x61, 0xCC, 0x82, 0xCC,
+ 0x83, 0xF6, 0x41, 0xCC, 0xA3, 0xCC, 0x82, 0xF6,
+ 0x61, 0xCC, 0xA3, 0xCC, 0x82, 0xF6, 0x41, 0xCC,
+ 0x86, 0xCC, 0x81, 0xF6, 0x61, 0xCC, 0x86, 0xCC,
+ 0x81, 0xF6, 0x41, 0xCC, 0x86, 0xCC, 0x80, 0xF6,
+ 0x61, 0xCC, 0x86, 0xCC, 0x80, 0xF6, 0x41, 0xCC,
+ 0x86, 0xCC, 0x89, 0xF6, 0x61, 0xCC, 0x86, 0xCC,
+ 0x89, 0xF6, 0x41, 0xCC, 0x86, 0xCC, 0x83, 0xF6,
+ 0x61, 0xCC, 0x86, 0xCC, 0x83, 0xF6, 0x41, 0xCC,
+ 0xA3, 0xCC, 0x86, 0xF6, 0x61, 0xCC, 0xA3, 0xCC,
+ 0x86, 0xF6, 0x45, 0xCC, 0xA3, 0xF6, 0x65, 0xCC,
+ 0xA3, 0xF6, 0x45, 0xCC, 0x89, 0xF6, 0x65, 0xCC,
+ 0x89, 0xF6, 0x45, 0xCC, 0x83, 0xF6, 0x65, 0xCC,
+ 0x83, 0xF6, 0x45, 0xCC, 0x82, 0xCC, 0x81, 0xF6,
+ 0x65, 0xCC, 0x82, 0xCC, 0x81, 0xF6, 0x45, 0xCC,
+ 0x82, 0xCC, 0x80, 0xF6, 0x65, 0xCC, 0x82, 0xCC,
+ 0x80, 0xF6, 0x45, 0xCC, 0x82, 0xCC, 0x89, 0xF6,
+ 0x65, 0xCC, 0x82, 0xCC, 0x89, 0xF6, 0x45, 0xCC,
+ 0x82, 0xCC, 0x83, 0xF6, 0x65, 0xCC, 0x82, 0xCC,
+ 0x83, 0xF6, 0x45, 0xCC, 0xA3, 0xCC, 0x82, 0xF6,
+ 0x65, 0xCC, 0xA3, 0xCC, 0x82, 0xF6, 0x49, 0xCC,
+ 0x89, 0xF6, 0x69, 0xCC, 0x89, 0xF6, 0x49, 0xCC,
+ 0xA3, 0xF6, 0x69, 0xCC, 0xA3, 0xF6, 0x4F, 0xCC,
+ 0xA3, 0xF6, 0x6F, 0xCC, 0xA3, 0xF6, 0x4F, 0xCC,
+ 0x89, 0xF6, 0x6F, 0xCC, 0x89, 0xF6, 0x4F, 0xCC,
+ 0x82, 0xCC, 0x81, 0xF6, 0x6F, 0xCC, 0x82, 0xCC,
+ 0x81, 0xF6, 0x4F, 0xCC, 0x82, 0xCC, 0x80, 0xF6,
+ 0x6F, 0xCC, 0x82, 0xCC, 0x80, 0xF6, 0x4F, 0xCC,
+ 0x82, 0xCC, 0x89, 0xF6, 0x6F, 0xCC, 0x82, 0xCC,
+ 0x89, 0xF6, 0x4F, 0xCC, 0x82, 0xCC, 0x83, 0xF6,
+ 0x6F, 0xCC, 0x82, 0xCC, 0x83, 0xF6, 0x4F, 0xCC,
+ 0xA3, 0xCC, 0x82, 0xF6, 0x6F, 0xCC, 0xA3, 0xCC,
+ 0x82, 0xF6, 0x4F, 0xCC, 0x9B, 0xCC, 0x81, 0xF6,
+ 0x6F, 0xCC, 0x9B, 0xCC, 0x81, 0xF6, 0x4F, 0xCC,
+ 0x9B, 0xCC, 0x80, 0xF6, 0x6F, 0xCC, 0x9B, 0xCC,
+ 0x80, 0xF6, 0x4F, 0xCC, 0x9B, 0xCC, 0x89, 0xF6,
+ 0x6F, 0xCC, 0x9B, 0xCC, 0x89, 0xF6, 0x4F, 0xCC,
+ 0x9B, 0xCC, 0x83, 0xF6, 0x6F, 0xCC, 0x9B, 0xCC,
+ 0x83, 0xF6, 0x4F, 0xCC, 0x9B, 0xCC, 0xA3, 0xF6,
+ 0x6F, 0xCC, 0x9B, 0xCC, 0xA3, 0xF6, 0x55, 0xCC,
+ 0xA3, 0xF6, 0x75, 0xCC, 0xA3, 0xF6, 0x55, 0xCC,
+ 0x89, 0xF6, 0x75, 0xCC, 0x89, 0xF6, 0x55, 0xCC,
+ 0x9B, 0xCC, 0x81, 0xF6, 0x75, 0xCC, 0x9B, 0xCC,
+ 0x81, 0xF6, 0x55, 0xCC, 0x9B, 0xCC, 0x80, 0xF6,
+ 0x75, 0xCC, 0x9B, 0xCC, 0x80, 0xF6, 0x55, 0xCC,
+ 0x9B, 0xCC, 0x89, 0xF6, 0x75, 0xCC, 0x9B, 0xCC,
+ 0x89, 0xF6, 0x55, 0xCC, 0x9B, 0xCC, 0x83, 0xF6,
+ 0x75, 0xCC, 0x9B, 0xCC, 0x83, 0xF6, 0x55, 0xCC,
+ 0x9B, 0xCC, 0xA3, 0xF6, 0x75, 0xCC, 0x9B, 0xCC,
+ 0xA3, 0xF6, 0x59, 0xCC, 0x80, 0xF6, 0x79, 0xCC,
+ 0x80, 0xF6, 0x59, 0xCC, 0xA3, 0xF6, 0x79, 0xCC,
+ 0xA3, 0xF6, 0x59, 0xCC, 0x89, 0xF6, 0x79, 0xCC,
+ 0x89, 0xF6, 0x59, 0xCC, 0x83, 0xF6, 0x79, 0xCC,
+ 0x83, 0xF6, 0xCE, 0xB1, 0xCC, 0x93, 0xF6, 0xCE,
+ 0xB1, 0xCC, 0x94, 0xF6, 0xCE, 0xB1, 0xCC, 0x93,
+ 0xCC, 0x80, 0xF6, 0xCE, 0xB1, 0xCC, 0x94, 0xCC,
+ 0x80, 0xF6, 0xCE, 0xB1, 0xCC, 0x93, 0xCC, 0x81,
+ 0xF6, 0xCE, 0xB1, 0xCC, 0x94, 0xCC, 0x81, 0xF6,
+ 0xCE, 0xB1, 0xCC, 0x93, 0xCD, 0x82, 0xF6, 0xCE,
+ 0xB1, 0xCC, 0x94, 0xCD, 0x82, 0xF6, 0xCE, 0x91,
+ 0xCC, 0x93, 0xF6, 0xCE, 0x91, 0xCC, 0x94, 0xF6,
+ 0xCE, 0x91, 0xCC, 0x93, 0xCC, 0x80, 0xF6, 0xCE,
+ 0x91, 0xCC, 0x94, 0xCC, 0x80, 0xF6, 0xCE, 0x91,
+ 0xCC, 0x93, 0xCC, 0x81, 0xF6, 0xCE, 0x91, 0xCC,
+ 0x94, 0xCC, 0x81, 0xF6, 0xCE, 0x91, 0xCC, 0x93,
+ 0xCD, 0x82, 0xF6, 0xCE, 0x91, 0xCC, 0x94, 0xCD,
+ 0x82, 0xF6, 0xCE, 0xB5, 0xCC, 0x93, 0xF6, 0xCE,
+ 0xB5, 0xCC, 0x94, 0xF6, 0xCE, 0xB5, 0xCC, 0x93,
+ 0xCC, 0x80, 0xF6, 0xCE, 0xB5, 0xCC, 0x94, 0xCC,
+ 0x80, 0xF6, 0xCE, 0xB5, 0xCC, 0x93, 0xCC, 0x81,
+ 0xF6, 0xCE, 0xB5, 0xCC, 0x94, 0xCC, 0x81, 0xF6,
+ 0xCE, 0x95, 0xCC, 0x93, 0xF6, 0xCE, 0x95, 0xCC,
+ 0x94, 0xF6, 0xCE, 0x95, 0xCC, 0x93, 0xCC, 0x80,
+ 0xF6, 0xCE, 0x95, 0xCC, 0x94, 0xCC, 0x80, 0xF6,
+ 0xCE, 0x95, 0xCC, 0x93, 0xCC, 0x81, 0xF6, 0xCE,
+ 0x95, 0xCC, 0x94, 0xCC, 0x81, 0xF6, 0xCE, 0xB7,
+ 0xCC, 0x93, 0xF6, 0xCE, 0xB7, 0xCC, 0x94, 0xF6,
+ 0xCE, 0xB7, 0xCC, 0x93, 0xCC, 0x80, 0xF6, 0xCE,
+ 0xB7, 0xCC, 0x94, 0xCC, 0x80, 0xF6, 0xCE, 0xB7,
+ 0xCC, 0x93, 0xCC, 0x81, 0xF6, 0xCE, 0xB7, 0xCC,
+ 0x94, 0xCC, 0x81, 0xF6, 0xCE, 0xB7, 0xCC, 0x93,
+ 0xCD, 0x82, 0xF6, 0xCE, 0xB7, 0xCC, 0x94, 0xCD,
+ 0x82, 0xF6, 0xCE, 0x97, 0xCC, 0x93, 0xF6, 0xCE,
+ 0x97, 0xCC, 0x94, 0xF6, 0xCE, 0x97, 0xCC, 0x93,
+ 0xCC, 0x80, 0xF6, 0xCE, 0x97, 0xCC, 0x94, 0xCC,
+ 0x80, 0xF6, 0xCE, 0x97, 0xCC, 0x93, 0xCC, 0x81,
+ 0xF6, 0xCE, 0x97, 0xCC, 0x94, 0xCC, 0x81, 0xF6,
+ 0xCE, 0x97, 0xCC, 0x93, 0xCD, 0x82, 0xF6, 0xCE,
+ 0x97, 0xCC, 0x94, 0xCD, 0x82, 0xF6, 0xCE, 0xB9,
+ 0xCC, 0x93, 0xF6, 0xCE, 0xB9, 0xCC, 0x94, 0xF6,
+ 0xCE, 0xB9, 0xCC, 0x93, 0xCC, 0x80, 0xF6, 0xCE,
+ 0xB9, 0xCC, 0x94, 0xCC, 0x80, 0xF6, 0xCE, 0xB9,
+ 0xCC, 0x93, 0xCC, 0x81, 0xF6, 0xCE, 0xB9, 0xCC,
+ 0x94, 0xCC, 0x81, 0xF6, 0xCE, 0xB9, 0xCC, 0x93,
+ 0xCD, 0x82, 0xF6, 0xCE, 0xB9, 0xCC, 0x94, 0xCD,
+ 0x82, 0xF6, 0xCE, 0x99, 0xCC, 0x93, 0xF6, 0xCE,
+ 0x99, 0xCC, 0x94, 0xF6, 0xCE, 0x99, 0xCC, 0x93,
+ 0xCC, 0x80, 0xF6, 0xCE, 0x99, 0xCC, 0x94, 0xCC,
+ 0x80, 0xF6, 0xCE, 0x99, 0xCC, 0x93, 0xCC, 0x81,
+ 0xF6, 0xCE, 0x99, 0xCC, 0x94, 0xCC, 0x81, 0xF6,
+ 0xCE, 0x99, 0xCC, 0x93, 0xCD, 0x82, 0xF6, 0xCE,
+ 0x99, 0xCC, 0x94, 0xCD, 0x82, 0xF6, 0xCE, 0xBF,
+ 0xCC, 0x93, 0xF6, 0xCE, 0xBF, 0xCC, 0x94, 0xF6,
+ 0xCE, 0xBF, 0xCC, 0x93, 0xCC, 0x80, 0xF6, 0xCE,
+ 0xBF, 0xCC, 0x94, 0xCC, 0x80, 0xF6, 0xCE, 0xBF,
+ 0xCC, 0x93, 0xCC, 0x81, 0xF6, 0xCE, 0xBF, 0xCC,
+ 0x94, 0xCC, 0x81, 0xF6, 0xCE, 0x9F, 0xCC, 0x93,
+ 0xF6, 0xCE, 0x9F, 0xCC, 0x94, 0xF6, 0xCE, 0x9F,
+ 0xCC, 0x93, 0xCC, 0x80, 0xF6, 0xCE, 0x9F, 0xCC,
+ 0x94, 0xCC, 0x80, 0xF6, 0xCE, 0x9F, 0xCC, 0x93,
+ 0xCC, 0x81, 0xF6, 0xCE, 0x9F, 0xCC, 0x94, 0xCC,
+ 0x81, 0xF6, 0xCF, 0x85, 0xCC, 0x93, 0xF6, 0xCF,
+ 0x85, 0xCC, 0x94, 0xF6, 0xCF, 0x85, 0xCC, 0x93,
+ 0xCC, 0x80, 0xF6, 0xCF, 0x85, 0xCC, 0x94, 0xCC,
+ 0x80, 0xF6, 0xCF, 0x85, 0xCC, 0x93, 0xCC, 0x81,
+ 0xF6, 0xCF, 0x85, 0xCC, 0x94, 0xCC, 0x81, 0xF6,
+ 0xCF, 0x85, 0xCC, 0x93, 0xCD, 0x82, 0xF6, 0xCF,
+ 0x85, 0xCC, 0x94, 0xCD, 0x82, 0xF6, 0xCE, 0xA5,
+ 0xCC, 0x94, 0xF6, 0xCE, 0xA5, 0xCC, 0x94, 0xCC,
+ 0x80, 0xF6, 0xCE, 0xA5, 0xCC, 0x94, 0xCC, 0x81,
+ 0xF6, 0xCE, 0xA5, 0xCC, 0x94, 0xCD, 0x82, 0xF6,
+ 0xCF, 0x89, 0xCC, 0x93, 0xF6, 0xCF, 0x89, 0xCC,
+ 0x94, 0xF6, 0xCF, 0x89, 0xCC, 0x93, 0xCC, 0x80,
+ 0xF6, 0xCF, 0x89, 0xCC, 0x94, 0xCC, 0x80, 0xF6,
+ 0xCF, 0x89, 0xCC, 0x93, 0xCC, 0x81, 0xF6, 0xCF,
+ 0x89, 0xCC, 0x94, 0xCC, 0x81, 0xF6, 0xCF, 0x89,
+ 0xCC, 0x93, 0xCD, 0x82, 0xF6, 0xCF, 0x89, 0xCC,
+ 0x94, 0xCD, 0x82, 0xF6, 0xCE, 0xA9, 0xCC, 0x93,
+ 0xF6, 0xCE, 0xA9, 0xCC, 0x94, 0xF6, 0xCE, 0xA9,
+ 0xCC, 0x93, 0xCC, 0x80, 0xF6, 0xCE, 0xA9, 0xCC,
+ 0x94, 0xCC, 0x80, 0xF6, 0xCE, 0xA9, 0xCC, 0x93,
+ 0xCC, 0x81, 0xF6, 0xCE, 0xA9, 0xCC, 0x94, 0xCC,
+ 0x81, 0xF6, 0xCE, 0xA9, 0xCC, 0x93, 0xCD, 0x82,
+ 0xF6, 0xCE, 0xA9, 0xCC, 0x94, 0xCD, 0x82, 0xF6,
+ 0xCE, 0xB1, 0xCC, 0x80, 0xF6, 0xCE, 0xB1, 0xCC,
+ 0x81, 0xF6, 0xCE, 0xB5, 0xCC, 0x80, 0xF6, 0xCE,
+ 0xB5, 0xCC, 0x81, 0xF6, 0xCE, 0xB7, 0xCC, 0x80,
+ 0xF6, 0xCE, 0xB7, 0xCC, 0x81, 0xF6, 0xCE, 0xB9,
+ 0xCC, 0x80, 0xF6, 0xCE, 0xB9, 0xCC, 0x81, 0xF6,
+ 0xCE, 0xBF, 0xCC, 0x80, 0xF6, 0xCE, 0xBF, 0xCC,
+ 0x81, 0xF6, 0xCF, 0x85, 0xCC, 0x80, 0xF6, 0xCF,
+ 0x85, 0xCC, 0x81, 0xF6, 0xCF, 0x89, 0xCC, 0x80,
+ 0xF6, 0xCF, 0x89, 0xCC, 0x81, 0xF6, 0xCE, 0xB1,
+ 0xCC, 0x93, 0xCD, 0x85, 0xF6, 0xCE, 0xB1, 0xCC,
+ 0x94, 0xCD, 0x85, 0xF6, 0xCE, 0xB1, 0xCC, 0x93,
+ 0xCC, 0x80, 0xCD, 0x85, 0xF6, 0xCE, 0xB1, 0xCC,
+ 0x94, 0xCC, 0x80, 0xCD, 0x85, 0xF6, 0xCE, 0xB1,
+ 0xCC, 0x93, 0xCC, 0x81, 0xCD, 0x85, 0xF6, 0xCE,
+ 0xB1, 0xCC, 0x94, 0xCC, 0x81, 0xCD, 0x85, 0xF6,
+ 0xCE, 0xB1, 0xCC, 0x93, 0xCD, 0x82, 0xCD, 0x85,
+ 0xF6, 0xCE, 0xB1, 0xCC, 0x94, 0xCD, 0x82, 0xCD,
+ 0x85, 0xF6, 0xCE, 0x91, 0xCC, 0x93, 0xCD, 0x85,
+ 0xF6, 0xCE, 0x91, 0xCC, 0x94, 0xCD, 0x85, 0xF6,
+ 0xCE, 0x91, 0xCC, 0x93, 0xCC, 0x80, 0xCD, 0x85,
+ 0xF6, 0xCE, 0x91, 0xCC, 0x94, 0xCC, 0x80, 0xCD,
+ 0x85, 0xF6, 0xCE, 0x91, 0xCC, 0x93, 0xCC, 0x81,
+ 0xCD, 0x85, 0xF6, 0xCE, 0x91, 0xCC, 0x94, 0xCC,
+ 0x81, 0xCD, 0x85, 0xF6, 0xCE, 0x91, 0xCC, 0x93,
+ 0xCD, 0x82, 0xCD, 0x85, 0xF6, 0xCE, 0x91, 0xCC,
+ 0x94, 0xCD, 0x82, 0xCD, 0x85, 0xF6, 0xCE, 0xB7,
+ 0xCC, 0x93, 0xCD, 0x85, 0xF6, 0xCE, 0xB7, 0xCC,
+ 0x94, 0xCD, 0x85, 0xF6, 0xCE, 0xB7, 0xCC, 0x93,
+ 0xCC, 0x80, 0xCD, 0x85, 0xF6, 0xCE, 0xB7, 0xCC,
+ 0x94, 0xCC, 0x80, 0xCD, 0x85, 0xF6, 0xCE, 0xB7,
+ 0xCC, 0x93, 0xCC, 0x81, 0xCD, 0x85, 0xF6, 0xCE,
+ 0xB7, 0xCC, 0x94, 0xCC, 0x81, 0xCD, 0x85, 0xF6,
+ 0xCE, 0xB7, 0xCC, 0x93, 0xCD, 0x82, 0xCD, 0x85,
+ 0xF6, 0xCE, 0xB7, 0xCC, 0x94, 0xCD, 0x82, 0xCD,
+ 0x85, 0xF6, 0xCE, 0x97, 0xCC, 0x93, 0xCD, 0x85,
+ 0xF6, 0xCE, 0x97, 0xCC, 0x94, 0xCD, 0x85, 0xF6,
+ 0xCE, 0x97, 0xCC, 0x93, 0xCC, 0x80, 0xCD, 0x85,
+ 0xF6, 0xCE, 0x97, 0xCC, 0x94, 0xCC, 0x80, 0xCD,
+ 0x85, 0xF6, 0xCE, 0x97, 0xCC, 0x93, 0xCC, 0x81,
+ 0xCD, 0x85, 0xF6, 0xCE, 0x97, 0xCC, 0x94, 0xCC,
+ 0x81, 0xCD, 0x85, 0xF6, 0xCE, 0x97, 0xCC, 0x93,
+ 0xCD, 0x82, 0xCD, 0x85, 0xF6, 0xCE, 0x97, 0xCC,
+ 0x94, 0xCD, 0x82, 0xCD, 0x85, 0xF6, 0xCF, 0x89,
+ 0xCC, 0x93, 0xCD, 0x85, 0xF6, 0xCF, 0x89, 0xCC,
+ 0x94, 0xCD, 0x85, 0xF6, 0xCF, 0x89, 0xCC, 0x93,
+ 0xCC, 0x80, 0xCD, 0x85, 0xF6, 0xCF, 0x89, 0xCC,
+ 0x94, 0xCC, 0x80, 0xCD, 0x85, 0xF6, 0xCF, 0x89,
+ 0xCC, 0x93, 0xCC, 0x81, 0xCD, 0x85, 0xF6, 0xCF,
+ 0x89, 0xCC, 0x94, 0xCC, 0x81, 0xCD, 0x85, 0xF6,
+ 0xCF, 0x89, 0xCC, 0x93, 0xCD, 0x82, 0xCD, 0x85,
+ 0xF6, 0xCF, 0x89, 0xCC, 0x94, 0xCD, 0x82, 0xCD,
+ 0x85, 0xF6, 0xCE, 0xA9, 0xCC, 0x93, 0xCD, 0x85,
+ 0xF6, 0xCE, 0xA9, 0xCC, 0x94, 0xCD, 0x85, 0xF6,
+ 0xCE, 0xA9, 0xCC, 0x93, 0xCC, 0x80, 0xCD, 0x85,
+ 0xF6, 0xCE, 0xA9, 0xCC, 0x94, 0xCC, 0x80, 0xCD,
+ 0x85, 0xF6, 0xCE, 0xA9, 0xCC, 0x93, 0xCC, 0x81,
+ 0xCD, 0x85, 0xF6, 0xCE, 0xA9, 0xCC, 0x94, 0xCC,
+ 0x81, 0xCD, 0x85, 0xF6, 0xCE, 0xA9, 0xCC, 0x93,
+ 0xCD, 0x82, 0xCD, 0x85, 0xF6, 0xCE, 0xA9, 0xCC,
+ 0x94, 0xCD, 0x82, 0xCD, 0x85, 0xF6, 0xCE, 0xB1,
+ 0xCC, 0x86, 0xF6, 0xCE, 0xB1, 0xCC, 0x84, 0xF6,
+ 0xCE, 0xB1, 0xCC, 0x80, 0xCD, 0x85, 0xF6, 0xCE,
+ 0xB1, 0xCD, 0x85, 0xF6, 0xCE, 0xB1, 0xCC, 0x81,
+ 0xCD, 0x85, 0xF6, 0xCE, 0xB1, 0xCD, 0x82, 0xF6,
+ 0xCE, 0xB1, 0xCD, 0x82, 0xCD, 0x85, 0xF6, 0xCE,
+ 0x91, 0xCC, 0x86, 0xF6, 0xCE, 0x91, 0xCC, 0x84,
+ 0xF6, 0xCE, 0x91, 0xCC, 0x80, 0xF6, 0xCE, 0x91,
+ 0xCC, 0x81, 0xF6, 0xCE, 0x91, 0xCD, 0x85, 0x20,
+ 0xCC, 0x93, 0xF6, 0xCE, 0xB9, 0x20, 0xCC, 0x93,
+ 0x20, 0xCD, 0x82, 0xF5, 0x05, 0xC2, 0xA8, 0xCD,
+ 0x82, 0x20, 0xCC, 0x88, 0xCD, 0x82, 0xF6, 0xCE,
+ 0xB7, 0xCC, 0x80, 0xCD, 0x85, 0xF6, 0xCE, 0xB7,
+ 0xCD, 0x85, 0xF6, 0xCE, 0xB7, 0xCC, 0x81, 0xCD,
+ 0x85, 0xF6, 0xCE, 0xB7, 0xCD, 0x82, 0xF6, 0xCE,
+ 0xB7, 0xCD, 0x82, 0xCD, 0x85, 0xF6, 0xCE, 0x95,
+ 0xCC, 0x80, 0xF6, 0xCE, 0x95, 0xCC, 0x81, 0xF6,
+ 0xCE, 0x97, 0xCC, 0x80, 0xF6, 0xCE, 0x97, 0xCC,
+ 0x81, 0xF6, 0xCE, 0x97, 0xCD, 0x85, 0xF5, 0x06,
+ 0xE1, 0xBE, 0xBF, 0xCC, 0x80, 0x20, 0xCC, 0x93,
+ 0xCC, 0x80, 0xF5, 0x06, 0xE1, 0xBE, 0xBF, 0xCC,
+ 0x81, 0x20, 0xCC, 0x93, 0xCC, 0x81, 0xF5, 0x06,
+ 0xE1, 0xBE, 0xBF, 0xCD, 0x82, 0x20, 0xCC, 0x93,
+ 0xCD, 0x82, 0xF6, 0xCE, 0xB9, 0xCC, 0x86, 0xF6,
+ 0xCE, 0xB9, 0xCC, 0x84, 0xF6, 0xCE, 0xB9, 0xCC,
+ 0x88, 0xCC, 0x80, 0xF6, 0xCE, 0xB9, 0xCC, 0x88,
+ 0xCC, 0x81, 0xF6, 0xCE, 0xB9, 0xCD, 0x82, 0xF6,
+ 0xCE, 0xB9, 0xCC, 0x88, 0xCD, 0x82, 0xF6, 0xCE,
+ 0x99, 0xCC, 0x86, 0xF6, 0xCE, 0x99, 0xCC, 0x84,
+ 0xF6, 0xCE, 0x99, 0xCC, 0x80, 0xF6, 0xCE, 0x99,
+ 0xCC, 0x81, 0xF5, 0x06, 0xE1, 0xBF, 0xBE, 0xCC,
+ 0x80, 0x20, 0xCC, 0x94, 0xCC, 0x80, 0xF5, 0x06,
+ 0xE1, 0xBF, 0xBE, 0xCC, 0x81, 0x20, 0xCC, 0x94,
+ 0xCC, 0x81, 0xF5, 0x06, 0xE1, 0xBF, 0xBE, 0xCD,
+ 0x82, 0x20, 0xCC, 0x94, 0xCD, 0x82, 0xF6, 0xCF,
+ 0x85, 0xCC, 0x86, 0xF6, 0xCF, 0x85, 0xCC, 0x84,
+ 0xF6, 0xCF, 0x85, 0xCC, 0x88, 0xCC, 0x80, 0xF6,
+ 0xCF, 0x85, 0xCC, 0x88, 0xCC, 0x81, 0xF6, 0xCF,
+ 0x81, 0xCC, 0x93, 0xF6, 0xCF, 0x81, 0xCC, 0x94,
+ 0xF6, 0xCF, 0x85, 0xCD, 0x82, 0xF6, 0xCF, 0x85,
+ 0xCC, 0x88, 0xCD, 0x82, 0xF6, 0xCE, 0xA5, 0xCC,
+ 0x86, 0xF6, 0xCE, 0xA5, 0xCC, 0x84, 0xF6, 0xCE,
+ 0xA5, 0xCC, 0x80, 0xF6, 0xCE, 0xA5, 0xCC, 0x81,
+ 0xF6, 0xCE, 0xA1, 0xCC, 0x94, 0xF5, 0x05, 0xC2,
+ 0xA8, 0xCC, 0x80, 0x20, 0xCC, 0x88, 0xCC, 0x80,
+ 0xF5, 0x05, 0xC2, 0xA8, 0xCC, 0x81, 0x20, 0xCC,
+ 0x88, 0xCC, 0x81, 0xF6, 0x60, 0xF6, 0xCF, 0x89,
+ 0xCC, 0x80, 0xCD, 0x85, 0xF6, 0xCF, 0x89, 0xCD,
+ 0x85, 0xF6, 0xCF, 0x89, 0xCC, 0x81, 0xCD, 0x85,
+ 0xF6, 0xCF, 0x89, 0xCD, 0x82, 0xF6, 0xCF, 0x89,
+ 0xCD, 0x82, 0xCD, 0x85, 0xF6, 0xCE, 0x9F, 0xCC,
+ 0x80, 0xF6, 0xCE, 0x9F, 0xCC, 0x81, 0xF6, 0xCE,
+ 0xA9, 0xCC, 0x80, 0xF6, 0xCE, 0xA9, 0xCC, 0x81,
+ 0xF6, 0xCE, 0xA9, 0xCD, 0x85, 0xF5, 0x03, 0xC2,
+ 0xB4, 0x20, 0xCC, 0x81, 0x20, 0xCC, 0x94, 0xF5,
+ 0x04, 0xE2, 0x80, 0x82, 0x20, 0xF5, 0x04, 0xE2,
+ 0x80, 0x83, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0xE2, 0x80, 0x90, 0x20,
+ 0xCC, 0xB3, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+ 0x20, 0xE2, 0x80, 0xB2, 0xE2, 0x80, 0xB2, 0xE2,
+ 0x80, 0xB2, 0xE2, 0x80, 0xB2, 0xE2, 0x80, 0xB2,
+ 0xE2, 0x80, 0xB5, 0xE2, 0x80, 0xB5, 0xE2, 0x80,
+ 0xB5, 0xE2, 0x80, 0xB5, 0xE2, 0x80, 0xB5, 0x21,
+ 0x21, 0x20, 0xCC, 0x85, 0x3F, 0x3F, 0x3F, 0x21,
+ 0x21, 0x3F, 0xE2, 0x80, 0xB2, 0xE2, 0x80, 0xB2,
+ 0xE2, 0x80, 0xB2, 0xE2, 0x80, 0xB2, 0x20, 0x30,
+ 0x69, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2B,
+ 0xE2, 0x88, 0x92, 0x3D, 0x28, 0x29, 0x6E, 0x30,
+ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+ 0x39, 0x2B, 0xE2, 0x88, 0x92, 0x3D, 0x28, 0x29,
+ 0x52, 0x73, 0x61, 0x2F, 0x63, 0x61, 0x2F, 0x73,
+ 0x43, 0xC2, 0xB0, 0x43, 0x63, 0x2F, 0x6F, 0x63,
+ 0x2F, 0x75, 0xC6, 0x90, 0xC2, 0xB0, 0x46, 0x67,
+ 0x48, 0x48, 0x48, 0x68, 0xC4, 0xA7, 0x49, 0x49,
+ 0x4C, 0x6C, 0x4E, 0x4E, 0x6F, 0x50, 0x51, 0x52,
+ 0x52, 0x52, 0x53, 0x4D, 0x54, 0x45, 0x4C, 0x54,
+ 0x4D, 0x5A, 0xF6, 0xCE, 0xA9, 0x5A, 0xF6, 0x4B,
+ 0xF6, 0x41, 0xCC, 0x8A, 0x42, 0x43, 0x65, 0x45,
+ 0x46, 0x4D, 0x6F, 0xD7, 0x90, 0xD7, 0x91, 0xD7,
+ 0x92, 0xD7, 0x93, 0x69, 0xCE, 0xB3, 0xCE, 0x93,
+ 0xCE, 0xA0, 0xE2, 0x88, 0x91, 0x44, 0x64, 0x65,
+ 0x69, 0x6A, 0x31, 0xE2, 0x81, 0x84, 0x33, 0x32,
+ 0xE2, 0x81, 0x84, 0x33, 0x31, 0xE2, 0x81, 0x84,
+ 0x35, 0x32, 0xE2, 0x81, 0x84, 0x35, 0x33, 0xE2,
+ 0x81, 0x84, 0x35, 0x34, 0xE2, 0x81, 0x84, 0x35,
+ 0x31, 0xE2, 0x81, 0x84, 0x36, 0x35, 0xE2, 0x81,
+ 0x84, 0x36, 0x31, 0xE2, 0x81, 0x84, 0x38, 0x33,
+ 0xE2, 0x81, 0x84, 0x38, 0x35, 0xE2, 0x81, 0x84,
+ 0x38, 0x37, 0xE2, 0x81, 0x84, 0x38, 0x31, 0xE2,
+ 0x81, 0x84, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
+ 0x49, 0x56, 0x56, 0x56, 0x49, 0x56, 0x49, 0x49,
+ 0x56, 0x49, 0x49, 0x49, 0x49, 0x58, 0x58, 0x58,
+ 0x49, 0x58, 0x49, 0x49, 0x4C, 0x43, 0x44, 0x4D,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x76,
+ 0x76, 0x76, 0x69, 0x76, 0x69, 0x69, 0x76, 0x69,
+ 0x69, 0x69, 0x69, 0x78, 0x78, 0x78, 0x69, 0x78,
+ 0x69, 0x69, 0x6C, 0x63, 0x64, 0x6D, 0xF6, 0xE2,
+ 0x86, 0x90, 0xCC, 0xB8, 0xF6, 0xE2, 0x86, 0x92,
+ 0xCC, 0xB8, 0xF6, 0xE2, 0x86, 0x94, 0xCC, 0xB8,
+ 0xF6, 0xE2, 0x87, 0x90, 0xCC, 0xB8, 0xF6, 0xE2,
+ 0x87, 0x94, 0xCC, 0xB8, 0xF6, 0xE2, 0x87, 0x92,
+ 0xCC, 0xB8, 0xF6, 0xE2, 0x88, 0x83, 0xCC, 0xB8,
+ 0xF6, 0xE2, 0x88, 0x88, 0xCC, 0xB8, 0xF6, 0xE2,
+ 0x88, 0x8B, 0xCC, 0xB8, 0xF6, 0xE2, 0x88, 0xA3,
+ 0xCC, 0xB8, 0xF6, 0xE2, 0x88, 0xA5, 0xCC, 0xB8,
+ 0xE2, 0x88, 0xAB, 0xE2, 0x88, 0xAB, 0xE2, 0x88,
+ 0xAB, 0xE2, 0x88, 0xAB, 0xE2, 0x88, 0xAB, 0xE2,
+ 0x88, 0xAE, 0xE2, 0x88, 0xAE, 0xE2, 0x88, 0xAE,
+ 0xE2, 0x88, 0xAE, 0xE2, 0x88, 0xAE, 0xF6, 0xE2,
+ 0x88, 0xBC, 0xCC, 0xB8, 0xF6, 0xE2, 0x89, 0x83,
+ 0xCC, 0xB8, 0xF6, 0xE2, 0x89, 0x85, 0xCC, 0xB8,
+ 0xF6, 0xE2, 0x89, 0x88, 0xCC, 0xB8, 0xF6, 0x3D,
+ 0xCC, 0xB8, 0xF6, 0xE2, 0x89, 0xA1, 0xCC, 0xB8,
+ 0xF6, 0xE2, 0x89, 0x8D, 0xCC, 0xB8, 0xF6, 0x3C,
+ 0xCC, 0xB8, 0xF6, 0x3E, 0xCC, 0xB8, 0xF6, 0xE2,
+ 0x89, 0xA4, 0xCC, 0xB8, 0xF6, 0xE2, 0x89, 0xA5,
+ 0xCC, 0xB8, 0xF6, 0xE2, 0x89, 0xB2, 0xCC, 0xB8,
+ 0xF6, 0xE2, 0x89, 0xB3, 0xCC, 0xB8, 0xF6, 0xE2,
+ 0x89, 0xB6, 0xCC, 0xB8, 0xF6, 0xE2, 0x89, 0xB7,
+ 0xCC, 0xB8, 0xF6, 0xE2, 0x89, 0xBA, 0xCC, 0xB8,
+ 0xF6, 0xE2, 0x89, 0xBB, 0xCC, 0xB8, 0xF6, 0xE2,
+ 0x8A, 0x82, 0xCC, 0xB8, 0xF6, 0xE2, 0x8A, 0x83,
+ 0xCC, 0xB8, 0xF6, 0xE2, 0x8A, 0x86, 0xCC, 0xB8,
+ 0xF6, 0xE2, 0x8A, 0x87, 0xCC, 0xB8, 0xF6, 0xE2,
+ 0x8A, 0xA2, 0xCC, 0xB8, 0xF6, 0xE2, 0x8A, 0xA8,
+ 0xCC, 0xB8, 0xF6, 0xE2, 0x8A, 0xA9, 0xCC, 0xB8,
+ 0xF6, 0xE2, 0x8A, 0xAB, 0xCC, 0xB8, 0xF6, 0xE2,
+ 0x89, 0xBC, 0xCC, 0xB8, 0xF6, 0xE2, 0x89, 0xBD,
+ 0xCC, 0xB8, 0xF6, 0xE2, 0x8A, 0x91, 0xCC, 0xB8,
+ 0xF6, 0xE2, 0x8A, 0x92, 0xCC, 0xB8, 0xF6, 0xE2,
+ 0x8A, 0xB2, 0xCC, 0xB8, 0xF6, 0xE2, 0x8A, 0xB3,
+ 0xCC, 0xB8, 0xF6, 0xE2, 0x8A, 0xB4, 0xCC, 0xB8,
+ 0xF6, 0xE2, 0x8A, 0xB5, 0xCC, 0xB8, 0xF6, 0xE3,
+ 0x80, 0x88, 0xF6, 0xE3, 0x80, 0x89, 0x31, 0x32,
+ 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x31,
+ 0x30, 0x31, 0x31, 0x31, 0x32, 0x31, 0x33, 0x31,
+ 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37, 0x31,
+ 0x38, 0x31, 0x39, 0x32, 0x30, 0x28, 0x31, 0x29,
+ 0x28, 0x32, 0x29, 0x28, 0x33, 0x29, 0x28, 0x34,
+ 0x29, 0x28, 0x35, 0x29, 0x28, 0x36, 0x29, 0x28,
+ 0x37, 0x29, 0x28, 0x38, 0x29, 0x28, 0x39, 0x29,
+ 0x28, 0x31, 0x30, 0x29, 0x28, 0x31, 0x31, 0x29,
+ 0x28, 0x31, 0x32, 0x29, 0x28, 0x31, 0x33, 0x29,
+ 0x28, 0x31, 0x34, 0x29, 0x28, 0x31, 0x35, 0x29,
+ 0x28, 0x31, 0x36, 0x29, 0x28, 0x31, 0x37, 0x29,
+ 0x28, 0x31, 0x38, 0x29, 0x28, 0x31, 0x39, 0x29,
+ 0x28, 0x32, 0x30, 0x29, 0x31, 0x2E, 0x32, 0x2E,
+ 0x33, 0x2E, 0x34, 0x2E, 0x35, 0x2E, 0x36, 0x2E,
+ 0x37, 0x2E, 0x38, 0x2E, 0x39, 0x2E, 0x31, 0x30,
+ 0x2E, 0x31, 0x31, 0x2E, 0x31, 0x32, 0x2E, 0x31,
+ 0x33, 0x2E, 0x31, 0x34, 0x2E, 0x31, 0x35, 0x2E,
+ 0x31, 0x36, 0x2E, 0x31, 0x37, 0x2E, 0x31, 0x38,
+ 0x2E, 0x31, 0x39, 0x2E, 0x32, 0x30, 0x2E, 0x28,
+ 0x61, 0x29, 0x28, 0x62, 0x29, 0x28, 0x63, 0x29,
+ 0x28, 0x64, 0x29, 0x28, 0x65, 0x29, 0x28, 0x66,
+ 0x29, 0x28, 0x67, 0x29, 0x28, 0x68, 0x29, 0x28,
+ 0x69, 0x29, 0x28, 0x6A, 0x29, 0x28, 0x6B, 0x29,
+ 0x28, 0x6C, 0x29, 0x28, 0x6D, 0x29, 0x28, 0x6E,
+ 0x29, 0x28, 0x6F, 0x29, 0x28, 0x70, 0x29, 0x28,
+ 0x71, 0x29, 0x28, 0x72, 0x29, 0x28, 0x73, 0x29,
+ 0x28, 0x74, 0x29, 0x28, 0x75, 0x29, 0x28, 0x76,
+ 0x29, 0x28, 0x77, 0x29, 0x28, 0x78, 0x29, 0x28,
+ 0x79, 0x29, 0x28, 0x7A, 0x29, 0x41, 0x42, 0x43,
+ 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B,
+ 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53,
+ 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61,
+ 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7A, 0x30, 0xE2, 0x88, 0xAB, 0xE2, 0x88, 0xAB,
+ 0xE2, 0x88, 0xAB, 0xE2, 0x88, 0xAB, 0x3A, 0x3A,
+ 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0xF6, 0xE2,
+ 0xAB, 0x9D, 0xCC, 0xB8, 0xE6, 0xAF, 0x8D, 0xE9,
+ 0xBE, 0x9F, 0xE4, 0xB8, 0x80, 0xE4, 0xB8, 0xA8,
+ 0xE4, 0xB8, 0xB6, 0xE4, 0xB8, 0xBF, 0xE4, 0xB9,
+ 0x99, 0xE4, 0xBA, 0x85, 0xE4, 0xBA, 0x8C, 0xE4,
+ 0xBA, 0xA0, 0xE4, 0xBA, 0xBA, 0xE5, 0x84, 0xBF,
+ 0xE5, 0x85, 0xA5, 0xE5, 0x85, 0xAB, 0xE5, 0x86,
+ 0x82, 0xE5, 0x86, 0x96, 0xE5, 0x86, 0xAB, 0xE5,
+ 0x87, 0xA0, 0xE5, 0x87, 0xB5, 0xE5, 0x88, 0x80,
+ 0xE5, 0x8A, 0x9B, 0xE5, 0x8B, 0xB9, 0xE5, 0x8C,
+ 0x95, 0xE5, 0x8C, 0x9A, 0xE5, 0x8C, 0xB8, 0xE5,
+ 0x8D, 0x81, 0xE5, 0x8D, 0x9C, 0xE5, 0x8D, 0xA9,
+ 0xE5, 0x8E, 0x82, 0xE5, 0x8E, 0xB6, 0xE5, 0x8F,
+ 0x88, 0xE5, 0x8F, 0xA3, 0xE5, 0x9B, 0x97, 0xE5,
+ 0x9C, 0x9F, 0xE5, 0xA3, 0xAB, 0xE5, 0xA4, 0x82,
+ 0xE5, 0xA4, 0x8A, 0xE5, 0xA4, 0x95, 0xE5, 0xA4,
+ 0xA7, 0xE5, 0xA5, 0xB3, 0xE5, 0xAD, 0x90, 0xE5,
+ 0xAE, 0x80, 0xE5, 0xAF, 0xB8, 0xE5, 0xB0, 0x8F,
+ 0xE5, 0xB0, 0xA2, 0xE5, 0xB0, 0xB8, 0xE5, 0xB1,
+ 0xAE, 0xE5, 0xB1, 0xB1, 0xE5, 0xB7, 0x9B, 0xE5,
+ 0xB7, 0xA5, 0xE5, 0xB7, 0xB1, 0xE5, 0xB7, 0xBE,
+ 0xE5, 0xB9, 0xB2, 0xE5, 0xB9, 0xBA, 0xE5, 0xB9,
+ 0xBF, 0xE5, 0xBB, 0xB4, 0xE5, 0xBB, 0xBE, 0xE5,
+ 0xBC, 0x8B, 0xE5, 0xBC, 0x93, 0xE5, 0xBD, 0x90,
+ 0xE5, 0xBD, 0xA1, 0xE5, 0xBD, 0xB3, 0xE5, 0xBF,
+ 0x83, 0xE6, 0x88, 0x88, 0xE6, 0x88, 0xB6, 0xE6,
+ 0x89, 0x8B, 0xE6, 0x94, 0xAF, 0xE6, 0x94, 0xB4,
+ 0xE6, 0x96, 0x87, 0xE6, 0x96, 0x97, 0xE6, 0x96,
+ 0xA4, 0xE6, 0x96, 0xB9, 0xE6, 0x97, 0xA0, 0xE6,
+ 0x97, 0xA5, 0xE6, 0x9B, 0xB0, 0xE6, 0x9C, 0x88,
+ 0xE6, 0x9C, 0xA8, 0xE6, 0xAC, 0xA0, 0xE6, 0xAD,
+ 0xA2, 0xE6, 0xAD, 0xB9, 0xE6, 0xAE, 0xB3, 0xE6,
+ 0xAF, 0x8B, 0xE6, 0xAF, 0x94, 0xE6, 0xAF, 0x9B,
+ 0xE6, 0xB0, 0x8F, 0xE6, 0xB0, 0x94, 0xE6, 0xB0,
+ 0xB4, 0xE7, 0x81, 0xAB, 0xE7, 0x88, 0xAA, 0xE7,
+ 0x88, 0xB6, 0xE7, 0x88, 0xBB, 0xE7, 0x88, 0xBF,
+ 0xE7, 0x89, 0x87, 0xE7, 0x89, 0x99, 0xE7, 0x89,
+ 0x9B, 0xE7, 0x8A, 0xAC, 0xE7, 0x8E, 0x84, 0xE7,
+ 0x8E, 0x89, 0xE7, 0x93, 0x9C, 0xE7, 0x93, 0xA6,
+ 0xE7, 0x94, 0x98, 0xE7, 0x94, 0x9F, 0xE7, 0x94,
+ 0xA8, 0xE7, 0x94, 0xB0, 0xE7, 0x96, 0x8B, 0xE7,
+ 0x96, 0x92, 0xE7, 0x99, 0xB6, 0xE7, 0x99, 0xBD,
+ 0xE7, 0x9A, 0xAE, 0xE7, 0x9A, 0xBF, 0xE7, 0x9B,
+ 0xAE, 0xE7, 0x9F, 0x9B, 0xE7, 0x9F, 0xA2, 0xE7,
+ 0x9F, 0xB3, 0xE7, 0xA4, 0xBA, 0xE7, 0xA6, 0xB8,
+ 0xE7, 0xA6, 0xBE, 0xE7, 0xA9, 0xB4, 0xE7, 0xAB,
+ 0x8B, 0xE7, 0xAB, 0xB9, 0xE7, 0xB1, 0xB3, 0xE7,
+ 0xB3, 0xB8, 0xE7, 0xBC, 0xB6, 0xE7, 0xBD, 0x91,
+ 0xE7, 0xBE, 0x8A, 0xE7, 0xBE, 0xBD, 0xE8, 0x80,
+ 0x81, 0xE8, 0x80, 0x8C, 0xE8, 0x80, 0x92, 0xE8,
+ 0x80, 0xB3, 0xE8, 0x81, 0xBF, 0xE8, 0x82, 0x89,
+ 0xE8, 0x87, 0xA3, 0xE8, 0x87, 0xAA, 0xE8, 0x87,
+ 0xB3, 0xE8, 0x87, 0xBC, 0xE8, 0x88, 0x8C, 0xE8,
+ 0x88, 0x9B, 0xE8, 0x88, 0x9F, 0xE8, 0x89, 0xAE,
+ 0xE8, 0x89, 0xB2, 0xE8, 0x89, 0xB8, 0xE8, 0x99,
+ 0x8D, 0xE8, 0x99, 0xAB, 0xE8, 0xA1, 0x80, 0xE8,
+ 0xA1, 0x8C, 0xE8, 0xA1, 0xA3, 0xE8, 0xA5, 0xBE,
+ 0xE8, 0xA6, 0x8B, 0xE8, 0xA7, 0x92, 0xE8, 0xA8,
+ 0x80, 0xE8, 0xB0, 0xB7, 0xE8, 0xB1, 0x86, 0xE8,
+ 0xB1, 0x95, 0xE8, 0xB1, 0xB8, 0xE8, 0xB2, 0x9D,
+ 0xE8, 0xB5, 0xA4, 0xE8, 0xB5, 0xB0, 0xE8, 0xB6,
+ 0xB3, 0xE8, 0xBA, 0xAB, 0xE8, 0xBB, 0x8A, 0xE8,
+ 0xBE, 0x9B, 0xE8, 0xBE, 0xB0, 0xE8, 0xBE, 0xB5,
+ 0xE9, 0x82, 0x91, 0xE9, 0x85, 0x89, 0xE9, 0x87,
+ 0x86, 0xE9, 0x87, 0x8C, 0xE9, 0x87, 0x91, 0xE9,
+ 0x95, 0xB7, 0xE9, 0x96, 0x80, 0xE9, 0x98, 0x9C,
+ 0xE9, 0x9A, 0xB6, 0xE9, 0x9A, 0xB9, 0xE9, 0x9B,
+ 0xA8, 0xE9, 0x9D, 0x91, 0xE9, 0x9D, 0x9E, 0xE9,
+ 0x9D, 0xA2, 0xE9, 0x9D, 0xA9, 0xE9, 0x9F, 0x8B,
+ 0xE9, 0x9F, 0xAD, 0xE9, 0x9F, 0xB3, 0xE9, 0xA0,
+ 0x81, 0xE9, 0xA2, 0xA8, 0xE9, 0xA3, 0x9B, 0xE9,
+ 0xA3, 0x9F, 0xE9, 0xA6, 0x96, 0xE9, 0xA6, 0x99,
+ 0xE9, 0xA6, 0xAC, 0xE9, 0xAA, 0xA8, 0xE9, 0xAB,
+ 0x98, 0xE9, 0xAB, 0x9F, 0xE9, 0xAC, 0xA5, 0xE9,
+ 0xAC, 0xAF, 0xE9, 0xAC, 0xB2, 0xE9, 0xAC, 0xBC,
+ 0xE9, 0xAD, 0x9A, 0xE9, 0xB3, 0xA5, 0xE9, 0xB9,
+ 0xB5, 0xE9, 0xB9, 0xBF, 0xE9, 0xBA, 0xA5, 0xE9,
+ 0xBA, 0xBB, 0xE9, 0xBB, 0x83, 0xE9, 0xBB, 0x8D,
+ 0xE9, 0xBB, 0x91, 0xE9, 0xBB, 0xB9, 0xE9, 0xBB,
+ 0xBD, 0xE9, 0xBC, 0x8E, 0xE9, 0xBC, 0x93, 0xE9,
+ 0xBC, 0xA0, 0xE9, 0xBC, 0xBB, 0xE9, 0xBD, 0x8A,
+ 0xE9, 0xBD, 0x92, 0xE9, 0xBE, 0x8D, 0xE9, 0xBE,
+ 0x9C, 0xE9, 0xBE, 0xA0, 0x20, 0xE3, 0x80, 0x92,
+ 0xE5, 0x8D, 0x81, 0xE5, 0x8D, 0x84, 0xE5, 0x8D,
+ 0x85, 0xF6, 0xE3, 0x81, 0x8B, 0xE3, 0x82, 0x99,
+ 0xF6, 0xE3, 0x81, 0x8D, 0xE3, 0x82, 0x99, 0xF6,
+ 0xE3, 0x81, 0x8F, 0xE3, 0x82, 0x99, 0xF6, 0xE3,
+ 0x81, 0x91, 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x81,
+ 0x93, 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x81, 0x95,
+ 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x81, 0x97, 0xE3,
+ 0x82, 0x99, 0xF6, 0xE3, 0x81, 0x99, 0xE3, 0x82,
+ 0x99, 0xF6, 0xE3, 0x81, 0x9B, 0xE3, 0x82, 0x99,
+ 0xF6, 0xE3, 0x81, 0x9D, 0xE3, 0x82, 0x99, 0xF6,
+ 0xE3, 0x81, 0x9F, 0xE3, 0x82, 0x99, 0xF6, 0xE3,
+ 0x81, 0xA1, 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x81,
+ 0xA4, 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x81, 0xA6,
+ 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x81, 0xA8, 0xE3,
+ 0x82, 0x99, 0xF6, 0xE3, 0x81, 0xAF, 0xE3, 0x82,
+ 0x99, 0xF6, 0xE3, 0x81, 0xAF, 0xE3, 0x82, 0x9A,
+ 0xF6, 0xE3, 0x81, 0xB2, 0xE3, 0x82, 0x99, 0xF6,
+ 0xE3, 0x81, 0xB2, 0xE3, 0x82, 0x9A, 0xF6, 0xE3,
+ 0x81, 0xB5, 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x81,
+ 0xB5, 0xE3, 0x82, 0x9A, 0xF6, 0xE3, 0x81, 0xB8,
+ 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x81, 0xB8, 0xE3,
+ 0x82, 0x9A, 0xF6, 0xE3, 0x81, 0xBB, 0xE3, 0x82,
+ 0x99, 0xF6, 0xE3, 0x81, 0xBB, 0xE3, 0x82, 0x9A,
+ 0xF6, 0xE3, 0x81, 0x86, 0xE3, 0x82, 0x99, 0x20,
+ 0xE3, 0x82, 0x99, 0x20, 0xE3, 0x82, 0x9A, 0xF6,
+ 0xE3, 0x82, 0x9D, 0xE3, 0x82, 0x99, 0xE3, 0x82,
+ 0x88, 0xE3, 0x82, 0x8A, 0xF6, 0xE3, 0x82, 0xAB,
+ 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x82, 0xAD, 0xE3,
+ 0x82, 0x99, 0xF6, 0xE3, 0x82, 0xAF, 0xE3, 0x82,
+ 0x99, 0xF6, 0xE3, 0x82, 0xB1, 0xE3, 0x82, 0x99,
+ 0xF6, 0xE3, 0x82, 0xB3, 0xE3, 0x82, 0x99, 0xF6,
+ 0xE3, 0x82, 0xB5, 0xE3, 0x82, 0x99, 0xF6, 0xE3,
+ 0x82, 0xB7, 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x82,
+ 0xB9, 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x82, 0xBB,
+ 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x82, 0xBD, 0xE3,
+ 0x82, 0x99, 0xF6, 0xE3, 0x82, 0xBF, 0xE3, 0x82,
+ 0x99, 0xF6, 0xE3, 0x83, 0x81, 0xE3, 0x82, 0x99,
+ 0xF6, 0xE3, 0x83, 0x84, 0xE3, 0x82, 0x99, 0xF6,
+ 0xE3, 0x83, 0x86, 0xE3, 0x82, 0x99, 0xF6, 0xE3,
+ 0x83, 0x88, 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x83,
+ 0x8F, 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x83, 0x8F,
+ 0xE3, 0x82, 0x9A, 0xF6, 0xE3, 0x83, 0x92, 0xE3,
+ 0x82, 0x99, 0xF6, 0xE3, 0x83, 0x92, 0xE3, 0x82,
+ 0x9A, 0xF6, 0xE3, 0x83, 0x95, 0xE3, 0x82, 0x99,
+ 0xF6, 0xE3, 0x83, 0x95, 0xE3, 0x82, 0x9A, 0xF6,
+ 0xE3, 0x83, 0x98, 0xE3, 0x82, 0x99, 0xF6, 0xE3,
+ 0x83, 0x98, 0xE3, 0x82, 0x9A, 0xF6, 0xE3, 0x83,
+ 0x9B, 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x83, 0x9B,
+ 0xE3, 0x82, 0x9A, 0xF6, 0xE3, 0x82, 0xA6, 0xE3,
+ 0x82, 0x99, 0xF6, 0xE3, 0x83, 0xAF, 0xE3, 0x82,
+ 0x99, 0xF6, 0xE3, 0x83, 0xB0, 0xE3, 0x82, 0x99,
+ 0xF6, 0xE3, 0x83, 0xB1, 0xE3, 0x82, 0x99, 0xF6,
+ 0xE3, 0x83, 0xB2, 0xE3, 0x82, 0x99, 0xF6, 0xE3,
+ 0x83, 0xBD, 0xE3, 0x82, 0x99, 0xE3, 0x82, 0xB3,
+ 0xE3, 0x83, 0x88, 0xE1, 0x84, 0x80, 0xE1, 0x84,
+ 0x81, 0xE1, 0x86, 0xAA, 0xE1, 0x84, 0x82, 0xE1,
+ 0x86, 0xAC, 0xE1, 0x86, 0xAD, 0xE1, 0x84, 0x83,
+ 0xE1, 0x84, 0x84, 0xE1, 0x84, 0x85, 0xE1, 0x86,
+ 0xB0, 0xE1, 0x86, 0xB1, 0xE1, 0x86, 0xB2, 0xE1,
+ 0x86, 0xB3, 0xE1, 0x86, 0xB4, 0xE1, 0x86, 0xB5,
+ 0xE1, 0x84, 0x9A, 0xE1, 0x84, 0x86, 0xE1, 0x84,
+ 0x87, 0xE1, 0x84, 0x88, 0xE1, 0x84, 0xA1, 0xE1,
+ 0x84, 0x89, 0xE1, 0x84, 0x8A, 0xE1, 0x84, 0x8B,
+ 0xE1, 0x84, 0x8C, 0xE1, 0x84, 0x8D, 0xE1, 0x84,
+ 0x8E, 0xE1, 0x84, 0x8F, 0xE1, 0x84, 0x90, 0xE1,
+ 0x84, 0x91, 0xE1, 0x84, 0x92, 0xE1, 0x85, 0xA1,
+ 0xE1, 0x85, 0xA2, 0xE1, 0x85, 0xA3, 0xE1, 0x85,
+ 0xA4, 0xE1, 0x85, 0xA5, 0xE1, 0x85, 0xA6, 0xE1,
+ 0x85, 0xA7, 0xE1, 0x85, 0xA8, 0xE1, 0x85, 0xA9,
+ 0xE1, 0x85, 0xAA, 0xE1, 0x85, 0xAB, 0xE1, 0x85,
+ 0xAC, 0xE1, 0x85, 0xAD, 0xE1, 0x85, 0xAE, 0xE1,
+ 0x85, 0xAF, 0xE1, 0x85, 0xB0, 0xE1, 0x85, 0xB1,
+ 0xE1, 0x85, 0xB2, 0xE1, 0x85, 0xB3, 0xE1, 0x85,
+ 0xB4, 0xE1, 0x85, 0xB5, 0xE1, 0x85, 0xA0, 0xE1,
+ 0x84, 0x94, 0xE1, 0x84, 0x95, 0xE1, 0x87, 0x87,
+ 0xE1, 0x87, 0x88, 0xE1, 0x87, 0x8C, 0xE1, 0x87,
+ 0x8E, 0xE1, 0x87, 0x93, 0xE1, 0x87, 0x97, 0xE1,
+ 0x87, 0x99, 0xE1, 0x84, 0x9C, 0xE1, 0x87, 0x9D,
+ 0xE1, 0x87, 0x9F, 0xE1, 0x84, 0x9D, 0xE1, 0x84,
+ 0x9E, 0xE1, 0x84, 0xA0, 0xE1, 0x84, 0xA2, 0xE1,
+ 0x84, 0xA3, 0xE1, 0x84, 0xA7, 0xE1, 0x84, 0xA9,
+ 0xE1, 0x84, 0xAB, 0xE1, 0x84, 0xAC, 0xE1, 0x84,
+ 0xAD, 0xE1, 0x84, 0xAE, 0xE1, 0x84, 0xAF, 0xE1,
+ 0x84, 0xB2, 0xE1, 0x84, 0xB6, 0xE1, 0x85, 0x80,
+ 0xE1, 0x85, 0x87, 0xE1, 0x85, 0x8C, 0xE1, 0x87,
+ 0xB1, 0xE1, 0x87, 0xB2, 0xE1, 0x85, 0x97, 0xE1,
+ 0x85, 0x98, 0xE1, 0x85, 0x99, 0xE1, 0x86, 0x84,
+ 0xE1, 0x86, 0x85, 0xE1, 0x86, 0x88, 0xE1, 0x86,
+ 0x91, 0xE1, 0x86, 0x92, 0xE1, 0x86, 0x94, 0xE1,
+ 0x86, 0x9E, 0xE1, 0x86, 0xA1, 0xE4, 0xB8, 0x80,
+ 0xE4, 0xBA, 0x8C, 0xE4, 0xB8, 0x89, 0xE5, 0x9B,
+ 0x9B, 0xE4, 0xB8, 0x8A, 0xE4, 0xB8, 0xAD, 0xE4,
+ 0xB8, 0x8B, 0xE7, 0x94, 0xB2, 0xE4, 0xB9, 0x99,
+ 0xE4, 0xB8, 0x99, 0xE4, 0xB8, 0x81, 0xE5, 0xA4,
+ 0xA9, 0xE5, 0x9C, 0xB0, 0xE4, 0xBA, 0xBA, 0x28,
+ 0xE1, 0x84, 0x80, 0x29, 0x28, 0xE1, 0x84, 0x82,
+ 0x29, 0x28, 0xE1, 0x84, 0x83, 0x29, 0x28, 0xE1,
+ 0x84, 0x85, 0x29, 0x28, 0xE1, 0x84, 0x86, 0x29,
+ 0x28, 0xE1, 0x84, 0x87, 0x29, 0x28, 0xE1, 0x84,
+ 0x89, 0x29, 0x28, 0xE1, 0x84, 0x8B, 0x29, 0x28,
+ 0xE1, 0x84, 0x8C, 0x29, 0x28, 0xE1, 0x84, 0x8E,
+ 0x29, 0x28, 0xE1, 0x84, 0x8F, 0x29, 0x28, 0xE1,
+ 0x84, 0x90, 0x29, 0x28, 0xE1, 0x84, 0x91, 0x29,
+ 0x28, 0xE1, 0x84, 0x92, 0x29, 0x28, 0xE1, 0x84,
+ 0x80, 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84,
+ 0x82, 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84,
+ 0x83, 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84,
+ 0x85, 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84,
+ 0x86, 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84,
+ 0x87, 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84,
+ 0x89, 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84,
+ 0x8B, 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84,
+ 0x8C, 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84,
+ 0x8E, 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84,
+ 0x8F, 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84,
+ 0x90, 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84,
+ 0x91, 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84,
+ 0x92, 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84,
+ 0x8C, 0xE1, 0x85, 0xAE, 0x29, 0x28, 0xE4, 0xB8,
+ 0x80, 0x29, 0x28, 0xE4, 0xBA, 0x8C, 0x29, 0x28,
+ 0xE4, 0xB8, 0x89, 0x29, 0x28, 0xE5, 0x9B, 0x9B,
+ 0x29, 0x28, 0xE4, 0xBA, 0x94, 0x29, 0x28, 0xE5,
+ 0x85, 0xAD, 0x29, 0x28, 0xE4, 0xB8, 0x83, 0x29,
+ 0x28, 0xE5, 0x85, 0xAB, 0x29, 0x28, 0xE4, 0xB9,
+ 0x9D, 0x29, 0x28, 0xE5, 0x8D, 0x81, 0x29, 0x28,
+ 0xE6, 0x9C, 0x88, 0x29, 0x28, 0xE7, 0x81, 0xAB,
+ 0x29, 0x28, 0xE6, 0xB0, 0xB4, 0x29, 0x28, 0xE6,
+ 0x9C, 0xA8, 0x29, 0x28, 0xE9, 0x87, 0x91, 0x29,
+ 0x28, 0xE5, 0x9C, 0x9F, 0x29, 0x28, 0xE6, 0x97,
+ 0xA5, 0x29, 0x28, 0xE6, 0xA0, 0xAA, 0x29, 0x28,
+ 0xE6, 0x9C, 0x89, 0x29, 0x28, 0xE7, 0xA4, 0xBE,
+ 0x29, 0x28, 0xE5, 0x90, 0x8D, 0x29, 0x28, 0xE7,
+ 0x89, 0xB9, 0x29, 0x28, 0xE8, 0xB2, 0xA1, 0x29,
+ 0x28, 0xE7, 0xA5, 0x9D, 0x29, 0x28, 0xE5, 0x8A,
+ 0xB4, 0x29, 0x28, 0xE4, 0xBB, 0xA3, 0x29, 0x28,
+ 0xE5, 0x91, 0xBC, 0x29, 0x28, 0xE5, 0xAD, 0xA6,
+ 0x29, 0x28, 0xE7, 0x9B, 0xA3, 0x29, 0x28, 0xE4,
+ 0xBC, 0x81, 0x29, 0x28, 0xE8, 0xB3, 0x87, 0x29,
+ 0x28, 0xE5, 0x8D, 0x94, 0x29, 0x28, 0xE7, 0xA5,
+ 0xAD, 0x29, 0x28, 0xE4, 0xBC, 0x91, 0x29, 0x28,
+ 0xE8, 0x87, 0xAA, 0x29, 0x28, 0xE8, 0x87, 0xB3,
+ 0x29, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33, 0x32,
+ 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32,
+ 0x38, 0x32, 0x39, 0x33, 0x30, 0x33, 0x31, 0x33,
+ 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35, 0xE1,
+ 0x84, 0x80, 0xE1, 0x84, 0x82, 0xE1, 0x84, 0x83,
+ 0xE1, 0x84, 0x85, 0xE1, 0x84, 0x86, 0xE1, 0x84,
+ 0x87, 0xE1, 0x84, 0x89, 0xE1, 0x84, 0x8B, 0xE1,
+ 0x84, 0x8C, 0xE1, 0x84, 0x8E, 0xE1, 0x84, 0x8F,
+ 0xE1, 0x84, 0x90, 0xE1, 0x84, 0x91, 0xE1, 0x84,
+ 0x92, 0xE1, 0x84, 0x80, 0xE1, 0x85, 0xA1, 0xE1,
+ 0x84, 0x82, 0xE1, 0x85, 0xA1, 0xE1, 0x84, 0x83,
+ 0xE1, 0x85, 0xA1, 0xE1, 0x84, 0x85, 0xE1, 0x85,
+ 0xA1, 0xE1, 0x84, 0x86, 0xE1, 0x85, 0xA1, 0xE1,
+ 0x84, 0x87, 0xE1, 0x85, 0xA1, 0xE1, 0x84, 0x89,
+ 0xE1, 0x85, 0xA1, 0xE1, 0x84, 0x8B, 0xE1, 0x85,
+ 0xA1, 0xE1, 0x84, 0x8C, 0xE1, 0x85, 0xA1, 0xE1,
+ 0x84, 0x8E, 0xE1, 0x85, 0xA1, 0xE1, 0x84, 0x8F,
+ 0xE1, 0x85, 0xA1, 0xE1, 0x84, 0x90, 0xE1, 0x85,
+ 0xA1, 0xE1, 0x84, 0x91, 0xE1, 0x85, 0xA1, 0xE1,
+ 0x84, 0x92, 0xE1, 0x85, 0xA1, 0xE4, 0xB8, 0x80,
+ 0xE4, 0xBA, 0x8C, 0xE4, 0xB8, 0x89, 0xE5, 0x9B,
+ 0x9B, 0xE4, 0xBA, 0x94, 0xE5, 0x85, 0xAD, 0xE4,
+ 0xB8, 0x83, 0xE5, 0x85, 0xAB, 0xE4, 0xB9, 0x9D,
+ 0xE5, 0x8D, 0x81, 0xE6, 0x9C, 0x88, 0xE7, 0x81,
+ 0xAB, 0xE6, 0xB0, 0xB4, 0xE6, 0x9C, 0xA8, 0xE9,
+ 0x87, 0x91, 0xE5, 0x9C, 0x9F, 0xE6, 0x97, 0xA5,
+ 0xE6, 0xA0, 0xAA, 0xE6, 0x9C, 0x89, 0xE7, 0xA4,
+ 0xBE, 0xE5, 0x90, 0x8D, 0xE7, 0x89, 0xB9, 0xE8,
+ 0xB2, 0xA1, 0xE7, 0xA5, 0x9D, 0xE5, 0x8A, 0xB4,
+ 0xE7, 0xA7, 0x98, 0xE7, 0x94, 0xB7, 0xE5, 0xA5,
+ 0xB3, 0xE9, 0x81, 0xA9, 0xE5, 0x84, 0xAA, 0xE5,
+ 0x8D, 0xB0, 0xE6, 0xB3, 0xA8, 0xE9, 0xA0, 0x85,
+ 0xE4, 0xBC, 0x91, 0xE5, 0x86, 0x99, 0xE6, 0xAD,
+ 0xA3, 0xE4, 0xB8, 0x8A, 0xE4, 0xB8, 0xAD, 0xE4,
+ 0xB8, 0x8B, 0xE5, 0xB7, 0xA6, 0xE5, 0x8F, 0xB3,
+ 0xE5, 0x8C, 0xBB, 0xE5, 0xAE, 0x97, 0xE5, 0xAD,
+ 0xA6, 0xE7, 0x9B, 0xA3, 0xE4, 0xBC, 0x81, 0xE8,
+ 0xB3, 0x87, 0xE5, 0x8D, 0x94, 0xE5, 0xA4, 0x9C,
+ 0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39,
+ 0x34, 0x30, 0x34, 0x31, 0x34, 0x32, 0x34, 0x33,
+ 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37,
+ 0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x31, 0xE6,
+ 0x9C, 0x88, 0x32, 0xE6, 0x9C, 0x88, 0x33, 0xE6,
+ 0x9C, 0x88, 0x34, 0xE6, 0x9C, 0x88, 0x35, 0xE6,
+ 0x9C, 0x88, 0x36, 0xE6, 0x9C, 0x88, 0x37, 0xE6,
+ 0x9C, 0x88, 0x38, 0xE6, 0x9C, 0x88, 0x39, 0xE6,
+ 0x9C, 0x88, 0x31, 0x30, 0xE6, 0x9C, 0x88, 0x31,
+ 0x31, 0xE6, 0x9C, 0x88, 0x31, 0x32, 0xE6, 0x9C,
+ 0x88, 0xE3, 0x82, 0xA2, 0xE3, 0x82, 0xA4, 0xE3,
+ 0x82, 0xA6, 0xE3, 0x82, 0xA8, 0xE3, 0x82, 0xAA,
+ 0xE3, 0x82, 0xAB, 0xE3, 0x82, 0xAD, 0xE3, 0x82,
+ 0xAF, 0xE3, 0x82, 0xB1, 0xE3, 0x82, 0xB3, 0xE3,
+ 0x82, 0xB5, 0xE3, 0x82, 0xB7, 0xE3, 0x82, 0xB9,
+ 0xE3, 0x82, 0xBB, 0xE3, 0x82, 0xBD, 0xE3, 0x82,
+ 0xBF, 0xE3, 0x83, 0x81, 0xE3, 0x83, 0x84, 0xE3,
+ 0x83, 0x86, 0xE3, 0x83, 0x88, 0xE3, 0x83, 0x8A,
+ 0xE3, 0x83, 0x8B, 0xE3, 0x83, 0x8C, 0xE3, 0x83,
+ 0x8D, 0xE3, 0x83, 0x8E, 0xE3, 0x83, 0x8F, 0xE3,
+ 0x83, 0x92, 0xE3, 0x83, 0x95, 0xE3, 0x83, 0x98,
+ 0xE3, 0x83, 0x9B, 0xE3, 0x83, 0x9E, 0xE3, 0x83,
+ 0x9F, 0xE3, 0x83, 0xA0, 0xE3, 0x83, 0xA1, 0xE3,
+ 0x83, 0xA2, 0xE3, 0x83, 0xA4, 0xE3, 0x83, 0xA6,
+ 0xE3, 0x83, 0xA8, 0xE3, 0x83, 0xA9, 0xE3, 0x83,
+ 0xAA, 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0xAC, 0xE3,
+ 0x83, 0xAD, 0xE3, 0x83, 0xAF, 0xE3, 0x83, 0xB0,
+ 0xE3, 0x83, 0xB1, 0xE3, 0x83, 0xB2, 0xE3, 0x82,
+ 0xA2, 0xE3, 0x83, 0x8F, 0xE3, 0x82, 0x9A, 0xE3,
+ 0x83, 0xBC, 0xE3, 0x83, 0x88, 0xE3, 0x82, 0xA2,
+ 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0x95, 0xE3, 0x82,
+ 0xA1, 0xE3, 0x82, 0xA2, 0xE3, 0x83, 0xB3, 0xE3,
+ 0x83, 0x98, 0xE3, 0x82, 0x9A, 0xE3, 0x82, 0xA2,
+ 0xE3, 0x82, 0xA2, 0xE3, 0x83, 0xBC, 0xE3, 0x83,
+ 0xAB, 0xE3, 0x82, 0xA4, 0xE3, 0x83, 0x8B, 0xE3,
+ 0x83, 0xB3, 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0x99,
+ 0xE3, 0x82, 0xA4, 0xE3, 0x83, 0xB3, 0xE3, 0x83,
+ 0x81, 0xE3, 0x82, 0xA6, 0xE3, 0x82, 0xA9, 0xE3,
+ 0x83, 0xB3, 0xE3, 0x82, 0xA8, 0xE3, 0x82, 0xB9,
+ 0xE3, 0x82, 0xAF, 0xE3, 0x83, 0xBC, 0xE3, 0x83,
+ 0x88, 0xE3, 0x82, 0x99, 0xE3, 0x82, 0xA8, 0xE3,
+ 0x83, 0xBC, 0xE3, 0x82, 0xAB, 0xE3, 0x83, 0xBC,
+ 0xE3, 0x82, 0xAA, 0xE3, 0x83, 0xB3, 0xE3, 0x82,
+ 0xB9, 0xE3, 0x82, 0xAA, 0xE3, 0x83, 0xBC, 0xE3,
+ 0x83, 0xA0, 0xE3, 0x82, 0xAB, 0xE3, 0x82, 0xA4,
+ 0xE3, 0x83, 0xAA, 0xE3, 0x82, 0xAB, 0xE3, 0x83,
+ 0xA9, 0xE3, 0x83, 0x83, 0xE3, 0x83, 0x88, 0xE3,
+ 0x82, 0xAB, 0xE3, 0x83, 0xAD, 0xE3, 0x83, 0xAA,
+ 0xE3, 0x83, 0xBC, 0xE3, 0x82, 0xAB, 0xE3, 0x82,
+ 0x99, 0xE3, 0x83, 0xAD, 0xE3, 0x83, 0xB3, 0xE3,
+ 0x82, 0xAB, 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xB3,
+ 0xE3, 0x83, 0x9E, 0xE3, 0x82, 0xAD, 0xE3, 0x82,
+ 0x99, 0xE3, 0x82, 0xAB, 0xE3, 0x82, 0x99, 0xE3,
+ 0x82, 0xAD, 0xE3, 0x82, 0x99, 0xE3, 0x83, 0x8B,
+ 0xE3, 0x83, 0xBC, 0xE3, 0x82, 0xAD, 0xE3, 0x83,
+ 0xA5, 0xE3, 0x83, 0xAA, 0xE3, 0x83, 0xBC, 0xE3,
+ 0x82, 0xAD, 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xAB,
+ 0xE3, 0x82, 0xBF, 0xE3, 0x82, 0x99, 0xE3, 0x83,
+ 0xBC, 0xE3, 0x82, 0xAD, 0xE3, 0x83, 0xAD, 0xE3,
+ 0x82, 0xAD, 0xE3, 0x83, 0xAD, 0xE3, 0x82, 0xAF,
+ 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xA9, 0xE3, 0x83,
+ 0xA0, 0xE3, 0x82, 0xAD, 0xE3, 0x83, 0xAD, 0xE3,
+ 0x83, 0xA1, 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0x88,
+ 0xE3, 0x83, 0xAB, 0xE3, 0x82, 0xAD, 0xE3, 0x83,
+ 0xAD, 0xE3, 0x83, 0xAF, 0xE3, 0x83, 0x83, 0xE3,
+ 0x83, 0x88, 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0x99,
+ 0xE3, 0x83, 0xA9, 0xE3, 0x83, 0xA0, 0xE3, 0x82,
+ 0xAF, 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xA9, 0xE3,
+ 0x83, 0xA0, 0xE3, 0x83, 0x88, 0xE3, 0x83, 0xB3,
+ 0xE3, 0x82, 0xAF, 0xE3, 0x83, 0xAB, 0xE3, 0x82,
+ 0xBB, 0xE3, 0x82, 0x99, 0xE3, 0x82, 0xA4, 0xE3,
+ 0x83, 0xAD, 0xE3, 0x82, 0xAF, 0xE3, 0x83, 0xAD,
+ 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0x8D, 0xE3, 0x82,
+ 0xB1, 0xE3, 0x83, 0xBC, 0xE3, 0x82, 0xB9, 0xE3,
+ 0x82, 0xB3, 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0x8A,
+ 0xE3, 0x82, 0xB3, 0xE3, 0x83, 0xBC, 0xE3, 0x83,
+ 0x9B, 0xE3, 0x82, 0x9A, 0xE3, 0x82, 0xB5, 0xE3,
+ 0x82, 0xA4, 0xE3, 0x82, 0xAF, 0xE3, 0x83, 0xAB,
+ 0xE3, 0x82, 0xB5, 0xE3, 0x83, 0xB3, 0xE3, 0x83,
+ 0x81, 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0xA0, 0xE3,
+ 0x82, 0xB7, 0xE3, 0x83, 0xAA, 0xE3, 0x83, 0xB3,
+ 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0x99, 0xE3, 0x82,
+ 0xBB, 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x81, 0xE3,
+ 0x82, 0xBB, 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x88,
+ 0xE3, 0x82, 0xBF, 0xE3, 0x82, 0x99, 0xE3, 0x83,
+ 0xBC, 0xE3, 0x82, 0xB9, 0xE3, 0x83, 0x86, 0xE3,
+ 0x82, 0x99, 0xE3, 0x82, 0xB7, 0xE3, 0x83, 0x88,
+ 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xAB, 0xE3, 0x83,
+ 0x88, 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x8A, 0xE3,
+ 0x83, 0x8E, 0xE3, 0x83, 0x8E, 0xE3, 0x83, 0x83,
+ 0xE3, 0x83, 0x88, 0xE3, 0x83, 0x8F, 0xE3, 0x82,
+ 0xA4, 0xE3, 0x83, 0x84, 0xE3, 0x83, 0x8F, 0xE3,
+ 0x82, 0x9A, 0xE3, 0x83, 0xBC, 0xE3, 0x82, 0xBB,
+ 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x88, 0xE3, 0x83,
+ 0x8F, 0xE3, 0x82, 0x9A, 0xE3, 0x83, 0xBC, 0xE3,
+ 0x83, 0x84, 0xE3, 0x83, 0x8F, 0xE3, 0x82, 0x99,
+ 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0xAC, 0xE3, 0x83,
+ 0xAB, 0xE3, 0x83, 0x92, 0xE3, 0x82, 0x9A, 0xE3,
+ 0x82, 0xA2, 0xE3, 0x82, 0xB9, 0xE3, 0x83, 0x88,
+ 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0x92, 0xE3, 0x82,
+ 0x9A, 0xE3, 0x82, 0xAF, 0xE3, 0x83, 0xAB, 0xE3,
+ 0x83, 0x92, 0xE3, 0x82, 0x9A, 0xE3, 0x82, 0xB3,
+ 0xE3, 0x83, 0x92, 0xE3, 0x82, 0x99, 0xE3, 0x83,
+ 0xAB, 0xE3, 0x83, 0x95, 0xE3, 0x82, 0xA1, 0xE3,
+ 0x83, 0xA9, 0xE3, 0x83, 0x83, 0xE3, 0x83, 0x88,
+ 0xE3, 0x82, 0x99, 0xE3, 0x83, 0x95, 0xE3, 0x82,
+ 0xA3, 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0x88, 0xE3,
+ 0x83, 0x95, 0xE3, 0x82, 0x99, 0xE3, 0x83, 0x83,
+ 0xE3, 0x82, 0xB7, 0xE3, 0x82, 0xA7, 0xE3, 0x83,
+ 0xAB, 0xE3, 0x83, 0x95, 0xE3, 0x83, 0xA9, 0xE3,
+ 0x83, 0xB3, 0xE3, 0x83, 0x98, 0xE3, 0x82, 0xAF,
+ 0xE3, 0x82, 0xBF, 0xE3, 0x83, 0xBC, 0xE3, 0x83,
+ 0xAB, 0xE3, 0x83, 0x98, 0xE3, 0x82, 0x9A, 0xE3,
+ 0x82, 0xBD, 0xE3, 0x83, 0x98, 0xE3, 0x82, 0x9A,
+ 0xE3, 0x83, 0x8B, 0xE3, 0x83, 0x92, 0xE3, 0x83,
+ 0x98, 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0x84, 0xE3,
+ 0x83, 0x98, 0xE3, 0x82, 0x9A, 0xE3, 0x83, 0xB3,
+ 0xE3, 0x82, 0xB9, 0xE3, 0x83, 0x98, 0xE3, 0x82,
+ 0x9A, 0xE3, 0x83, 0xBC, 0xE3, 0x82, 0xB7, 0xE3,
+ 0x82, 0x99, 0xE3, 0x83, 0x98, 0xE3, 0x82, 0x99,
+ 0xE3, 0x83, 0xBC, 0xE3, 0x82, 0xBF, 0xE3, 0x83,
+ 0x9B, 0xE3, 0x82, 0x9A, 0xE3, 0x82, 0xA4, 0xE3,
+ 0x83, 0xB3, 0xE3, 0x83, 0x88, 0xE3, 0x83, 0x9B,
+ 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xAB, 0xE3, 0x83,
+ 0x88, 0xE3, 0x83, 0x9B, 0xE3, 0x83, 0xB3, 0xE3,
+ 0x83, 0x9B, 0xE3, 0x82, 0x9A, 0xE3, 0x83, 0xB3,
+ 0xE3, 0x83, 0x88, 0xE3, 0x82, 0x99, 0xE3, 0x83,
+ 0x9B, 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0xAB, 0xE3,
+ 0x83, 0x9B, 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0xB3,
+ 0xE3, 0x83, 0x9E, 0xE3, 0x82, 0xA4, 0xE3, 0x82,
+ 0xAF, 0xE3, 0x83, 0xAD, 0xE3, 0x83, 0x9E, 0xE3,
+ 0x82, 0xA4, 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0x9E,
+ 0xE3, 0x83, 0x83, 0xE3, 0x83, 0x8F, 0xE3, 0x83,
+ 0x9E, 0xE3, 0x83, 0xAB, 0xE3, 0x82, 0xAF, 0xE3,
+ 0x83, 0x9E, 0xE3, 0x83, 0xB3, 0xE3, 0x82, 0xB7,
+ 0xE3, 0x83, 0xA7, 0xE3, 0x83, 0xB3, 0xE3, 0x83,
+ 0x9F, 0xE3, 0x82, 0xAF, 0xE3, 0x83, 0xAD, 0xE3,
+ 0x83, 0xB3, 0xE3, 0x83, 0x9F, 0xE3, 0x83, 0xAA,
+ 0xE3, 0x83, 0x9F, 0xE3, 0x83, 0xAA, 0xE3, 0x83,
+ 0x8F, 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xBC, 0xE3,
+ 0x83, 0xAB, 0xE3, 0x83, 0xA1, 0xE3, 0x82, 0xAB,
+ 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xA1, 0xE3, 0x82,
+ 0xAB, 0xE3, 0x82, 0x99, 0xE3, 0x83, 0x88, 0xE3,
+ 0x83, 0xB3, 0xE3, 0x83, 0xA1, 0xE3, 0x83, 0xBC,
+ 0xE3, 0x83, 0x88, 0xE3, 0x83, 0xAB, 0xE3, 0x83,
+ 0xA4, 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0x88, 0xE3,
+ 0x82, 0x99, 0xE3, 0x83, 0xA4, 0xE3, 0x83, 0xBC,
+ 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0xA6, 0xE3, 0x82,
+ 0xA2, 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0xAA, 0xE3,
+ 0x83, 0x83, 0xE3, 0x83, 0x88, 0xE3, 0x83, 0xAB,
+ 0xE3, 0x83, 0xAA, 0xE3, 0x83, 0xA9, 0xE3, 0x83,
+ 0xAB, 0xE3, 0x83, 0x92, 0xE3, 0x82, 0x9A, 0xE3,
+ 0x83, 0xBC, 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0xBC,
+ 0xE3, 0x83, 0x95, 0xE3, 0x82, 0x99, 0xE3, 0x83,
+ 0xAB, 0xE3, 0x83, 0xAC, 0xE3, 0x83, 0xA0, 0xE3,
+ 0x83, 0xAC, 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x88,
+ 0xE3, 0x82, 0xB1, 0xE3, 0x82, 0x99, 0xE3, 0x83,
+ 0xB3, 0xE3, 0x83, 0xAF, 0xE3, 0x83, 0x83, 0xE3,
+ 0x83, 0x88, 0x30, 0xE7, 0x82, 0xB9, 0x31, 0xE7,
+ 0x82, 0xB9, 0x32, 0xE7, 0x82, 0xB9, 0x33, 0xE7,
+ 0x82, 0xB9, 0x34, 0xE7, 0x82, 0xB9, 0x35, 0xE7,
+ 0x82, 0xB9, 0x36, 0xE7, 0x82, 0xB9, 0x37, 0xE7,
+ 0x82, 0xB9, 0x38, 0xE7, 0x82, 0xB9, 0x39, 0xE7,
+ 0x82, 0xB9, 0x31, 0x30, 0xE7, 0x82, 0xB9, 0x31,
+ 0x31, 0xE7, 0x82, 0xB9, 0x31, 0x32, 0xE7, 0x82,
+ 0xB9, 0x31, 0x33, 0xE7, 0x82, 0xB9, 0x31, 0x34,
+ 0xE7, 0x82, 0xB9, 0x31, 0x35, 0xE7, 0x82, 0xB9,
+ 0x31, 0x36, 0xE7, 0x82, 0xB9, 0x31, 0x37, 0xE7,
+ 0x82, 0xB9, 0x31, 0x38, 0xE7, 0x82, 0xB9, 0x31,
+ 0x39, 0xE7, 0x82, 0xB9, 0x32, 0x30, 0xE7, 0x82,
+ 0xB9, 0x32, 0x31, 0xE7, 0x82, 0xB9, 0x32, 0x32,
+ 0xE7, 0x82, 0xB9, 0x32, 0x33, 0xE7, 0x82, 0xB9,
+ 0x32, 0x34, 0xE7, 0x82, 0xB9, 0x68, 0x50, 0x61,
+ 0x64, 0x61, 0x41, 0x55, 0x62, 0x61, 0x72, 0x6F,
+ 0x56, 0x70, 0x63, 0xE5, 0xB9, 0xB3, 0xE6, 0x88,
+ 0x90, 0xE6, 0x98, 0xAD, 0xE5, 0x92, 0x8C, 0xE5,
+ 0xA4, 0xA7, 0xE6, 0xAD, 0xA3, 0xE6, 0x98, 0x8E,
+ 0xE6, 0xB2, 0xBB, 0xE6, 0xA0, 0xAA, 0xE5, 0xBC,
+ 0x8F, 0xE4, 0xBC, 0x9A, 0xE7, 0xA4, 0xBE, 0x70,
+ 0x41, 0x6E, 0x41, 0xCE, 0xBC, 0x41, 0x6D, 0x41,
+ 0x6B, 0x41, 0x4B, 0x42, 0x4D, 0x42, 0x47, 0x42,
+ 0x63, 0x61, 0x6C, 0x6B, 0x63, 0x61, 0x6C, 0x70,
+ 0x46, 0x6E, 0x46, 0xCE, 0xBC, 0x46, 0xCE, 0xBC,
+ 0x67, 0x6D, 0x67, 0x6B, 0x67, 0x48, 0x7A, 0x6B,
+ 0x48, 0x7A, 0x4D, 0x48, 0x7A, 0x47, 0x48, 0x7A,
+ 0x54, 0x48, 0x7A, 0xCE, 0xBC, 0x6C, 0x6D, 0x6C,
+ 0x64, 0x6C, 0x6B, 0x6C, 0x66, 0x6D, 0x6E, 0x6D,
+ 0xCE, 0xBC, 0x6D, 0x6D, 0x6D, 0x63, 0x6D, 0x6B,
+ 0x6D, 0x6D, 0x6D, 0x32, 0x63, 0x6D, 0x32, 0x6D,
+ 0x32, 0x6B, 0x6D, 0x32, 0x6D, 0x6D, 0x33, 0x63,
+ 0x6D, 0x33, 0x6D, 0x33, 0x6B, 0x6D, 0x33, 0x6D,
+ 0xE2, 0x88, 0x95, 0x73, 0x6D, 0xE2, 0x88, 0x95,
+ 0x73, 0x32, 0x50, 0x61, 0x6B, 0x50, 0x61, 0x4D,
+ 0x50, 0x61, 0x47, 0x50, 0x61, 0x72, 0x61, 0x64,
+ 0x72, 0x61, 0x64, 0xE2, 0x88, 0x95, 0x73, 0x72,
+ 0x61, 0x64, 0xE2, 0x88, 0x95, 0x73, 0x32, 0x70,
+ 0x73, 0x6E, 0x73, 0xCE, 0xBC, 0x73, 0x6D, 0x73,
+ 0x70, 0x56, 0x6E, 0x56, 0xCE, 0xBC, 0x56, 0x6D,
+ 0x56, 0x6B, 0x56, 0x4D, 0x56, 0x70, 0x57, 0x6E,
+ 0x57, 0xCE, 0xBC, 0x57, 0x6D, 0x57, 0x6B, 0x57,
+ 0x4D, 0x57, 0x6B, 0xCE, 0xA9, 0x4D, 0xCE, 0xA9,
+ 0x61, 0x2E, 0x6D, 0x2E, 0x42, 0x71, 0x63, 0x63,
+ 0x63, 0x64, 0x43, 0xE2, 0x88, 0x95, 0x6B, 0x67,
+ 0x43, 0x6F, 0x2E, 0x64, 0x42, 0x47, 0x79, 0x68,
+ 0x61, 0x48, 0x50, 0x69, 0x6E, 0x4B, 0x4B, 0x4B,
+ 0x4D, 0x6B, 0x74, 0x6C, 0x6D, 0x6C, 0x6E, 0x6C,
+ 0x6F, 0x67, 0x6C, 0x78, 0x6D, 0x62, 0x6D, 0x69,
+ 0x6C, 0x6D, 0x6F, 0x6C, 0x50, 0x48, 0x70, 0x2E,
+ 0x6D, 0x2E, 0x50, 0x50, 0x4D, 0x50, 0x52, 0x73,
+ 0x72, 0x53, 0x76, 0x57, 0x62, 0x31, 0xE6, 0x97,
+ 0xA5, 0x32, 0xE6, 0x97, 0xA5, 0x33, 0xE6, 0x97,
+ 0xA5, 0x34, 0xE6, 0x97, 0xA5, 0x35, 0xE6, 0x97,
+ 0xA5, 0x36, 0xE6, 0x97, 0xA5, 0x37, 0xE6, 0x97,
+ 0xA5, 0x38, 0xE6, 0x97, 0xA5, 0x39, 0xE6, 0x97,
+ 0xA5, 0x31, 0x30, 0xE6, 0x97, 0xA5, 0x31, 0x31,
+ 0xE6, 0x97, 0xA5, 0x31, 0x32, 0xE6, 0x97, 0xA5,
+ 0x31, 0x33, 0xE6, 0x97, 0xA5, 0x31, 0x34, 0xE6,
+ 0x97, 0xA5, 0x31, 0x35, 0xE6, 0x97, 0xA5, 0x31,
+ 0x36, 0xE6, 0x97, 0xA5, 0x31, 0x37, 0xE6, 0x97,
+ 0xA5, 0x31, 0x38, 0xE6, 0x97, 0xA5, 0x31, 0x39,
+ 0xE6, 0x97, 0xA5, 0x32, 0x30, 0xE6, 0x97, 0xA5,
+ 0x32, 0x31, 0xE6, 0x97, 0xA5, 0x32, 0x32, 0xE6,
+ 0x97, 0xA5, 0x32, 0x33, 0xE6, 0x97, 0xA5, 0x32,
+ 0x34, 0xE6, 0x97, 0xA5, 0x32, 0x35, 0xE6, 0x97,
+ 0xA5, 0x32, 0x36, 0xE6, 0x97, 0xA5, 0x32, 0x37,
+ 0xE6, 0x97, 0xA5, 0x32, 0x38, 0xE6, 0x97, 0xA5,
+ 0x32, 0x39, 0xE6, 0x97, 0xA5, 0x33, 0x30, 0xE6,
+ 0x97, 0xA5, 0x33, 0x31, 0xE6, 0x97, 0xA5, 0xF6,
+ 0xE8, 0xB1, 0x88, 0xF6, 0xE6, 0x9B, 0xB4, 0xF6,
+ 0xE8, 0xBB, 0x8A, 0xF6, 0xE8, 0xB3, 0x88, 0xF6,
+ 0xE6, 0xBB, 0x91, 0xF6, 0xE4, 0xB8, 0xB2, 0xF6,
+ 0xE5, 0x8F, 0xA5, 0xF6, 0xE9, 0xBE, 0x9C, 0xF6,
+ 0xE9, 0xBE, 0x9C, 0xF6, 0xE5, 0xA5, 0x91, 0xF6,
+ 0xE9, 0x87, 0x91, 0xF6, 0xE5, 0x96, 0x87, 0xF6,
+ 0xE5, 0xA5, 0x88, 0xF6, 0xE6, 0x87, 0xB6, 0xF6,
+ 0xE7, 0x99, 0xA9, 0xF6, 0xE7, 0xBE, 0x85, 0xF6,
+ 0xE8, 0x98, 0xBF, 0xF6, 0xE8, 0x9E, 0xBA, 0xF6,
+ 0xE8, 0xA3, 0xB8, 0xF6, 0xE9, 0x82, 0x8F, 0xF6,
+ 0xE6, 0xA8, 0x82, 0xF6, 0xE6, 0xB4, 0x9B, 0xF6,
+ 0xE7, 0x83, 0x99, 0xF6, 0xE7, 0x8F, 0x9E, 0xF6,
+ 0xE8, 0x90, 0xBD, 0xF6, 0xE9, 0x85, 0xAA, 0xF6,
+ 0xE9, 0xA7, 0xB1, 0xF6, 0xE4, 0xBA, 0x82, 0xF6,
+ 0xE5, 0x8D, 0xB5, 0xF6, 0xE6, 0xAC, 0x84, 0xF6,
+ 0xE7, 0x88, 0x9B, 0xF6, 0xE8, 0x98, 0xAD, 0xF6,
+ 0xE9, 0xB8, 0x9E, 0xF6, 0xE5, 0xB5, 0x90, 0xF6,
+ 0xE6, 0xBF, 0xAB, 0xF6, 0xE8, 0x97, 0x8D, 0xF6,
+ 0xE8, 0xA5, 0xA4, 0xF6, 0xE6, 0x8B, 0x89, 0xF6,
+ 0xE8, 0x87, 0x98, 0xF6, 0xE8, 0xA0, 0x9F, 0xF6,
+ 0xE5, 0xBB, 0x8A, 0xF6, 0xE6, 0x9C, 0x97, 0xF6,
+ 0xE6, 0xB5, 0xAA, 0xF6, 0xE7, 0x8B, 0xBC, 0xF6,
+ 0xE9, 0x83, 0x8E, 0xF6, 0xE4, 0xBE, 0x86, 0xF6,
+ 0xE5, 0x86, 0xB7, 0xF6, 0xE5, 0x8B, 0x9E, 0xF6,
+ 0xE6, 0x93, 0x84, 0xF6, 0xE6, 0xAB, 0x93, 0xF6,
+ 0xE7, 0x88, 0x90, 0xF6, 0xE7, 0x9B, 0xA7, 0xF6,
+ 0xE8, 0x80, 0x81, 0xF6, 0xE8, 0x98, 0x86, 0xF6,
+ 0xE8, 0x99, 0x9C, 0xF6, 0xE8, 0xB7, 0xAF, 0xF6,
+ 0xE9, 0x9C, 0xB2, 0xF6, 0xE9, 0xAD, 0xAF, 0xF6,
+ 0xE9, 0xB7, 0xBA, 0xF6, 0xE7, 0xA2, 0x8C, 0xF6,
+ 0xE7, 0xA5, 0xBF, 0xF6, 0xE7, 0xB6, 0xA0, 0xF6,
+ 0xE8, 0x8F, 0x89, 0xF6, 0xE9, 0x8C, 0x84, 0xF6,
+ 0xE9, 0xB9, 0xBF, 0xF6, 0xE8, 0xAB, 0x96, 0xF6,
+ 0xE5, 0xA3, 0x9F, 0xF6, 0xE5, 0xBC, 0x84, 0xF6,
+ 0xE7, 0xB1, 0xA0, 0xF6, 0xE8, 0x81, 0xBE, 0xF6,
+ 0xE7, 0x89, 0xA2, 0xF6, 0xE7, 0xA3, 0x8A, 0xF6,
+ 0xE8, 0xB3, 0x82, 0xF6, 0xE9, 0x9B, 0xB7, 0xF6,
+ 0xE5, 0xA3, 0x98, 0xF6, 0xE5, 0xB1, 0xA2, 0xF6,
+ 0xE6, 0xA8, 0x93, 0xF6, 0xE6, 0xB7, 0x9A, 0xF6,
+ 0xE6, 0xBC, 0x8F, 0xF6, 0xE7, 0xB4, 0xAF, 0xF6,
+ 0xE7, 0xB8, 0xB7, 0xF6, 0xE9, 0x99, 0x8B, 0xF6,
+ 0xE5, 0x8B, 0x92, 0xF6, 0xE8, 0x82, 0x8B, 0xF6,
+ 0xE5, 0x87, 0x9C, 0xF6, 0xE5, 0x87, 0x8C, 0xF6,
+ 0xE7, 0xA8, 0x9C, 0xF6, 0xE7, 0xB6, 0xBE, 0xF6,
+ 0xE8, 0x8F, 0xB1, 0xF6, 0xE9, 0x99, 0xB5, 0xF6,
+ 0xE8, 0xAE, 0x80, 0xF6, 0xE6, 0x8B, 0x8F, 0xF6,
+ 0xE6, 0xA8, 0x82, 0xF6, 0xE8, 0xAB, 0xBE, 0xF6,
+ 0xE4, 0xB8, 0xB9, 0xF6, 0xE5, 0xAF, 0xA7, 0xF6,
+ 0xE6, 0x80, 0x92, 0xF6, 0xE7, 0x8E, 0x87, 0xF6,
+ 0xE7, 0x95, 0xB0, 0xF6, 0xE5, 0x8C, 0x97, 0xF6,
+ 0xE7, 0xA3, 0xBB, 0xF6, 0xE4, 0xBE, 0xBF, 0xF6,
+ 0xE5, 0xBE, 0xA9, 0xF6, 0xE4, 0xB8, 0x8D, 0xF6,
+ 0xE6, 0xB3, 0x8C, 0xF6, 0xE6, 0x95, 0xB8, 0xF6,
+ 0xE7, 0xB4, 0xA2, 0xF6, 0xE5, 0x8F, 0x83, 0xF6,
+ 0xE5, 0xA1, 0x9E, 0xF6, 0xE7, 0x9C, 0x81, 0xF6,
+ 0xE8, 0x91, 0x89, 0xF6, 0xE8, 0xAA, 0xAA, 0xF6,
+ 0xE6, 0xAE, 0xBA, 0xF6, 0xE8, 0xBE, 0xB0, 0xF6,
+ 0xE6, 0xB2, 0x88, 0xF6, 0xE6, 0x8B, 0xBE, 0xF6,
+ 0xE8, 0x8B, 0xA5, 0xF6, 0xE6, 0x8E, 0xA0, 0xF6,
+ 0xE7, 0x95, 0xA5, 0xF6, 0xE4, 0xBA, 0xAE, 0xF6,
+ 0xE5, 0x85, 0xA9, 0xF6, 0xE5, 0x87, 0x89, 0xF6,
+ 0xE6, 0xA2, 0x81, 0xF6, 0xE7, 0xB3, 0xA7, 0xF6,
+ 0xE8, 0x89, 0xAF, 0xF6, 0xE8, 0xAB, 0x92, 0xF6,
+ 0xE9, 0x87, 0x8F, 0xF6, 0xE5, 0x8B, 0xB5, 0xF6,
+ 0xE5, 0x91, 0x82, 0xF6, 0xE5, 0xA5, 0xB3, 0xF6,
+ 0xE5, 0xBB, 0xAC, 0xF6, 0xE6, 0x97, 0x85, 0xF6,
+ 0xE6, 0xBF, 0xBE, 0xF6, 0xE7, 0xA4, 0xAA, 0xF6,
+ 0xE9, 0x96, 0xAD, 0xF6, 0xE9, 0xA9, 0xAA, 0xF6,
+ 0xE9, 0xBA, 0x97, 0xF6, 0xE9, 0xBB, 0x8E, 0xF6,
+ 0xE5, 0x8A, 0x9B, 0xF6, 0xE6, 0x9B, 0x86, 0xF6,
+ 0xE6, 0xAD, 0xB7, 0xF6, 0xE8, 0xBD, 0xA2, 0xF6,
+ 0xE5, 0xB9, 0xB4, 0xF6, 0xE6, 0x86, 0x90, 0xF6,
+ 0xE6, 0x88, 0x80, 0xF6, 0xE6, 0x92, 0x9A, 0xF6,
+ 0xE6, 0xBC, 0xA3, 0xF6, 0xE7, 0x85, 0x89, 0xF6,
+ 0xE7, 0x92, 0x89, 0xF6, 0xE7, 0xA7, 0x8A, 0xF6,
+ 0xE7, 0xB7, 0xB4, 0xF6, 0xE8, 0x81, 0xAF, 0xF6,
+ 0xE8, 0xBC, 0xA6, 0xF6, 0xE8, 0x93, 0xAE, 0xF6,
+ 0xE9, 0x80, 0xA3, 0xF6, 0xE9, 0x8D, 0x8A, 0xF6,
+ 0xE5, 0x88, 0x97, 0xF6, 0xE5, 0x8A, 0xA3, 0xF6,
+ 0xE5, 0x92, 0xBD, 0xF6, 0xE7, 0x83, 0x88, 0xF6,
+ 0xE8, 0xA3, 0x82, 0xF6, 0xE8, 0xAA, 0xAA, 0xF6,
+ 0xE5, 0xBB, 0x89, 0xF6, 0xE5, 0xBF, 0xB5, 0xF6,
+ 0xE6, 0x8D, 0xBB, 0xF6, 0xE6, 0xAE, 0xAE, 0xF6,
+ 0xE7, 0xB0, 0xBE, 0xF6, 0xE7, 0x8D, 0xB5, 0xF6,
+ 0xE4, 0xBB, 0xA4, 0xF6, 0xE5, 0x9B, 0xB9, 0xF6,
+ 0xE5, 0xAF, 0xA7, 0xF6, 0xE5, 0xB6, 0xBA, 0xF6,
+ 0xE6, 0x80, 0x9C, 0xF6, 0xE7, 0x8E, 0xB2, 0xF6,
+ 0xE7, 0x91, 0xA9, 0xF6, 0xE7, 0xBE, 0x9A, 0xF6,
+ 0xE8, 0x81, 0x86, 0xF6, 0xE9, 0x88, 0xB4, 0xF6,
+ 0xE9, 0x9B, 0xB6, 0xF6, 0xE9, 0x9D, 0x88, 0xF6,
+ 0xE9, 0xA0, 0x98, 0xF6, 0xE4, 0xBE, 0x8B, 0xF6,
+ 0xE7, 0xA6, 0xAE, 0xF6, 0xE9, 0x86, 0xB4, 0xF6,
+ 0xE9, 0x9A, 0xB8, 0xF6, 0xE6, 0x83, 0xA1, 0xF6,
+ 0xE4, 0xBA, 0x86, 0xF6, 0xE5, 0x83, 0x9A, 0xF6,
+ 0xE5, 0xAF, 0xAE, 0xF6, 0xE5, 0xB0, 0xBF, 0xF6,
+ 0xE6, 0x96, 0x99, 0xF6, 0xE6, 0xA8, 0x82, 0xF6,
+ 0xE7, 0x87, 0x8E, 0xF6, 0xE7, 0x99, 0x82, 0xF6,
+ 0xE8, 0x93, 0xBC, 0xF6, 0xE9, 0x81, 0xBC, 0xF6,
+ 0xE9, 0xBE, 0x8D, 0xF6, 0xE6, 0x9A, 0x88, 0xF6,
+ 0xE9, 0x98, 0xAE, 0xF6, 0xE5, 0x8A, 0x89, 0xF6,
+ 0xE6, 0x9D, 0xBB, 0xF6, 0xE6, 0x9F, 0xB3, 0xF6,
+ 0xE6, 0xB5, 0x81, 0xF6, 0xE6, 0xBA, 0x9C, 0xF6,
+ 0xE7, 0x90, 0x89, 0xF6, 0xE7, 0x95, 0x99, 0xF6,
+ 0xE7, 0xA1, 0xAB, 0xF6, 0xE7, 0xB4, 0x90, 0xF6,
+ 0xE9, 0xA1, 0x9E, 0xF6, 0xE5, 0x85, 0xAD, 0xF6,
+ 0xE6, 0x88, 0xAE, 0xF6, 0xE9, 0x99, 0xB8, 0xF6,
+ 0xE5, 0x80, 0xAB, 0xF6, 0xE5, 0xB4, 0x99, 0xF6,
+ 0xE6, 0xB7, 0xAA, 0xF6, 0xE8, 0xBC, 0xAA, 0xF6,
+ 0xE5, 0xBE, 0x8B, 0xF6, 0xE6, 0x85, 0x84, 0xF6,
+ 0xE6, 0xA0, 0x97, 0xF6, 0xE7, 0x8E, 0x87, 0xF6,
+ 0xE9, 0x9A, 0x86, 0xF6, 0xE5, 0x88, 0xA9, 0xF6,
+ 0xE5, 0x90, 0x8F, 0xF6, 0xE5, 0xB1, 0xA5, 0xF6,
+ 0xE6, 0x98, 0x93, 0xF6, 0xE6, 0x9D, 0x8E, 0xF6,
+ 0xE6, 0xA2, 0xA8, 0xF6, 0xE6, 0xB3, 0xA5, 0xF6,
+ 0xE7, 0x90, 0x86, 0xF6, 0xE7, 0x97, 0xA2, 0xF6,
+ 0xE7, 0xBD, 0xB9, 0xF6, 0xE8, 0xA3, 0x8F, 0xF6,
+ 0xE8, 0xA3, 0xA1, 0xF6, 0xE9, 0x87, 0x8C, 0xF6,
+ 0xE9, 0x9B, 0xA2, 0xF6, 0xE5, 0x8C, 0xBF, 0xF6,
+ 0xE6, 0xBA, 0xBA, 0xF6, 0xE5, 0x90, 0x9D, 0xF6,
+ 0xE7, 0x87, 0x90, 0xF6, 0xE7, 0x92, 0x98, 0xF6,
+ 0xE8, 0x97, 0xBA, 0xF6, 0xE9, 0x9A, 0xA3, 0xF6,
+ 0xE9, 0xB1, 0x97, 0xF6, 0xE9, 0xBA, 0x9F, 0xF6,
+ 0xE6, 0x9E, 0x97, 0xF6, 0xE6, 0xB7, 0x8B, 0xF6,
+ 0xE8, 0x87, 0xA8, 0xF6, 0xE7, 0xAB, 0x8B, 0xF6,
+ 0xE7, 0xAC, 0xA0, 0xF6, 0xE7, 0xB2, 0x92, 0xF6,
+ 0xE7, 0x8B, 0x80, 0xF6, 0xE7, 0x82, 0x99, 0xF6,
+ 0xE8, 0xAD, 0x98, 0xF6, 0xE4, 0xBB, 0x80, 0xF6,
+ 0xE8, 0x8C, 0xB6, 0xF6, 0xE5, 0x88, 0xBA, 0xF6,
+ 0xE5, 0x88, 0x87, 0xF6, 0xE5, 0xBA, 0xA6, 0xF6,
+ 0xE6, 0x8B, 0x93, 0xF6, 0xE7, 0xB3, 0x96, 0xF6,
+ 0xE5, 0xAE, 0x85, 0xF6, 0xE6, 0xB4, 0x9E, 0xF6,
+ 0xE6, 0x9A, 0xB4, 0xF6, 0xE8, 0xBC, 0xBB, 0xF6,
+ 0xE8, 0xA1, 0x8C, 0xF6, 0xE9, 0x99, 0x8D, 0xF6,
+ 0xE8, 0xA6, 0x8B, 0xF6, 0xE5, 0xBB, 0x93, 0xF6,
+ 0xE5, 0x85, 0x80, 0xF6, 0xE5, 0x97, 0x80, 0xF6,
+ 0xE5, 0xA1, 0x9A, 0xF6, 0xE6, 0x99, 0xB4, 0xF6,
+ 0xE5, 0x87, 0x9E, 0xF6, 0xE7, 0x8C, 0xAA, 0xF6,
+ 0xE7, 0x9B, 0x8A, 0xF6, 0xE7, 0xA4, 0xBC, 0xF6,
+ 0xE7, 0xA5, 0x9E, 0xF6, 0xE7, 0xA5, 0xA5, 0xF6,
+ 0xE7, 0xA6, 0x8F, 0xF6, 0xE9, 0x9D, 0x96, 0xF6,
+ 0xE7, 0xB2, 0xBE, 0xF6, 0xE7, 0xBE, 0xBD, 0xF6,
+ 0xE8, 0x98, 0x92, 0xF6, 0xE8, 0xAB, 0xB8, 0xF6,
+ 0xE9, 0x80, 0xB8, 0xF6, 0xE9, 0x83, 0xBD, 0xF6,
+ 0xE9, 0xA3, 0xAF, 0xF6, 0xE9, 0xA3, 0xBC, 0xF6,
+ 0xE9, 0xA4, 0xA8, 0xF6, 0xE9, 0xB6, 0xB4, 0xF6,
+ 0xE4, 0xBE, 0xAE, 0xF6, 0xE5, 0x83, 0xA7, 0xF6,
+ 0xE5, 0x85, 0x8D, 0xF6, 0xE5, 0x8B, 0x89, 0xF6,
+ 0xE5, 0x8B, 0xA4, 0xF6, 0xE5, 0x8D, 0x91, 0xF6,
+ 0xE5, 0x96, 0x9D, 0xF6, 0xE5, 0x98, 0x86, 0xF6,
+ 0xE5, 0x99, 0xA8, 0xF6, 0xE5, 0xA1, 0x80, 0xF6,
+ 0xE5, 0xA2, 0xA8, 0xF6, 0xE5, 0xB1, 0xA4, 0xF6,
+ 0xE5, 0xB1, 0xAE, 0xF6, 0xE6, 0x82, 0x94, 0xF6,
+ 0xE6, 0x85, 0xA8, 0xF6, 0xE6, 0x86, 0x8E, 0xF6,
+ 0xE6, 0x87, 0xB2, 0xF6, 0xE6, 0x95, 0x8F, 0xF6,
+ 0xE6, 0x97, 0xA2, 0xF6, 0xE6, 0x9A, 0x91, 0xF6,
+ 0xE6, 0xA2, 0x85, 0xF6, 0xE6, 0xB5, 0xB7, 0xF6,
+ 0xE6, 0xB8, 0x9A, 0xF6, 0xE6, 0xBC, 0xA2, 0xF6,
+ 0xE7, 0x85, 0xAE, 0xF6, 0xE7, 0x88, 0xAB, 0xF6,
+ 0xE7, 0x90, 0xA2, 0xF6, 0xE7, 0xA2, 0x91, 0xF6,
+ 0xE7, 0xA4, 0xBE, 0xF6, 0xE7, 0xA5, 0x89, 0xF6,
+ 0xE7, 0xA5, 0x88, 0xF6, 0xE7, 0xA5, 0x90, 0xF6,
+ 0xE7, 0xA5, 0x96, 0xF6, 0xE7, 0xA5, 0x9D, 0xF6,
+ 0xE7, 0xA6, 0x8D, 0xF6, 0xE7, 0xA6, 0x8E, 0xF6,
+ 0xE7, 0xA9, 0x80, 0xF6, 0xE7, 0xAA, 0x81, 0xF6,
+ 0xE7, 0xAF, 0x80, 0xF6, 0xE7, 0xB7, 0xB4, 0xF6,
+ 0xE7, 0xB8, 0x89, 0xF6, 0xE7, 0xB9, 0x81, 0xF6,
+ 0xE7, 0xBD, 0xB2, 0xF6, 0xE8, 0x80, 0x85, 0xF6,
+ 0xE8, 0x87, 0xAD, 0xF6, 0xE8, 0x89, 0xB9, 0xF6,
+ 0xE8, 0x89, 0xB9, 0xF6, 0xE8, 0x91, 0x97, 0xF6,
+ 0xE8, 0xA4, 0x90, 0xF6, 0xE8, 0xA6, 0x96, 0xF6,
+ 0xE8, 0xAC, 0x81, 0xF6, 0xE8, 0xAC, 0xB9, 0xF6,
+ 0xE8, 0xB3, 0x93, 0xF6, 0xE8, 0xB4, 0x88, 0xF6,
+ 0xE8, 0xBE, 0xB6, 0xF6, 0xE9, 0x80, 0xB8, 0xF6,
+ 0xE9, 0x9B, 0xA3, 0xF6, 0xE9, 0x9F, 0xBF, 0xF6,
+ 0xE9, 0xA0, 0xBB, 0x66, 0x66, 0x66, 0x69, 0x66,
+ 0x6C, 0x66, 0x66, 0x69, 0x66, 0x66, 0x6C, 0x73,
+ 0x74, 0x73, 0x74, 0xD5, 0xB4, 0xD5, 0xB6, 0xD5,
+ 0xB4, 0xD5, 0xA5, 0xD5, 0xB4, 0xD5, 0xAB, 0xD5,
+ 0xBE, 0xD5, 0xB6, 0xD5, 0xB4, 0xD5, 0xAD, 0xF6,
+ 0xD7, 0x99, 0xD6, 0xB4, 0xF6, 0xD7, 0xB2, 0xD6,
+ 0xB7, 0xD7, 0xA2, 0xD7, 0x90, 0xD7, 0x93, 0xD7,
+ 0x94, 0xD7, 0x9B, 0xD7, 0x9C, 0xD7, 0x9D, 0xD7,
+ 0xA8, 0xD7, 0xAA, 0x2B, 0xF6, 0xD7, 0xA9, 0xD7,
+ 0x81, 0xF6, 0xD7, 0xA9, 0xD7, 0x82, 0xF6, 0xD7,
+ 0xA9, 0xD6, 0xBC, 0xD7, 0x81, 0xF6, 0xD7, 0xA9,
+ 0xD6, 0xBC, 0xD7, 0x82, 0xF6, 0xD7, 0x90, 0xD6,
+ 0xB7, 0xF6, 0xD7, 0x90, 0xD6, 0xB8, 0xF6, 0xD7,
+ 0x90, 0xD6, 0xBC, 0xF6, 0xD7, 0x91, 0xD6, 0xBC,
+ 0xF6, 0xD7, 0x92, 0xD6, 0xBC, 0xF6, 0xD7, 0x93,
+ 0xD6, 0xBC, 0xF6, 0xD7, 0x94, 0xD6, 0xBC, 0xF6,
+ 0xD7, 0x95, 0xD6, 0xBC, 0xF6, 0xD7, 0x96, 0xD6,
+ 0xBC, 0xF6, 0xD7, 0x98, 0xD6, 0xBC, 0xF6, 0xD7,
+ 0x99, 0xD6, 0xBC, 0xF6, 0xD7, 0x9A, 0xD6, 0xBC,
+ 0xF6, 0xD7, 0x9B, 0xD6, 0xBC, 0xF6, 0xD7, 0x9C,
+ 0xD6, 0xBC, 0xF6, 0xD7, 0x9E, 0xD6, 0xBC, 0xF6,
+ 0xD7, 0xA0, 0xD6, 0xBC, 0xF6, 0xD7, 0xA1, 0xD6,
+ 0xBC, 0xF6, 0xD7, 0xA3, 0xD6, 0xBC, 0xF6, 0xD7,
+ 0xA4, 0xD6, 0xBC, 0xF6, 0xD7, 0xA6, 0xD6, 0xBC,
+ 0xF6, 0xD7, 0xA7, 0xD6, 0xBC, 0xF6, 0xD7, 0xA8,
+ 0xD6, 0xBC, 0xF6, 0xD7, 0xA9, 0xD6, 0xBC, 0xF6,
+ 0xD7, 0xAA, 0xD6, 0xBC, 0xF6, 0xD7, 0x95, 0xD6,
+ 0xB9, 0xF6, 0xD7, 0x91, 0xD6, 0xBF, 0xF6, 0xD7,
+ 0x9B, 0xD6, 0xBF, 0xF6, 0xD7, 0xA4, 0xD6, 0xBF,
+ 0xD7, 0x90, 0xD7, 0x9C, 0xD9, 0xB1, 0xD9, 0xB1,
+ 0xD9, 0xBB, 0xD9, 0xBB, 0xD9, 0xBB, 0xD9, 0xBB,
+ 0xD9, 0xBE, 0xD9, 0xBE, 0xD9, 0xBE, 0xD9, 0xBE,
+ 0xDA, 0x80, 0xDA, 0x80, 0xDA, 0x80, 0xDA, 0x80,
+ 0xD9, 0xBA, 0xD9, 0xBA, 0xD9, 0xBA, 0xD9, 0xBA,
+ 0xD9, 0xBF, 0xD9, 0xBF, 0xD9, 0xBF, 0xD9, 0xBF,
+ 0xD9, 0xB9, 0xD9, 0xB9, 0xD9, 0xB9, 0xD9, 0xB9,
+ 0xDA, 0xA4, 0xDA, 0xA4, 0xDA, 0xA4, 0xDA, 0xA4,
+ 0xDA, 0xA6, 0xDA, 0xA6, 0xDA, 0xA6, 0xDA, 0xA6,
+ 0xDA, 0x84, 0xDA, 0x84, 0xDA, 0x84, 0xDA, 0x84,
+ 0xDA, 0x83, 0xDA, 0x83, 0xDA, 0x83, 0xDA, 0x83,
+ 0xDA, 0x86, 0xDA, 0x86, 0xDA, 0x86, 0xDA, 0x86,
+ 0xDA, 0x87, 0xDA, 0x87, 0xDA, 0x87, 0xDA, 0x87,
+ 0xDA, 0x8D, 0xDA, 0x8D, 0xDA, 0x8C, 0xDA, 0x8C,
+ 0xDA, 0x8E, 0xDA, 0x8E, 0xDA, 0x88, 0xDA, 0x88,
+ 0xDA, 0x98, 0xDA, 0x98, 0xDA, 0x91, 0xDA, 0x91,
+ 0xDA, 0xA9, 0xDA, 0xA9, 0xDA, 0xA9, 0xDA, 0xA9,
+ 0xDA, 0xAF, 0xDA, 0xAF, 0xDA, 0xAF, 0xDA, 0xAF,
+ 0xDA, 0xB3, 0xDA, 0xB3, 0xDA, 0xB3, 0xDA, 0xB3,
+ 0xDA, 0xB1, 0xDA, 0xB1, 0xDA, 0xB1, 0xDA, 0xB1,
+ 0xDA, 0xBA, 0xDA, 0xBA, 0xDA, 0xBB, 0xDA, 0xBB,
+ 0xDA, 0xBB, 0xDA, 0xBB, 0xDB, 0x95, 0xD9, 0x94,
+ 0xDB, 0x95, 0xD9, 0x94, 0xDB, 0x81, 0xDB, 0x81,
+ 0xDB, 0x81, 0xDB, 0x81, 0xDA, 0xBE, 0xDA, 0xBE,
+ 0xDA, 0xBE, 0xDA, 0xBE, 0xDB, 0x92, 0xDB, 0x92,
+ 0xDB, 0x92, 0xD9, 0x94, 0xDB, 0x92, 0xD9, 0x94,
+ 0xDA, 0xAD, 0xDA, 0xAD, 0xDA, 0xAD, 0xDA, 0xAD,
+ 0xDB, 0x87, 0xDB, 0x87, 0xDB, 0x86, 0xDB, 0x86,
+ 0xDB, 0x88, 0xDB, 0x88, 0xDB, 0x87, 0xD9, 0xB4,
+ 0xDB, 0x8B, 0xDB, 0x8B, 0xDB, 0x85, 0xDB, 0x85,
+ 0xDB, 0x89, 0xDB, 0x89, 0xDB, 0x90, 0xDB, 0x90,
+ 0xDB, 0x90, 0xDB, 0x90, 0xD9, 0x89, 0xD9, 0x89,
+ 0xD9, 0x8A, 0xD9, 0x94, 0xD8, 0xA7, 0xD9, 0x8A,
+ 0xD9, 0x94, 0xD8, 0xA7, 0xD9, 0x8A, 0xD9, 0x94,
+ 0xDB, 0x95, 0xD9, 0x8A, 0xD9, 0x94, 0xDB, 0x95,
+ 0xD9, 0x8A, 0xD9, 0x94, 0xD9, 0x88, 0xD9, 0x8A,
+ 0xD9, 0x94, 0xD9, 0x88, 0xD9, 0x8A, 0xD9, 0x94,
+ 0xDB, 0x87, 0xD9, 0x8A, 0xD9, 0x94, 0xDB, 0x87,
+ 0xD9, 0x8A, 0xD9, 0x94, 0xDB, 0x86, 0xD9, 0x8A,
+ 0xD9, 0x94, 0xDB, 0x86, 0xD9, 0x8A, 0xD9, 0x94,
+ 0xDB, 0x88, 0xD9, 0x8A, 0xD9, 0x94, 0xDB, 0x88,
+ 0xD9, 0x8A, 0xD9, 0x94, 0xDB, 0x90, 0xD9, 0x8A,
+ 0xD9, 0x94, 0xDB, 0x90, 0xD9, 0x8A, 0xD9, 0x94,
+ 0xDB, 0x90, 0xD9, 0x8A, 0xD9, 0x94, 0xD9, 0x89,
+ 0xD9, 0x8A, 0xD9, 0x94, 0xD9, 0x89, 0xD9, 0x8A,
+ 0xD9, 0x94, 0xD9, 0x89, 0xDB, 0x8C, 0xDB, 0x8C,
+ 0xDB, 0x8C, 0xDB, 0x8C, 0xD9, 0x8A, 0xD9, 0x94,
+ 0xD8, 0xAC, 0xD9, 0x8A, 0xD9, 0x94, 0xD8, 0xAD,
+ 0xD9, 0x8A, 0xD9, 0x94, 0xD9, 0x85, 0xD9, 0x8A,
+ 0xD9, 0x94, 0xD9, 0x89, 0xD9, 0x8A, 0xD9, 0x94,
+ 0xD9, 0x8A, 0xD8, 0xA8, 0xD8, 0xAC, 0xD8, 0xA8,
+ 0xD8, 0xAD, 0xD8, 0xA8, 0xD8, 0xAE, 0xD8, 0xA8,
+ 0xD9, 0x85, 0xD8, 0xA8, 0xD9, 0x89, 0xD8, 0xA8,
+ 0xD9, 0x8A, 0xD8, 0xAA, 0xD8, 0xAC, 0xD8, 0xAA,
+ 0xD8, 0xAD, 0xD8, 0xAA, 0xD8, 0xAE, 0xD8, 0xAA,
+ 0xD9, 0x85, 0xD8, 0xAA, 0xD9, 0x89, 0xD8, 0xAA,
+ 0xD9, 0x8A, 0xD8, 0xAB, 0xD8, 0xAC, 0xD8, 0xAB,
+ 0xD9, 0x85, 0xD8, 0xAB, 0xD9, 0x89, 0xD8, 0xAB,
+ 0xD9, 0x8A, 0xD8, 0xAC, 0xD8, 0xAD, 0xD8, 0xAC,
+ 0xD9, 0x85, 0xD8, 0xAD, 0xD8, 0xAC, 0xD8, 0xAD,
+ 0xD9, 0x85, 0xD8, 0xAE, 0xD8, 0xAC, 0xD8, 0xAE,
+ 0xD8, 0xAD, 0xD8, 0xAE, 0xD9, 0x85, 0xD8, 0xB3,
+ 0xD8, 0xAC, 0xD8, 0xB3, 0xD8, 0xAD, 0xD8, 0xB3,
+ 0xD8, 0xAE, 0xD8, 0xB3, 0xD9, 0x85, 0xD8, 0xB5,
+ 0xD8, 0xAD, 0xD8, 0xB5, 0xD9, 0x85, 0xD8, 0xB6,
+ 0xD8, 0xAC, 0xD8, 0xB6, 0xD8, 0xAD, 0xD8, 0xB6,
+ 0xD8, 0xAE, 0xD8, 0xB6, 0xD9, 0x85, 0xD8, 0xB7,
+ 0xD8, 0xAD, 0xD8, 0xB7, 0xD9, 0x85, 0xD8, 0xB8,
+ 0xD9, 0x85, 0xD8, 0xB9, 0xD8, 0xAC, 0xD8, 0xB9,
+ 0xD9, 0x85, 0xD8, 0xBA, 0xD8, 0xAC, 0xD8, 0xBA,
+ 0xD9, 0x85, 0xD9, 0x81, 0xD8, 0xAC, 0xD9, 0x81,
+ 0xD8, 0xAD, 0xD9, 0x81, 0xD8, 0xAE, 0xD9, 0x81,
+ 0xD9, 0x85, 0xD9, 0x81, 0xD9, 0x89, 0xD9, 0x81,
+ 0xD9, 0x8A, 0xD9, 0x82, 0xD8, 0xAD, 0xD9, 0x82,
+ 0xD9, 0x85, 0xD9, 0x82, 0xD9, 0x89, 0xD9, 0x82,
+ 0xD9, 0x8A, 0xD9, 0x83, 0xD8, 0xA7, 0xD9, 0x83,
+ 0xD8, 0xAC, 0xD9, 0x83, 0xD8, 0xAD, 0xD9, 0x83,
+ 0xD8, 0xAE, 0xD9, 0x83, 0xD9, 0x84, 0xD9, 0x83,
+ 0xD9, 0x85, 0xD9, 0x83, 0xD9, 0x89, 0xD9, 0x83,
+ 0xD9, 0x8A, 0xD9, 0x84, 0xD8, 0xAC, 0xD9, 0x84,
+ 0xD8, 0xAD, 0xD9, 0x84, 0xD8, 0xAE, 0xD9, 0x84,
+ 0xD9, 0x85, 0xD9, 0x84, 0xD9, 0x89, 0xD9, 0x84,
+ 0xD9, 0x8A, 0xD9, 0x85, 0xD8, 0xAC, 0xD9, 0x85,
+ 0xD8, 0xAD, 0xD9, 0x85, 0xD8, 0xAE, 0xD9, 0x85,
+ 0xD9, 0x85, 0xD9, 0x85, 0xD9, 0x89, 0xD9, 0x85,
+ 0xD9, 0x8A, 0xD9, 0x86, 0xD8, 0xAC, 0xD9, 0x86,
+ 0xD8, 0xAD, 0xD9, 0x86, 0xD8, 0xAE, 0xD9, 0x86,
+ 0xD9, 0x85, 0xD9, 0x86, 0xD9, 0x89, 0xD9, 0x86,
+ 0xD9, 0x8A, 0xD9, 0x87, 0xD8, 0xAC, 0xD9, 0x87,
+ 0xD9, 0x85, 0xD9, 0x87, 0xD9, 0x89, 0xD9, 0x87,
+ 0xD9, 0x8A, 0xD9, 0x8A, 0xD8, 0xAC, 0xD9, 0x8A,
+ 0xD8, 0xAD, 0xD9, 0x8A, 0xD8, 0xAE, 0xD9, 0x8A,
+ 0xD9, 0x85, 0xD9, 0x8A, 0xD9, 0x89, 0xD9, 0x8A,
+ 0xD9, 0x8A, 0xD8, 0xB0, 0xD9, 0xB0, 0xD8, 0xB1,
+ 0xD9, 0xB0, 0xD9, 0x89, 0xD9, 0xB0, 0x20, 0xD9,
+ 0x8C, 0xD9, 0x91, 0x20, 0xD9, 0x8D, 0xD9, 0x91,
+ 0x20, 0xD9, 0x8E, 0xD9, 0x91, 0x20, 0xD9, 0x8F,
+ 0xD9, 0x91, 0x20, 0xD9, 0x90, 0xD9, 0x91, 0x20,
+ 0xD9, 0x91, 0xD9, 0xB0, 0xD9, 0x8A, 0xD9, 0x94,
+ 0xD8, 0xB1, 0xD9, 0x8A, 0xD9, 0x94, 0xD8, 0xB2,
+ 0xD9, 0x8A, 0xD9, 0x94, 0xD9, 0x85, 0xD9, 0x8A,
+ 0xD9, 0x94, 0xD9, 0x86, 0xD9, 0x8A, 0xD9, 0x94,
+ 0xD9, 0x89, 0xD9, 0x8A, 0xD9, 0x94, 0xD9, 0x8A,
+ 0xD8, 0xA8, 0xD8, 0xB1, 0xD8, 0xA8, 0xD8, 0xB2,
+ 0xD8, 0xA8, 0xD9, 0x85, 0xD8, 0xA8, 0xD9, 0x86,
+ 0xD8, 0xA8, 0xD9, 0x89, 0xD8, 0xA8, 0xD9, 0x8A,
+ 0xD8, 0xAA, 0xD8, 0xB1, 0xD8, 0xAA, 0xD8, 0xB2,
+ 0xD8, 0xAA, 0xD9, 0x85, 0xD8, 0xAA, 0xD9, 0x86,
+ 0xD8, 0xAA, 0xD9, 0x89, 0xD8, 0xAA, 0xD9, 0x8A,
+ 0xD8, 0xAB, 0xD8, 0xB1, 0xD8, 0xAB, 0xD8, 0xB2,
+ 0xD8, 0xAB, 0xD9, 0x85, 0xD8, 0xAB, 0xD9, 0x86,
+ 0xD8, 0xAB, 0xD9, 0x89, 0xD8, 0xAB, 0xD9, 0x8A,
+ 0xD9, 0x81, 0xD9, 0x89, 0xD9, 0x81, 0xD9, 0x8A,
+ 0xD9, 0x82, 0xD9, 0x89, 0xD9, 0x82, 0xD9, 0x8A,
+ 0xD9, 0x83, 0xD8, 0xA7, 0xD9, 0x83, 0xD9, 0x84,
+ 0xD9, 0x83, 0xD9, 0x85, 0xD9, 0x83, 0xD9, 0x89,
+ 0xD9, 0x83, 0xD9, 0x8A, 0xD9, 0x84, 0xD9, 0x85,
+ 0xD9, 0x84, 0xD9, 0x89, 0xD9, 0x84, 0xD9, 0x8A,
+ 0xD9, 0x85, 0xD8, 0xA7, 0xD9, 0x85, 0xD9, 0x85,
+ 0xD9, 0x86, 0xD8, 0xB1, 0xD9, 0x86, 0xD8, 0xB2,
+ 0xD9, 0x86, 0xD9, 0x85, 0xD9, 0x86, 0xD9, 0x86,
+ 0xD9, 0x86, 0xD9, 0x89, 0xD9, 0x86, 0xD9, 0x8A,
+ 0xD9, 0x89, 0xD9, 0xB0, 0xD9, 0x8A, 0xD8, 0xB1,
+ 0xD9, 0x8A, 0xD8, 0xB2, 0xD9, 0x8A, 0xD9, 0x85,
+ 0xD9, 0x8A, 0xD9, 0x86, 0xD9, 0x8A, 0xD9, 0x89,
+ 0xD9, 0x8A, 0xD9, 0x8A, 0xD9, 0x8A, 0xD9, 0x94,
+ 0xD8, 0xAC, 0xD9, 0x8A, 0xD9, 0x94, 0xD8, 0xAD,
+ 0xD9, 0x8A, 0xD9, 0x94, 0xD8, 0xAE, 0xD9, 0x8A,
+ 0xD9, 0x94, 0xD9, 0x85, 0xD9, 0x8A, 0xD9, 0x94,
+ 0xD9, 0x87, 0xD8, 0xA8, 0xD8, 0xAC, 0xD8, 0xA8,
+ 0xD8, 0xAD, 0xD8, 0xA8, 0xD8, 0xAE, 0xD8, 0xA8,
+ 0xD9, 0x85, 0xD8, 0xA8, 0xD9, 0x87, 0xD8, 0xAA,
+ 0xD8, 0xAC, 0xD8, 0xAA, 0xD8, 0xAD, 0xD8, 0xAA,
+ 0xD8, 0xAE, 0xD8, 0xAA, 0xD9, 0x85, 0xD8, 0xAA,
+ 0xD9, 0x87, 0xD8, 0xAB, 0xD9, 0x85, 0xD8, 0xAC,
+ 0xD8, 0xAD, 0xD8, 0xAC, 0xD9, 0x85, 0xD8, 0xAD,
+ 0xD8, 0xAC, 0xD8, 0xAD, 0xD9, 0x85, 0xD8, 0xAE,
+ 0xD8, 0xAC, 0xD8, 0xAE, 0xD9, 0x85, 0xD8, 0xB3,
+ 0xD8, 0xAC, 0xD8, 0xB3, 0xD8, 0xAD, 0xD8, 0xB3,
+ 0xD8, 0xAE, 0xD8, 0xB3, 0xD9, 0x85, 0xD8, 0xB5,
+ 0xD8, 0xAD, 0xD8, 0xB5, 0xD8, 0xAE, 0xD8, 0xB5,
+ 0xD9, 0x85, 0xD8, 0xB6, 0xD8, 0xAC, 0xD8, 0xB6,
+ 0xD8, 0xAD, 0xD8, 0xB6, 0xD8, 0xAE, 0xD8, 0xB6,
+ 0xD9, 0x85, 0xD8, 0xB7, 0xD8, 0xAD, 0xD8, 0xB8,
+ 0xD9, 0x85, 0xD8, 0xB9, 0xD8, 0xAC, 0xD8, 0xB9,
+ 0xD9, 0x85, 0xD8, 0xBA, 0xD8, 0xAC, 0xD8, 0xBA,
+ 0xD9, 0x85, 0xD9, 0x81, 0xD8, 0xAC, 0xD9, 0x81,
+ 0xD8, 0xAD, 0xD9, 0x81, 0xD8, 0xAE, 0xD9, 0x81,
+ 0xD9, 0x85, 0xD9, 0x82, 0xD8, 0xAD, 0xD9, 0x82,
+ 0xD9, 0x85, 0xD9, 0x83, 0xD8, 0xAC, 0xD9, 0x83,
+ 0xD8, 0xAD, 0xD9, 0x83, 0xD8, 0xAE, 0xD9, 0x83,
+ 0xD9, 0x84, 0xD9, 0x83, 0xD9, 0x85, 0xD9, 0x84,
+ 0xD8, 0xAC, 0xD9, 0x84, 0xD8, 0xAD, 0xD9, 0x84,
+ 0xD8, 0xAE, 0xD9, 0x84, 0xD9, 0x85, 0xD9, 0x84,
+ 0xD9, 0x87, 0xD9, 0x85, 0xD8, 0xAC, 0xD9, 0x85,
+ 0xD8, 0xAD, 0xD9, 0x85, 0xD8, 0xAE, 0xD9, 0x85,
+ 0xD9, 0x85, 0xD9, 0x86, 0xD8, 0xAC, 0xD9, 0x86,
+ 0xD8, 0xAD, 0xD9, 0x86, 0xD8, 0xAE, 0xD9, 0x86,
+ 0xD9, 0x85, 0xD9, 0x86, 0xD9, 0x87, 0xD9, 0x87,
+ 0xD8, 0xAC, 0xD9, 0x87, 0xD9, 0x85, 0xD9, 0x87,
+ 0xD9, 0xB0, 0xD9, 0x8A, 0xD8, 0xAC, 0xD9, 0x8A,
+ 0xD8, 0xAD, 0xD9, 0x8A, 0xD8, 0xAE, 0xD9, 0x8A,
+ 0xD9, 0x85, 0xD9, 0x8A, 0xD9, 0x87, 0xD9, 0x8A,
+ 0xD9, 0x94, 0xD9, 0x85, 0xD9, 0x8A, 0xD9, 0x94,
+ 0xD9, 0x87, 0xD8, 0xA8, 0xD9, 0x85, 0xD8, 0xA8,
+ 0xD9, 0x87, 0xD8, 0xAA, 0xD9, 0x85, 0xD8, 0xAA,
+ 0xD9, 0x87, 0xD8, 0xAB, 0xD9, 0x85, 0xD8, 0xAB,
+ 0xD9, 0x87, 0xD8, 0xB3, 0xD9, 0x85, 0xD8, 0xB3,
+ 0xD9, 0x87, 0xD8, 0xB4, 0xD9, 0x85, 0xD8, 0xB4,
+ 0xD9, 0x87, 0xD9, 0x83, 0xD9, 0x84, 0xD9, 0x83,
+ 0xD9, 0x85, 0xD9, 0x84, 0xD9, 0x85, 0xD9, 0x86,
+ 0xD9, 0x85, 0xD9, 0x86, 0xD9, 0x87, 0xD9, 0x8A,
+ 0xD9, 0x85, 0xD9, 0x8A, 0xD9, 0x87, 0xD9, 0x80,
+ 0xD9, 0x8E, 0xD9, 0x91, 0xD9, 0x80, 0xD9, 0x8F,
+ 0xD9, 0x91, 0xD9, 0x80, 0xD9, 0x90, 0xD9, 0x91,
+ 0xD8, 0xB7, 0xD9, 0x89, 0xD8, 0xB7, 0xD9, 0x8A,
+ 0xD8, 0xB9, 0xD9, 0x89, 0xD8, 0xB9, 0xD9, 0x8A,
+ 0xD8, 0xBA, 0xD9, 0x89, 0xD8, 0xBA, 0xD9, 0x8A,
+ 0xD8, 0xB3, 0xD9, 0x89, 0xD8, 0xB3, 0xD9, 0x8A,
+ 0xD8, 0xB4, 0xD9, 0x89, 0xD8, 0xB4, 0xD9, 0x8A,
+ 0xD8, 0xAD, 0xD9, 0x89, 0xD8, 0xAD, 0xD9, 0x8A,
+ 0xD8, 0xAC, 0xD9, 0x89, 0xD8, 0xAC, 0xD9, 0x8A,
+ 0xD8, 0xAE, 0xD9, 0x89, 0xD8, 0xAE, 0xD9, 0x8A,
+ 0xD8, 0xB5, 0xD9, 0x89, 0xD8, 0xB5, 0xD9, 0x8A,
+ 0xD8, 0xB6, 0xD9, 0x89, 0xD8, 0xB6, 0xD9, 0x8A,
+ 0xD8, 0xB4, 0xD8, 0xAC, 0xD8, 0xB4, 0xD8, 0xAD,
+ 0xD8, 0xB4, 0xD8, 0xAE, 0xD8, 0xB4, 0xD9, 0x85,
+ 0xD8, 0xB4, 0xD8, 0xB1, 0xD8, 0xB3, 0xD8, 0xB1,
+ 0xD8, 0xB5, 0xD8, 0xB1, 0xD8, 0xB6, 0xD8, 0xB1,
+ 0xD8, 0xB7, 0xD9, 0x89, 0xD8, 0xB7, 0xD9, 0x8A,
+ 0xD8, 0xB9, 0xD9, 0x89, 0xD8, 0xB9, 0xD9, 0x8A,
+ 0xD8, 0xBA, 0xD9, 0x89, 0xD8, 0xBA, 0xD9, 0x8A,
+ 0xD8, 0xB3, 0xD9, 0x89, 0xD8, 0xB3, 0xD9, 0x8A,
+ 0xD8, 0xB4, 0xD9, 0x89, 0xD8, 0xB4, 0xD9, 0x8A,
+ 0xD8, 0xAD, 0xD9, 0x89, 0xD8, 0xAD, 0xD9, 0x8A,
+ 0xD8, 0xAC, 0xD9, 0x89, 0xD8, 0xAC, 0xD9, 0x8A,
+ 0xD8, 0xAE, 0xD9, 0x89, 0xD8, 0xAE, 0xD9, 0x8A,
+ 0xD8, 0xB5, 0xD9, 0x89, 0xD8, 0xB5, 0xD9, 0x8A,
+ 0xD8, 0xB6, 0xD9, 0x89, 0xD8, 0xB6, 0xD9, 0x8A,
+ 0xD8, 0xB4, 0xD8, 0xAC, 0xD8, 0xB4, 0xD8, 0xAD,
+ 0xD8, 0xB4, 0xD8, 0xAE, 0xD8, 0xB4, 0xD9, 0x85,
+ 0xD8, 0xB4, 0xD8, 0xB1, 0xD8, 0xB3, 0xD8, 0xB1,
+ 0xD8, 0xB5, 0xD8, 0xB1, 0xD8, 0xB6, 0xD8, 0xB1,
+ 0xD8, 0xB4, 0xD8, 0xAC, 0xD8, 0xB4, 0xD8, 0xAD,
+ 0xD8, 0xB4, 0xD8, 0xAE, 0xD8, 0xB4, 0xD9, 0x85,
+ 0xD8, 0xB3, 0xD9, 0x87, 0xD8, 0xB4, 0xD9, 0x87,
+ 0xD8, 0xB7, 0xD9, 0x85, 0xD8, 0xB3, 0xD8, 0xAC,
+ 0xD8, 0xB3, 0xD8, 0xAD, 0xD8, 0xB3, 0xD8, 0xAE,
+ 0xD8, 0xB4, 0xD8, 0xAC, 0xD8, 0xB4, 0xD8, 0xAD,
+ 0xD8, 0xB4, 0xD8, 0xAE, 0xD8, 0xB7, 0xD9, 0x85,
+ 0xD8, 0xB8, 0xD9, 0x85, 0xD8, 0xA7, 0xD9, 0x8B,
+ 0xD8, 0xA7, 0xD9, 0x8B, 0xD8, 0xAA, 0xD8, 0xAC,
+ 0xD9, 0x85, 0xD8, 0xAA, 0xD8, 0xAD, 0xD8, 0xAC,
+ 0xD8, 0xAA, 0xD8, 0xAD, 0xD8, 0xAC, 0xD8, 0xAA,
+ 0xD8, 0xAD, 0xD9, 0x85, 0xD8, 0xAA, 0xD8, 0xAE,
+ 0xD9, 0x85, 0xD8, 0xAA, 0xD9, 0x85, 0xD8, 0xAC,
+ 0xD8, 0xAA, 0xD9, 0x85, 0xD8, 0xAD, 0xD8, 0xAA,
+ 0xD9, 0x85, 0xD8, 0xAE, 0xD8, 0xAC, 0xD9, 0x85,
+ 0xD8, 0xAD, 0xD8, 0xAC, 0xD9, 0x85, 0xD8, 0xAD,
+ 0xD8, 0xAD, 0xD9, 0x85, 0xD9, 0x8A, 0xD8, 0xAD,
+ 0xD9, 0x85, 0xD9, 0x89, 0xD8, 0xB3, 0xD8, 0xAD,
+ 0xD8, 0xAC, 0xD8, 0xB3, 0xD8, 0xAC, 0xD8, 0xAD,
+ 0xD8, 0xB3, 0xD8, 0xAC, 0xD9, 0x89, 0xD8, 0xB3,
+ 0xD9, 0x85, 0xD8, 0xAD, 0xD8, 0xB3, 0xD9, 0x85,
+ 0xD8, 0xAD, 0xD8, 0xB3, 0xD9, 0x85, 0xD8, 0xAC,
+ 0xD8, 0xB3, 0xD9, 0x85, 0xD9, 0x85, 0xD8, 0xB3,
+ 0xD9, 0x85, 0xD9, 0x85, 0xD8, 0xB5, 0xD8, 0xAD,
+ 0xD8, 0xAD, 0xD8, 0xB5, 0xD8, 0xAD, 0xD8, 0xAD,
+ 0xD8, 0xB5, 0xD9, 0x85, 0xD9, 0x85, 0xD8, 0xB4,
+ 0xD8, 0xAD, 0xD9, 0x85, 0xD8, 0xB4, 0xD8, 0xAD,
+ 0xD9, 0x85, 0xD8, 0xB4, 0xD8, 0xAC, 0xD9, 0x8A,
+ 0xD8, 0xB4, 0xD9, 0x85, 0xD8, 0xAE, 0xD8, 0xB4,
+ 0xD9, 0x85, 0xD8, 0xAE, 0xD8, 0xB4, 0xD9, 0x85,
+ 0xD9, 0x85, 0xD8, 0xB4, 0xD9, 0x85, 0xD9, 0x85,
+ 0xD8, 0xB6, 0xD8, 0xAD, 0xD9, 0x89, 0xD8, 0xB6,
+ 0xD8, 0xAE, 0xD9, 0x85, 0xD8, 0xB6, 0xD8, 0xAE,
+ 0xD9, 0x85, 0xD8, 0xB7, 0xD9, 0x85, 0xD8, 0xAD,
+ 0xD8, 0xB7, 0xD9, 0x85, 0xD8, 0xAD, 0xD8, 0xB7,
+ 0xD9, 0x85, 0xD9, 0x85, 0xD8, 0xB7, 0xD9, 0x85,
+ 0xD9, 0x8A, 0xD8, 0xB9, 0xD8, 0xAC, 0xD9, 0x85,
+ 0xD8, 0xB9, 0xD9, 0x85, 0xD9, 0x85, 0xD8, 0xB9,
+ 0xD9, 0x85, 0xD9, 0x85, 0xD8, 0xB9, 0xD9, 0x85,
+ 0xD9, 0x89, 0xD8, 0xBA, 0xD9, 0x85, 0xD9, 0x85,
+ 0xD8, 0xBA, 0xD9, 0x85, 0xD9, 0x8A, 0xD8, 0xBA,
+ 0xD9, 0x85, 0xD9, 0x89, 0xD9, 0x81, 0xD8, 0xAE,
+ 0xD9, 0x85, 0xD9, 0x81, 0xD8, 0xAE, 0xD9, 0x85,
+ 0xD9, 0x82, 0xD9, 0x85, 0xD8, 0xAD, 0xD9, 0x82,
+ 0xD9, 0x85, 0xD9, 0x85, 0xD9, 0x84, 0xD8, 0xAD,
+ 0xD9, 0x85, 0xD9, 0x84, 0xD8, 0xAD, 0xD9, 0x8A,
+ 0xD9, 0x84, 0xD8, 0xAD, 0xD9, 0x89, 0xD9, 0x84,
+ 0xD8, 0xAC, 0xD8, 0xAC, 0xD9, 0x84, 0xD8, 0xAC,
+ 0xD8, 0xAC, 0xD9, 0x84, 0xD8, 0xAE, 0xD9, 0x85,
+ 0xD9, 0x84, 0xD8, 0xAE, 0xD9, 0x85, 0xD9, 0x84,
+ 0xD9, 0x85, 0xD8, 0xAD, 0xD9, 0x84, 0xD9, 0x85,
+ 0xD8, 0xAD, 0xD9, 0x85, 0xD8, 0xAD, 0xD8, 0xAC,
+ 0xD9, 0x85, 0xD8, 0xAD, 0xD9, 0x85, 0xD9, 0x85,
+ 0xD8, 0xAD, 0xD9, 0x8A, 0xD9, 0x85, 0xD8, 0xAC,
+ 0xD8, 0xAD, 0xD9, 0x85, 0xD8, 0xAC, 0xD9, 0x85,
+ 0xD9, 0x85, 0xD8, 0xAE, 0xD8, 0xAC, 0xD9, 0x85,
+ 0xD8, 0xAE, 0xD9, 0x85, 0xD9, 0x85, 0xD8, 0xAC,
+ 0xD8, 0xAE, 0xD9, 0x87, 0xD9, 0x85, 0xD8, 0xAC,
+ 0xD9, 0x87, 0xD9, 0x85, 0xD9, 0x85, 0xD9, 0x86,
+ 0xD8, 0xAD, 0xD9, 0x85, 0xD9, 0x86, 0xD8, 0xAD,
+ 0xD9, 0x89, 0xD9, 0x86, 0xD8, 0xAC, 0xD9, 0x85,
+ 0xD9, 0x86, 0xD8, 0xAC, 0xD9, 0x85, 0xD9, 0x86,
+ 0xD8, 0xAC, 0xD9, 0x89, 0xD9, 0x86, 0xD9, 0x85,
+ 0xD9, 0x8A, 0xD9, 0x86, 0xD9, 0x85, 0xD9, 0x89,
+ 0xD9, 0x8A, 0xD9, 0x85, 0xD9, 0x85, 0xD9, 0x8A,
+ 0xD9, 0x85, 0xD9, 0x85, 0xD8, 0xA8, 0xD8, 0xAE,
+ 0xD9, 0x8A, 0xD8, 0xAA, 0xD8, 0xAC, 0xD9, 0x8A,
+ 0xD8, 0xAA, 0xD8, 0xAC, 0xD9, 0x89, 0xD8, 0xAA,
+ 0xD8, 0xAE, 0xD9, 0x8A, 0xD8, 0xAA, 0xD8, 0xAE,
+ 0xD9, 0x89, 0xD8, 0xAA, 0xD9, 0x85, 0xD9, 0x8A,
+ 0xD8, 0xAA, 0xD9, 0x85, 0xD9, 0x89, 0xD8, 0xAC,
+ 0xD9, 0x85, 0xD9, 0x8A, 0xD8, 0xAC, 0xD8, 0xAD,
+ 0xD9, 0x89, 0xD8, 0xAC, 0xD9, 0x85, 0xD9, 0x89,
+ 0xD8, 0xB3, 0xD8, 0xAE, 0xD9, 0x89, 0xD8, 0xB5,
+ 0xD8, 0xAD, 0xD9, 0x8A, 0xD8, 0xB4, 0xD8, 0xAD,
+ 0xD9, 0x8A, 0xD8, 0xB6, 0xD8, 0xAD, 0xD9, 0x8A,
+ 0xD9, 0x84, 0xD8, 0xAC, 0xD9, 0x8A, 0xD9, 0x84,
+ 0xD9, 0x85, 0xD9, 0x8A, 0xD9, 0x8A, 0xD8, 0xAD,
+ 0xD9, 0x8A, 0xD9, 0x8A, 0xD8, 0xAC, 0xD9, 0x8A,
+ 0xD9, 0x8A, 0xD9, 0x85, 0xD9, 0x8A, 0xD9, 0x85,
+ 0xD9, 0x85, 0xD9, 0x8A, 0xD9, 0x82, 0xD9, 0x85,
+ 0xD9, 0x8A, 0xD9, 0x86, 0xD8, 0xAD, 0xD9, 0x8A,
+ 0xD9, 0x82, 0xD9, 0x85, 0xD8, 0xAD, 0xD9, 0x84,
+ 0xD8, 0xAD, 0xD9, 0x85, 0xD8, 0xB9, 0xD9, 0x85,
+ 0xD9, 0x8A, 0xD9, 0x83, 0xD9, 0x85, 0xD9, 0x8A,
+ 0xD9, 0x86, 0xD8, 0xAC, 0xD8, 0xAD, 0xD9, 0x85,
+ 0xD8, 0xAE, 0xD9, 0x8A, 0xD9, 0x84, 0xD8, 0xAC,
+ 0xD9, 0x85, 0xD9, 0x83, 0xD9, 0x85, 0xD9, 0x85,
+ 0xD9, 0x84, 0xD8, 0xAC, 0xD9, 0x85, 0xD9, 0x86,
+ 0xD8, 0xAC, 0xD8, 0xAD, 0xD8, 0xAC, 0xD8, 0xAD,
+ 0xD9, 0x8A, 0xD8, 0xAD, 0xD8, 0xAC, 0xD9, 0x8A,
+ 0xD9, 0x85, 0xD8, 0xAC, 0xD9, 0x8A, 0xD9, 0x81,
+ 0xD9, 0x85, 0xD9, 0x8A, 0xD8, 0xA8, 0xD8, 0xAD,
+ 0xD9, 0x8A, 0xD9, 0x83, 0xD9, 0x85, 0xD9, 0x85,
+ 0xD8, 0xB9, 0xD8, 0xAC, 0xD9, 0x85, 0xD8, 0xB5,
+ 0xD9, 0x85, 0xD9, 0x85, 0xD8, 0xB3, 0xD8, 0xAE,
+ 0xD9, 0x8A, 0xD9, 0x86, 0xD8, 0xAC, 0xD9, 0x8A,
+ 0xD8, 0xB5, 0xD9, 0x84, 0xDB, 0x92, 0xD9, 0x82,
+ 0xD9, 0x84, 0xDB, 0x92, 0xD8, 0xA7, 0xD9, 0x84,
+ 0xD9, 0x84, 0xD9, 0x87, 0xD8, 0xA7, 0xD9, 0x83,
+ 0xD8, 0xA8, 0xD8, 0xB1, 0xD9, 0x85, 0xD8, 0xAD,
+ 0xD9, 0x85, 0xD8, 0xAF, 0xD8, 0xB5, 0xD9, 0x84,
+ 0xD8, 0xB9, 0xD9, 0x85, 0xD8, 0xB1, 0xD8, 0xB3,
+ 0xD9, 0x88, 0xD9, 0x84, 0xD8, 0xB9, 0xD9, 0x84,
+ 0xD9, 0x8A, 0xD9, 0x87, 0xD9, 0x88, 0xD8, 0xB3,
+ 0xD9, 0x84, 0xD9, 0x85, 0xD8, 0xB5, 0xD9, 0x84,
+ 0xD9, 0x89, 0xD8, 0xB5, 0xD9, 0x84, 0xD9, 0x89,
+ 0x20, 0xD8, 0xA7, 0xD9, 0x84, 0xD9, 0x84, 0xD9,
+ 0x87, 0x20, 0xD8, 0xB9, 0xD9, 0x84, 0xD9, 0x8A,
+ 0xD9, 0x87, 0x20, 0xD9, 0x88, 0xD8, 0xB3, 0xD9,
+ 0x84, 0xD9, 0x85, 0xD8, 0xAC, 0xD9, 0x84, 0x20,
+ 0xD8, 0xAC, 0xD9, 0x84, 0xD8, 0xA7, 0xD9, 0x84,
+ 0xD9, 0x87, 0xD8, 0xB1, 0xDB, 0x8C, 0xD8, 0xA7,
+ 0xD9, 0x84, 0x2E, 0x2E, 0xE2, 0x80, 0x94, 0xE2,
+ 0x80, 0x93, 0x5F, 0x5F, 0x28, 0x29, 0x7B, 0x7D,
+ 0xE3, 0x80, 0x94, 0xE3, 0x80, 0x95, 0xE3, 0x80,
+ 0x90, 0xE3, 0x80, 0x91, 0xE3, 0x80, 0x8A, 0xE3,
+ 0x80, 0x8B, 0xE3, 0x80, 0x88, 0xE3, 0x80, 0x89,
+ 0xE3, 0x80, 0x8C, 0xE3, 0x80, 0x8D, 0xE3, 0x80,
+ 0x8E, 0xE3, 0x80, 0x8F, 0x20, 0xCC, 0x85, 0x20,
+ 0xCC, 0x85, 0x20, 0xCC, 0x85, 0x20, 0xCC, 0x85,
+ 0x5F, 0x5F, 0x5F, 0x2C, 0xE3, 0x80, 0x81, 0x2E,
+ 0x3B, 0x3A, 0x3F, 0x21, 0xE2, 0x80, 0x94, 0x28,
+ 0x29, 0x7B, 0x7D, 0xE3, 0x80, 0x94, 0xE3, 0x80,
+ 0x95, 0x23, 0x26, 0x2A, 0x2B, 0x2D, 0x3C, 0x3E,
+ 0x3D, 0x5C, 0x24, 0x25, 0x40, 0x20, 0xD9, 0x8B,
+ 0xD9, 0x80, 0xD9, 0x8B, 0x20, 0xD9, 0x8C, 0x20,
+ 0xD9, 0x8D, 0x20, 0xD9, 0x8E, 0xD9, 0x80, 0xD9,
+ 0x8E, 0x20, 0xD9, 0x8F, 0xD9, 0x80, 0xD9, 0x8F,
+ 0x20, 0xD9, 0x90, 0xD9, 0x80, 0xD9, 0x90, 0x20,
+ 0xD9, 0x91, 0xD9, 0x80, 0xD9, 0x91, 0x20, 0xD9,
+ 0x92, 0xD9, 0x80, 0xD9, 0x92, 0xD8, 0xA1, 0xD8,
+ 0xA7, 0xD9, 0x93, 0xD8, 0xA7, 0xD9, 0x93, 0xD8,
+ 0xA7, 0xD9, 0x94, 0xD8, 0xA7, 0xD9, 0x94, 0xD9,
+ 0x88, 0xD9, 0x94, 0xD9, 0x88, 0xD9, 0x94, 0xD8,
+ 0xA7, 0xD9, 0x95, 0xD8, 0xA7, 0xD9, 0x95, 0xD9,
+ 0x8A, 0xD9, 0x94, 0xD9, 0x8A, 0xD9, 0x94, 0xD9,
+ 0x8A, 0xD9, 0x94, 0xD9, 0x8A, 0xD9, 0x94, 0xD8,
+ 0xA7, 0xD8, 0xA7, 0xD8, 0xA8, 0xD8, 0xA8, 0xD8,
+ 0xA8, 0xD8, 0xA8, 0xD8, 0xA9, 0xD8, 0xA9, 0xD8,
+ 0xAA, 0xD8, 0xAA, 0xD8, 0xAA, 0xD8, 0xAA, 0xD8,
+ 0xAB, 0xD8, 0xAB, 0xD8, 0xAB, 0xD8, 0xAB, 0xD8,
+ 0xAC, 0xD8, 0xAC, 0xD8, 0xAC, 0xD8, 0xAC, 0xD8,
+ 0xAD, 0xD8, 0xAD, 0xD8, 0xAD, 0xD8, 0xAD, 0xD8,
+ 0xAE, 0xD8, 0xAE, 0xD8, 0xAE, 0xD8, 0xAE, 0xD8,
+ 0xAF, 0xD8, 0xAF, 0xD8, 0xB0, 0xD8, 0xB0, 0xD8,
+ 0xB1, 0xD8, 0xB1, 0xD8, 0xB2, 0xD8, 0xB2, 0xD8,
+ 0xB3, 0xD8, 0xB3, 0xD8, 0xB3, 0xD8, 0xB3, 0xD8,
+ 0xB4, 0xD8, 0xB4, 0xD8, 0xB4, 0xD8, 0xB4, 0xD8,
+ 0xB5, 0xD8, 0xB5, 0xD8, 0xB5, 0xD8, 0xB5, 0xD8,
+ 0xB6, 0xD8, 0xB6, 0xD8, 0xB6, 0xD8, 0xB6, 0xD8,
+ 0xB7, 0xD8, 0xB7, 0xD8, 0xB7, 0xD8, 0xB7, 0xD8,
+ 0xB8, 0xD8, 0xB8, 0xD8, 0xB8, 0xD8, 0xB8, 0xD8,
+ 0xB9, 0xD8, 0xB9, 0xD8, 0xB9, 0xD8, 0xB9, 0xD8,
+ 0xBA, 0xD8, 0xBA, 0xD8, 0xBA, 0xD8, 0xBA, 0xD9,
+ 0x81, 0xD9, 0x81, 0xD9, 0x81, 0xD9, 0x81, 0xD9,
+ 0x82, 0xD9, 0x82, 0xD9, 0x82, 0xD9, 0x82, 0xD9,
+ 0x83, 0xD9, 0x83, 0xD9, 0x83, 0xD9, 0x83, 0xD9,
+ 0x84, 0xD9, 0x84, 0xD9, 0x84, 0xD9, 0x84, 0xD9,
+ 0x85, 0xD9, 0x85, 0xD9, 0x85, 0xD9, 0x85, 0xD9,
+ 0x86, 0xD9, 0x86, 0xD9, 0x86, 0xD9, 0x86, 0xD9,
+ 0x87, 0xD9, 0x87, 0xD9, 0x87, 0xD9, 0x87, 0xD9,
+ 0x88, 0xD9, 0x88, 0xD9, 0x89, 0xD9, 0x89, 0xD9,
+ 0x8A, 0xD9, 0x8A, 0xD9, 0x8A, 0xD9, 0x8A, 0xD9,
+ 0x84, 0xD8, 0xA7, 0xD9, 0x93, 0xD9, 0x84, 0xD8,
+ 0xA7, 0xD9, 0x93, 0xD9, 0x84, 0xD8, 0xA7, 0xD9,
+ 0x94, 0xD9, 0x84, 0xD8, 0xA7, 0xD9, 0x94, 0xD9,
+ 0x84, 0xD8, 0xA7, 0xD9, 0x95, 0xD9, 0x84, 0xD8,
+ 0xA7, 0xD9, 0x95, 0xD9, 0x84, 0xD8, 0xA7, 0xD9,
+ 0x84, 0xD8, 0xA7, 0x21, 0x22, 0x23, 0x24, 0x25,
+ 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D,
+ 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+ 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D,
+ 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
+ 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D,
+ 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D,
+ 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D,
+ 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D,
+ 0x7E, 0xE2, 0xA6, 0x85, 0xE2, 0xA6, 0x86, 0xE3,
+ 0x80, 0x82, 0xE3, 0x80, 0x8C, 0xE3, 0x80, 0x8D,
+ 0xE3, 0x80, 0x81, 0xE3, 0x83, 0xBB, 0xE3, 0x83,
+ 0xB2, 0xE3, 0x82, 0xA1, 0xE3, 0x82, 0xA3, 0xE3,
+ 0x82, 0xA5, 0xE3, 0x82, 0xA7, 0xE3, 0x82, 0xA9,
+ 0xE3, 0x83, 0xA3, 0xE3, 0x83, 0xA5, 0xE3, 0x83,
+ 0xA7, 0xE3, 0x83, 0x83, 0xE3, 0x83, 0xBC, 0xE3,
+ 0x82, 0xA2, 0xE3, 0x82, 0xA4, 0xE3, 0x82, 0xA6,
+ 0xE3, 0x82, 0xA8, 0xE3, 0x82, 0xAA, 0xE3, 0x82,
+ 0xAB, 0xE3, 0x82, 0xAD, 0xE3, 0x82, 0xAF, 0xE3,
+ 0x82, 0xB1, 0xE3, 0x82, 0xB3, 0xE3, 0x82, 0xB5,
+ 0xE3, 0x82, 0xB7, 0xE3, 0x82, 0xB9, 0xE3, 0x82,
+ 0xBB, 0xE3, 0x82, 0xBD, 0xE3, 0x82, 0xBF, 0xE3,
+ 0x83, 0x81, 0xE3, 0x83, 0x84, 0xE3, 0x83, 0x86,
+ 0xE3, 0x83, 0x88, 0xE3, 0x83, 0x8A, 0xE3, 0x83,
+ 0x8B, 0xE3, 0x83, 0x8C, 0xE3, 0x83, 0x8D, 0xE3,
+ 0x83, 0x8E, 0xE3, 0x83, 0x8F, 0xE3, 0x83, 0x92,
+ 0xE3, 0x83, 0x95, 0xE3, 0x83, 0x98, 0xE3, 0x83,
+ 0x9B, 0xE3, 0x83, 0x9E, 0xE3, 0x83, 0x9F, 0xE3,
+ 0x83, 0xA0, 0xE3, 0x83, 0xA1, 0xE3, 0x83, 0xA2,
+ 0xE3, 0x83, 0xA4, 0xE3, 0x83, 0xA6, 0xE3, 0x83,
+ 0xA8, 0xE3, 0x83, 0xA9, 0xE3, 0x83, 0xAA, 0xE3,
+ 0x83, 0xAB, 0xE3, 0x83, 0xAC, 0xE3, 0x83, 0xAD,
+ 0xE3, 0x83, 0xAF, 0xE3, 0x83, 0xB3, 0xE3, 0x82,
+ 0x99, 0xE3, 0x82, 0x9A, 0xE1, 0x85, 0xA0, 0xE1,
+ 0x84, 0x80, 0xE1, 0x84, 0x81, 0xE1, 0x86, 0xAA,
+ 0xE1, 0x84, 0x82, 0xE1, 0x86, 0xAC, 0xE1, 0x86,
+ 0xAD, 0xE1, 0x84, 0x83, 0xE1, 0x84, 0x84, 0xE1,
+ 0x84, 0x85, 0xE1, 0x86, 0xB0, 0xE1, 0x86, 0xB1,
+ 0xE1, 0x86, 0xB2, 0xE1, 0x86, 0xB3, 0xE1, 0x86,
+ 0xB4, 0xE1, 0x86, 0xB5, 0xE1, 0x84, 0x9A, 0xE1,
+ 0x84, 0x86, 0xE1, 0x84, 0x87, 0xE1, 0x84, 0x88,
+ 0xE1, 0x84, 0xA1, 0xE1, 0x84, 0x89, 0xE1, 0x84,
+ 0x8A, 0xE1, 0x84, 0x8B, 0xE1, 0x84, 0x8C, 0xE1,
+ 0x84, 0x8D, 0xE1, 0x84, 0x8E, 0xE1, 0x84, 0x8F,
+ 0xE1, 0x84, 0x90, 0xE1, 0x84, 0x91, 0xE1, 0x84,
+ 0x92, 0xE1, 0x85, 0xA1, 0xE1, 0x85, 0xA2, 0xE1,
+ 0x85, 0xA3, 0xE1, 0x85, 0xA4, 0xE1, 0x85, 0xA5,
+ 0xE1, 0x85, 0xA6, 0xE1, 0x85, 0xA7, 0xE1, 0x85,
+ 0xA8, 0xE1, 0x85, 0xA9, 0xE1, 0x85, 0xAA, 0xE1,
+ 0x85, 0xAB, 0xE1, 0x85, 0xAC, 0xE1, 0x85, 0xAD,
+ 0xE1, 0x85, 0xAE, 0xE1, 0x85, 0xAF, 0xE1, 0x85,
+ 0xB0, 0xE1, 0x85, 0xB1, 0xE1, 0x85, 0xB2, 0xE1,
+ 0x85, 0xB3, 0xE1, 0x85, 0xB4, 0xE1, 0x85, 0xB5,
+ 0xC2, 0xA2, 0xC2, 0xA3, 0xC2, 0xAC, 0x20, 0xCC,
+ 0x84, 0xC2, 0xA6, 0xC2, 0xA5, 0xE2, 0x82, 0xA9,
+ 0xE2, 0x94, 0x82, 0xE2, 0x86, 0x90, 0xE2, 0x86,
+ 0x91, 0xE2, 0x86, 0x92, 0xE2, 0x86, 0x93, 0xE2,
+ 0x96, 0xA0, 0xE2, 0x97, 0x8B, 0xF6, 0xF0, 0x9D,
+ 0x85, 0x97, 0xF0, 0x9D, 0x85, 0xA5, 0xF6, 0xF0,
+ 0x9D, 0x85, 0x98, 0xF0, 0x9D, 0x85, 0xA5, 0xF6,
+ 0xF0, 0x9D, 0x85, 0x98, 0xF0, 0x9D, 0x85, 0xA5,
+ 0xF0, 0x9D, 0x85, 0xAE, 0xF6, 0xF0, 0x9D, 0x85,
+ 0x98, 0xF0, 0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85,
+ 0xAF, 0xF6, 0xF0, 0x9D, 0x85, 0x98, 0xF0, 0x9D,
+ 0x85, 0xA5, 0xF0, 0x9D, 0x85, 0xB0, 0xF6, 0xF0,
+ 0x9D, 0x85, 0x98, 0xF0, 0x9D, 0x85, 0xA5, 0xF0,
+ 0x9D, 0x85, 0xB1, 0xF6, 0xF0, 0x9D, 0x85, 0x98,
+ 0xF0, 0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85, 0xB2,
+ 0xF6, 0xF0, 0x9D, 0x86, 0xB9, 0xF0, 0x9D, 0x85,
+ 0xA5, 0xF6, 0xF0, 0x9D, 0x86, 0xBA, 0xF0, 0x9D,
+ 0x85, 0xA5, 0xF6, 0xF0, 0x9D, 0x86, 0xB9, 0xF0,
+ 0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85, 0xAE, 0xF6,
+ 0xF0, 0x9D, 0x86, 0xBA, 0xF0, 0x9D, 0x85, 0xA5,
+ 0xF0, 0x9D, 0x85, 0xAE, 0xF6, 0xF0, 0x9D, 0x86,
+ 0xB9, 0xF0, 0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85,
+ 0xAF, 0xF6, 0xF0, 0x9D, 0x86, 0xBA, 0xF0, 0x9D,
+ 0x85, 0xA5, 0xF0, 0x9D, 0x85, 0xAF, 0x41, 0x42,
+ 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A,
+ 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52,
+ 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A,
+ 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+ 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
+ 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7A, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
+ 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E,
+ 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56,
+ 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64,
+ 0x65, 0x66, 0x67, 0x69, 0x6A, 0x6B, 0x6C, 0x6D,
+ 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7A, 0x41, 0x42, 0x43,
+ 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B,
+ 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53,
+ 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61,
+ 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7A, 0x41, 0x43, 0x44, 0x47, 0x4A, 0x4B, 0x4E,
+ 0x4F, 0x50, 0x51, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x66,
+ 0x68, 0x69, 0x6A, 0x6B, 0x6D, 0x6E, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7A, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D,
+ 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7A, 0x41, 0x42, 0x44,
+ 0x45, 0x46, 0x47, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E,
+ 0x4F, 0x50, 0x51, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
+ 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E,
+ 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x79, 0x7A, 0x41, 0x42, 0x44, 0x45,
+ 0x46, 0x47, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4F,
+ 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x61,
+ 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7A, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D,
+ 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7A, 0x41, 0x42, 0x43,
+ 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B,
+ 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53,
+ 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61,
+ 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7A, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D,
+ 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7A, 0x41, 0x42, 0x43,
+ 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B,
+ 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53,
+ 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61,
+ 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7A, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D,
+ 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7A, 0x41, 0x42, 0x43,
+ 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B,
+ 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53,
+ 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61,
+ 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7A, 0xCE, 0x91, 0xCE, 0x92, 0xCE, 0x93, 0xCE,
+ 0x94, 0xCE, 0x95, 0xCE, 0x96, 0xCE, 0x97, 0xCE,
+ 0x98, 0xCE, 0x99, 0xCE, 0x9A, 0xCE, 0x9B, 0xCE,
+ 0x9C, 0xCE, 0x9D, 0xCE, 0x9E, 0xCE, 0x9F, 0xCE,
+ 0xA0, 0xCE, 0xA1, 0xCE, 0x98, 0xCE, 0xA3, 0xCE,
+ 0xA4, 0xCE, 0xA5, 0xCE, 0xA6, 0xCE, 0xA7, 0xCE,
+ 0xA8, 0xCE, 0xA9, 0xE2, 0x88, 0x87, 0xCE, 0xB1,
+ 0xCE, 0xB2, 0xCE, 0xB3, 0xCE, 0xB4, 0xCE, 0xB5,
+ 0xCE, 0xB6, 0xCE, 0xB7, 0xCE, 0xB8, 0xCE, 0xB9,
+ 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xBC, 0xCE, 0xBD,
+ 0xCE, 0xBE, 0xCE, 0xBF, 0xCF, 0x80, 0xCF, 0x81,
+ 0xCF, 0x82, 0xCF, 0x83, 0xCF, 0x84, 0xCF, 0x85,
+ 0xCF, 0x86, 0xCF, 0x87, 0xCF, 0x88, 0xCF, 0x89,
+ 0xE2, 0x88, 0x82, 0xCE, 0xB5, 0xCE, 0xB8, 0xCE,
+ 0xBA, 0xCF, 0x86, 0xCF, 0x81, 0xCF, 0x80, 0xCE,
+ 0x91, 0xCE, 0x92, 0xCE, 0x93, 0xCE, 0x94, 0xCE,
+ 0x95, 0xCE, 0x96, 0xCE, 0x97, 0xCE, 0x98, 0xCE,
+ 0x99, 0xCE, 0x9A, 0xCE, 0x9B, 0xCE, 0x9C, 0xCE,
+ 0x9D, 0xCE, 0x9E, 0xCE, 0x9F, 0xCE, 0xA0, 0xCE,
+ 0xA1, 0xCE, 0x98, 0xCE, 0xA3, 0xCE, 0xA4, 0xCE,
+ 0xA5, 0xCE, 0xA6, 0xCE, 0xA7, 0xCE, 0xA8, 0xCE,
+ 0xA9, 0xE2, 0x88, 0x87, 0xCE, 0xB1, 0xCE, 0xB2,
+ 0xCE, 0xB3, 0xCE, 0xB4, 0xCE, 0xB5, 0xCE, 0xB6,
+ 0xCE, 0xB7, 0xCE, 0xB8, 0xCE, 0xB9, 0xCE, 0xBA,
+ 0xCE, 0xBB, 0xCE, 0xBC, 0xCE, 0xBD, 0xCE, 0xBE,
+ 0xCE, 0xBF, 0xCF, 0x80, 0xCF, 0x81, 0xCF, 0x82,
+ 0xCF, 0x83, 0xCF, 0x84, 0xCF, 0x85, 0xCF, 0x86,
+ 0xCF, 0x87, 0xCF, 0x88, 0xCF, 0x89, 0xE2, 0x88,
+ 0x82, 0xCE, 0xB5, 0xCE, 0xB8, 0xCE, 0xBA, 0xCF,
+ 0x86, 0xCF, 0x81, 0xCF, 0x80, 0xCE, 0x91, 0xCE,
+ 0x92, 0xCE, 0x93, 0xCE, 0x94, 0xCE, 0x95, 0xCE,
+ 0x96, 0xCE, 0x97, 0xCE, 0x98, 0xCE, 0x99, 0xCE,
+ 0x9A, 0xCE, 0x9B, 0xCE, 0x9C, 0xCE, 0x9D, 0xCE,
+ 0x9E, 0xCE, 0x9F, 0xCE, 0xA0, 0xCE, 0xA1, 0xCE,
+ 0x98, 0xCE, 0xA3, 0xCE, 0xA4, 0xCE, 0xA5, 0xCE,
+ 0xA6, 0xCE, 0xA7, 0xCE, 0xA8, 0xCE, 0xA9, 0xE2,
+ 0x88, 0x87, 0xCE, 0xB1, 0xCE, 0xB2, 0xCE, 0xB3,
+ 0xCE, 0xB4, 0xCE, 0xB5, 0xCE, 0xB6, 0xCE, 0xB7,
+ 0xCE, 0xB8, 0xCE, 0xB9, 0xCE, 0xBA, 0xCE, 0xBB,
+ 0xCE, 0xBC, 0xCE, 0xBD, 0xCE, 0xBE, 0xCE, 0xBF,
+ 0xCF, 0x80, 0xCF, 0x81, 0xCF, 0x82, 0xCF, 0x83,
+ 0xCF, 0x84, 0xCF, 0x85, 0xCF, 0x86, 0xCF, 0x87,
+ 0xCF, 0x88, 0xCF, 0x89, 0xE2, 0x88, 0x82, 0xCE,
+ 0xB5, 0xCE, 0xB8, 0xCE, 0xBA, 0xCF, 0x86, 0xCF,
+ 0x81, 0xCF, 0x80, 0xCE, 0x91, 0xCE, 0x92, 0xCE,
+ 0x93, 0xCE, 0x94, 0xCE, 0x95, 0xCE, 0x96, 0xCE,
+ 0x97, 0xCE, 0x98, 0xCE, 0x99, 0xCE, 0x9A, 0xCE,
+ 0x9B, 0xCE, 0x9C, 0xCE, 0x9D, 0xCE, 0x9E, 0xCE,
+ 0x9F, 0xCE, 0xA0, 0xCE, 0xA1, 0xCE, 0x98, 0xCE,
+ 0xA3, 0xCE, 0xA4, 0xCE, 0xA5, 0xCE, 0xA6, 0xCE,
+ 0xA7, 0xCE, 0xA8, 0xCE, 0xA9, 0xE2, 0x88, 0x87,
+ 0xCE, 0xB1, 0xCE, 0xB2, 0xCE, 0xB3, 0xCE, 0xB4,
+ 0xCE, 0xB5, 0xCE, 0xB6, 0xCE, 0xB7, 0xCE, 0xB8,
+ 0xCE, 0xB9, 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xBC,
+ 0xCE, 0xBD, 0xCE, 0xBE, 0xCE, 0xBF, 0xCF, 0x80,
+ 0xCF, 0x81, 0xCF, 0x82, 0xCF, 0x83, 0xCF, 0x84,
+ 0xCF, 0x85, 0xCF, 0x86, 0xCF, 0x87, 0xCF, 0x88,
+ 0xCF, 0x89, 0xE2, 0x88, 0x82, 0xCE, 0xB5, 0xCE,
+ 0xB8, 0xCE, 0xBA, 0xCF, 0x86, 0xCF, 0x81, 0xCF,
+ 0x80, 0xCE, 0x91, 0xCE, 0x92, 0xCE, 0x93, 0xCE,
+ 0x94, 0xCE, 0x95, 0xCE, 0x96, 0xCE, 0x97, 0xCE,
+ 0x98, 0xCE, 0x99, 0xCE, 0x9A, 0xCE, 0x9B, 0xCE,
+ 0x9C, 0xCE, 0x9D, 0xCE, 0x9E, 0xCE, 0x9F, 0xCE,
+ 0xA0, 0xCE, 0xA1, 0xCE, 0x98, 0xCE, 0xA3, 0xCE,
+ 0xA4, 0xCE, 0xA5, 0xCE, 0xA6, 0xCE, 0xA7, 0xCE,
+ 0xA8, 0xCE, 0xA9, 0xE2, 0x88, 0x87, 0xCE, 0xB1,
+ 0xCE, 0xB2, 0xCE, 0xB3, 0xCE, 0xB4, 0xCE, 0xB5,
+ 0xCE, 0xB6, 0xCE, 0xB7, 0xCE, 0xB8, 0xCE, 0xB9,
+ 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xBC, 0xCE, 0xBD,
+ 0xCE, 0xBE, 0xCE, 0xBF, 0xCF, 0x80, 0xCF, 0x81,
+ 0xCF, 0x82, 0xCF, 0x83, 0xCF, 0x84, 0xCF, 0x85,
+ 0xCF, 0x86, 0xCF, 0x87, 0xCF, 0x88, 0xCF, 0x89,
+ 0xE2, 0x88, 0x82, 0xCE, 0xB5, 0xCE, 0xB8, 0xCE,
+ 0xBA, 0xCF, 0x86, 0xCF, 0x81, 0xCF, 0x80, 0x30,
+ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+ 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
+ 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34,
+ 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32,
+ 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30,
+ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+ 0x39, 0xF6, 0xE4, 0xB8, 0xBD, 0xF6, 0xE4, 0xB8,
+ 0xB8, 0xF6, 0xE4, 0xB9, 0x81, 0xF6, 0xF0, 0xA0,
+ 0x84, 0xA2, 0xF6, 0xE4, 0xBD, 0xA0, 0xF6, 0xE4,
+ 0xBE, 0xAE, 0xF6, 0xE4, 0xBE, 0xBB, 0xF6, 0xE5,
+ 0x80, 0x82, 0xF6, 0xE5, 0x81, 0xBA, 0xF6, 0xE5,
+ 0x82, 0x99, 0xF6, 0xE5, 0x83, 0xA7, 0xF6, 0xE5,
+ 0x83, 0x8F, 0xF6, 0xE3, 0x92, 0x9E, 0xF6, 0xF0,
+ 0xA0, 0x98, 0xBA, 0xF6, 0xE5, 0x85, 0x8D, 0xF6,
+ 0xE5, 0x85, 0x94, 0xF6, 0xE5, 0x85, 0xA4, 0xF6,
+ 0xE5, 0x85, 0xB7, 0xF6, 0xF0, 0xA0, 0x94, 0x9C,
+ 0xF6, 0xE3, 0x92, 0xB9, 0xF6, 0xE5, 0x85, 0xA7,
+ 0xF6, 0xE5, 0x86, 0x8D, 0xF6, 0xF0, 0xA0, 0x95,
+ 0x8B, 0xF6, 0xE5, 0x86, 0x97, 0xF6, 0xE5, 0x86,
+ 0xA4, 0xF6, 0xE4, 0xBB, 0x8C, 0xF6, 0xE5, 0x86,
+ 0xAC, 0xF6, 0xE5, 0x86, 0xB5, 0xF6, 0xF0, 0xA9,
+ 0x87, 0x9F, 0xF6, 0xE5, 0x87, 0xB5, 0xF6, 0xE5,
+ 0x88, 0x83, 0xF6, 0xE3, 0x93, 0x9F, 0xF6, 0xE5,
+ 0x88, 0xBB, 0xF6, 0xE5, 0x89, 0x86, 0xF6, 0xE5,
+ 0x89, 0xB2, 0xF6, 0xE5, 0x89, 0xB7, 0xF6, 0xE3,
+ 0x94, 0x95, 0xF6, 0xE5, 0x8B, 0x87, 0xF6, 0xE5,
+ 0x8B, 0x89, 0xF6, 0xE5, 0x8B, 0xA4, 0xF6, 0xE5,
+ 0x8B, 0xBA, 0xF6, 0xE5, 0x8C, 0x85, 0xF6, 0xE5,
+ 0x8C, 0x86, 0xF6, 0xE5, 0x8C, 0x97, 0xF6, 0xE5,
+ 0x8D, 0x89, 0xF6, 0xE5, 0x8D, 0x91, 0xF6, 0xE5,
+ 0x8D, 0x9A, 0xF6, 0xE5, 0x8D, 0xB3, 0xF6, 0xE5,
+ 0x8D, 0xBD, 0xF6, 0xE5, 0x8D, 0xBF, 0xF6, 0xE5,
+ 0x8D, 0xBF, 0xF6, 0xE5, 0x8D, 0xBF, 0xF6, 0xF0,
+ 0xA0, 0xA8, 0xAC, 0xF6, 0xE7, 0x81, 0xB0, 0xF6,
+ 0xE5, 0x8F, 0x8A, 0xF6, 0xE5, 0x8F, 0x9F, 0xF6,
+ 0xF0, 0xA0, 0xAD, 0xA3, 0xF6, 0xE5, 0x8F, 0xAB,
+ 0xF6, 0xE5, 0x8F, 0xB1, 0xF6, 0xE5, 0x90, 0x86,
+ 0xF6, 0xE5, 0x92, 0x9E, 0xF6, 0xE5, 0x90, 0xB8,
+ 0xF6, 0xE5, 0x91, 0x88, 0xF6, 0xE5, 0x91, 0xA8,
+ 0xF6, 0xE5, 0x92, 0xA2, 0xF6, 0xE5, 0x93, 0xB6,
+ 0xF6, 0xE5, 0x94, 0x90, 0xF6, 0xE5, 0x95, 0x93,
+ 0xF6, 0xE5, 0x95, 0xA3, 0xF6, 0xE5, 0x96, 0x84,
+ 0xF6, 0xE5, 0x96, 0x84, 0xF6, 0xE5, 0x96, 0x99,
+ 0xF6, 0xE5, 0x96, 0xAB, 0xF6, 0xE5, 0x96, 0xB3,
+ 0xF6, 0xE5, 0x97, 0x82, 0xF6, 0xE5, 0x9C, 0x96,
+ 0xF6, 0xE5, 0x98, 0x86, 0xF6, 0xE5, 0x9C, 0x97,
+ 0xF6, 0xE5, 0x99, 0x91, 0xF6, 0xE5, 0x99, 0xB4,
+ 0xF6, 0xE5, 0x88, 0x87, 0xF6, 0xE5, 0xA3, 0xAE,
+ 0xF6, 0xE5, 0x9F, 0x8E, 0xF6, 0xE5, 0x9F, 0xB4,
+ 0xF6, 0xE5, 0xA0, 0x8D, 0xF6, 0xE5, 0x9E, 0x8B,
+ 0xF6, 0xE5, 0xA0, 0xB2, 0xF6, 0xE5, 0xA0, 0xB1,
+ 0xF6, 0xE5, 0xA2, 0xAC, 0xF6, 0xF0, 0xA1, 0x93,
+ 0xA4, 0xF6, 0xE5, 0xA3, 0xB2, 0xF6, 0xE5, 0xA3,
+ 0xB7, 0xF6, 0xE5, 0xA4, 0x86, 0xF6, 0xE5, 0xA4,
+ 0x9A, 0xF6, 0xE5, 0xA4, 0xA2, 0xF6, 0xE5, 0xA5,
+ 0xA2, 0xF6, 0xF0, 0xA1, 0x9A, 0xA8, 0xF6, 0xF0,
+ 0xA1, 0x9B, 0xAA, 0xF6, 0xE5, 0xA7, 0xAC, 0xF6,
+ 0xE5, 0xA8, 0x9B, 0xF6, 0xE5, 0xA8, 0xA7, 0xF6,
+ 0xE5, 0xA7, 0x98, 0xF6, 0xE5, 0xA9, 0xA6, 0xF6,
+ 0xE3, 0x9B, 0xAE, 0xF6, 0xE3, 0x9B, 0xBC, 0xF6,
+ 0xE5, 0xAC, 0x88, 0xF6, 0xE5, 0xAC, 0xBE, 0xF6,
+ 0xE5, 0xAC, 0xBE, 0xF6, 0xF0, 0xA1, 0xA7, 0x88,
+ 0xF6, 0xE5, 0xAF, 0x83, 0xF6, 0xE5, 0xAF, 0x98,
+ 0xF6, 0xE5, 0xAF, 0xA7, 0xF6, 0xE5, 0xAF, 0xB3,
+ 0xF6, 0xF0, 0xA1, 0xAC, 0x98, 0xF6, 0xE5, 0xAF,
+ 0xBF, 0xF6, 0xE5, 0xB0, 0x86, 0xF6, 0xE5, 0xBD,
+ 0x93, 0xF6, 0xE5, 0xB0, 0xA2, 0xF6, 0xE3, 0x9E,
+ 0x81, 0xF6, 0xE5, 0xB1, 0xA0, 0xF6, 0xE5, 0xB1,
+ 0xAE, 0xF6, 0xE5, 0xB3, 0x80, 0xF6, 0xE5, 0xB2,
+ 0x8D, 0xF6, 0xF0, 0xA1, 0xB7, 0xA4, 0xF6, 0xE5,
+ 0xB5, 0x83, 0xF6, 0xF0, 0xA1, 0xB7, 0xA6, 0xF6,
+ 0xE5, 0xB5, 0xAE, 0xF6, 0xE5, 0xB5, 0xAB, 0xF6,
+ 0xE5, 0xB5, 0xBC, 0xF6, 0xE5, 0xB7, 0xA1, 0xF6,
+ 0xE5, 0xB7, 0xA2, 0xF6, 0xE3, 0xA0, 0xAF, 0xF6,
+ 0xE5, 0xB7, 0xBD, 0xF6, 0xE5, 0xB8, 0xA8, 0xF6,
+ 0xE5, 0xB8, 0xBD, 0xF6, 0xE5, 0xB9, 0xA9, 0xF6,
+ 0xE3, 0xA1, 0xA2, 0xF6, 0xF0, 0xA2, 0x86, 0x83,
+ 0xF6, 0xE3, 0xA1, 0xBC, 0xF6, 0xE5, 0xBA, 0xB0,
+ 0xF6, 0xE5, 0xBA, 0xB3, 0xF6, 0xE5, 0xBA, 0xB6,
+ 0xF6, 0xE5, 0xBB, 0x8A, 0xF6, 0xF0, 0xAA, 0x8E,
+ 0x92, 0xF6, 0xE5, 0xBB, 0xBE, 0xF6, 0xF0, 0xA2,
+ 0x8C, 0xB1, 0xF6, 0xF0, 0xA2, 0x8C, 0xB1, 0xF6,
+ 0xE8, 0x88, 0x81, 0xF6, 0xE5, 0xBC, 0xA2, 0xF6,
+ 0xE5, 0xBC, 0xA2, 0xF6, 0xE3, 0xA3, 0x87, 0xF6,
+ 0xF0, 0xA3, 0x8A, 0xB8, 0xF6, 0xF0, 0xA6, 0x87,
+ 0x9A, 0xF6, 0xE5, 0xBD, 0xA2, 0xF6, 0xE5, 0xBD,
+ 0xAB, 0xF6, 0xE3, 0xA3, 0xA3, 0xF6, 0xE5, 0xBE,
+ 0x9A, 0xF6, 0xE5, 0xBF, 0x8D, 0xF6, 0xE5, 0xBF,
+ 0x97, 0xF6, 0xE5, 0xBF, 0xB9, 0xF6, 0xE6, 0x82,
+ 0x81, 0xF6, 0xE3, 0xA4, 0xBA, 0xF6, 0xE3, 0xA4,
+ 0x9C, 0xF6, 0xE6, 0x82, 0x94, 0xF6, 0xF0, 0xA2,
+ 0x9B, 0x94, 0xF6, 0xE6, 0x83, 0x87, 0xF6, 0xE6,
+ 0x85, 0x88, 0xF6, 0xE6, 0x85, 0x8C, 0xF6, 0xE6,
+ 0x85, 0x8E, 0xF6, 0xE6, 0x85, 0x8C, 0xF6, 0xE6,
+ 0x85, 0xBA, 0xF6, 0xE6, 0x86, 0x8E, 0xF6, 0xE6,
+ 0x86, 0xB2, 0xF6, 0xE6, 0x86, 0xA4, 0xF6, 0xE6,
+ 0x86, 0xAF, 0xF6, 0xE6, 0x87, 0x9E, 0xF6, 0xE6,
+ 0x87, 0xB2, 0xF6, 0xE6, 0x87, 0xB6, 0xF6, 0xE6,
+ 0x88, 0x90, 0xF6, 0xE6, 0x88, 0x9B, 0xF6, 0xE6,
+ 0x89, 0x9D, 0xF6, 0xE6, 0x8A, 0xB1, 0xF6, 0xE6,
+ 0x8B, 0x94, 0xF6, 0xE6, 0x8D, 0x90, 0xF6, 0xF0,
+ 0xA2, 0xAC, 0x8C, 0xF6, 0xE6, 0x8C, 0xBD, 0xF6,
+ 0xE6, 0x8B, 0xBC, 0xF6, 0xE6, 0x8D, 0xA8, 0xF6,
+ 0xE6, 0x8E, 0x83, 0xF6, 0xE6, 0x8F, 0xA4, 0xF6,
+ 0xF0, 0xA2, 0xAF, 0xB1, 0xF6, 0xE6, 0x90, 0xA2,
+ 0xF6, 0xE6, 0x8F, 0x85, 0xF6, 0xE6, 0x8E, 0xA9,
+ 0xF6, 0xE3, 0xA8, 0xAE, 0xF6, 0xE6, 0x91, 0xA9,
+ 0xF6, 0xE6, 0x91, 0xBE, 0xF6, 0xE6, 0x92, 0x9D,
+ 0xF6, 0xE6, 0x91, 0xB7, 0xF6, 0xE3, 0xA9, 0xAC,
+ 0xF6, 0xE6, 0x95, 0x8F, 0xF6, 0xE6, 0x95, 0xAC,
+ 0xF6, 0xF0, 0xA3, 0x80, 0x8A, 0xF6, 0xE6, 0x97,
+ 0xA3, 0xF6, 0xE6, 0x9B, 0xB8, 0xF6, 0xE6, 0x99,
+ 0x89, 0xF6, 0xE3, 0xAC, 0x99, 0xF6, 0xE6, 0x9A,
+ 0x91, 0xF6, 0xE3, 0xAC, 0x88, 0xF6, 0xE3, 0xAB,
+ 0xA4, 0xF6, 0xE5, 0x86, 0x92, 0xF6, 0xE5, 0x86,
+ 0x95, 0xF6, 0xE6, 0x9C, 0x80, 0xF6, 0xE6, 0x9A,
+ 0x9C, 0xF6, 0xE8, 0x82, 0xAD, 0xF6, 0xE4, 0x8F,
+ 0x99, 0xF6, 0xE6, 0x9C, 0x97, 0xF6, 0xE6, 0x9C,
+ 0x9B, 0xF6, 0xE6, 0x9C, 0xA1, 0xF6, 0xE6, 0x9D,
+ 0x9E, 0xF6, 0xE6, 0x9D, 0x93, 0xF6, 0xF0, 0xA3,
+ 0x8F, 0x83, 0xF6, 0xE3, 0xAD, 0x89, 0xF6, 0xE6,
+ 0x9F, 0xBA, 0xF6, 0xE6, 0x9E, 0x85, 0xF6, 0xE6,
+ 0xA1, 0x92, 0xF6, 0xE6, 0xA2, 0x85, 0xF6, 0xF0,
+ 0xA3, 0x91, 0xAD, 0xF6, 0xE6, 0xA2, 0x8E, 0xF6,
+ 0xE6, 0xA0, 0x9F, 0xF6, 0xE6, 0xA4, 0x94, 0xF6,
+ 0xE3, 0xAE, 0x9D, 0xF6, 0xE6, 0xA5, 0x82, 0xF6,
+ 0xE6, 0xA6, 0xA3, 0xF6, 0xE6, 0xA7, 0xAA, 0xF6,
+ 0xE6, 0xAA, 0xA8, 0xF6, 0xF0, 0xA3, 0x9A, 0xA3,
+ 0xF6, 0xE6, 0xAB, 0x9B, 0xF6, 0xE3, 0xB0, 0x98,
+ 0xF6, 0xE6, 0xAC, 0xA1, 0xF6, 0xF0, 0xA3, 0xA2,
+ 0xA7, 0xF6, 0xE6, 0xAD, 0x94, 0xF6, 0xE3, 0xB1,
+ 0x8E, 0xF6, 0xE6, 0xAD, 0xB2, 0xF6, 0xE6, 0xAE,
+ 0x9F, 0xF6, 0xE6, 0xAE, 0xBA, 0xF6, 0xE6, 0xAE,
+ 0xBB, 0xF6, 0xF0, 0xA3, 0xAA, 0x8D, 0xF6, 0xF0,
+ 0xA1, 0xB4, 0x8B, 0xF6, 0xF0, 0xA3, 0xAB, 0xBA,
+ 0xF6, 0xE6, 0xB1, 0x8E, 0xF6, 0xF0, 0xA3, 0xB2,
+ 0xBC, 0xF6, 0xE6, 0xB2, 0xBF, 0xF6, 0xE6, 0xB3,
+ 0x8D, 0xF6, 0xE6, 0xB1, 0xA7, 0xF6, 0xE6, 0xB4,
+ 0x96, 0xF6, 0xE6, 0xB4, 0xBE, 0xF6, 0xE6, 0xB5,
+ 0xB7, 0xF6, 0xE6, 0xB5, 0x81, 0xF6, 0xE6, 0xB5,
+ 0xA9, 0xF6, 0xE6, 0xB5, 0xB8, 0xF6, 0xE6, 0xB6,
+ 0x85, 0xF6, 0xF0, 0xA3, 0xB4, 0x9E, 0xF6, 0xE6,
+ 0xB4, 0xB4, 0xF6, 0xE6, 0xB8, 0xAF, 0xF6, 0xE6,
+ 0xB9, 0xAE, 0xF6, 0xE3, 0xB4, 0xB3, 0xF6, 0xE6,
+ 0xBB, 0x8B, 0xF6, 0xE6, 0xBB, 0x87, 0xF6, 0xF0,
+ 0xA3, 0xBB, 0x91, 0xF6, 0xE6, 0xB7, 0xB9, 0xF6,
+ 0xE6, 0xBD, 0xAE, 0xF6, 0xF0, 0xA3, 0xBD, 0x9E,
+ 0xF6, 0xF0, 0xA3, 0xBE, 0x8E, 0xF6, 0xE6, 0xBF,
+ 0x86, 0xF6, 0xE7, 0x80, 0xB9, 0xF6, 0xE7, 0x80,
+ 0x9E, 0xF6, 0xE7, 0x80, 0x9B, 0xF6, 0xE3, 0xB6,
+ 0x96, 0xF6, 0xE7, 0x81, 0x8A, 0xF6, 0xE7, 0x81,
+ 0xBD, 0xF6, 0xE7, 0x81, 0xB7, 0xF6, 0xE7, 0x82,
+ 0xAD, 0xF6, 0xF0, 0xA0, 0x94, 0xA5, 0xF6, 0xE7,
+ 0x85, 0x85, 0xF6, 0xF0, 0xA4, 0x89, 0xA3, 0xF6,
+ 0xE7, 0x86, 0x9C, 0xF6, 0xF0, 0xA4, 0x8E, 0xAB,
+ 0xF6, 0xE7, 0x88, 0xA8, 0xF6, 0xE7, 0x88, 0xB5,
+ 0xF6, 0xE7, 0x89, 0x90, 0xF6, 0xF0, 0xA4, 0x98,
+ 0x88, 0xF6, 0xE7, 0x8A, 0x80, 0xF6, 0xE7, 0x8A,
+ 0x95, 0xF6, 0xF0, 0xA4, 0x9C, 0xB5, 0xF6, 0xF0,
+ 0xA4, 0xA0, 0x94, 0xF6, 0xE7, 0x8D, 0xBA, 0xF6,
+ 0xE7, 0x8E, 0x8B, 0xF6, 0xE3, 0xBA, 0xAC, 0xF6,
+ 0xE7, 0x8E, 0xA5, 0xF6, 0xE3, 0xBA, 0xB8, 0xF6,
+ 0xE3, 0xBA, 0xB8, 0xF6, 0xE7, 0x91, 0x87, 0xF6,
+ 0xE7, 0x91, 0x9C, 0xF6, 0xE7, 0x91, 0xB1, 0xF6,
+ 0xE7, 0x92, 0x85, 0xF6, 0xE7, 0x93, 0x8A, 0xF6,
+ 0xE3, 0xBC, 0x9B, 0xF6, 0xE7, 0x94, 0xA4, 0xF6,
+ 0xF0, 0xA4, 0xB0, 0xB6, 0xF6, 0xE7, 0x94, 0xBE,
+ 0xF6, 0xF0, 0xA4, 0xB2, 0x92, 0xF6, 0xE7, 0x95,
+ 0xB0, 0xF6, 0xF0, 0xA2, 0x86, 0x9F, 0xF6, 0xE7,
+ 0x98, 0x90, 0xF6, 0xF0, 0xA4, 0xBE, 0xA1, 0xF6,
+ 0xF0, 0xA4, 0xBE, 0xB8, 0xF6, 0xF0, 0xA5, 0x81,
+ 0x84, 0xF6, 0xE3, 0xBF, 0xBC, 0xF6, 0xE4, 0x80,
+ 0x88, 0xF6, 0xE7, 0x9B, 0xB4, 0xF6, 0xF0, 0xA5,
+ 0x83, 0xB3, 0xF6, 0xF0, 0xA5, 0x83, 0xB2, 0xF6,
+ 0xF0, 0xA5, 0x84, 0x99, 0xF6, 0xF0, 0xA5, 0x84,
+ 0xB3, 0xF6, 0xE7, 0x9C, 0x9E, 0xF6, 0xE7, 0x9C,
+ 0x9F, 0xF6, 0xE7, 0x9C, 0x9F, 0xF6, 0xE7, 0x9D,
+ 0x8A, 0xF6, 0xE4, 0x80, 0xB9, 0xF6, 0xE7, 0x9E,
+ 0x8B, 0xF6, 0xE4, 0x81, 0x86, 0xF6, 0xE4, 0x82,
+ 0x96, 0xF6, 0xF0, 0xA5, 0x90, 0x9D, 0xF6, 0xE7,
+ 0xA1, 0x8E, 0xF6, 0xE7, 0xA2, 0x8C, 0xF6, 0xE7,
+ 0xA3, 0x8C, 0xF6, 0xE4, 0x83, 0xA3, 0xF6, 0xF0,
+ 0xA5, 0x98, 0xA6, 0xF6, 0xE7, 0xA5, 0x96, 0xF6,
+ 0xF0, 0xA5, 0x9A, 0x9A, 0xF6, 0xF0, 0xA5, 0x9B,
+ 0x85, 0xF6, 0xE7, 0xA6, 0x8F, 0xF6, 0xE7, 0xA7,
+ 0xAB, 0xF6, 0xE4, 0x84, 0xAF, 0xF6, 0xE7, 0xA9,
+ 0x80, 0xF6, 0xE7, 0xA9, 0x8A, 0xF6, 0xE7, 0xA9,
+ 0x8F, 0xF6, 0xF0, 0xA5, 0xA5, 0xBC, 0xF6, 0xF0,
+ 0xA5, 0xAA, 0xA7, 0xF6, 0xF0, 0xA5, 0xAA, 0xA7,
+ 0xF6, 0xE7, 0xAB, 0xAE, 0xF6, 0xE4, 0x88, 0x82,
+ 0xF6, 0xF0, 0xA5, 0xAE, 0xAB, 0xF6, 0xE7, 0xAF,
+ 0x86, 0xF6, 0xE7, 0xAF, 0x89, 0xF6, 0xE4, 0x88,
+ 0xA7, 0xF6, 0xF0, 0xA5, 0xB2, 0x80, 0xF6, 0xE7,
+ 0xB3, 0x92, 0xF6, 0xE4, 0x8A, 0xA0, 0xF6, 0xE7,
+ 0xB3, 0xA8, 0xF6, 0xE7, 0xB3, 0xA3, 0xF6, 0xE7,
+ 0xB4, 0x80, 0xF6, 0xF0, 0xA5, 0xBE, 0x86, 0xF6,
+ 0xE7, 0xB5, 0xA3, 0xF6, 0xE4, 0x8C, 0x81, 0xF6,
+ 0xE7, 0xB7, 0x87, 0xF6, 0xE7, 0xB8, 0x82, 0xF6,
+ 0xE7, 0xB9, 0x85, 0xF6, 0xE4, 0x8C, 0xB4, 0xF6,
+ 0xF0, 0xA6, 0x88, 0xA8, 0xF6, 0xF0, 0xA6, 0x89,
+ 0x87, 0xF6, 0xE4, 0x8D, 0x99, 0xF6, 0xF0, 0xA6,
+ 0x8B, 0x99, 0xF6, 0xE7, 0xBD, 0xBA, 0xF6, 0xF0,
+ 0xA6, 0x8C, 0xBE, 0xF6, 0xE7, 0xBE, 0x95, 0xF6,
+ 0xE7, 0xBF, 0xBA, 0xF6, 0xE8, 0x80, 0x85, 0xF6,
+ 0xF0, 0xA6, 0x93, 0x9A, 0xF6, 0xF0, 0xA6, 0x94,
+ 0xA3, 0xF6, 0xE8, 0x81, 0xA0, 0xF6, 0xF0, 0xA6,
+ 0x96, 0xA8, 0xF6, 0xE8, 0x81, 0xB0, 0xF6, 0xF0,
+ 0xA3, 0x8D, 0x9F, 0xF6, 0xE4, 0x8F, 0x95, 0xF6,
+ 0xE8, 0x82, 0xB2, 0xF6, 0xE8, 0x84, 0x83, 0xF6,
+ 0xE4, 0x90, 0x8B, 0xF6, 0xE8, 0x84, 0xBE, 0xF6,
+ 0xE5, 0xAA, 0xB5, 0xF6, 0xF0, 0xA6, 0x9E, 0xA7,
+ 0xF6, 0xF0, 0xA6, 0x9E, 0xB5, 0xF6, 0xF0, 0xA3,
+ 0x8E, 0x93, 0xF6, 0xF0, 0xA3, 0x8E, 0x9C, 0xF6,
+ 0xE8, 0x88, 0x81, 0xF6, 0xE8, 0x88, 0x84, 0xF6,
+ 0xE8, 0xBE, 0x9E, 0xF6, 0xE4, 0x91, 0xAB, 0xF6,
+ 0xE8, 0x8A, 0x91, 0xF6, 0xE8, 0x8A, 0x8B, 0xF6,
+ 0xE8, 0x8A, 0x9D, 0xF6, 0xE5, 0x8A, 0xB3, 0xF6,
+ 0xE8, 0x8A, 0xB1, 0xF6, 0xE8, 0x8A, 0xB3, 0xF6,
+ 0xE8, 0x8A, 0xBD, 0xF6, 0xE8, 0x8B, 0xA6, 0xF6,
+ 0xF0, 0xA6, 0xAC, 0xBC, 0xF6, 0xE8, 0x8B, 0xA5,
+ 0xF6, 0xE8, 0x8C, 0x9D, 0xF6, 0xE8, 0x8D, 0xA3,
+ 0xF6, 0xE8, 0x8E, 0xAD, 0xF6, 0xE8, 0x8C, 0xA3,
+ 0xF6, 0xE8, 0x8E, 0xBD, 0xF6, 0xE8, 0x8F, 0xA7,
+ 0xF6, 0xE8, 0x91, 0x97, 0xF6, 0xE8, 0x8D, 0x93,
+ 0xF6, 0xE8, 0x8F, 0x8A, 0xF6, 0xE8, 0x8F, 0x8C,
+ 0xF6, 0xE8, 0x8F, 0x9C, 0xF6, 0xF0, 0xA6, 0xB0,
+ 0xB6, 0xF6, 0xF0, 0xA6, 0xB5, 0xAB, 0xF6, 0xF0,
+ 0xA6, 0xB3, 0x95, 0xF6, 0xE4, 0x94, 0xAB, 0xF6,
+ 0xE8, 0x93, 0xB1, 0xF6, 0xE8, 0x93, 0xB3, 0xF6,
+ 0xE8, 0x94, 0x96, 0xF6, 0xF0, 0xA7, 0x8F, 0x8A,
+ 0xF6, 0xE8, 0x95, 0xA4, 0xF6, 0xF0, 0xA6, 0xBC,
+ 0xAC, 0xF6, 0xE4, 0x95, 0x9D, 0xF6, 0xE4, 0x95,
+ 0xA1, 0xF6, 0xF0, 0xA6, 0xBE, 0xB1, 0xF6, 0xF0,
+ 0xA7, 0x83, 0x92, 0xF6, 0xE4, 0x95, 0xAB, 0xF6,
+ 0xE8, 0x99, 0x90, 0xF6, 0xE8, 0x99, 0x9C, 0xF6,
+ 0xE8, 0x99, 0xA7, 0xF6, 0xE8, 0x99, 0xA9, 0xF6,
+ 0xE8, 0x9A, 0xA9, 0xF6, 0xE8, 0x9A, 0x88, 0xF6,
+ 0xE8, 0x9C, 0x8E, 0xF6, 0xE8, 0x9B, 0xA2, 0xF6,
+ 0xE8, 0x9D, 0xB9, 0xF6, 0xE8, 0x9C, 0xA8, 0xF6,
+ 0xE8, 0x9D, 0xAB, 0xF6, 0xE8, 0x9E, 0x86, 0xF6,
+ 0xE4, 0x97, 0x97, 0xF6, 0xE8, 0x9F, 0xA1, 0xF6,
+ 0xE8, 0xA0, 0x81, 0xF6, 0xE4, 0x97, 0xB9, 0xF6,
+ 0xE8, 0xA1, 0xA0, 0xF6, 0xE8, 0xA1, 0xA3, 0xF6,
+ 0xF0, 0xA7, 0x99, 0xA7, 0xF6, 0xE8, 0xA3, 0x97,
+ 0xF6, 0xE8, 0xA3, 0x9E, 0xF6, 0xE4, 0x98, 0xB5,
+ 0xF6, 0xE8, 0xA3, 0xBA, 0xF6, 0xE3, 0x92, 0xBB,
+ 0xF6, 0xF0, 0xA7, 0xA2, 0xAE, 0xF6, 0xF0, 0xA7,
+ 0xA5, 0xA6, 0xF6, 0xE4, 0x9A, 0xBE, 0xF6, 0xE4,
+ 0x9B, 0x87, 0xF6, 0xE8, 0xAA, 0xA0, 0xF6, 0xE8,
+ 0xAB, 0xAD, 0xF6, 0xE8, 0xAE, 0x8A, 0xF6, 0xE8,
+ 0xB1, 0x95, 0xF6, 0xF0, 0xA7, 0xB2, 0xA8, 0xF6,
+ 0xE8, 0xB2, 0xAB, 0xF6, 0xE8, 0xB3, 0x81, 0xF6,
+ 0xE8, 0xB4, 0x9B, 0xF6, 0xE8, 0xB5, 0xB7, 0xF6,
+ 0xF0, 0xA7, 0xBC, 0xAF, 0xF6, 0xF0, 0xA0, 0xA0,
+ 0x84, 0xF6, 0xE8, 0xB7, 0x8B, 0xF6, 0xE8, 0xB6,
+ 0xBC, 0xF6, 0xE8, 0xB7, 0xB0, 0xF6, 0xF0, 0xA0,
+ 0xA3, 0x9E, 0xF6, 0xE8, 0xBB, 0x94, 0xF6, 0xE8,
+ 0xBC, 0xB8, 0xF6, 0xF0, 0xA8, 0x97, 0x92, 0xF6,
+ 0xF0, 0xA8, 0x97, 0xAD, 0xF6, 0xE9, 0x82, 0x94,
+ 0xF6, 0xE9, 0x83, 0xB1, 0xF6, 0xE9, 0x84, 0x91,
+ 0xF6, 0xF0, 0xA8, 0x9C, 0xAE, 0xF6, 0xE9, 0x84,
+ 0x9B, 0xF6, 0xE9, 0x88, 0xB8, 0xF6, 0xE9, 0x8B,
+ 0x97, 0xF6, 0xE9, 0x8B, 0x98, 0xF6, 0xE9, 0x89,
+ 0xBC, 0xF6, 0xE9, 0x8F, 0xB9, 0xF6, 0xE9, 0x90,
+ 0x95, 0xF6, 0xF0, 0xA8, 0xAF, 0xBA, 0xF6, 0xE9,
+ 0x96, 0x8B, 0xF6, 0xE4, 0xA6, 0x95, 0xF6, 0xE9,
+ 0x96, 0xB7, 0xF6, 0xF0, 0xA8, 0xB5, 0xB7, 0xF6,
+ 0xE4, 0xA7, 0xA6, 0xF6, 0xE9, 0x9B, 0x83, 0xF6,
+ 0xE5, 0xB6, 0xB2, 0xF6, 0xE9, 0x9C, 0xA3, 0xF6,
+ 0xF0, 0xA9, 0x85, 0x85, 0xF6, 0xF0, 0xA9, 0x88,
+ 0x9A, 0xF6, 0xE4, 0xA9, 0xAE, 0xF6, 0xE4, 0xA9,
+ 0xB6, 0xF6, 0xE9, 0x9F, 0xA0, 0xF6, 0xF0, 0xA9,
+ 0x90, 0x8A, 0xF6, 0xE4, 0xAA, 0xB2, 0xF6, 0xF0,
+ 0xA9, 0x92, 0x96, 0xF6, 0xE9, 0xA0, 0x8B, 0xF6,
+ 0xE9, 0xA0, 0x8B, 0xF6, 0xE9, 0xA0, 0xA9, 0xF6,
+ 0xF0, 0xA9, 0x96, 0xB6, 0xF6, 0xE9, 0xA3, 0xA2,
+ 0xF6, 0xE4, 0xAC, 0xB3, 0xF6, 0xE9, 0xA4, 0xA9,
+ 0xF6, 0xE9, 0xA6, 0xA7, 0xF6, 0xE9, 0xA7, 0x82,
+ 0xF6, 0xE9, 0xA7, 0xBE, 0xF6, 0xE4, 0xAF, 0x8E,
+ 0xF6, 0xF0, 0xA9, 0xAC, 0xB0, 0xF6, 0xE9, 0xAC,
+ 0x92, 0xF6, 0xE9, 0xB1, 0x80, 0xF6, 0xE9, 0xB3,
+ 0xBD, 0xF6, 0xE4, 0xB3, 0x8E, 0xF6, 0xE4, 0xB3,
+ 0xAD, 0xF6, 0xE9, 0xB5, 0xA7, 0xF6, 0xF0, 0xAA,
+ 0x83, 0x8E, 0xF6, 0xE4, 0xB3, 0xB8, 0xF6, 0xF0,
+ 0xAA, 0x84, 0x85, 0xF6, 0xF0, 0xAA, 0x88, 0x8E,
+ 0xF6, 0xF0, 0xAA, 0x8A, 0x91, 0xF6, 0xE9, 0xBA,
+ 0xBB, 0xF6, 0xE4, 0xB5, 0x96, 0xF6, 0xE9, 0xBB,
+ 0xB9, 0xF6, 0xE9, 0xBB, 0xBE, 0xF6, 0xE9, 0xBC,
+ 0x85, 0xF6, 0xE9, 0xBC, 0x8F, 0xF6, 0xE9, 0xBC,
+ 0x96, 0xF6, 0xE9, 0xBC, 0xBB, 0xF6, 0xF0, 0xAA,
+ 0x98, 0x80, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0,
+ },
+ {
+ 0x20, 0x20, 0xCC, 0x88, 0x61, 0x20, 0xCC, 0x84,
+ 0x32, 0x33, 0x20, 0xCC, 0x81, 0xCE, 0xBC, 0x20,
+ 0xCC, 0xA7, 0x31, 0x6F, 0x31, 0xE2, 0x81, 0x84,
+ 0x34, 0x31, 0xE2, 0x81, 0x84, 0x32, 0x33, 0xE2,
+ 0x81, 0x84, 0x34, 0xF6, 0x41, 0xCC, 0x80, 0xF6,
+ 0x41, 0xCC, 0x81, 0xF6, 0x41, 0xCC, 0x82, 0xF6,
+ 0x41, 0xCC, 0x83, 0xF6, 0x41, 0xCC, 0x88, 0xF6,
+ 0x41, 0xCC, 0x8A, 0xF6, 0x43, 0xCC, 0xA7, 0xF6,
+ 0x45, 0xCC, 0x80, 0xF6, 0x45, 0xCC, 0x81, 0xF6,
+ 0x45, 0xCC, 0x82, 0xF6, 0x45, 0xCC, 0x88, 0xF6,
+ 0x49, 0xCC, 0x80, 0xF6, 0x49, 0xCC, 0x81, 0xF6,
+ 0x49, 0xCC, 0x82, 0xF6, 0x49, 0xCC, 0x88, 0xF6,
+ 0x4E, 0xCC, 0x83, 0xF6, 0x4F, 0xCC, 0x80, 0xF6,
+ 0x4F, 0xCC, 0x81, 0xF6, 0x4F, 0xCC, 0x82, 0xF6,
+ 0x4F, 0xCC, 0x83, 0xF6, 0x4F, 0xCC, 0x88, 0xF6,
+ 0x55, 0xCC, 0x80, 0xF6, 0x55, 0xCC, 0x81, 0xF6,
+ 0x55, 0xCC, 0x82, 0xF6, 0x55, 0xCC, 0x88, 0xF6,
+ 0x59, 0xCC, 0x81, 0xF6, 0x61, 0xCC, 0x80, 0xF6,
+ 0x61, 0xCC, 0x81, 0xF6, 0x61, 0xCC, 0x82, 0xF6,
+ 0x61, 0xCC, 0x83, 0xF6, 0x61, 0xCC, 0x88, 0xF6,
+ 0x61, 0xCC, 0x8A, 0xF6, 0x63, 0xCC, 0xA7, 0xF6,
+ 0x65, 0xCC, 0x80, 0xF6, 0x65, 0xCC, 0x81, 0xF6,
+ 0x65, 0xCC, 0x82, 0xF6, 0x65, 0xCC, 0x88, 0xF6,
+ 0x69, 0xCC, 0x80, 0xF6, 0x69, 0xCC, 0x81, 0xF6,
+ 0x69, 0xCC, 0x82, 0xF6, 0x69, 0xCC, 0x88, 0xF6,
+ 0x6E, 0xCC, 0x83, 0xF6, 0x6F, 0xCC, 0x80, 0xF6,
+ 0x6F, 0xCC, 0x81, 0xF6, 0x6F, 0xCC, 0x82, 0xF6,
+ 0x6F, 0xCC, 0x83, 0xF6, 0x6F, 0xCC, 0x88, 0xF6,
+ 0x75, 0xCC, 0x80, 0xF6, 0x75, 0xCC, 0x81, 0xF6,
+ 0x75, 0xCC, 0x82, 0xF6, 0x75, 0xCC, 0x88, 0xF6,
+ 0x79, 0xCC, 0x81, 0xF6, 0x79, 0xCC, 0x88, 0xF6,
+ 0x41, 0xCC, 0x84, 0xF6, 0x61, 0xCC, 0x84, 0xF6,
+ 0x41, 0xCC, 0x86, 0xF6, 0x61, 0xCC, 0x86, 0xF6,
+ 0x41, 0xCC, 0xA8, 0xF6, 0x61, 0xCC, 0xA8, 0xF6,
+ 0x43, 0xCC, 0x81, 0xF6, 0x63, 0xCC, 0x81, 0xF6,
+ 0x43, 0xCC, 0x82, 0xF6, 0x63, 0xCC, 0x82, 0xF6,
+ 0x43, 0xCC, 0x87, 0xF6, 0x63, 0xCC, 0x87, 0xF6,
+ 0x43, 0xCC, 0x8C, 0xF6, 0x63, 0xCC, 0x8C, 0xF6,
+ 0x44, 0xCC, 0x8C, 0xF6, 0x64, 0xCC, 0x8C, 0xF6,
+ 0x45, 0xCC, 0x84, 0xF6, 0x65, 0xCC, 0x84, 0xF6,
+ 0x45, 0xCC, 0x86, 0xF6, 0x65, 0xCC, 0x86, 0xF6,
+ 0x45, 0xCC, 0x87, 0xF6, 0x65, 0xCC, 0x87, 0xF6,
+ 0x45, 0xCC, 0xA8, 0xF6, 0x65, 0xCC, 0xA8, 0xF6,
+ 0x45, 0xCC, 0x8C, 0xF6, 0x65, 0xCC, 0x8C, 0xF6,
+ 0x47, 0xCC, 0x82, 0xF6, 0x67, 0xCC, 0x82, 0xF6,
+ 0x47, 0xCC, 0x86, 0xF6, 0x67, 0xCC, 0x86, 0xF6,
+ 0x47, 0xCC, 0x87, 0xF6, 0x67, 0xCC, 0x87, 0xF6,
+ 0x47, 0xCC, 0xA7, 0xF6, 0x67, 0xCC, 0xA7, 0xF6,
+ 0x48, 0xCC, 0x82, 0xF6, 0x68, 0xCC, 0x82, 0xF6,
+ 0x49, 0xCC, 0x83, 0xF6, 0x69, 0xCC, 0x83, 0xF6,
+ 0x49, 0xCC, 0x84, 0xF6, 0x69, 0xCC, 0x84, 0xF6,
+ 0x49, 0xCC, 0x86, 0xF6, 0x69, 0xCC, 0x86, 0xF6,
+ 0x49, 0xCC, 0xA8, 0xF6, 0x69, 0xCC, 0xA8, 0xF6,
+ 0x49, 0xCC, 0x87, 0x49, 0x4A, 0x69, 0x6A, 0xF6,
+ 0x4A, 0xCC, 0x82, 0xF6, 0x6A, 0xCC, 0x82, 0xF6,
+ 0x4B, 0xCC, 0xA7, 0xF6, 0x6B, 0xCC, 0xA7, 0xF6,
+ 0x4C, 0xCC, 0x81, 0xF6, 0x6C, 0xCC, 0x81, 0xF6,
+ 0x4C, 0xCC, 0xA7, 0xF6, 0x6C, 0xCC, 0xA7, 0xF6,
+ 0x4C, 0xCC, 0x8C, 0xF6, 0x6C, 0xCC, 0x8C, 0x4C,
+ 0xC2, 0xB7, 0x6C, 0xC2, 0xB7, 0xF6, 0x4E, 0xCC,
+ 0x81, 0xF6, 0x6E, 0xCC, 0x81, 0xF6, 0x4E, 0xCC,
+ 0xA7, 0xF6, 0x6E, 0xCC, 0xA7, 0xF6, 0x4E, 0xCC,
+ 0x8C, 0xF6, 0x6E, 0xCC, 0x8C, 0xCA, 0xBC, 0x6E,
+ 0xF6, 0x4F, 0xCC, 0x84, 0xF6, 0x6F, 0xCC, 0x84,
+ 0xF6, 0x4F, 0xCC, 0x86, 0xF6, 0x6F, 0xCC, 0x86,
+ 0xF6, 0x4F, 0xCC, 0x8B, 0xF6, 0x6F, 0xCC, 0x8B,
+ 0xF6, 0x52, 0xCC, 0x81, 0xF6, 0x72, 0xCC, 0x81,
+ 0xF6, 0x52, 0xCC, 0xA7, 0xF6, 0x72, 0xCC, 0xA7,
+ 0xF6, 0x52, 0xCC, 0x8C, 0xF6, 0x72, 0xCC, 0x8C,
+ 0xF6, 0x53, 0xCC, 0x81, 0xF6, 0x73, 0xCC, 0x81,
+ 0xF6, 0x53, 0xCC, 0x82, 0xF6, 0x73, 0xCC, 0x82,
+ 0xF6, 0x53, 0xCC, 0xA7, 0xF6, 0x73, 0xCC, 0xA7,
+ 0xF6, 0x53, 0xCC, 0x8C, 0xF6, 0x73, 0xCC, 0x8C,
+ 0xF6, 0x54, 0xCC, 0xA7, 0xF6, 0x74, 0xCC, 0xA7,
+ 0xF6, 0x54, 0xCC, 0x8C, 0xF6, 0x74, 0xCC, 0x8C,
+ 0xF6, 0x55, 0xCC, 0x83, 0xF6, 0x75, 0xCC, 0x83,
+ 0xF6, 0x55, 0xCC, 0x84, 0xF6, 0x75, 0xCC, 0x84,
+ 0xF6, 0x55, 0xCC, 0x86, 0xF6, 0x75, 0xCC, 0x86,
+ 0xF6, 0x55, 0xCC, 0x8A, 0xF6, 0x75, 0xCC, 0x8A,
+ 0xF6, 0x55, 0xCC, 0x8B, 0xF6, 0x75, 0xCC, 0x8B,
+ 0xF6, 0x55, 0xCC, 0xA8, 0xF6, 0x75, 0xCC, 0xA8,
+ 0xF6, 0x57, 0xCC, 0x82, 0xF6, 0x77, 0xCC, 0x82,
+ 0xF6, 0x59, 0xCC, 0x82, 0xF6, 0x79, 0xCC, 0x82,
+ 0xF6, 0x59, 0xCC, 0x88, 0xF6, 0x5A, 0xCC, 0x81,
+ 0xF6, 0x7A, 0xCC, 0x81, 0xF6, 0x5A, 0xCC, 0x87,
+ 0xF6, 0x7A, 0xCC, 0x87, 0xF6, 0x5A, 0xCC, 0x8C,
+ 0xF6, 0x7A, 0xCC, 0x8C, 0x73, 0xF6, 0x4F, 0xCC,
+ 0x9B, 0xF6, 0x6F, 0xCC, 0x9B, 0xF6, 0x55, 0xCC,
+ 0x9B, 0xF6, 0x75, 0xCC, 0x9B, 0x44, 0x5A, 0xCC,
+ 0x8C, 0x44, 0x7A, 0xCC, 0x8C, 0x64, 0x7A, 0xCC,
+ 0x8C, 0x4C, 0x4A, 0x4C, 0x6A, 0x6C, 0x6A, 0x4E,
+ 0x4A, 0x4E, 0x6A, 0x6E, 0x6A, 0xF6, 0x41, 0xCC,
+ 0x8C, 0xF6, 0x61, 0xCC, 0x8C, 0xF6, 0x49, 0xCC,
+ 0x8C, 0xF6, 0x69, 0xCC, 0x8C, 0xF6, 0x4F, 0xCC,
+ 0x8C, 0xF6, 0x6F, 0xCC, 0x8C, 0xF6, 0x55, 0xCC,
+ 0x8C, 0xF6, 0x75, 0xCC, 0x8C, 0xF6, 0x55, 0xCC,
+ 0x88, 0xCC, 0x84, 0xF6, 0x75, 0xCC, 0x88, 0xCC,
+ 0x84, 0xF6, 0x55, 0xCC, 0x88, 0xCC, 0x81, 0xF6,
+ 0x75, 0xCC, 0x88, 0xCC, 0x81, 0xF6, 0x55, 0xCC,
+ 0x88, 0xCC, 0x8C, 0xF6, 0x75, 0xCC, 0x88, 0xCC,
+ 0x8C, 0xF6, 0x55, 0xCC, 0x88, 0xCC, 0x80, 0xF6,
+ 0x75, 0xCC, 0x88, 0xCC, 0x80, 0xF6, 0x41, 0xCC,
+ 0x88, 0xCC, 0x84, 0xF6, 0x61, 0xCC, 0x88, 0xCC,
+ 0x84, 0xF6, 0x41, 0xCC, 0x87, 0xCC, 0x84, 0xF6,
+ 0x61, 0xCC, 0x87, 0xCC, 0x84, 0xF6, 0xC3, 0x86,
+ 0xCC, 0x84, 0xF6, 0xC3, 0xA6, 0xCC, 0x84, 0xF6,
+ 0x47, 0xCC, 0x8C, 0xF6, 0x67, 0xCC, 0x8C, 0xF6,
+ 0x4B, 0xCC, 0x8C, 0xF6, 0x6B, 0xCC, 0x8C, 0xF6,
+ 0x4F, 0xCC, 0xA8, 0xF6, 0x6F, 0xCC, 0xA8, 0xF6,
+ 0x4F, 0xCC, 0xA8, 0xCC, 0x84, 0xF6, 0x6F, 0xCC,
+ 0xA8, 0xCC, 0x84, 0xF6, 0xC6, 0xB7, 0xCC, 0x8C,
+ 0xF6, 0xCA, 0x92, 0xCC, 0x8C, 0xF6, 0x6A, 0xCC,
+ 0x8C, 0x44, 0x5A, 0x44, 0x7A, 0x64, 0x7A, 0xF6,
+ 0x47, 0xCC, 0x81, 0xF6, 0x67, 0xCC, 0x81, 0xF6,
+ 0x4E, 0xCC, 0x80, 0xF6, 0x6E, 0xCC, 0x80, 0xF6,
+ 0x41, 0xCC, 0x8A, 0xCC, 0x81, 0xF6, 0x61, 0xCC,
+ 0x8A, 0xCC, 0x81, 0xF6, 0xC3, 0x86, 0xCC, 0x81,
+ 0xF6, 0xC3, 0xA6, 0xCC, 0x81, 0xF6, 0xC3, 0x98,
+ 0xCC, 0x81, 0xF6, 0xC3, 0xB8, 0xCC, 0x81, 0xF6,
+ 0x41, 0xCC, 0x8F, 0xF6, 0x61, 0xCC, 0x8F, 0xF6,
+ 0x41, 0xCC, 0x91, 0xF6, 0x61, 0xCC, 0x91, 0xF6,
+ 0x45, 0xCC, 0x8F, 0xF6, 0x65, 0xCC, 0x8F, 0xF6,
+ 0x45, 0xCC, 0x91, 0xF6, 0x65, 0xCC, 0x91, 0xF6,
+ 0x49, 0xCC, 0x8F, 0xF6, 0x69, 0xCC, 0x8F, 0xF6,
+ 0x49, 0xCC, 0x91, 0xF6, 0x69, 0xCC, 0x91, 0xF6,
+ 0x4F, 0xCC, 0x8F, 0xF6, 0x6F, 0xCC, 0x8F, 0xF6,
+ 0x4F, 0xCC, 0x91, 0xF6, 0x6F, 0xCC, 0x91, 0xF6,
+ 0x52, 0xCC, 0x8F, 0xF6, 0x72, 0xCC, 0x8F, 0xF6,
+ 0x52, 0xCC, 0x91, 0xF6, 0x72, 0xCC, 0x91, 0xF6,
+ 0x55, 0xCC, 0x8F, 0xF6, 0x75, 0xCC, 0x8F, 0xF6,
+ 0x55, 0xCC, 0x91, 0xF6, 0x75, 0xCC, 0x91, 0xF6,
+ 0x53, 0xCC, 0xA6, 0xF6, 0x73, 0xCC, 0xA6, 0xF6,
+ 0x54, 0xCC, 0xA6, 0xF6, 0x74, 0xCC, 0xA6, 0xF6,
+ 0x48, 0xCC, 0x8C, 0xF6, 0x68, 0xCC, 0x8C, 0xF6,
+ 0x41, 0xCC, 0x87, 0xF6, 0x61, 0xCC, 0x87, 0xF6,
+ 0x45, 0xCC, 0xA7, 0xF6, 0x65, 0xCC, 0xA7, 0xF6,
+ 0x4F, 0xCC, 0x88, 0xCC, 0x84, 0xF6, 0x6F, 0xCC,
+ 0x88, 0xCC, 0x84, 0xF6, 0x4F, 0xCC, 0x83, 0xCC,
+ 0x84, 0xF6, 0x6F, 0xCC, 0x83, 0xCC, 0x84, 0xF6,
+ 0x4F, 0xCC, 0x87, 0xF6, 0x6F, 0xCC, 0x87, 0xF6,
+ 0x4F, 0xCC, 0x87, 0xCC, 0x84, 0xF6, 0x6F, 0xCC,
+ 0x87, 0xCC, 0x84, 0xF6, 0x59, 0xCC, 0x84, 0xF6,
+ 0x79, 0xCC, 0x84, 0x68, 0xC9, 0xA6, 0x6A, 0x72,
+ 0xC9, 0xB9, 0xC9, 0xBB, 0xCA, 0x81, 0x77, 0x79,
+ 0x20, 0xCC, 0x86, 0x20, 0xCC, 0x87, 0x20, 0xCC,
+ 0x8A, 0x20, 0xCC, 0xA8, 0x20, 0xCC, 0x83, 0x20,
+ 0xCC, 0x8B, 0xC9, 0xA3, 0x6C, 0x73, 0x78, 0xCA,
+ 0x95, 0xF6, 0xCC, 0x80, 0xF6, 0xCC, 0x81, 0xF6,
+ 0xCC, 0x93, 0xF6, 0xCC, 0x88, 0xCC, 0x81, 0xF6,
+ 0xCA, 0xB9, 0x20, 0xCD, 0x85, 0xF6, 0x3B, 0x20,
+ 0xCC, 0x81, 0xF5, 0x05, 0xC2, 0xA8, 0xCC, 0x81,
+ 0x20, 0xCC, 0x88, 0xCC, 0x81, 0xF6, 0xCE, 0x91,
+ 0xCC, 0x81, 0xF6, 0xC2, 0xB7, 0xF6, 0xCE, 0x95,
+ 0xCC, 0x81, 0xF6, 0xCE, 0x97, 0xCC, 0x81, 0xF6,
+ 0xCE, 0x99, 0xCC, 0x81, 0xF6, 0xCE, 0x9F, 0xCC,
+ 0x81, 0xF6, 0xCE, 0xA5, 0xCC, 0x81, 0xF6, 0xCE,
+ 0xA9, 0xCC, 0x81, 0xF6, 0xCE, 0xB9, 0xCC, 0x88,
+ 0xCC, 0x81, 0xF6, 0xCE, 0x99, 0xCC, 0x88, 0xF6,
+ 0xCE, 0xA5, 0xCC, 0x88, 0xF6, 0xCE, 0xB1, 0xCC,
+ 0x81, 0xF6, 0xCE, 0xB5, 0xCC, 0x81, 0xF6, 0xCE,
+ 0xB7, 0xCC, 0x81, 0xF6, 0xCE, 0xB9, 0xCC, 0x81,
+ 0xF6, 0xCF, 0x85, 0xCC, 0x88, 0xCC, 0x81, 0xF6,
+ 0xCE, 0xB9, 0xCC, 0x88, 0xF6, 0xCF, 0x85, 0xCC,
+ 0x88, 0xF6, 0xCE, 0xBF, 0xCC, 0x81, 0xF6, 0xCF,
+ 0x85, 0xCC, 0x81, 0xF6, 0xCF, 0x89, 0xCC, 0x81,
+ 0xCE, 0xB2, 0xCE, 0xB8, 0xCE, 0xA5, 0xF5, 0x05,
+ 0xCF, 0x92, 0xCC, 0x81, 0xCE, 0xA5, 0xCC, 0x81,
+ 0xF5, 0x05, 0xCF, 0x92, 0xCC, 0x88, 0xCE, 0xA5,
+ 0xCC, 0x88, 0xCF, 0x86, 0xCF, 0x80, 0xCE, 0xBA,
+ 0xCF, 0x81, 0xCF, 0x82, 0xCE, 0x98, 0xCE, 0xB5,
+ 0xCE, 0xA3, 0xF6, 0xD0, 0x95, 0xCC, 0x80, 0xF6,
+ 0xD0, 0x95, 0xCC, 0x88, 0xF6, 0xD0, 0x93, 0xCC,
+ 0x81, 0xF6, 0xD0, 0x86, 0xCC, 0x88, 0xF6, 0xD0,
+ 0x9A, 0xCC, 0x81, 0xF6, 0xD0, 0x98, 0xCC, 0x80,
+ 0xF6, 0xD0, 0xA3, 0xCC, 0x86, 0xF6, 0xD0, 0x98,
+ 0xCC, 0x86, 0xF6, 0xD0, 0xB8, 0xCC, 0x86, 0xF6,
+ 0xD0, 0xB5, 0xCC, 0x80, 0xF6, 0xD0, 0xB5, 0xCC,
+ 0x88, 0xF6, 0xD0, 0xB3, 0xCC, 0x81, 0xF6, 0xD1,
+ 0x96, 0xCC, 0x88, 0xF6, 0xD0, 0xBA, 0xCC, 0x81,
+ 0xF6, 0xD0, 0xB8, 0xCC, 0x80, 0xF6, 0xD1, 0x83,
+ 0xCC, 0x86, 0xF6, 0xD1, 0xB4, 0xCC, 0x8F, 0xF6,
+ 0xD1, 0xB5, 0xCC, 0x8F, 0xF6, 0xD0, 0x96, 0xCC,
+ 0x86, 0xF6, 0xD0, 0xB6, 0xCC, 0x86, 0xF6, 0xD0,
+ 0x90, 0xCC, 0x86, 0xF6, 0xD0, 0xB0, 0xCC, 0x86,
+ 0xF6, 0xD0, 0x90, 0xCC, 0x88, 0xF6, 0xD0, 0xB0,
+ 0xCC, 0x88, 0xF6, 0xD0, 0x95, 0xCC, 0x86, 0xF6,
+ 0xD0, 0xB5, 0xCC, 0x86, 0xF6, 0xD3, 0x98, 0xCC,
+ 0x88, 0xF6, 0xD3, 0x99, 0xCC, 0x88, 0xF6, 0xD0,
+ 0x96, 0xCC, 0x88, 0xF6, 0xD0, 0xB6, 0xCC, 0x88,
+ 0xF6, 0xD0, 0x97, 0xCC, 0x88, 0xF6, 0xD0, 0xB7,
+ 0xCC, 0x88, 0xF6, 0xD0, 0x98, 0xCC, 0x84, 0xF6,
+ 0xD0, 0xB8, 0xCC, 0x84, 0xF6, 0xD0, 0x98, 0xCC,
+ 0x88, 0xF6, 0xD0, 0xB8, 0xCC, 0x88, 0xF6, 0xD0,
+ 0x9E, 0xCC, 0x88, 0xF6, 0xD0, 0xBE, 0xCC, 0x88,
+ 0xF6, 0xD3, 0xA8, 0xCC, 0x88, 0xF6, 0xD3, 0xA9,
+ 0xCC, 0x88, 0xF6, 0xD0, 0xAD, 0xCC, 0x88, 0xF6,
+ 0xD1, 0x8D, 0xCC, 0x88, 0xF6, 0xD0, 0xA3, 0xCC,
+ 0x84, 0xF6, 0xD1, 0x83, 0xCC, 0x84, 0xF6, 0xD0,
+ 0xA3, 0xCC, 0x88, 0xF6, 0xD1, 0x83, 0xCC, 0x88,
+ 0xF6, 0xD0, 0xA3, 0xCC, 0x8B, 0xF6, 0xD1, 0x83,
+ 0xCC, 0x8B, 0xF6, 0xD0, 0xA7, 0xCC, 0x88, 0xF6,
+ 0xD1, 0x87, 0xCC, 0x88, 0xF6, 0xD0, 0xAB, 0xCC,
+ 0x88, 0xF6, 0xD1, 0x8B, 0xCC, 0x88, 0xD5, 0xA5,
+ 0xD6, 0x82, 0xF6, 0xD8, 0xA7, 0xD9, 0x93, 0xF6,
+ 0xD8, 0xA7, 0xD9, 0x94, 0xF6, 0xD9, 0x88, 0xD9,
+ 0x94, 0xF6, 0xD8, 0xA7, 0xD9, 0x95, 0xF6, 0xD9,
+ 0x8A, 0xD9, 0x94, 0xD8, 0xA7, 0xD9, 0xB4, 0xD9,
+ 0x88, 0xD9, 0xB4, 0xDB, 0x87, 0xD9, 0xB4, 0xD9,
+ 0x8A, 0xD9, 0xB4, 0xF6, 0xDB, 0x95, 0xD9, 0x94,
+ 0xF6, 0xDB, 0x81, 0xD9, 0x94, 0xF6, 0xDB, 0x92,
+ 0xD9, 0x94, 0xF6, 0xE0, 0xA4, 0xA8, 0xE0, 0xA4,
+ 0xBC, 0xF6, 0xE0, 0xA4, 0xB0, 0xE0, 0xA4, 0xBC,
+ 0xF6, 0xE0, 0xA4, 0xB3, 0xE0, 0xA4, 0xBC, 0xF6,
+ 0xE0, 0xA4, 0x95, 0xE0, 0xA4, 0xBC, 0xF6, 0xE0,
+ 0xA4, 0x96, 0xE0, 0xA4, 0xBC, 0xF6, 0xE0, 0xA4,
+ 0x97, 0xE0, 0xA4, 0xBC, 0xF6, 0xE0, 0xA4, 0x9C,
+ 0xE0, 0xA4, 0xBC, 0xF6, 0xE0, 0xA4, 0xA1, 0xE0,
+ 0xA4, 0xBC, 0xF6, 0xE0, 0xA4, 0xA2, 0xE0, 0xA4,
+ 0xBC, 0xF6, 0xE0, 0xA4, 0xAB, 0xE0, 0xA4, 0xBC,
+ 0xF6, 0xE0, 0xA4, 0xAF, 0xE0, 0xA4, 0xBC, 0xF6,
+ 0xE0, 0xA7, 0x87, 0xE0, 0xA6, 0xBE, 0xF6, 0xE0,
+ 0xA7, 0x87, 0xE0, 0xA7, 0x97, 0xF6, 0xE0, 0xA6,
+ 0xA1, 0xE0, 0xA6, 0xBC, 0xF6, 0xE0, 0xA6, 0xA2,
+ 0xE0, 0xA6, 0xBC, 0xF6, 0xE0, 0xA6, 0xAF, 0xE0,
+ 0xA6, 0xBC, 0xF6, 0xE0, 0xA8, 0xB2, 0xE0, 0xA8,
+ 0xBC, 0xF6, 0xE0, 0xA8, 0xB8, 0xE0, 0xA8, 0xBC,
+ 0xF6, 0xE0, 0xA8, 0x96, 0xE0, 0xA8, 0xBC, 0xF6,
+ 0xE0, 0xA8, 0x97, 0xE0, 0xA8, 0xBC, 0xF6, 0xE0,
+ 0xA8, 0x9C, 0xE0, 0xA8, 0xBC, 0xF6, 0xE0, 0xA8,
+ 0xAB, 0xE0, 0xA8, 0xBC, 0xF6, 0xE0, 0xAD, 0x87,
+ 0xE0, 0xAD, 0x96, 0xF6, 0xE0, 0xAD, 0x87, 0xE0,
+ 0xAC, 0xBE, 0xF6, 0xE0, 0xAD, 0x87, 0xE0, 0xAD,
+ 0x97, 0xF6, 0xE0, 0xAC, 0xA1, 0xE0, 0xAC, 0xBC,
+ 0xF6, 0xE0, 0xAC, 0xA2, 0xE0, 0xAC, 0xBC, 0xF6,
+ 0xE0, 0xAE, 0x92, 0xE0, 0xAF, 0x97, 0xF6, 0xE0,
+ 0xAF, 0x86, 0xE0, 0xAE, 0xBE, 0xF6, 0xE0, 0xAF,
+ 0x87, 0xE0, 0xAE, 0xBE, 0xF6, 0xE0, 0xAF, 0x86,
+ 0xE0, 0xAF, 0x97, 0xF6, 0xE0, 0xB1, 0x86, 0xE0,
+ 0xB1, 0x96, 0xF6, 0xE0, 0xB2, 0xBF, 0xE0, 0xB3,
+ 0x95, 0xF6, 0xE0, 0xB3, 0x86, 0xE0, 0xB3, 0x95,
+ 0xF6, 0xE0, 0xB3, 0x86, 0xE0, 0xB3, 0x96, 0xF6,
+ 0xE0, 0xB3, 0x86, 0xE0, 0xB3, 0x82, 0xF6, 0xE0,
+ 0xB3, 0x86, 0xE0, 0xB3, 0x82, 0xE0, 0xB3, 0x95,
+ 0xF6, 0xE0, 0xB5, 0x86, 0xE0, 0xB4, 0xBE, 0xF6,
+ 0xE0, 0xB5, 0x87, 0xE0, 0xB4, 0xBE, 0xF6, 0xE0,
+ 0xB5, 0x86, 0xE0, 0xB5, 0x97, 0xF6, 0xE0, 0xB7,
+ 0x99, 0xE0, 0xB7, 0x8A, 0xF6, 0xE0, 0xB7, 0x99,
+ 0xE0, 0xB7, 0x8F, 0xF6, 0xE0, 0xB7, 0x99, 0xE0,
+ 0xB7, 0x8F, 0xE0, 0xB7, 0x8A, 0xF6, 0xE0, 0xB7,
+ 0x99, 0xE0, 0xB7, 0x9F, 0xE0, 0xB9, 0x8D, 0xE0,
+ 0xB8, 0xB2, 0xE0, 0xBB, 0x8D, 0xE0, 0xBA, 0xB2,
+ 0xE0, 0xBA, 0xAB, 0xE0, 0xBA, 0x99, 0xE0, 0xBA,
+ 0xAB, 0xE0, 0xBA, 0xA1, 0xE0, 0xBC, 0x8B, 0xF6,
+ 0xE0, 0xBD, 0x82, 0xE0, 0xBE, 0xB7, 0xF6, 0xE0,
+ 0xBD, 0x8C, 0xE0, 0xBE, 0xB7, 0xF6, 0xE0, 0xBD,
+ 0x91, 0xE0, 0xBE, 0xB7, 0xF6, 0xE0, 0xBD, 0x96,
+ 0xE0, 0xBE, 0xB7, 0xF6, 0xE0, 0xBD, 0x9B, 0xE0,
+ 0xBE, 0xB7, 0xF6, 0xE0, 0xBD, 0x80, 0xE0, 0xBE,
+ 0xB5, 0xF6, 0xE0, 0xBD, 0xB1, 0xE0, 0xBD, 0xB2,
+ 0xF6, 0xE0, 0xBD, 0xB1, 0xE0, 0xBD, 0xB4, 0xF6,
+ 0xE0, 0xBE, 0xB2, 0xE0, 0xBE, 0x80, 0xE0, 0xBE,
+ 0xB2, 0xE0, 0xBD, 0xB1, 0xE0, 0xBE, 0x80, 0xF6,
+ 0xE0, 0xBE, 0xB3, 0xE0, 0xBE, 0x80, 0xE0, 0xBE,
+ 0xB3, 0xE0, 0xBD, 0xB1, 0xE0, 0xBE, 0x80, 0xF6,
+ 0xE0, 0xBD, 0xB1, 0xE0, 0xBE, 0x80, 0xF6, 0xE0,
+ 0xBE, 0x92, 0xE0, 0xBE, 0xB7, 0xF6, 0xE0, 0xBE,
+ 0x9C, 0xE0, 0xBE, 0xB7, 0xF6, 0xE0, 0xBE, 0xA1,
+ 0xE0, 0xBE, 0xB7, 0xF6, 0xE0, 0xBE, 0xA6, 0xE0,
+ 0xBE, 0xB7, 0xF6, 0xE0, 0xBE, 0xAB, 0xE0, 0xBE,
+ 0xB7, 0xF6, 0xE0, 0xBE, 0x90, 0xE0, 0xBE, 0xB5,
+ 0xF6, 0xE1, 0x80, 0xA5, 0xE1, 0x80, 0xAE, 0xE1,
+ 0x83, 0x9C, 0xF6, 0xE1, 0xAC, 0x85, 0xE1, 0xAC,
+ 0xB5, 0xF6, 0xE1, 0xAC, 0x87, 0xE1, 0xAC, 0xB5,
+ 0xF6, 0xE1, 0xAC, 0x89, 0xE1, 0xAC, 0xB5, 0xF6,
+ 0xE1, 0xAC, 0x8B, 0xE1, 0xAC, 0xB5, 0xF6, 0xE1,
+ 0xAC, 0x8D, 0xE1, 0xAC, 0xB5, 0xF6, 0xE1, 0xAC,
+ 0x91, 0xE1, 0xAC, 0xB5, 0xF6, 0xE1, 0xAC, 0xBA,
+ 0xE1, 0xAC, 0xB5, 0xF6, 0xE1, 0xAC, 0xBC, 0xE1,
+ 0xAC, 0xB5, 0xF6, 0xE1, 0xAC, 0xBE, 0xE1, 0xAC,
+ 0xB5, 0xF6, 0xE1, 0xAC, 0xBF, 0xE1, 0xAC, 0xB5,
+ 0xF6, 0xE1, 0xAD, 0x82, 0xE1, 0xAC, 0xB5, 0x41,
+ 0xC3, 0x86, 0x42, 0x44, 0x45, 0xC6, 0x8E, 0x47,
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0xC8, 0xA2, 0x50, 0x52, 0x54, 0x55, 0x57, 0x61,
+ 0xC9, 0x90, 0xC9, 0x91, 0xE1, 0xB4, 0x82, 0x62,
+ 0x64, 0x65, 0xC9, 0x99, 0xC9, 0x9B, 0xC9, 0x9C,
+ 0x67, 0x6B, 0x6D, 0xC5, 0x8B, 0x6F, 0xC9, 0x94,
+ 0xE1, 0xB4, 0x96, 0xE1, 0xB4, 0x97, 0x70, 0x74,
+ 0x75, 0xE1, 0xB4, 0x9D, 0xC9, 0xAF, 0x76, 0xE1,
+ 0xB4, 0xA5, 0xCE, 0xB2, 0xCE, 0xB3, 0xCE, 0xB4,
+ 0xCF, 0x86, 0xCF, 0x87, 0x69, 0x72, 0x75, 0x76,
+ 0xCE, 0xB2, 0xCE, 0xB3, 0xCF, 0x81, 0xCF, 0x86,
+ 0xCF, 0x87, 0xD0, 0xBD, 0xC9, 0x92, 0x63, 0xC9,
+ 0x95, 0xC3, 0xB0, 0xC9, 0x9C, 0x66, 0xC9, 0x9F,
+ 0xC9, 0xA1, 0xC9, 0xA5, 0xC9, 0xA8, 0xC9, 0xA9,
+ 0xC9, 0xAA, 0xE1, 0xB5, 0xBB, 0xCA, 0x9D, 0xC9,
+ 0xAD, 0xE1, 0xB6, 0x85, 0xCA, 0x9F, 0xC9, 0xB1,
+ 0xC9, 0xB0, 0xC9, 0xB2, 0xC9, 0xB3, 0xC9, 0xB4,
+ 0xC9, 0xB5, 0xC9, 0xB8, 0xCA, 0x82, 0xCA, 0x83,
+ 0xC6, 0xAB, 0xCA, 0x89, 0xCA, 0x8A, 0xE1, 0xB4,
+ 0x9C, 0xCA, 0x8B, 0xCA, 0x8C, 0x7A, 0xCA, 0x90,
+ 0xCA, 0x91, 0xCA, 0x92, 0xCE, 0xB8, 0xF6, 0x41,
+ 0xCC, 0xA5, 0xF6, 0x61, 0xCC, 0xA5, 0xF6, 0x42,
+ 0xCC, 0x87, 0xF6, 0x62, 0xCC, 0x87, 0xF6, 0x42,
+ 0xCC, 0xA3, 0xF6, 0x62, 0xCC, 0xA3, 0xF6, 0x42,
+ 0xCC, 0xB1, 0xF6, 0x62, 0xCC, 0xB1, 0xF6, 0x43,
+ 0xCC, 0xA7, 0xCC, 0x81, 0xF6, 0x63, 0xCC, 0xA7,
+ 0xCC, 0x81, 0xF6, 0x44, 0xCC, 0x87, 0xF6, 0x64,
+ 0xCC, 0x87, 0xF6, 0x44, 0xCC, 0xA3, 0xF6, 0x64,
+ 0xCC, 0xA3, 0xF6, 0x44, 0xCC, 0xB1, 0xF6, 0x64,
+ 0xCC, 0xB1, 0xF6, 0x44, 0xCC, 0xA7, 0xF6, 0x64,
+ 0xCC, 0xA7, 0xF6, 0x44, 0xCC, 0xAD, 0xF6, 0x64,
+ 0xCC, 0xAD, 0xF6, 0x45, 0xCC, 0x84, 0xCC, 0x80,
+ 0xF6, 0x65, 0xCC, 0x84, 0xCC, 0x80, 0xF6, 0x45,
+ 0xCC, 0x84, 0xCC, 0x81, 0xF6, 0x65, 0xCC, 0x84,
+ 0xCC, 0x81, 0xF6, 0x45, 0xCC, 0xAD, 0xF6, 0x65,
+ 0xCC, 0xAD, 0xF6, 0x45, 0xCC, 0xB0, 0xF6, 0x65,
+ 0xCC, 0xB0, 0xF6, 0x45, 0xCC, 0xA7, 0xCC, 0x86,
+ 0xF6, 0x65, 0xCC, 0xA7, 0xCC, 0x86, 0xF6, 0x46,
+ 0xCC, 0x87, 0xF6, 0x66, 0xCC, 0x87, 0xF6, 0x47,
+ 0xCC, 0x84, 0xF6, 0x67, 0xCC, 0x84, 0xF6, 0x48,
+ 0xCC, 0x87, 0xF6, 0x68, 0xCC, 0x87, 0xF6, 0x48,
+ 0xCC, 0xA3, 0xF6, 0x68, 0xCC, 0xA3, 0xF6, 0x48,
+ 0xCC, 0x88, 0xF6, 0x68, 0xCC, 0x88, 0xF6, 0x48,
+ 0xCC, 0xA7, 0xF6, 0x68, 0xCC, 0xA7, 0xF6, 0x48,
+ 0xCC, 0xAE, 0xF6, 0x68, 0xCC, 0xAE, 0xF6, 0x49,
+ 0xCC, 0xB0, 0xF6, 0x69, 0xCC, 0xB0, 0xF6, 0x49,
+ 0xCC, 0x88, 0xCC, 0x81, 0xF6, 0x69, 0xCC, 0x88,
+ 0xCC, 0x81, 0xF6, 0x4B, 0xCC, 0x81, 0xF6, 0x6B,
+ 0xCC, 0x81, 0xF6, 0x4B, 0xCC, 0xA3, 0xF6, 0x6B,
+ 0xCC, 0xA3, 0xF6, 0x4B, 0xCC, 0xB1, 0xF6, 0x6B,
+ 0xCC, 0xB1, 0xF6, 0x4C, 0xCC, 0xA3, 0xF6, 0x6C,
+ 0xCC, 0xA3, 0xF6, 0x4C, 0xCC, 0xA3, 0xCC, 0x84,
+ 0xF6, 0x6C, 0xCC, 0xA3, 0xCC, 0x84, 0xF6, 0x4C,
+ 0xCC, 0xB1, 0xF6, 0x6C, 0xCC, 0xB1, 0xF6, 0x4C,
+ 0xCC, 0xAD, 0xF6, 0x6C, 0xCC, 0xAD, 0xF6, 0x4D,
+ 0xCC, 0x81, 0xF6, 0x6D, 0xCC, 0x81, 0xF6, 0x4D,
+ 0xCC, 0x87, 0xF6, 0x6D, 0xCC, 0x87, 0xF6, 0x4D,
+ 0xCC, 0xA3, 0xF6, 0x6D, 0xCC, 0xA3, 0xF6, 0x4E,
+ 0xCC, 0x87, 0xF6, 0x6E, 0xCC, 0x87, 0xF6, 0x4E,
+ 0xCC, 0xA3, 0xF6, 0x6E, 0xCC, 0xA3, 0xF6, 0x4E,
+ 0xCC, 0xB1, 0xF6, 0x6E, 0xCC, 0xB1, 0xF6, 0x4E,
+ 0xCC, 0xAD, 0xF6, 0x6E, 0xCC, 0xAD, 0xF6, 0x4F,
+ 0xCC, 0x83, 0xCC, 0x81, 0xF6, 0x6F, 0xCC, 0x83,
+ 0xCC, 0x81, 0xF6, 0x4F, 0xCC, 0x83, 0xCC, 0x88,
+ 0xF6, 0x6F, 0xCC, 0x83, 0xCC, 0x88, 0xF6, 0x4F,
+ 0xCC, 0x84, 0xCC, 0x80, 0xF6, 0x6F, 0xCC, 0x84,
+ 0xCC, 0x80, 0xF6, 0x4F, 0xCC, 0x84, 0xCC, 0x81,
+ 0xF6, 0x6F, 0xCC, 0x84, 0xCC, 0x81, 0xF6, 0x50,
+ 0xCC, 0x81, 0xF6, 0x70, 0xCC, 0x81, 0xF6, 0x50,
+ 0xCC, 0x87, 0xF6, 0x70, 0xCC, 0x87, 0xF6, 0x52,
+ 0xCC, 0x87, 0xF6, 0x72, 0xCC, 0x87, 0xF6, 0x52,
+ 0xCC, 0xA3, 0xF6, 0x72, 0xCC, 0xA3, 0xF6, 0x52,
+ 0xCC, 0xA3, 0xCC, 0x84, 0xF6, 0x72, 0xCC, 0xA3,
+ 0xCC, 0x84, 0xF6, 0x52, 0xCC, 0xB1, 0xF6, 0x72,
+ 0xCC, 0xB1, 0xF6, 0x53, 0xCC, 0x87, 0xF6, 0x73,
+ 0xCC, 0x87, 0xF6, 0x53, 0xCC, 0xA3, 0xF6, 0x73,
+ 0xCC, 0xA3, 0xF6, 0x53, 0xCC, 0x81, 0xCC, 0x87,
+ 0xF6, 0x73, 0xCC, 0x81, 0xCC, 0x87, 0xF6, 0x53,
+ 0xCC, 0x8C, 0xCC, 0x87, 0xF6, 0x73, 0xCC, 0x8C,
+ 0xCC, 0x87, 0xF6, 0x53, 0xCC, 0xA3, 0xCC, 0x87,
+ 0xF6, 0x73, 0xCC, 0xA3, 0xCC, 0x87, 0xF6, 0x54,
+ 0xCC, 0x87, 0xF6, 0x74, 0xCC, 0x87, 0xF6, 0x54,
+ 0xCC, 0xA3, 0xF6, 0x74, 0xCC, 0xA3, 0xF6, 0x54,
+ 0xCC, 0xB1, 0xF6, 0x74, 0xCC, 0xB1, 0xF6, 0x54,
+ 0xCC, 0xAD, 0xF6, 0x74, 0xCC, 0xAD, 0xF6, 0x55,
+ 0xCC, 0xA4, 0xF6, 0x75, 0xCC, 0xA4, 0xF6, 0x55,
+ 0xCC, 0xB0, 0xF6, 0x75, 0xCC, 0xB0, 0xF6, 0x55,
+ 0xCC, 0xAD, 0xF6, 0x75, 0xCC, 0xAD, 0xF6, 0x55,
+ 0xCC, 0x83, 0xCC, 0x81, 0xF6, 0x75, 0xCC, 0x83,
+ 0xCC, 0x81, 0xF6, 0x55, 0xCC, 0x84, 0xCC, 0x88,
+ 0xF6, 0x75, 0xCC, 0x84, 0xCC, 0x88, 0xF6, 0x56,
+ 0xCC, 0x83, 0xF6, 0x76, 0xCC, 0x83, 0xF6, 0x56,
+ 0xCC, 0xA3, 0xF6, 0x76, 0xCC, 0xA3, 0xF6, 0x57,
+ 0xCC, 0x80, 0xF6, 0x77, 0xCC, 0x80, 0xF6, 0x57,
+ 0xCC, 0x81, 0xF6, 0x77, 0xCC, 0x81, 0xF6, 0x57,
+ 0xCC, 0x88, 0xF6, 0x77, 0xCC, 0x88, 0xF6, 0x57,
+ 0xCC, 0x87, 0xF6, 0x77, 0xCC, 0x87, 0xF6, 0x57,
+ 0xCC, 0xA3, 0xF6, 0x77, 0xCC, 0xA3, 0xF6, 0x58,
+ 0xCC, 0x87, 0xF6, 0x78, 0xCC, 0x87, 0xF6, 0x58,
+ 0xCC, 0x88, 0xF6, 0x78, 0xCC, 0x88, 0xF6, 0x59,
+ 0xCC, 0x87, 0xF6, 0x79, 0xCC, 0x87, 0xF6, 0x5A,
+ 0xCC, 0x82, 0xF6, 0x7A, 0xCC, 0x82, 0xF6, 0x5A,
+ 0xCC, 0xA3, 0xF6, 0x7A, 0xCC, 0xA3, 0xF6, 0x5A,
+ 0xCC, 0xB1, 0xF6, 0x7A, 0xCC, 0xB1, 0xF6, 0x68,
+ 0xCC, 0xB1, 0xF6, 0x74, 0xCC, 0x88, 0xF6, 0x77,
+ 0xCC, 0x8A, 0xF6, 0x79, 0xCC, 0x8A, 0x61, 0xCA,
+ 0xBE, 0xF5, 0x05, 0xC5, 0xBF, 0xCC, 0x87, 0x73,
+ 0xCC, 0x87, 0xF6, 0x41, 0xCC, 0xA3, 0xF6, 0x61,
+ 0xCC, 0xA3, 0xF6, 0x41, 0xCC, 0x89, 0xF6, 0x61,
+ 0xCC, 0x89, 0xF6, 0x41, 0xCC, 0x82, 0xCC, 0x81,
+ 0xF6, 0x61, 0xCC, 0x82, 0xCC, 0x81, 0xF6, 0x41,
+ 0xCC, 0x82, 0xCC, 0x80, 0xF6, 0x61, 0xCC, 0x82,
+ 0xCC, 0x80, 0xF6, 0x41, 0xCC, 0x82, 0xCC, 0x89,
+ 0xF6, 0x61, 0xCC, 0x82, 0xCC, 0x89, 0xF6, 0x41,
+ 0xCC, 0x82, 0xCC, 0x83, 0xF6, 0x61, 0xCC, 0x82,
+ 0xCC, 0x83, 0xF6, 0x41, 0xCC, 0xA3, 0xCC, 0x82,
+ 0xF6, 0x61, 0xCC, 0xA3, 0xCC, 0x82, 0xF6, 0x41,
+ 0xCC, 0x86, 0xCC, 0x81, 0xF6, 0x61, 0xCC, 0x86,
+ 0xCC, 0x81, 0xF6, 0x41, 0xCC, 0x86, 0xCC, 0x80,
+ 0xF6, 0x61, 0xCC, 0x86, 0xCC, 0x80, 0xF6, 0x41,
+ 0xCC, 0x86, 0xCC, 0x89, 0xF6, 0x61, 0xCC, 0x86,
+ 0xCC, 0x89, 0xF6, 0x41, 0xCC, 0x86, 0xCC, 0x83,
+ 0xF6, 0x61, 0xCC, 0x86, 0xCC, 0x83, 0xF6, 0x41,
+ 0xCC, 0xA3, 0xCC, 0x86, 0xF6, 0x61, 0xCC, 0xA3,
+ 0xCC, 0x86, 0xF6, 0x45, 0xCC, 0xA3, 0xF6, 0x65,
+ 0xCC, 0xA3, 0xF6, 0x45, 0xCC, 0x89, 0xF6, 0x65,
+ 0xCC, 0x89, 0xF6, 0x45, 0xCC, 0x83, 0xF6, 0x65,
+ 0xCC, 0x83, 0xF6, 0x45, 0xCC, 0x82, 0xCC, 0x81,
+ 0xF6, 0x65, 0xCC, 0x82, 0xCC, 0x81, 0xF6, 0x45,
+ 0xCC, 0x82, 0xCC, 0x80, 0xF6, 0x65, 0xCC, 0x82,
+ 0xCC, 0x80, 0xF6, 0x45, 0xCC, 0x82, 0xCC, 0x89,
+ 0xF6, 0x65, 0xCC, 0x82, 0xCC, 0x89, 0xF6, 0x45,
+ 0xCC, 0x82, 0xCC, 0x83, 0xF6, 0x65, 0xCC, 0x82,
+ 0xCC, 0x83, 0xF6, 0x45, 0xCC, 0xA3, 0xCC, 0x82,
+ 0xF6, 0x65, 0xCC, 0xA3, 0xCC, 0x82, 0xF6, 0x49,
+ 0xCC, 0x89, 0xF6, 0x69, 0xCC, 0x89, 0xF6, 0x49,
+ 0xCC, 0xA3, 0xF6, 0x69, 0xCC, 0xA3, 0xF6, 0x4F,
+ 0xCC, 0xA3, 0xF6, 0x6F, 0xCC, 0xA3, 0xF6, 0x4F,
+ 0xCC, 0x89, 0xF6, 0x6F, 0xCC, 0x89, 0xF6, 0x4F,
+ 0xCC, 0x82, 0xCC, 0x81, 0xF6, 0x6F, 0xCC, 0x82,
+ 0xCC, 0x81, 0xF6, 0x4F, 0xCC, 0x82, 0xCC, 0x80,
+ 0xF6, 0x6F, 0xCC, 0x82, 0xCC, 0x80, 0xF6, 0x4F,
+ 0xCC, 0x82, 0xCC, 0x89, 0xF6, 0x6F, 0xCC, 0x82,
+ 0xCC, 0x89, 0xF6, 0x4F, 0xCC, 0x82, 0xCC, 0x83,
+ 0xF6, 0x6F, 0xCC, 0x82, 0xCC, 0x83, 0xF6, 0x4F,
+ 0xCC, 0xA3, 0xCC, 0x82, 0xF6, 0x6F, 0xCC, 0xA3,
+ 0xCC, 0x82, 0xF6, 0x4F, 0xCC, 0x9B, 0xCC, 0x81,
+ 0xF6, 0x6F, 0xCC, 0x9B, 0xCC, 0x81, 0xF6, 0x4F,
+ 0xCC, 0x9B, 0xCC, 0x80, 0xF6, 0x6F, 0xCC, 0x9B,
+ 0xCC, 0x80, 0xF6, 0x4F, 0xCC, 0x9B, 0xCC, 0x89,
+ 0xF6, 0x6F, 0xCC, 0x9B, 0xCC, 0x89, 0xF6, 0x4F,
+ 0xCC, 0x9B, 0xCC, 0x83, 0xF6, 0x6F, 0xCC, 0x9B,
+ 0xCC, 0x83, 0xF6, 0x4F, 0xCC, 0x9B, 0xCC, 0xA3,
+ 0xF6, 0x6F, 0xCC, 0x9B, 0xCC, 0xA3, 0xF6, 0x55,
+ 0xCC, 0xA3, 0xF6, 0x75, 0xCC, 0xA3, 0xF6, 0x55,
+ 0xCC, 0x89, 0xF6, 0x75, 0xCC, 0x89, 0xF6, 0x55,
+ 0xCC, 0x9B, 0xCC, 0x81, 0xF6, 0x75, 0xCC, 0x9B,
+ 0xCC, 0x81, 0xF6, 0x55, 0xCC, 0x9B, 0xCC, 0x80,
+ 0xF6, 0x75, 0xCC, 0x9B, 0xCC, 0x80, 0xF6, 0x55,
+ 0xCC, 0x9B, 0xCC, 0x89, 0xF6, 0x75, 0xCC, 0x9B,
+ 0xCC, 0x89, 0xF6, 0x55, 0xCC, 0x9B, 0xCC, 0x83,
+ 0xF6, 0x75, 0xCC, 0x9B, 0xCC, 0x83, 0xF6, 0x55,
+ 0xCC, 0x9B, 0xCC, 0xA3, 0xF6, 0x75, 0xCC, 0x9B,
+ 0xCC, 0xA3, 0xF6, 0x59, 0xCC, 0x80, 0xF6, 0x79,
+ 0xCC, 0x80, 0xF6, 0x59, 0xCC, 0xA3, 0xF6, 0x79,
+ 0xCC, 0xA3, 0xF6, 0x59, 0xCC, 0x89, 0xF6, 0x79,
+ 0xCC, 0x89, 0xF6, 0x59, 0xCC, 0x83, 0xF6, 0x79,
+ 0xCC, 0x83, 0xF6, 0xCE, 0xB1, 0xCC, 0x93, 0xF6,
+ 0xCE, 0xB1, 0xCC, 0x94, 0xF6, 0xCE, 0xB1, 0xCC,
+ 0x93, 0xCC, 0x80, 0xF6, 0xCE, 0xB1, 0xCC, 0x94,
+ 0xCC, 0x80, 0xF6, 0xCE, 0xB1, 0xCC, 0x93, 0xCC,
+ 0x81, 0xF6, 0xCE, 0xB1, 0xCC, 0x94, 0xCC, 0x81,
+ 0xF6, 0xCE, 0xB1, 0xCC, 0x93, 0xCD, 0x82, 0xF6,
+ 0xCE, 0xB1, 0xCC, 0x94, 0xCD, 0x82, 0xF6, 0xCE,
+ 0x91, 0xCC, 0x93, 0xF6, 0xCE, 0x91, 0xCC, 0x94,
+ 0xF6, 0xCE, 0x91, 0xCC, 0x93, 0xCC, 0x80, 0xF6,
+ 0xCE, 0x91, 0xCC, 0x94, 0xCC, 0x80, 0xF6, 0xCE,
+ 0x91, 0xCC, 0x93, 0xCC, 0x81, 0xF6, 0xCE, 0x91,
+ 0xCC, 0x94, 0xCC, 0x81, 0xF6, 0xCE, 0x91, 0xCC,
+ 0x93, 0xCD, 0x82, 0xF6, 0xCE, 0x91, 0xCC, 0x94,
+ 0xCD, 0x82, 0xF6, 0xCE, 0xB5, 0xCC, 0x93, 0xF6,
+ 0xCE, 0xB5, 0xCC, 0x94, 0xF6, 0xCE, 0xB5, 0xCC,
+ 0x93, 0xCC, 0x80, 0xF6, 0xCE, 0xB5, 0xCC, 0x94,
+ 0xCC, 0x80, 0xF6, 0xCE, 0xB5, 0xCC, 0x93, 0xCC,
+ 0x81, 0xF6, 0xCE, 0xB5, 0xCC, 0x94, 0xCC, 0x81,
+ 0xF6, 0xCE, 0x95, 0xCC, 0x93, 0xF6, 0xCE, 0x95,
+ 0xCC, 0x94, 0xF6, 0xCE, 0x95, 0xCC, 0x93, 0xCC,
+ 0x80, 0xF6, 0xCE, 0x95, 0xCC, 0x94, 0xCC, 0x80,
+ 0xF6, 0xCE, 0x95, 0xCC, 0x93, 0xCC, 0x81, 0xF6,
+ 0xCE, 0x95, 0xCC, 0x94, 0xCC, 0x81, 0xF6, 0xCE,
+ 0xB7, 0xCC, 0x93, 0xF6, 0xCE, 0xB7, 0xCC, 0x94,
+ 0xF6, 0xCE, 0xB7, 0xCC, 0x93, 0xCC, 0x80, 0xF6,
+ 0xCE, 0xB7, 0xCC, 0x94, 0xCC, 0x80, 0xF6, 0xCE,
+ 0xB7, 0xCC, 0x93, 0xCC, 0x81, 0xF6, 0xCE, 0xB7,
+ 0xCC, 0x94, 0xCC, 0x81, 0xF6, 0xCE, 0xB7, 0xCC,
+ 0x93, 0xCD, 0x82, 0xF6, 0xCE, 0xB7, 0xCC, 0x94,
+ 0xCD, 0x82, 0xF6, 0xCE, 0x97, 0xCC, 0x93, 0xF6,
+ 0xCE, 0x97, 0xCC, 0x94, 0xF6, 0xCE, 0x97, 0xCC,
+ 0x93, 0xCC, 0x80, 0xF6, 0xCE, 0x97, 0xCC, 0x94,
+ 0xCC, 0x80, 0xF6, 0xCE, 0x97, 0xCC, 0x93, 0xCC,
+ 0x81, 0xF6, 0xCE, 0x97, 0xCC, 0x94, 0xCC, 0x81,
+ 0xF6, 0xCE, 0x97, 0xCC, 0x93, 0xCD, 0x82, 0xF6,
+ 0xCE, 0x97, 0xCC, 0x94, 0xCD, 0x82, 0xF6, 0xCE,
+ 0xB9, 0xCC, 0x93, 0xF6, 0xCE, 0xB9, 0xCC, 0x94,
+ 0xF6, 0xCE, 0xB9, 0xCC, 0x93, 0xCC, 0x80, 0xF6,
+ 0xCE, 0xB9, 0xCC, 0x94, 0xCC, 0x80, 0xF6, 0xCE,
+ 0xB9, 0xCC, 0x93, 0xCC, 0x81, 0xF6, 0xCE, 0xB9,
+ 0xCC, 0x94, 0xCC, 0x81, 0xF6, 0xCE, 0xB9, 0xCC,
+ 0x93, 0xCD, 0x82, 0xF6, 0xCE, 0xB9, 0xCC, 0x94,
+ 0xCD, 0x82, 0xF6, 0xCE, 0x99, 0xCC, 0x93, 0xF6,
+ 0xCE, 0x99, 0xCC, 0x94, 0xF6, 0xCE, 0x99, 0xCC,
+ 0x93, 0xCC, 0x80, 0xF6, 0xCE, 0x99, 0xCC, 0x94,
+ 0xCC, 0x80, 0xF6, 0xCE, 0x99, 0xCC, 0x93, 0xCC,
+ 0x81, 0xF6, 0xCE, 0x99, 0xCC, 0x94, 0xCC, 0x81,
+ 0xF6, 0xCE, 0x99, 0xCC, 0x93, 0xCD, 0x82, 0xF6,
+ 0xCE, 0x99, 0xCC, 0x94, 0xCD, 0x82, 0xF6, 0xCE,
+ 0xBF, 0xCC, 0x93, 0xF6, 0xCE, 0xBF, 0xCC, 0x94,
+ 0xF6, 0xCE, 0xBF, 0xCC, 0x93, 0xCC, 0x80, 0xF6,
+ 0xCE, 0xBF, 0xCC, 0x94, 0xCC, 0x80, 0xF6, 0xCE,
+ 0xBF, 0xCC, 0x93, 0xCC, 0x81, 0xF6, 0xCE, 0xBF,
+ 0xCC, 0x94, 0xCC, 0x81, 0xF6, 0xCE, 0x9F, 0xCC,
+ 0x93, 0xF6, 0xCE, 0x9F, 0xCC, 0x94, 0xF6, 0xCE,
+ 0x9F, 0xCC, 0x93, 0xCC, 0x80, 0xF6, 0xCE, 0x9F,
+ 0xCC, 0x94, 0xCC, 0x80, 0xF6, 0xCE, 0x9F, 0xCC,
+ 0x93, 0xCC, 0x81, 0xF6, 0xCE, 0x9F, 0xCC, 0x94,
+ 0xCC, 0x81, 0xF6, 0xCF, 0x85, 0xCC, 0x93, 0xF6,
+ 0xCF, 0x85, 0xCC, 0x94, 0xF6, 0xCF, 0x85, 0xCC,
+ 0x93, 0xCC, 0x80, 0xF6, 0xCF, 0x85, 0xCC, 0x94,
+ 0xCC, 0x80, 0xF6, 0xCF, 0x85, 0xCC, 0x93, 0xCC,
+ 0x81, 0xF6, 0xCF, 0x85, 0xCC, 0x94, 0xCC, 0x81,
+ 0xF6, 0xCF, 0x85, 0xCC, 0x93, 0xCD, 0x82, 0xF6,
+ 0xCF, 0x85, 0xCC, 0x94, 0xCD, 0x82, 0xF6, 0xCE,
+ 0xA5, 0xCC, 0x94, 0xF6, 0xCE, 0xA5, 0xCC, 0x94,
+ 0xCC, 0x80, 0xF6, 0xCE, 0xA5, 0xCC, 0x94, 0xCC,
+ 0x81, 0xF6, 0xCE, 0xA5, 0xCC, 0x94, 0xCD, 0x82,
+ 0xF6, 0xCF, 0x89, 0xCC, 0x93, 0xF6, 0xCF, 0x89,
+ 0xCC, 0x94, 0xF6, 0xCF, 0x89, 0xCC, 0x93, 0xCC,
+ 0x80, 0xF6, 0xCF, 0x89, 0xCC, 0x94, 0xCC, 0x80,
+ 0xF6, 0xCF, 0x89, 0xCC, 0x93, 0xCC, 0x81, 0xF6,
+ 0xCF, 0x89, 0xCC, 0x94, 0xCC, 0x81, 0xF6, 0xCF,
+ 0x89, 0xCC, 0x93, 0xCD, 0x82, 0xF6, 0xCF, 0x89,
+ 0xCC, 0x94, 0xCD, 0x82, 0xF6, 0xCE, 0xA9, 0xCC,
+ 0x93, 0xF6, 0xCE, 0xA9, 0xCC, 0x94, 0xF6, 0xCE,
+ 0xA9, 0xCC, 0x93, 0xCC, 0x80, 0xF6, 0xCE, 0xA9,
+ 0xCC, 0x94, 0xCC, 0x80, 0xF6, 0xCE, 0xA9, 0xCC,
+ 0x93, 0xCC, 0x81, 0xF6, 0xCE, 0xA9, 0xCC, 0x94,
+ 0xCC, 0x81, 0xF6, 0xCE, 0xA9, 0xCC, 0x93, 0xCD,
+ 0x82, 0xF6, 0xCE, 0xA9, 0xCC, 0x94, 0xCD, 0x82,
+ 0xF6, 0xCE, 0xB1, 0xCC, 0x80, 0xF6, 0xCE, 0xB1,
+ 0xCC, 0x81, 0xF6, 0xCE, 0xB5, 0xCC, 0x80, 0xF6,
+ 0xCE, 0xB5, 0xCC, 0x81, 0xF6, 0xCE, 0xB7, 0xCC,
+ 0x80, 0xF6, 0xCE, 0xB7, 0xCC, 0x81, 0xF6, 0xCE,
+ 0xB9, 0xCC, 0x80, 0xF6, 0xCE, 0xB9, 0xCC, 0x81,
+ 0xF6, 0xCE, 0xBF, 0xCC, 0x80, 0xF6, 0xCE, 0xBF,
+ 0xCC, 0x81, 0xF6, 0xCF, 0x85, 0xCC, 0x80, 0xF6,
+ 0xCF, 0x85, 0xCC, 0x81, 0xF6, 0xCF, 0x89, 0xCC,
+ 0x80, 0xF6, 0xCF, 0x89, 0xCC, 0x81, 0xF6, 0xCE,
+ 0xB1, 0xCC, 0x93, 0xCD, 0x85, 0xF6, 0xCE, 0xB1,
+ 0xCC, 0x94, 0xCD, 0x85, 0xF6, 0xCE, 0xB1, 0xCC,
+ 0x93, 0xCC, 0x80, 0xCD, 0x85, 0xF6, 0xCE, 0xB1,
+ 0xCC, 0x94, 0xCC, 0x80, 0xCD, 0x85, 0xF6, 0xCE,
+ 0xB1, 0xCC, 0x93, 0xCC, 0x81, 0xCD, 0x85, 0xF6,
+ 0xCE, 0xB1, 0xCC, 0x94, 0xCC, 0x81, 0xCD, 0x85,
+ 0xF6, 0xCE, 0xB1, 0xCC, 0x93, 0xCD, 0x82, 0xCD,
+ 0x85, 0xF6, 0xCE, 0xB1, 0xCC, 0x94, 0xCD, 0x82,
+ 0xCD, 0x85, 0xF6, 0xCE, 0x91, 0xCC, 0x93, 0xCD,
+ 0x85, 0xF6, 0xCE, 0x91, 0xCC, 0x94, 0xCD, 0x85,
+ 0xF6, 0xCE, 0x91, 0xCC, 0x93, 0xCC, 0x80, 0xCD,
+ 0x85, 0xF6, 0xCE, 0x91, 0xCC, 0x94, 0xCC, 0x80,
+ 0xCD, 0x85, 0xF6, 0xCE, 0x91, 0xCC, 0x93, 0xCC,
+ 0x81, 0xCD, 0x85, 0xF6, 0xCE, 0x91, 0xCC, 0x94,
+ 0xCC, 0x81, 0xCD, 0x85, 0xF6, 0xCE, 0x91, 0xCC,
+ 0x93, 0xCD, 0x82, 0xCD, 0x85, 0xF6, 0xCE, 0x91,
+ 0xCC, 0x94, 0xCD, 0x82, 0xCD, 0x85, 0xF6, 0xCE,
+ 0xB7, 0xCC, 0x93, 0xCD, 0x85, 0xF6, 0xCE, 0xB7,
+ 0xCC, 0x94, 0xCD, 0x85, 0xF6, 0xCE, 0xB7, 0xCC,
+ 0x93, 0xCC, 0x80, 0xCD, 0x85, 0xF6, 0xCE, 0xB7,
+ 0xCC, 0x94, 0xCC, 0x80, 0xCD, 0x85, 0xF6, 0xCE,
+ 0xB7, 0xCC, 0x93, 0xCC, 0x81, 0xCD, 0x85, 0xF6,
+ 0xCE, 0xB7, 0xCC, 0x94, 0xCC, 0x81, 0xCD, 0x85,
+ 0xF6, 0xCE, 0xB7, 0xCC, 0x93, 0xCD, 0x82, 0xCD,
+ 0x85, 0xF6, 0xCE, 0xB7, 0xCC, 0x94, 0xCD, 0x82,
+ 0xCD, 0x85, 0xF6, 0xCE, 0x97, 0xCC, 0x93, 0xCD,
+ 0x85, 0xF6, 0xCE, 0x97, 0xCC, 0x94, 0xCD, 0x85,
+ 0xF6, 0xCE, 0x97, 0xCC, 0x93, 0xCC, 0x80, 0xCD,
+ 0x85, 0xF6, 0xCE, 0x97, 0xCC, 0x94, 0xCC, 0x80,
+ 0xCD, 0x85, 0xF6, 0xCE, 0x97, 0xCC, 0x93, 0xCC,
+ 0x81, 0xCD, 0x85, 0xF6, 0xCE, 0x97, 0xCC, 0x94,
+ 0xCC, 0x81, 0xCD, 0x85, 0xF6, 0xCE, 0x97, 0xCC,
+ 0x93, 0xCD, 0x82, 0xCD, 0x85, 0xF6, 0xCE, 0x97,
+ 0xCC, 0x94, 0xCD, 0x82, 0xCD, 0x85, 0xF6, 0xCF,
+ 0x89, 0xCC, 0x93, 0xCD, 0x85, 0xF6, 0xCF, 0x89,
+ 0xCC, 0x94, 0xCD, 0x85, 0xF6, 0xCF, 0x89, 0xCC,
+ 0x93, 0xCC, 0x80, 0xCD, 0x85, 0xF6, 0xCF, 0x89,
+ 0xCC, 0x94, 0xCC, 0x80, 0xCD, 0x85, 0xF6, 0xCF,
+ 0x89, 0xCC, 0x93, 0xCC, 0x81, 0xCD, 0x85, 0xF6,
+ 0xCF, 0x89, 0xCC, 0x94, 0xCC, 0x81, 0xCD, 0x85,
+ 0xF6, 0xCF, 0x89, 0xCC, 0x93, 0xCD, 0x82, 0xCD,
+ 0x85, 0xF6, 0xCF, 0x89, 0xCC, 0x94, 0xCD, 0x82,
+ 0xCD, 0x85, 0xF6, 0xCE, 0xA9, 0xCC, 0x93, 0xCD,
+ 0x85, 0xF6, 0xCE, 0xA9, 0xCC, 0x94, 0xCD, 0x85,
+ 0xF6, 0xCE, 0xA9, 0xCC, 0x93, 0xCC, 0x80, 0xCD,
+ 0x85, 0xF6, 0xCE, 0xA9, 0xCC, 0x94, 0xCC, 0x80,
+ 0xCD, 0x85, 0xF6, 0xCE, 0xA9, 0xCC, 0x93, 0xCC,
+ 0x81, 0xCD, 0x85, 0xF6, 0xCE, 0xA9, 0xCC, 0x94,
+ 0xCC, 0x81, 0xCD, 0x85, 0xF6, 0xCE, 0xA9, 0xCC,
+ 0x93, 0xCD, 0x82, 0xCD, 0x85, 0xF6, 0xCE, 0xA9,
+ 0xCC, 0x94, 0xCD, 0x82, 0xCD, 0x85, 0xF6, 0xCE,
+ 0xB1, 0xCC, 0x86, 0xF6, 0xCE, 0xB1, 0xCC, 0x84,
+ 0xF6, 0xCE, 0xB1, 0xCC, 0x80, 0xCD, 0x85, 0xF6,
+ 0xCE, 0xB1, 0xCD, 0x85, 0xF6, 0xCE, 0xB1, 0xCC,
+ 0x81, 0xCD, 0x85, 0xF6, 0xCE, 0xB1, 0xCD, 0x82,
+ 0xF6, 0xCE, 0xB1, 0xCD, 0x82, 0xCD, 0x85, 0xF6,
+ 0xCE, 0x91, 0xCC, 0x86, 0xF6, 0xCE, 0x91, 0xCC,
+ 0x84, 0xF6, 0xCE, 0x91, 0xCC, 0x80, 0xF6, 0xCE,
+ 0x91, 0xCC, 0x81, 0xF6, 0xCE, 0x91, 0xCD, 0x85,
+ 0x20, 0xCC, 0x93, 0xF6, 0xCE, 0xB9, 0x20, 0xCC,
+ 0x93, 0x20, 0xCD, 0x82, 0xF5, 0x05, 0xC2, 0xA8,
+ 0xCD, 0x82, 0x20, 0xCC, 0x88, 0xCD, 0x82, 0xF6,
+ 0xCE, 0xB7, 0xCC, 0x80, 0xCD, 0x85, 0xF6, 0xCE,
+ 0xB7, 0xCD, 0x85, 0xF6, 0xCE, 0xB7, 0xCC, 0x81,
+ 0xCD, 0x85, 0xF6, 0xCE, 0xB7, 0xCD, 0x82, 0xF6,
+ 0xCE, 0xB7, 0xCD, 0x82, 0xCD, 0x85, 0xF6, 0xCE,
+ 0x95, 0xCC, 0x80, 0xF6, 0xCE, 0x95, 0xCC, 0x81,
+ 0xF6, 0xCE, 0x97, 0xCC, 0x80, 0xF6, 0xCE, 0x97,
+ 0xCC, 0x81, 0xF6, 0xCE, 0x97, 0xCD, 0x85, 0xF5,
+ 0x06, 0xE1, 0xBE, 0xBF, 0xCC, 0x80, 0x20, 0xCC,
+ 0x93, 0xCC, 0x80, 0xF5, 0x06, 0xE1, 0xBE, 0xBF,
+ 0xCC, 0x81, 0x20, 0xCC, 0x93, 0xCC, 0x81, 0xF5,
+ 0x06, 0xE1, 0xBE, 0xBF, 0xCD, 0x82, 0x20, 0xCC,
+ 0x93, 0xCD, 0x82, 0xF6, 0xCE, 0xB9, 0xCC, 0x86,
+ 0xF6, 0xCE, 0xB9, 0xCC, 0x84, 0xF6, 0xCE, 0xB9,
+ 0xCC, 0x88, 0xCC, 0x80, 0xF6, 0xCE, 0xB9, 0xCC,
+ 0x88, 0xCC, 0x81, 0xF6, 0xCE, 0xB9, 0xCD, 0x82,
+ 0xF6, 0xCE, 0xB9, 0xCC, 0x88, 0xCD, 0x82, 0xF6,
+ 0xCE, 0x99, 0xCC, 0x86, 0xF6, 0xCE, 0x99, 0xCC,
+ 0x84, 0xF6, 0xCE, 0x99, 0xCC, 0x80, 0xF6, 0xCE,
+ 0x99, 0xCC, 0x81, 0xF5, 0x06, 0xE1, 0xBF, 0xBE,
+ 0xCC, 0x80, 0x20, 0xCC, 0x94, 0xCC, 0x80, 0xF5,
+ 0x06, 0xE1, 0xBF, 0xBE, 0xCC, 0x81, 0x20, 0xCC,
+ 0x94, 0xCC, 0x81, 0xF5, 0x06, 0xE1, 0xBF, 0xBE,
+ 0xCD, 0x82, 0x20, 0xCC, 0x94, 0xCD, 0x82, 0xF6,
+ 0xCF, 0x85, 0xCC, 0x86, 0xF6, 0xCF, 0x85, 0xCC,
+ 0x84, 0xF6, 0xCF, 0x85, 0xCC, 0x88, 0xCC, 0x80,
+ 0xF6, 0xCF, 0x85, 0xCC, 0x88, 0xCC, 0x81, 0xF6,
+ 0xCF, 0x81, 0xCC, 0x93, 0xF6, 0xCF, 0x81, 0xCC,
+ 0x94, 0xF6, 0xCF, 0x85, 0xCD, 0x82, 0xF6, 0xCF,
+ 0x85, 0xCC, 0x88, 0xCD, 0x82, 0xF6, 0xCE, 0xA5,
+ 0xCC, 0x86, 0xF6, 0xCE, 0xA5, 0xCC, 0x84, 0xF6,
+ 0xCE, 0xA5, 0xCC, 0x80, 0xF6, 0xCE, 0xA5, 0xCC,
+ 0x81, 0xF6, 0xCE, 0xA1, 0xCC, 0x94, 0xF5, 0x05,
+ 0xC2, 0xA8, 0xCC, 0x80, 0x20, 0xCC, 0x88, 0xCC,
+ 0x80, 0xF5, 0x05, 0xC2, 0xA8, 0xCC, 0x81, 0x20,
+ 0xCC, 0x88, 0xCC, 0x81, 0xF6, 0x60, 0xF6, 0xCF,
+ 0x89, 0xCC, 0x80, 0xCD, 0x85, 0xF6, 0xCF, 0x89,
+ 0xCD, 0x85, 0xF6, 0xCF, 0x89, 0xCC, 0x81, 0xCD,
+ 0x85, 0xF6, 0xCF, 0x89, 0xCD, 0x82, 0xF6, 0xCF,
+ 0x89, 0xCD, 0x82, 0xCD, 0x85, 0xF6, 0xCE, 0x9F,
+ 0xCC, 0x80, 0xF6, 0xCE, 0x9F, 0xCC, 0x81, 0xF6,
+ 0xCE, 0xA9, 0xCC, 0x80, 0xF6, 0xCE, 0xA9, 0xCC,
+ 0x81, 0xF6, 0xCE, 0xA9, 0xCD, 0x85, 0xF5, 0x03,
+ 0xC2, 0xB4, 0x20, 0xCC, 0x81, 0x20, 0xCC, 0x94,
+ 0xF5, 0x04, 0xE2, 0x80, 0x82, 0x20, 0xF5, 0x04,
+ 0xE2, 0x80, 0x83, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0xE2, 0x80, 0x90,
+ 0x20, 0xCC, 0xB3, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+ 0x2E, 0x20, 0xE2, 0x80, 0xB2, 0xE2, 0x80, 0xB2,
+ 0xE2, 0x80, 0xB2, 0xE2, 0x80, 0xB2, 0xE2, 0x80,
+ 0xB2, 0xE2, 0x80, 0xB5, 0xE2, 0x80, 0xB5, 0xE2,
+ 0x80, 0xB5, 0xE2, 0x80, 0xB5, 0xE2, 0x80, 0xB5,
+ 0x21, 0x21, 0x20, 0xCC, 0x85, 0x3F, 0x3F, 0x3F,
+ 0x21, 0x21, 0x3F, 0xE2, 0x80, 0xB2, 0xE2, 0x80,
+ 0xB2, 0xE2, 0x80, 0xB2, 0xE2, 0x80, 0xB2, 0x20,
+ 0x30, 0x69, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x2B, 0xE2, 0x88, 0x92, 0x3D, 0x28, 0x29, 0x6E,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x2B, 0xE2, 0x88, 0x92, 0x3D, 0x28,
+ 0x29, 0x61, 0x65, 0x6F, 0x78, 0xC9, 0x99, 0x52,
+ 0x73, 0x61, 0x2F, 0x63, 0x61, 0x2F, 0x73, 0x43,
+ 0xC2, 0xB0, 0x43, 0x63, 0x2F, 0x6F, 0x63, 0x2F,
+ 0x75, 0xC6, 0x90, 0xC2, 0xB0, 0x46, 0x67, 0x48,
+ 0x48, 0x48, 0x68, 0xC4, 0xA7, 0x49, 0x49, 0x4C,
+ 0x6C, 0x4E, 0x4E, 0x6F, 0x50, 0x51, 0x52, 0x52,
+ 0x52, 0x53, 0x4D, 0x54, 0x45, 0x4C, 0x54, 0x4D,
+ 0x5A, 0xF6, 0xCE, 0xA9, 0x5A, 0xF6, 0x4B, 0xF6,
+ 0x41, 0xCC, 0x8A, 0x42, 0x43, 0x65, 0x45, 0x46,
+ 0x4D, 0x6F, 0xD7, 0x90, 0xD7, 0x91, 0xD7, 0x92,
+ 0xD7, 0x93, 0x69, 0x46, 0x41, 0x58, 0xCF, 0x80,
+ 0xCE, 0xB3, 0xCE, 0x93, 0xCE, 0xA0, 0xE2, 0x88,
+ 0x91, 0x44, 0x64, 0x65, 0x69, 0x6A, 0x31, 0xE2,
+ 0x81, 0x84, 0x33, 0x32, 0xE2, 0x81, 0x84, 0x33,
+ 0x31, 0xE2, 0x81, 0x84, 0x35, 0x32, 0xE2, 0x81,
+ 0x84, 0x35, 0x33, 0xE2, 0x81, 0x84, 0x35, 0x34,
+ 0xE2, 0x81, 0x84, 0x35, 0x31, 0xE2, 0x81, 0x84,
+ 0x36, 0x35, 0xE2, 0x81, 0x84, 0x36, 0x31, 0xE2,
+ 0x81, 0x84, 0x38, 0x33, 0xE2, 0x81, 0x84, 0x38,
+ 0x35, 0xE2, 0x81, 0x84, 0x38, 0x37, 0xE2, 0x81,
+ 0x84, 0x38, 0x31, 0xE2, 0x81, 0x84, 0x49, 0x49,
+ 0x49, 0x49, 0x49, 0x49, 0x49, 0x56, 0x56, 0x56,
+ 0x49, 0x56, 0x49, 0x49, 0x56, 0x49, 0x49, 0x49,
+ 0x49, 0x58, 0x58, 0x58, 0x49, 0x58, 0x49, 0x49,
+ 0x4C, 0x43, 0x44, 0x4D, 0x69, 0x69, 0x69, 0x69,
+ 0x69, 0x69, 0x69, 0x76, 0x76, 0x76, 0x69, 0x76,
+ 0x69, 0x69, 0x76, 0x69, 0x69, 0x69, 0x69, 0x78,
+ 0x78, 0x78, 0x69, 0x78, 0x69, 0x69, 0x6C, 0x63,
+ 0x64, 0x6D, 0xF6, 0xE2, 0x86, 0x90, 0xCC, 0xB8,
+ 0xF6, 0xE2, 0x86, 0x92, 0xCC, 0xB8, 0xF6, 0xE2,
+ 0x86, 0x94, 0xCC, 0xB8, 0xF6, 0xE2, 0x87, 0x90,
+ 0xCC, 0xB8, 0xF6, 0xE2, 0x87, 0x94, 0xCC, 0xB8,
+ 0xF6, 0xE2, 0x87, 0x92, 0xCC, 0xB8, 0xF6, 0xE2,
+ 0x88, 0x83, 0xCC, 0xB8, 0xF6, 0xE2, 0x88, 0x88,
+ 0xCC, 0xB8, 0xF6, 0xE2, 0x88, 0x8B, 0xCC, 0xB8,
+ 0xF6, 0xE2, 0x88, 0xA3, 0xCC, 0xB8, 0xF6, 0xE2,
+ 0x88, 0xA5, 0xCC, 0xB8, 0xE2, 0x88, 0xAB, 0xE2,
+ 0x88, 0xAB, 0xE2, 0x88, 0xAB, 0xE2, 0x88, 0xAB,
+ 0xE2, 0x88, 0xAB, 0xE2, 0x88, 0xAE, 0xE2, 0x88,
+ 0xAE, 0xE2, 0x88, 0xAE, 0xE2, 0x88, 0xAE, 0xE2,
+ 0x88, 0xAE, 0xF6, 0xE2, 0x88, 0xBC, 0xCC, 0xB8,
+ 0xF6, 0xE2, 0x89, 0x83, 0xCC, 0xB8, 0xF6, 0xE2,
+ 0x89, 0x85, 0xCC, 0xB8, 0xF6, 0xE2, 0x89, 0x88,
+ 0xCC, 0xB8, 0xF6, 0x3D, 0xCC, 0xB8, 0xF6, 0xE2,
+ 0x89, 0xA1, 0xCC, 0xB8, 0xF6, 0xE2, 0x89, 0x8D,
+ 0xCC, 0xB8, 0xF6, 0x3C, 0xCC, 0xB8, 0xF6, 0x3E,
+ 0xCC, 0xB8, 0xF6, 0xE2, 0x89, 0xA4, 0xCC, 0xB8,
+ 0xF6, 0xE2, 0x89, 0xA5, 0xCC, 0xB8, 0xF6, 0xE2,
+ 0x89, 0xB2, 0xCC, 0xB8, 0xF6, 0xE2, 0x89, 0xB3,
+ 0xCC, 0xB8, 0xF6, 0xE2, 0x89, 0xB6, 0xCC, 0xB8,
+ 0xF6, 0xE2, 0x89, 0xB7, 0xCC, 0xB8, 0xF6, 0xE2,
+ 0x89, 0xBA, 0xCC, 0xB8, 0xF6, 0xE2, 0x89, 0xBB,
+ 0xCC, 0xB8, 0xF6, 0xE2, 0x8A, 0x82, 0xCC, 0xB8,
+ 0xF6, 0xE2, 0x8A, 0x83, 0xCC, 0xB8, 0xF6, 0xE2,
+ 0x8A, 0x86, 0xCC, 0xB8, 0xF6, 0xE2, 0x8A, 0x87,
+ 0xCC, 0xB8, 0xF6, 0xE2, 0x8A, 0xA2, 0xCC, 0xB8,
+ 0xF6, 0xE2, 0x8A, 0xA8, 0xCC, 0xB8, 0xF6, 0xE2,
+ 0x8A, 0xA9, 0xCC, 0xB8, 0xF6, 0xE2, 0x8A, 0xAB,
+ 0xCC, 0xB8, 0xF6, 0xE2, 0x89, 0xBC, 0xCC, 0xB8,
+ 0xF6, 0xE2, 0x89, 0xBD, 0xCC, 0xB8, 0xF6, 0xE2,
+ 0x8A, 0x91, 0xCC, 0xB8, 0xF6, 0xE2, 0x8A, 0x92,
+ 0xCC, 0xB8, 0xF6, 0xE2, 0x8A, 0xB2, 0xCC, 0xB8,
+ 0xF6, 0xE2, 0x8A, 0xB3, 0xCC, 0xB8, 0xF6, 0xE2,
+ 0x8A, 0xB4, 0xCC, 0xB8, 0xF6, 0xE2, 0x8A, 0xB5,
+ 0xCC, 0xB8, 0xF6, 0xE3, 0x80, 0x88, 0xF6, 0xE3,
+ 0x80, 0x89, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
+ 0x37, 0x38, 0x39, 0x31, 0x30, 0x31, 0x31, 0x31,
+ 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31,
+ 0x36, 0x31, 0x37, 0x31, 0x38, 0x31, 0x39, 0x32,
+ 0x30, 0x28, 0x31, 0x29, 0x28, 0x32, 0x29, 0x28,
+ 0x33, 0x29, 0x28, 0x34, 0x29, 0x28, 0x35, 0x29,
+ 0x28, 0x36, 0x29, 0x28, 0x37, 0x29, 0x28, 0x38,
+ 0x29, 0x28, 0x39, 0x29, 0x28, 0x31, 0x30, 0x29,
+ 0x28, 0x31, 0x31, 0x29, 0x28, 0x31, 0x32, 0x29,
+ 0x28, 0x31, 0x33, 0x29, 0x28, 0x31, 0x34, 0x29,
+ 0x28, 0x31, 0x35, 0x29, 0x28, 0x31, 0x36, 0x29,
+ 0x28, 0x31, 0x37, 0x29, 0x28, 0x31, 0x38, 0x29,
+ 0x28, 0x31, 0x39, 0x29, 0x28, 0x32, 0x30, 0x29,
+ 0x31, 0x2E, 0x32, 0x2E, 0x33, 0x2E, 0x34, 0x2E,
+ 0x35, 0x2E, 0x36, 0x2E, 0x37, 0x2E, 0x38, 0x2E,
+ 0x39, 0x2E, 0x31, 0x30, 0x2E, 0x31, 0x31, 0x2E,
+ 0x31, 0x32, 0x2E, 0x31, 0x33, 0x2E, 0x31, 0x34,
+ 0x2E, 0x31, 0x35, 0x2E, 0x31, 0x36, 0x2E, 0x31,
+ 0x37, 0x2E, 0x31, 0x38, 0x2E, 0x31, 0x39, 0x2E,
+ 0x32, 0x30, 0x2E, 0x28, 0x61, 0x29, 0x28, 0x62,
+ 0x29, 0x28, 0x63, 0x29, 0x28, 0x64, 0x29, 0x28,
+ 0x65, 0x29, 0x28, 0x66, 0x29, 0x28, 0x67, 0x29,
+ 0x28, 0x68, 0x29, 0x28, 0x69, 0x29, 0x28, 0x6A,
+ 0x29, 0x28, 0x6B, 0x29, 0x28, 0x6C, 0x29, 0x28,
+ 0x6D, 0x29, 0x28, 0x6E, 0x29, 0x28, 0x6F, 0x29,
+ 0x28, 0x70, 0x29, 0x28, 0x71, 0x29, 0x28, 0x72,
+ 0x29, 0x28, 0x73, 0x29, 0x28, 0x74, 0x29, 0x28,
+ 0x75, 0x29, 0x28, 0x76, 0x29, 0x28, 0x77, 0x29,
+ 0x28, 0x78, 0x29, 0x28, 0x79, 0x29, 0x28, 0x7A,
+ 0x29, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D,
+ 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7A, 0x30, 0xE2, 0x88,
+ 0xAB, 0xE2, 0x88, 0xAB, 0xE2, 0x88, 0xAB, 0xE2,
+ 0x88, 0xAB, 0x3A, 0x3A, 0x3D, 0x3D, 0x3D, 0x3D,
+ 0x3D, 0x3D, 0xF6, 0xE2, 0xAB, 0x9D, 0xCC, 0xB8,
+ 0xE2, 0xB5, 0xA1, 0xE6, 0xAF, 0x8D, 0xE9, 0xBE,
+ 0x9F, 0xE4, 0xB8, 0x80, 0xE4, 0xB8, 0xA8, 0xE4,
+ 0xB8, 0xB6, 0xE4, 0xB8, 0xBF, 0xE4, 0xB9, 0x99,
+ 0xE4, 0xBA, 0x85, 0xE4, 0xBA, 0x8C, 0xE4, 0xBA,
+ 0xA0, 0xE4, 0xBA, 0xBA, 0xE5, 0x84, 0xBF, 0xE5,
+ 0x85, 0xA5, 0xE5, 0x85, 0xAB, 0xE5, 0x86, 0x82,
+ 0xE5, 0x86, 0x96, 0xE5, 0x86, 0xAB, 0xE5, 0x87,
+ 0xA0, 0xE5, 0x87, 0xB5, 0xE5, 0x88, 0x80, 0xE5,
+ 0x8A, 0x9B, 0xE5, 0x8B, 0xB9, 0xE5, 0x8C, 0x95,
+ 0xE5, 0x8C, 0x9A, 0xE5, 0x8C, 0xB8, 0xE5, 0x8D,
+ 0x81, 0xE5, 0x8D, 0x9C, 0xE5, 0x8D, 0xA9, 0xE5,
+ 0x8E, 0x82, 0xE5, 0x8E, 0xB6, 0xE5, 0x8F, 0x88,
+ 0xE5, 0x8F, 0xA3, 0xE5, 0x9B, 0x97, 0xE5, 0x9C,
+ 0x9F, 0xE5, 0xA3, 0xAB, 0xE5, 0xA4, 0x82, 0xE5,
+ 0xA4, 0x8A, 0xE5, 0xA4, 0x95, 0xE5, 0xA4, 0xA7,
+ 0xE5, 0xA5, 0xB3, 0xE5, 0xAD, 0x90, 0xE5, 0xAE,
+ 0x80, 0xE5, 0xAF, 0xB8, 0xE5, 0xB0, 0x8F, 0xE5,
+ 0xB0, 0xA2, 0xE5, 0xB0, 0xB8, 0xE5, 0xB1, 0xAE,
+ 0xE5, 0xB1, 0xB1, 0xE5, 0xB7, 0x9B, 0xE5, 0xB7,
+ 0xA5, 0xE5, 0xB7, 0xB1, 0xE5, 0xB7, 0xBE, 0xE5,
+ 0xB9, 0xB2, 0xE5, 0xB9, 0xBA, 0xE5, 0xB9, 0xBF,
+ 0xE5, 0xBB, 0xB4, 0xE5, 0xBB, 0xBE, 0xE5, 0xBC,
+ 0x8B, 0xE5, 0xBC, 0x93, 0xE5, 0xBD, 0x90, 0xE5,
+ 0xBD, 0xA1, 0xE5, 0xBD, 0xB3, 0xE5, 0xBF, 0x83,
+ 0xE6, 0x88, 0x88, 0xE6, 0x88, 0xB6, 0xE6, 0x89,
+ 0x8B, 0xE6, 0x94, 0xAF, 0xE6, 0x94, 0xB4, 0xE6,
+ 0x96, 0x87, 0xE6, 0x96, 0x97, 0xE6, 0x96, 0xA4,
+ 0xE6, 0x96, 0xB9, 0xE6, 0x97, 0xA0, 0xE6, 0x97,
+ 0xA5, 0xE6, 0x9B, 0xB0, 0xE6, 0x9C, 0x88, 0xE6,
+ 0x9C, 0xA8, 0xE6, 0xAC, 0xA0, 0xE6, 0xAD, 0xA2,
+ 0xE6, 0xAD, 0xB9, 0xE6, 0xAE, 0xB3, 0xE6, 0xAF,
+ 0x8B, 0xE6, 0xAF, 0x94, 0xE6, 0xAF, 0x9B, 0xE6,
+ 0xB0, 0x8F, 0xE6, 0xB0, 0x94, 0xE6, 0xB0, 0xB4,
+ 0xE7, 0x81, 0xAB, 0xE7, 0x88, 0xAA, 0xE7, 0x88,
+ 0xB6, 0xE7, 0x88, 0xBB, 0xE7, 0x88, 0xBF, 0xE7,
+ 0x89, 0x87, 0xE7, 0x89, 0x99, 0xE7, 0x89, 0x9B,
+ 0xE7, 0x8A, 0xAC, 0xE7, 0x8E, 0x84, 0xE7, 0x8E,
+ 0x89, 0xE7, 0x93, 0x9C, 0xE7, 0x93, 0xA6, 0xE7,
+ 0x94, 0x98, 0xE7, 0x94, 0x9F, 0xE7, 0x94, 0xA8,
+ 0xE7, 0x94, 0xB0, 0xE7, 0x96, 0x8B, 0xE7, 0x96,
+ 0x92, 0xE7, 0x99, 0xB6, 0xE7, 0x99, 0xBD, 0xE7,
+ 0x9A, 0xAE, 0xE7, 0x9A, 0xBF, 0xE7, 0x9B, 0xAE,
+ 0xE7, 0x9F, 0x9B, 0xE7, 0x9F, 0xA2, 0xE7, 0x9F,
+ 0xB3, 0xE7, 0xA4, 0xBA, 0xE7, 0xA6, 0xB8, 0xE7,
+ 0xA6, 0xBE, 0xE7, 0xA9, 0xB4, 0xE7, 0xAB, 0x8B,
+ 0xE7, 0xAB, 0xB9, 0xE7, 0xB1, 0xB3, 0xE7, 0xB3,
+ 0xB8, 0xE7, 0xBC, 0xB6, 0xE7, 0xBD, 0x91, 0xE7,
+ 0xBE, 0x8A, 0xE7, 0xBE, 0xBD, 0xE8, 0x80, 0x81,
+ 0xE8, 0x80, 0x8C, 0xE8, 0x80, 0x92, 0xE8, 0x80,
+ 0xB3, 0xE8, 0x81, 0xBF, 0xE8, 0x82, 0x89, 0xE8,
+ 0x87, 0xA3, 0xE8, 0x87, 0xAA, 0xE8, 0x87, 0xB3,
+ 0xE8, 0x87, 0xBC, 0xE8, 0x88, 0x8C, 0xE8, 0x88,
+ 0x9B, 0xE8, 0x88, 0x9F, 0xE8, 0x89, 0xAE, 0xE8,
+ 0x89, 0xB2, 0xE8, 0x89, 0xB8, 0xE8, 0x99, 0x8D,
+ 0xE8, 0x99, 0xAB, 0xE8, 0xA1, 0x80, 0xE8, 0xA1,
+ 0x8C, 0xE8, 0xA1, 0xA3, 0xE8, 0xA5, 0xBE, 0xE8,
+ 0xA6, 0x8B, 0xE8, 0xA7, 0x92, 0xE8, 0xA8, 0x80,
+ 0xE8, 0xB0, 0xB7, 0xE8, 0xB1, 0x86, 0xE8, 0xB1,
+ 0x95, 0xE8, 0xB1, 0xB8, 0xE8, 0xB2, 0x9D, 0xE8,
+ 0xB5, 0xA4, 0xE8, 0xB5, 0xB0, 0xE8, 0xB6, 0xB3,
+ 0xE8, 0xBA, 0xAB, 0xE8, 0xBB, 0x8A, 0xE8, 0xBE,
+ 0x9B, 0xE8, 0xBE, 0xB0, 0xE8, 0xBE, 0xB5, 0xE9,
+ 0x82, 0x91, 0xE9, 0x85, 0x89, 0xE9, 0x87, 0x86,
+ 0xE9, 0x87, 0x8C, 0xE9, 0x87, 0x91, 0xE9, 0x95,
+ 0xB7, 0xE9, 0x96, 0x80, 0xE9, 0x98, 0x9C, 0xE9,
+ 0x9A, 0xB6, 0xE9, 0x9A, 0xB9, 0xE9, 0x9B, 0xA8,
+ 0xE9, 0x9D, 0x91, 0xE9, 0x9D, 0x9E, 0xE9, 0x9D,
+ 0xA2, 0xE9, 0x9D, 0xA9, 0xE9, 0x9F, 0x8B, 0xE9,
+ 0x9F, 0xAD, 0xE9, 0x9F, 0xB3, 0xE9, 0xA0, 0x81,
+ 0xE9, 0xA2, 0xA8, 0xE9, 0xA3, 0x9B, 0xE9, 0xA3,
+ 0x9F, 0xE9, 0xA6, 0x96, 0xE9, 0xA6, 0x99, 0xE9,
+ 0xA6, 0xAC, 0xE9, 0xAA, 0xA8, 0xE9, 0xAB, 0x98,
+ 0xE9, 0xAB, 0x9F, 0xE9, 0xAC, 0xA5, 0xE9, 0xAC,
+ 0xAF, 0xE9, 0xAC, 0xB2, 0xE9, 0xAC, 0xBC, 0xE9,
+ 0xAD, 0x9A, 0xE9, 0xB3, 0xA5, 0xE9, 0xB9, 0xB5,
+ 0xE9, 0xB9, 0xBF, 0xE9, 0xBA, 0xA5, 0xE9, 0xBA,
+ 0xBB, 0xE9, 0xBB, 0x83, 0xE9, 0xBB, 0x8D, 0xE9,
+ 0xBB, 0x91, 0xE9, 0xBB, 0xB9, 0xE9, 0xBB, 0xBD,
+ 0xE9, 0xBC, 0x8E, 0xE9, 0xBC, 0x93, 0xE9, 0xBC,
+ 0xA0, 0xE9, 0xBC, 0xBB, 0xE9, 0xBD, 0x8A, 0xE9,
+ 0xBD, 0x92, 0xE9, 0xBE, 0x8D, 0xE9, 0xBE, 0x9C,
+ 0xE9, 0xBE, 0xA0, 0x20, 0xE3, 0x80, 0x92, 0xE5,
+ 0x8D, 0x81, 0xE5, 0x8D, 0x84, 0xE5, 0x8D, 0x85,
+ 0xF6, 0xE3, 0x81, 0x8B, 0xE3, 0x82, 0x99, 0xF6,
+ 0xE3, 0x81, 0x8D, 0xE3, 0x82, 0x99, 0xF6, 0xE3,
+ 0x81, 0x8F, 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x81,
+ 0x91, 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x81, 0x93,
+ 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x81, 0x95, 0xE3,
+ 0x82, 0x99, 0xF6, 0xE3, 0x81, 0x97, 0xE3, 0x82,
+ 0x99, 0xF6, 0xE3, 0x81, 0x99, 0xE3, 0x82, 0x99,
+ 0xF6, 0xE3, 0x81, 0x9B, 0xE3, 0x82, 0x99, 0xF6,
+ 0xE3, 0x81, 0x9D, 0xE3, 0x82, 0x99, 0xF6, 0xE3,
+ 0x81, 0x9F, 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x81,
+ 0xA1, 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x81, 0xA4,
+ 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x81, 0xA6, 0xE3,
+ 0x82, 0x99, 0xF6, 0xE3, 0x81, 0xA8, 0xE3, 0x82,
+ 0x99, 0xF6, 0xE3, 0x81, 0xAF, 0xE3, 0x82, 0x99,
+ 0xF6, 0xE3, 0x81, 0xAF, 0xE3, 0x82, 0x9A, 0xF6,
+ 0xE3, 0x81, 0xB2, 0xE3, 0x82, 0x99, 0xF6, 0xE3,
+ 0x81, 0xB2, 0xE3, 0x82, 0x9A, 0xF6, 0xE3, 0x81,
+ 0xB5, 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x81, 0xB5,
+ 0xE3, 0x82, 0x9A, 0xF6, 0xE3, 0x81, 0xB8, 0xE3,
+ 0x82, 0x99, 0xF6, 0xE3, 0x81, 0xB8, 0xE3, 0x82,
+ 0x9A, 0xF6, 0xE3, 0x81, 0xBB, 0xE3, 0x82, 0x99,
+ 0xF6, 0xE3, 0x81, 0xBB, 0xE3, 0x82, 0x9A, 0xF6,
+ 0xE3, 0x81, 0x86, 0xE3, 0x82, 0x99, 0x20, 0xE3,
+ 0x82, 0x99, 0x20, 0xE3, 0x82, 0x9A, 0xF6, 0xE3,
+ 0x82, 0x9D, 0xE3, 0x82, 0x99, 0xE3, 0x82, 0x88,
+ 0xE3, 0x82, 0x8A, 0xF6, 0xE3, 0x82, 0xAB, 0xE3,
+ 0x82, 0x99, 0xF6, 0xE3, 0x82, 0xAD, 0xE3, 0x82,
+ 0x99, 0xF6, 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0x99,
+ 0xF6, 0xE3, 0x82, 0xB1, 0xE3, 0x82, 0x99, 0xF6,
+ 0xE3, 0x82, 0xB3, 0xE3, 0x82, 0x99, 0xF6, 0xE3,
+ 0x82, 0xB5, 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x82,
+ 0xB7, 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x82, 0xB9,
+ 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x82, 0xBB, 0xE3,
+ 0x82, 0x99, 0xF6, 0xE3, 0x82, 0xBD, 0xE3, 0x82,
+ 0x99, 0xF6, 0xE3, 0x82, 0xBF, 0xE3, 0x82, 0x99,
+ 0xF6, 0xE3, 0x83, 0x81, 0xE3, 0x82, 0x99, 0xF6,
+ 0xE3, 0x83, 0x84, 0xE3, 0x82, 0x99, 0xF6, 0xE3,
+ 0x83, 0x86, 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x83,
+ 0x88, 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x83, 0x8F,
+ 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x83, 0x8F, 0xE3,
+ 0x82, 0x9A, 0xF6, 0xE3, 0x83, 0x92, 0xE3, 0x82,
+ 0x99, 0xF6, 0xE3, 0x83, 0x92, 0xE3, 0x82, 0x9A,
+ 0xF6, 0xE3, 0x83, 0x95, 0xE3, 0x82, 0x99, 0xF6,
+ 0xE3, 0x83, 0x95, 0xE3, 0x82, 0x9A, 0xF6, 0xE3,
+ 0x83, 0x98, 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x83,
+ 0x98, 0xE3, 0x82, 0x9A, 0xF6, 0xE3, 0x83, 0x9B,
+ 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x83, 0x9B, 0xE3,
+ 0x82, 0x9A, 0xF6, 0xE3, 0x82, 0xA6, 0xE3, 0x82,
+ 0x99, 0xF6, 0xE3, 0x83, 0xAF, 0xE3, 0x82, 0x99,
+ 0xF6, 0xE3, 0x83, 0xB0, 0xE3, 0x82, 0x99, 0xF6,
+ 0xE3, 0x83, 0xB1, 0xE3, 0x82, 0x99, 0xF6, 0xE3,
+ 0x83, 0xB2, 0xE3, 0x82, 0x99, 0xF6, 0xE3, 0x83,
+ 0xBD, 0xE3, 0x82, 0x99, 0xE3, 0x82, 0xB3, 0xE3,
+ 0x83, 0x88, 0xE1, 0x84, 0x80, 0xE1, 0x84, 0x81,
+ 0xE1, 0x86, 0xAA, 0xE1, 0x84, 0x82, 0xE1, 0x86,
+ 0xAC, 0xE1, 0x86, 0xAD, 0xE1, 0x84, 0x83, 0xE1,
+ 0x84, 0x84, 0xE1, 0x84, 0x85, 0xE1, 0x86, 0xB0,
+ 0xE1, 0x86, 0xB1, 0xE1, 0x86, 0xB2, 0xE1, 0x86,
+ 0xB3, 0xE1, 0x86, 0xB4, 0xE1, 0x86, 0xB5, 0xE1,
+ 0x84, 0x9A, 0xE1, 0x84, 0x86, 0xE1, 0x84, 0x87,
+ 0xE1, 0x84, 0x88, 0xE1, 0x84, 0xA1, 0xE1, 0x84,
+ 0x89, 0xE1, 0x84, 0x8A, 0xE1, 0x84, 0x8B, 0xE1,
+ 0x84, 0x8C, 0xE1, 0x84, 0x8D, 0xE1, 0x84, 0x8E,
+ 0xE1, 0x84, 0x8F, 0xE1, 0x84, 0x90, 0xE1, 0x84,
+ 0x91, 0xE1, 0x84, 0x92, 0xE1, 0x85, 0xA1, 0xE1,
+ 0x85, 0xA2, 0xE1, 0x85, 0xA3, 0xE1, 0x85, 0xA4,
+ 0xE1, 0x85, 0xA5, 0xE1, 0x85, 0xA6, 0xE1, 0x85,
+ 0xA7, 0xE1, 0x85, 0xA8, 0xE1, 0x85, 0xA9, 0xE1,
+ 0x85, 0xAA, 0xE1, 0x85, 0xAB, 0xE1, 0x85, 0xAC,
+ 0xE1, 0x85, 0xAD, 0xE1, 0x85, 0xAE, 0xE1, 0x85,
+ 0xAF, 0xE1, 0x85, 0xB0, 0xE1, 0x85, 0xB1, 0xE1,
+ 0x85, 0xB2, 0xE1, 0x85, 0xB3, 0xE1, 0x85, 0xB4,
+ 0xE1, 0x85, 0xB5, 0xE1, 0x85, 0xA0, 0xE1, 0x84,
+ 0x94, 0xE1, 0x84, 0x95, 0xE1, 0x87, 0x87, 0xE1,
+ 0x87, 0x88, 0xE1, 0x87, 0x8C, 0xE1, 0x87, 0x8E,
+ 0xE1, 0x87, 0x93, 0xE1, 0x87, 0x97, 0xE1, 0x87,
+ 0x99, 0xE1, 0x84, 0x9C, 0xE1, 0x87, 0x9D, 0xE1,
+ 0x87, 0x9F, 0xE1, 0x84, 0x9D, 0xE1, 0x84, 0x9E,
+ 0xE1, 0x84, 0xA0, 0xE1, 0x84, 0xA2, 0xE1, 0x84,
+ 0xA3, 0xE1, 0x84, 0xA7, 0xE1, 0x84, 0xA9, 0xE1,
+ 0x84, 0xAB, 0xE1, 0x84, 0xAC, 0xE1, 0x84, 0xAD,
+ 0xE1, 0x84, 0xAE, 0xE1, 0x84, 0xAF, 0xE1, 0x84,
+ 0xB2, 0xE1, 0x84, 0xB6, 0xE1, 0x85, 0x80, 0xE1,
+ 0x85, 0x87, 0xE1, 0x85, 0x8C, 0xE1, 0x87, 0xB1,
+ 0xE1, 0x87, 0xB2, 0xE1, 0x85, 0x97, 0xE1, 0x85,
+ 0x98, 0xE1, 0x85, 0x99, 0xE1, 0x86, 0x84, 0xE1,
+ 0x86, 0x85, 0xE1, 0x86, 0x88, 0xE1, 0x86, 0x91,
+ 0xE1, 0x86, 0x92, 0xE1, 0x86, 0x94, 0xE1, 0x86,
+ 0x9E, 0xE1, 0x86, 0xA1, 0xE4, 0xB8, 0x80, 0xE4,
+ 0xBA, 0x8C, 0xE4, 0xB8, 0x89, 0xE5, 0x9B, 0x9B,
+ 0xE4, 0xB8, 0x8A, 0xE4, 0xB8, 0xAD, 0xE4, 0xB8,
+ 0x8B, 0xE7, 0x94, 0xB2, 0xE4, 0xB9, 0x99, 0xE4,
+ 0xB8, 0x99, 0xE4, 0xB8, 0x81, 0xE5, 0xA4, 0xA9,
+ 0xE5, 0x9C, 0xB0, 0xE4, 0xBA, 0xBA, 0x28, 0xE1,
+ 0x84, 0x80, 0x29, 0x28, 0xE1, 0x84, 0x82, 0x29,
+ 0x28, 0xE1, 0x84, 0x83, 0x29, 0x28, 0xE1, 0x84,
+ 0x85, 0x29, 0x28, 0xE1, 0x84, 0x86, 0x29, 0x28,
+ 0xE1, 0x84, 0x87, 0x29, 0x28, 0xE1, 0x84, 0x89,
+ 0x29, 0x28, 0xE1, 0x84, 0x8B, 0x29, 0x28, 0xE1,
+ 0x84, 0x8C, 0x29, 0x28, 0xE1, 0x84, 0x8E, 0x29,
+ 0x28, 0xE1, 0x84, 0x8F, 0x29, 0x28, 0xE1, 0x84,
+ 0x90, 0x29, 0x28, 0xE1, 0x84, 0x91, 0x29, 0x28,
+ 0xE1, 0x84, 0x92, 0x29, 0x28, 0xE1, 0x84, 0x80,
+ 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84, 0x82,
+ 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84, 0x83,
+ 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84, 0x85,
+ 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84, 0x86,
+ 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84, 0x87,
+ 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84, 0x89,
+ 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84, 0x8B,
+ 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84, 0x8C,
+ 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84, 0x8E,
+ 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84, 0x8F,
+ 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84, 0x90,
+ 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84, 0x91,
+ 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84, 0x92,
+ 0xE1, 0x85, 0xA1, 0x29, 0x28, 0xE1, 0x84, 0x8C,
+ 0xE1, 0x85, 0xAE, 0x29, 0x28, 0xE1, 0x84, 0x8B,
+ 0xE1, 0x85, 0xA9, 0xE1, 0x84, 0x8C, 0xE1, 0x85,
+ 0xA5, 0xE1, 0x86, 0xAB, 0x29, 0x28, 0xE1, 0x84,
+ 0x8B, 0xE1, 0x85, 0xA9, 0xE1, 0x84, 0x92, 0xE1,
+ 0x85, 0xAE, 0x29, 0x28, 0xE4, 0xB8, 0x80, 0x29,
+ 0x28, 0xE4, 0xBA, 0x8C, 0x29, 0x28, 0xE4, 0xB8,
+ 0x89, 0x29, 0x28, 0xE5, 0x9B, 0x9B, 0x29, 0x28,
+ 0xE4, 0xBA, 0x94, 0x29, 0x28, 0xE5, 0x85, 0xAD,
+ 0x29, 0x28, 0xE4, 0xB8, 0x83, 0x29, 0x28, 0xE5,
+ 0x85, 0xAB, 0x29, 0x28, 0xE4, 0xB9, 0x9D, 0x29,
+ 0x28, 0xE5, 0x8D, 0x81, 0x29, 0x28, 0xE6, 0x9C,
+ 0x88, 0x29, 0x28, 0xE7, 0x81, 0xAB, 0x29, 0x28,
+ 0xE6, 0xB0, 0xB4, 0x29, 0x28, 0xE6, 0x9C, 0xA8,
+ 0x29, 0x28, 0xE9, 0x87, 0x91, 0x29, 0x28, 0xE5,
+ 0x9C, 0x9F, 0x29, 0x28, 0xE6, 0x97, 0xA5, 0x29,
+ 0x28, 0xE6, 0xA0, 0xAA, 0x29, 0x28, 0xE6, 0x9C,
+ 0x89, 0x29, 0x28, 0xE7, 0xA4, 0xBE, 0x29, 0x28,
+ 0xE5, 0x90, 0x8D, 0x29, 0x28, 0xE7, 0x89, 0xB9,
+ 0x29, 0x28, 0xE8, 0xB2, 0xA1, 0x29, 0x28, 0xE7,
+ 0xA5, 0x9D, 0x29, 0x28, 0xE5, 0x8A, 0xB4, 0x29,
+ 0x28, 0xE4, 0xBB, 0xA3, 0x29, 0x28, 0xE5, 0x91,
+ 0xBC, 0x29, 0x28, 0xE5, 0xAD, 0xA6, 0x29, 0x28,
+ 0xE7, 0x9B, 0xA3, 0x29, 0x28, 0xE4, 0xBC, 0x81,
+ 0x29, 0x28, 0xE8, 0xB3, 0x87, 0x29, 0x28, 0xE5,
+ 0x8D, 0x94, 0x29, 0x28, 0xE7, 0xA5, 0xAD, 0x29,
+ 0x28, 0xE4, 0xBC, 0x91, 0x29, 0x28, 0xE8, 0x87,
+ 0xAA, 0x29, 0x28, 0xE8, 0x87, 0xB3, 0x29, 0x50,
+ 0x54, 0x45, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33,
+ 0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37,
+ 0x32, 0x38, 0x32, 0x39, 0x33, 0x30, 0x33, 0x31,
+ 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35,
+ 0xE1, 0x84, 0x80, 0xE1, 0x84, 0x82, 0xE1, 0x84,
+ 0x83, 0xE1, 0x84, 0x85, 0xE1, 0x84, 0x86, 0xE1,
+ 0x84, 0x87, 0xE1, 0x84, 0x89, 0xE1, 0x84, 0x8B,
+ 0xE1, 0x84, 0x8C, 0xE1, 0x84, 0x8E, 0xE1, 0x84,
+ 0x8F, 0xE1, 0x84, 0x90, 0xE1, 0x84, 0x91, 0xE1,
+ 0x84, 0x92, 0xE1, 0x84, 0x80, 0xE1, 0x85, 0xA1,
+ 0xE1, 0x84, 0x82, 0xE1, 0x85, 0xA1, 0xE1, 0x84,
+ 0x83, 0xE1, 0x85, 0xA1, 0xE1, 0x84, 0x85, 0xE1,
+ 0x85, 0xA1, 0xE1, 0x84, 0x86, 0xE1, 0x85, 0xA1,
+ 0xE1, 0x84, 0x87, 0xE1, 0x85, 0xA1, 0xE1, 0x84,
+ 0x89, 0xE1, 0x85, 0xA1, 0xE1, 0x84, 0x8B, 0xE1,
+ 0x85, 0xA1, 0xE1, 0x84, 0x8C, 0xE1, 0x85, 0xA1,
+ 0xE1, 0x84, 0x8E, 0xE1, 0x85, 0xA1, 0xE1, 0x84,
+ 0x8F, 0xE1, 0x85, 0xA1, 0xE1, 0x84, 0x90, 0xE1,
+ 0x85, 0xA1, 0xE1, 0x84, 0x91, 0xE1, 0x85, 0xA1,
+ 0xE1, 0x84, 0x92, 0xE1, 0x85, 0xA1, 0xE1, 0x84,
+ 0x8E, 0xE1, 0x85, 0xA1, 0xE1, 0x86, 0xB7, 0xE1,
+ 0x84, 0x80, 0xE1, 0x85, 0xA9, 0xE1, 0x84, 0x8C,
+ 0xE1, 0x85, 0xAE, 0xE1, 0x84, 0x8B, 0xE1, 0x85,
+ 0xB4, 0xE1, 0x84, 0x8B, 0xE1, 0x85, 0xAE, 0xE4,
+ 0xB8, 0x80, 0xE4, 0xBA, 0x8C, 0xE4, 0xB8, 0x89,
+ 0xE5, 0x9B, 0x9B, 0xE4, 0xBA, 0x94, 0xE5, 0x85,
+ 0xAD, 0xE4, 0xB8, 0x83, 0xE5, 0x85, 0xAB, 0xE4,
+ 0xB9, 0x9D, 0xE5, 0x8D, 0x81, 0xE6, 0x9C, 0x88,
+ 0xE7, 0x81, 0xAB, 0xE6, 0xB0, 0xB4, 0xE6, 0x9C,
+ 0xA8, 0xE9, 0x87, 0x91, 0xE5, 0x9C, 0x9F, 0xE6,
+ 0x97, 0xA5, 0xE6, 0xA0, 0xAA, 0xE6, 0x9C, 0x89,
+ 0xE7, 0xA4, 0xBE, 0xE5, 0x90, 0x8D, 0xE7, 0x89,
+ 0xB9, 0xE8, 0xB2, 0xA1, 0xE7, 0xA5, 0x9D, 0xE5,
+ 0x8A, 0xB4, 0xE7, 0xA7, 0x98, 0xE7, 0x94, 0xB7,
+ 0xE5, 0xA5, 0xB3, 0xE9, 0x81, 0xA9, 0xE5, 0x84,
+ 0xAA, 0xE5, 0x8D, 0xB0, 0xE6, 0xB3, 0xA8, 0xE9,
+ 0xA0, 0x85, 0xE4, 0xBC, 0x91, 0xE5, 0x86, 0x99,
+ 0xE6, 0xAD, 0xA3, 0xE4, 0xB8, 0x8A, 0xE4, 0xB8,
+ 0xAD, 0xE4, 0xB8, 0x8B, 0xE5, 0xB7, 0xA6, 0xE5,
+ 0x8F, 0xB3, 0xE5, 0x8C, 0xBB, 0xE5, 0xAE, 0x97,
+ 0xE5, 0xAD, 0xA6, 0xE7, 0x9B, 0xA3, 0xE4, 0xBC,
+ 0x81, 0xE8, 0xB3, 0x87, 0xE5, 0x8D, 0x94, 0xE5,
+ 0xA4, 0x9C, 0x33, 0x36, 0x33, 0x37, 0x33, 0x38,
+ 0x33, 0x39, 0x34, 0x30, 0x34, 0x31, 0x34, 0x32,
+ 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36,
+ 0x34, 0x37, 0x34, 0x38, 0x34, 0x39, 0x35, 0x30,
+ 0x31, 0xE6, 0x9C, 0x88, 0x32, 0xE6, 0x9C, 0x88,
+ 0x33, 0xE6, 0x9C, 0x88, 0x34, 0xE6, 0x9C, 0x88,
+ 0x35, 0xE6, 0x9C, 0x88, 0x36, 0xE6, 0x9C, 0x88,
+ 0x37, 0xE6, 0x9C, 0x88, 0x38, 0xE6, 0x9C, 0x88,
+ 0x39, 0xE6, 0x9C, 0x88, 0x31, 0x30, 0xE6, 0x9C,
+ 0x88, 0x31, 0x31, 0xE6, 0x9C, 0x88, 0x31, 0x32,
+ 0xE6, 0x9C, 0x88, 0x48, 0x67, 0x65, 0x72, 0x67,
+ 0x65, 0x56, 0x4C, 0x54, 0x44, 0xE3, 0x82, 0xA2,
+ 0xE3, 0x82, 0xA4, 0xE3, 0x82, 0xA6, 0xE3, 0x82,
+ 0xA8, 0xE3, 0x82, 0xAA, 0xE3, 0x82, 0xAB, 0xE3,
+ 0x82, 0xAD, 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0xB1,
+ 0xE3, 0x82, 0xB3, 0xE3, 0x82, 0xB5, 0xE3, 0x82,
+ 0xB7, 0xE3, 0x82, 0xB9, 0xE3, 0x82, 0xBB, 0xE3,
+ 0x82, 0xBD, 0xE3, 0x82, 0xBF, 0xE3, 0x83, 0x81,
+ 0xE3, 0x83, 0x84, 0xE3, 0x83, 0x86, 0xE3, 0x83,
+ 0x88, 0xE3, 0x83, 0x8A, 0xE3, 0x83, 0x8B, 0xE3,
+ 0x83, 0x8C, 0xE3, 0x83, 0x8D, 0xE3, 0x83, 0x8E,
+ 0xE3, 0x83, 0x8F, 0xE3, 0x83, 0x92, 0xE3, 0x83,
+ 0x95, 0xE3, 0x83, 0x98, 0xE3, 0x83, 0x9B, 0xE3,
+ 0x83, 0x9E, 0xE3, 0x83, 0x9F, 0xE3, 0x83, 0xA0,
+ 0xE3, 0x83, 0xA1, 0xE3, 0x83, 0xA2, 0xE3, 0x83,
+ 0xA4, 0xE3, 0x83, 0xA6, 0xE3, 0x83, 0xA8, 0xE3,
+ 0x83, 0xA9, 0xE3, 0x83, 0xAA, 0xE3, 0x83, 0xAB,
+ 0xE3, 0x83, 0xAC, 0xE3, 0x83, 0xAD, 0xE3, 0x83,
+ 0xAF, 0xE3, 0x83, 0xB0, 0xE3, 0x83, 0xB1, 0xE3,
+ 0x83, 0xB2, 0xE3, 0x82, 0xA2, 0xE3, 0x83, 0x8F,
+ 0xE3, 0x82, 0x9A, 0xE3, 0x83, 0xBC, 0xE3, 0x83,
+ 0x88, 0xE3, 0x82, 0xA2, 0xE3, 0x83, 0xAB, 0xE3,
+ 0x83, 0x95, 0xE3, 0x82, 0xA1, 0xE3, 0x82, 0xA2,
+ 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x98, 0xE3, 0x82,
+ 0x9A, 0xE3, 0x82, 0xA2, 0xE3, 0x82, 0xA2, 0xE3,
+ 0x83, 0xBC, 0xE3, 0x83, 0xAB, 0xE3, 0x82, 0xA4,
+ 0xE3, 0x83, 0x8B, 0xE3, 0x83, 0xB3, 0xE3, 0x82,
+ 0xAF, 0xE3, 0x82, 0x99, 0xE3, 0x82, 0xA4, 0xE3,
+ 0x83, 0xB3, 0xE3, 0x83, 0x81, 0xE3, 0x82, 0xA6,
+ 0xE3, 0x82, 0xA9, 0xE3, 0x83, 0xB3, 0xE3, 0x82,
+ 0xA8, 0xE3, 0x82, 0xB9, 0xE3, 0x82, 0xAF, 0xE3,
+ 0x83, 0xBC, 0xE3, 0x83, 0x88, 0xE3, 0x82, 0x99,
+ 0xE3, 0x82, 0xA8, 0xE3, 0x83, 0xBC, 0xE3, 0x82,
+ 0xAB, 0xE3, 0x83, 0xBC, 0xE3, 0x82, 0xAA, 0xE3,
+ 0x83, 0xB3, 0xE3, 0x82, 0xB9, 0xE3, 0x82, 0xAA,
+ 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0xA0, 0xE3, 0x82,
+ 0xAB, 0xE3, 0x82, 0xA4, 0xE3, 0x83, 0xAA, 0xE3,
+ 0x82, 0xAB, 0xE3, 0x83, 0xA9, 0xE3, 0x83, 0x83,
+ 0xE3, 0x83, 0x88, 0xE3, 0x82, 0xAB, 0xE3, 0x83,
+ 0xAD, 0xE3, 0x83, 0xAA, 0xE3, 0x83, 0xBC, 0xE3,
+ 0x82, 0xAB, 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xAD,
+ 0xE3, 0x83, 0xB3, 0xE3, 0x82, 0xAB, 0xE3, 0x82,
+ 0x99, 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x9E, 0xE3,
+ 0x82, 0xAD, 0xE3, 0x82, 0x99, 0xE3, 0x82, 0xAB,
+ 0xE3, 0x82, 0x99, 0xE3, 0x82, 0xAD, 0xE3, 0x82,
+ 0x99, 0xE3, 0x83, 0x8B, 0xE3, 0x83, 0xBC, 0xE3,
+ 0x82, 0xAD, 0xE3, 0x83, 0xA5, 0xE3, 0x83, 0xAA,
+ 0xE3, 0x83, 0xBC, 0xE3, 0x82, 0xAD, 0xE3, 0x82,
+ 0x99, 0xE3, 0x83, 0xAB, 0xE3, 0x82, 0xBF, 0xE3,
+ 0x82, 0x99, 0xE3, 0x83, 0xBC, 0xE3, 0x82, 0xAD,
+ 0xE3, 0x83, 0xAD, 0xE3, 0x82, 0xAD, 0xE3, 0x83,
+ 0xAD, 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0x99, 0xE3,
+ 0x83, 0xA9, 0xE3, 0x83, 0xA0, 0xE3, 0x82, 0xAD,
+ 0xE3, 0x83, 0xAD, 0xE3, 0x83, 0xA1, 0xE3, 0x83,
+ 0xBC, 0xE3, 0x83, 0x88, 0xE3, 0x83, 0xAB, 0xE3,
+ 0x82, 0xAD, 0xE3, 0x83, 0xAD, 0xE3, 0x83, 0xAF,
+ 0xE3, 0x83, 0x83, 0xE3, 0x83, 0x88, 0xE3, 0x82,
+ 0xAF, 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xA9, 0xE3,
+ 0x83, 0xA0, 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0x99,
+ 0xE3, 0x83, 0xA9, 0xE3, 0x83, 0xA0, 0xE3, 0x83,
+ 0x88, 0xE3, 0x83, 0xB3, 0xE3, 0x82, 0xAF, 0xE3,
+ 0x83, 0xAB, 0xE3, 0x82, 0xBB, 0xE3, 0x82, 0x99,
+ 0xE3, 0x82, 0xA4, 0xE3, 0x83, 0xAD, 0xE3, 0x82,
+ 0xAF, 0xE3, 0x83, 0xAD, 0xE3, 0x83, 0xBC, 0xE3,
+ 0x83, 0x8D, 0xE3, 0x82, 0xB1, 0xE3, 0x83, 0xBC,
+ 0xE3, 0x82, 0xB9, 0xE3, 0x82, 0xB3, 0xE3, 0x83,
+ 0xAB, 0xE3, 0x83, 0x8A, 0xE3, 0x82, 0xB3, 0xE3,
+ 0x83, 0xBC, 0xE3, 0x83, 0x9B, 0xE3, 0x82, 0x9A,
+ 0xE3, 0x82, 0xB5, 0xE3, 0x82, 0xA4, 0xE3, 0x82,
+ 0xAF, 0xE3, 0x83, 0xAB, 0xE3, 0x82, 0xB5, 0xE3,
+ 0x83, 0xB3, 0xE3, 0x83, 0x81, 0xE3, 0x83, 0xBC,
+ 0xE3, 0x83, 0xA0, 0xE3, 0x82, 0xB7, 0xE3, 0x83,
+ 0xAA, 0xE3, 0x83, 0xB3, 0xE3, 0x82, 0xAF, 0xE3,
+ 0x82, 0x99, 0xE3, 0x82, 0xBB, 0xE3, 0x83, 0xB3,
+ 0xE3, 0x83, 0x81, 0xE3, 0x82, 0xBB, 0xE3, 0x83,
+ 0xB3, 0xE3, 0x83, 0x88, 0xE3, 0x82, 0xBF, 0xE3,
+ 0x82, 0x99, 0xE3, 0x83, 0xBC, 0xE3, 0x82, 0xB9,
+ 0xE3, 0x83, 0x86, 0xE3, 0x82, 0x99, 0xE3, 0x82,
+ 0xB7, 0xE3, 0x83, 0x88, 0xE3, 0x82, 0x99, 0xE3,
+ 0x83, 0xAB, 0xE3, 0x83, 0x88, 0xE3, 0x83, 0xB3,
+ 0xE3, 0x83, 0x8A, 0xE3, 0x83, 0x8E, 0xE3, 0x83,
+ 0x8E, 0xE3, 0x83, 0x83, 0xE3, 0x83, 0x88, 0xE3,
+ 0x83, 0x8F, 0xE3, 0x82, 0xA4, 0xE3, 0x83, 0x84,
+ 0xE3, 0x83, 0x8F, 0xE3, 0x82, 0x9A, 0xE3, 0x83,
+ 0xBC, 0xE3, 0x82, 0xBB, 0xE3, 0x83, 0xB3, 0xE3,
+ 0x83, 0x88, 0xE3, 0x83, 0x8F, 0xE3, 0x82, 0x9A,
+ 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0x84, 0xE3, 0x83,
+ 0x8F, 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xBC, 0xE3,
+ 0x83, 0xAC, 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0x92,
+ 0xE3, 0x82, 0x9A, 0xE3, 0x82, 0xA2, 0xE3, 0x82,
+ 0xB9, 0xE3, 0x83, 0x88, 0xE3, 0x83, 0xAB, 0xE3,
+ 0x83, 0x92, 0xE3, 0x82, 0x9A, 0xE3, 0x82, 0xAF,
+ 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0x92, 0xE3, 0x82,
+ 0x9A, 0xE3, 0x82, 0xB3, 0xE3, 0x83, 0x92, 0xE3,
+ 0x82, 0x99, 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0x95,
+ 0xE3, 0x82, 0xA1, 0xE3, 0x83, 0xA9, 0xE3, 0x83,
+ 0x83, 0xE3, 0x83, 0x88, 0xE3, 0x82, 0x99, 0xE3,
+ 0x83, 0x95, 0xE3, 0x82, 0xA3, 0xE3, 0x83, 0xBC,
+ 0xE3, 0x83, 0x88, 0xE3, 0x83, 0x95, 0xE3, 0x82,
+ 0x99, 0xE3, 0x83, 0x83, 0xE3, 0x82, 0xB7, 0xE3,
+ 0x82, 0xA7, 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0x95,
+ 0xE3, 0x83, 0xA9, 0xE3, 0x83, 0xB3, 0xE3, 0x83,
+ 0x98, 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0xBF, 0xE3,
+ 0x83, 0xBC, 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0x98,
+ 0xE3, 0x82, 0x9A, 0xE3, 0x82, 0xBD, 0xE3, 0x83,
+ 0x98, 0xE3, 0x82, 0x9A, 0xE3, 0x83, 0x8B, 0xE3,
+ 0x83, 0x92, 0xE3, 0x83, 0x98, 0xE3, 0x83, 0xAB,
+ 0xE3, 0x83, 0x84, 0xE3, 0x83, 0x98, 0xE3, 0x82,
+ 0x9A, 0xE3, 0x83, 0xB3, 0xE3, 0x82, 0xB9, 0xE3,
+ 0x83, 0x98, 0xE3, 0x82, 0x9A, 0xE3, 0x83, 0xBC,
+ 0xE3, 0x82, 0xB7, 0xE3, 0x82, 0x99, 0xE3, 0x83,
+ 0x98, 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xBC, 0xE3,
+ 0x82, 0xBF, 0xE3, 0x83, 0x9B, 0xE3, 0x82, 0x9A,
+ 0xE3, 0x82, 0xA4, 0xE3, 0x83, 0xB3, 0xE3, 0x83,
+ 0x88, 0xE3, 0x83, 0x9B, 0xE3, 0x82, 0x99, 0xE3,
+ 0x83, 0xAB, 0xE3, 0x83, 0x88, 0xE3, 0x83, 0x9B,
+ 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x9B, 0xE3, 0x82,
+ 0x9A, 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x88, 0xE3,
+ 0x82, 0x99, 0xE3, 0x83, 0x9B, 0xE3, 0x83, 0xBC,
+ 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0x9B, 0xE3, 0x83,
+ 0xBC, 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x9E, 0xE3,
+ 0x82, 0xA4, 0xE3, 0x82, 0xAF, 0xE3, 0x83, 0xAD,
+ 0xE3, 0x83, 0x9E, 0xE3, 0x82, 0xA4, 0xE3, 0x83,
+ 0xAB, 0xE3, 0x83, 0x9E, 0xE3, 0x83, 0x83, 0xE3,
+ 0x83, 0x8F, 0xE3, 0x83, 0x9E, 0xE3, 0x83, 0xAB,
+ 0xE3, 0x82, 0xAF, 0xE3, 0x83, 0x9E, 0xE3, 0x83,
+ 0xB3, 0xE3, 0x82, 0xB7, 0xE3, 0x83, 0xA7, 0xE3,
+ 0x83, 0xB3, 0xE3, 0x83, 0x9F, 0xE3, 0x82, 0xAF,
+ 0xE3, 0x83, 0xAD, 0xE3, 0x83, 0xB3, 0xE3, 0x83,
+ 0x9F, 0xE3, 0x83, 0xAA, 0xE3, 0x83, 0x9F, 0xE3,
+ 0x83, 0xAA, 0xE3, 0x83, 0x8F, 0xE3, 0x82, 0x99,
+ 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0xAB, 0xE3, 0x83,
+ 0xA1, 0xE3, 0x82, 0xAB, 0xE3, 0x82, 0x99, 0xE3,
+ 0x83, 0xA1, 0xE3, 0x82, 0xAB, 0xE3, 0x82, 0x99,
+ 0xE3, 0x83, 0x88, 0xE3, 0x83, 0xB3, 0xE3, 0x83,
+ 0xA1, 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0x88, 0xE3,
+ 0x83, 0xAB, 0xE3, 0x83, 0xA4, 0xE3, 0x83, 0xBC,
+ 0xE3, 0x83, 0x88, 0xE3, 0x82, 0x99, 0xE3, 0x83,
+ 0xA4, 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0xAB, 0xE3,
+ 0x83, 0xA6, 0xE3, 0x82, 0xA2, 0xE3, 0x83, 0xB3,
+ 0xE3, 0x83, 0xAA, 0xE3, 0x83, 0x83, 0xE3, 0x83,
+ 0x88, 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0xAA, 0xE3,
+ 0x83, 0xA9, 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0x92,
+ 0xE3, 0x82, 0x9A, 0xE3, 0x83, 0xBC, 0xE3, 0x83,
+ 0xAB, 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0x95, 0xE3,
+ 0x82, 0x99, 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0xAC,
+ 0xE3, 0x83, 0xA0, 0xE3, 0x83, 0xAC, 0xE3, 0x83,
+ 0xB3, 0xE3, 0x83, 0x88, 0xE3, 0x82, 0xB1, 0xE3,
+ 0x82, 0x99, 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0xAF,
+ 0xE3, 0x83, 0x83, 0xE3, 0x83, 0x88, 0x30, 0xE7,
+ 0x82, 0xB9, 0x31, 0xE7, 0x82, 0xB9, 0x32, 0xE7,
+ 0x82, 0xB9, 0x33, 0xE7, 0x82, 0xB9, 0x34, 0xE7,
+ 0x82, 0xB9, 0x35, 0xE7, 0x82, 0xB9, 0x36, 0xE7,
+ 0x82, 0xB9, 0x37, 0xE7, 0x82, 0xB9, 0x38, 0xE7,
+ 0x82, 0xB9, 0x39, 0xE7, 0x82, 0xB9, 0x31, 0x30,
+ 0xE7, 0x82, 0xB9, 0x31, 0x31, 0xE7, 0x82, 0xB9,
+ 0x31, 0x32, 0xE7, 0x82, 0xB9, 0x31, 0x33, 0xE7,
+ 0x82, 0xB9, 0x31, 0x34, 0xE7, 0x82, 0xB9, 0x31,
+ 0x35, 0xE7, 0x82, 0xB9, 0x31, 0x36, 0xE7, 0x82,
+ 0xB9, 0x31, 0x37, 0xE7, 0x82, 0xB9, 0x31, 0x38,
+ 0xE7, 0x82, 0xB9, 0x31, 0x39, 0xE7, 0x82, 0xB9,
+ 0x32, 0x30, 0xE7, 0x82, 0xB9, 0x32, 0x31, 0xE7,
+ 0x82, 0xB9, 0x32, 0x32, 0xE7, 0x82, 0xB9, 0x32,
+ 0x33, 0xE7, 0x82, 0xB9, 0x32, 0x34, 0xE7, 0x82,
+ 0xB9, 0x68, 0x50, 0x61, 0x64, 0x61, 0x41, 0x55,
+ 0x62, 0x61, 0x72, 0x6F, 0x56, 0x70, 0x63, 0x64,
+ 0x6D, 0x64, 0x6D, 0x32, 0x64, 0x6D, 0x33, 0x49,
+ 0x55, 0xE5, 0xB9, 0xB3, 0xE6, 0x88, 0x90, 0xE6,
+ 0x98, 0xAD, 0xE5, 0x92, 0x8C, 0xE5, 0xA4, 0xA7,
+ 0xE6, 0xAD, 0xA3, 0xE6, 0x98, 0x8E, 0xE6, 0xB2,
+ 0xBB, 0xE6, 0xA0, 0xAA, 0xE5, 0xBC, 0x8F, 0xE4,
+ 0xBC, 0x9A, 0xE7, 0xA4, 0xBE, 0x70, 0x41, 0x6E,
+ 0x41, 0xCE, 0xBC, 0x41, 0x6D, 0x41, 0x6B, 0x41,
+ 0x4B, 0x42, 0x4D, 0x42, 0x47, 0x42, 0x63, 0x61,
+ 0x6C, 0x6B, 0x63, 0x61, 0x6C, 0x70, 0x46, 0x6E,
+ 0x46, 0xCE, 0xBC, 0x46, 0xCE, 0xBC, 0x67, 0x6D,
+ 0x67, 0x6B, 0x67, 0x48, 0x7A, 0x6B, 0x48, 0x7A,
+ 0x4D, 0x48, 0x7A, 0x47, 0x48, 0x7A, 0x54, 0x48,
+ 0x7A, 0xCE, 0xBC, 0x6C, 0x6D, 0x6C, 0x64, 0x6C,
+ 0x6B, 0x6C, 0x66, 0x6D, 0x6E, 0x6D, 0xCE, 0xBC,
+ 0x6D, 0x6D, 0x6D, 0x63, 0x6D, 0x6B, 0x6D, 0x6D,
+ 0x6D, 0x32, 0x63, 0x6D, 0x32, 0x6D, 0x32, 0x6B,
+ 0x6D, 0x32, 0x6D, 0x6D, 0x33, 0x63, 0x6D, 0x33,
+ 0x6D, 0x33, 0x6B, 0x6D, 0x33, 0x6D, 0xE2, 0x88,
+ 0x95, 0x73, 0x6D, 0xE2, 0x88, 0x95, 0x73, 0x32,
+ 0x50, 0x61, 0x6B, 0x50, 0x61, 0x4D, 0x50, 0x61,
+ 0x47, 0x50, 0x61, 0x72, 0x61, 0x64, 0x72, 0x61,
+ 0x64, 0xE2, 0x88, 0x95, 0x73, 0x72, 0x61, 0x64,
+ 0xE2, 0x88, 0x95, 0x73, 0x32, 0x70, 0x73, 0x6E,
+ 0x73, 0xCE, 0xBC, 0x73, 0x6D, 0x73, 0x70, 0x56,
+ 0x6E, 0x56, 0xCE, 0xBC, 0x56, 0x6D, 0x56, 0x6B,
+ 0x56, 0x4D, 0x56, 0x70, 0x57, 0x6E, 0x57, 0xCE,
+ 0xBC, 0x57, 0x6D, 0x57, 0x6B, 0x57, 0x4D, 0x57,
+ 0x6B, 0xCE, 0xA9, 0x4D, 0xCE, 0xA9, 0x61, 0x2E,
+ 0x6D, 0x2E, 0x42, 0x71, 0x63, 0x63, 0x63, 0x64,
+ 0x43, 0xE2, 0x88, 0x95, 0x6B, 0x67, 0x43, 0x6F,
+ 0x2E, 0x64, 0x42, 0x47, 0x79, 0x68, 0x61, 0x48,
+ 0x50, 0x69, 0x6E, 0x4B, 0x4B, 0x4B, 0x4D, 0x6B,
+ 0x74, 0x6C, 0x6D, 0x6C, 0x6E, 0x6C, 0x6F, 0x67,
+ 0x6C, 0x78, 0x6D, 0x62, 0x6D, 0x69, 0x6C, 0x6D,
+ 0x6F, 0x6C, 0x50, 0x48, 0x70, 0x2E, 0x6D, 0x2E,
+ 0x50, 0x50, 0x4D, 0x50, 0x52, 0x73, 0x72, 0x53,
+ 0x76, 0x57, 0x62, 0x56, 0xE2, 0x88, 0x95, 0x6D,
+ 0x41, 0xE2, 0x88, 0x95, 0x6D, 0x31, 0xE6, 0x97,
+ 0xA5, 0x32, 0xE6, 0x97, 0xA5, 0x33, 0xE6, 0x97,
+ 0xA5, 0x34, 0xE6, 0x97, 0xA5, 0x35, 0xE6, 0x97,
+ 0xA5, 0x36, 0xE6, 0x97, 0xA5, 0x37, 0xE6, 0x97,
+ 0xA5, 0x38, 0xE6, 0x97, 0xA5, 0x39, 0xE6, 0x97,
+ 0xA5, 0x31, 0x30, 0xE6, 0x97, 0xA5, 0x31, 0x31,
+ 0xE6, 0x97, 0xA5, 0x31, 0x32, 0xE6, 0x97, 0xA5,
+ 0x31, 0x33, 0xE6, 0x97, 0xA5, 0x31, 0x34, 0xE6,
+ 0x97, 0xA5, 0x31, 0x35, 0xE6, 0x97, 0xA5, 0x31,
+ 0x36, 0xE6, 0x97, 0xA5, 0x31, 0x37, 0xE6, 0x97,
+ 0xA5, 0x31, 0x38, 0xE6, 0x97, 0xA5, 0x31, 0x39,
+ 0xE6, 0x97, 0xA5, 0x32, 0x30, 0xE6, 0x97, 0xA5,
+ 0x32, 0x31, 0xE6, 0x97, 0xA5, 0x32, 0x32, 0xE6,
+ 0x97, 0xA5, 0x32, 0x33, 0xE6, 0x97, 0xA5, 0x32,
+ 0x34, 0xE6, 0x97, 0xA5, 0x32, 0x35, 0xE6, 0x97,
+ 0xA5, 0x32, 0x36, 0xE6, 0x97, 0xA5, 0x32, 0x37,
+ 0xE6, 0x97, 0xA5, 0x32, 0x38, 0xE6, 0x97, 0xA5,
+ 0x32, 0x39, 0xE6, 0x97, 0xA5, 0x33, 0x30, 0xE6,
+ 0x97, 0xA5, 0x33, 0x31, 0xE6, 0x97, 0xA5, 0x67,
+ 0x61, 0x6C, 0xF6, 0xE8, 0xB1, 0x88, 0xF6, 0xE6,
+ 0x9B, 0xB4, 0xF6, 0xE8, 0xBB, 0x8A, 0xF6, 0xE8,
+ 0xB3, 0x88, 0xF6, 0xE6, 0xBB, 0x91, 0xF6, 0xE4,
+ 0xB8, 0xB2, 0xF6, 0xE5, 0x8F, 0xA5, 0xF6, 0xE9,
+ 0xBE, 0x9C, 0xF6, 0xE9, 0xBE, 0x9C, 0xF6, 0xE5,
+ 0xA5, 0x91, 0xF6, 0xE9, 0x87, 0x91, 0xF6, 0xE5,
+ 0x96, 0x87, 0xF6, 0xE5, 0xA5, 0x88, 0xF6, 0xE6,
+ 0x87, 0xB6, 0xF6, 0xE7, 0x99, 0xA9, 0xF6, 0xE7,
+ 0xBE, 0x85, 0xF6, 0xE8, 0x98, 0xBF, 0xF6, 0xE8,
+ 0x9E, 0xBA, 0xF6, 0xE8, 0xA3, 0xB8, 0xF6, 0xE9,
+ 0x82, 0x8F, 0xF6, 0xE6, 0xA8, 0x82, 0xF6, 0xE6,
+ 0xB4, 0x9B, 0xF6, 0xE7, 0x83, 0x99, 0xF6, 0xE7,
+ 0x8F, 0x9E, 0xF6, 0xE8, 0x90, 0xBD, 0xF6, 0xE9,
+ 0x85, 0xAA, 0xF6, 0xE9, 0xA7, 0xB1, 0xF6, 0xE4,
+ 0xBA, 0x82, 0xF6, 0xE5, 0x8D, 0xB5, 0xF6, 0xE6,
+ 0xAC, 0x84, 0xF6, 0xE7, 0x88, 0x9B, 0xF6, 0xE8,
+ 0x98, 0xAD, 0xF6, 0xE9, 0xB8, 0x9E, 0xF6, 0xE5,
+ 0xB5, 0x90, 0xF6, 0xE6, 0xBF, 0xAB, 0xF6, 0xE8,
+ 0x97, 0x8D, 0xF6, 0xE8, 0xA5, 0xA4, 0xF6, 0xE6,
+ 0x8B, 0x89, 0xF6, 0xE8, 0x87, 0x98, 0xF6, 0xE8,
+ 0xA0, 0x9F, 0xF6, 0xE5, 0xBB, 0x8A, 0xF6, 0xE6,
+ 0x9C, 0x97, 0xF6, 0xE6, 0xB5, 0xAA, 0xF6, 0xE7,
+ 0x8B, 0xBC, 0xF6, 0xE9, 0x83, 0x8E, 0xF6, 0xE4,
+ 0xBE, 0x86, 0xF6, 0xE5, 0x86, 0xB7, 0xF6, 0xE5,
+ 0x8B, 0x9E, 0xF6, 0xE6, 0x93, 0x84, 0xF6, 0xE6,
+ 0xAB, 0x93, 0xF6, 0xE7, 0x88, 0x90, 0xF6, 0xE7,
+ 0x9B, 0xA7, 0xF6, 0xE8, 0x80, 0x81, 0xF6, 0xE8,
+ 0x98, 0x86, 0xF6, 0xE8, 0x99, 0x9C, 0xF6, 0xE8,
+ 0xB7, 0xAF, 0xF6, 0xE9, 0x9C, 0xB2, 0xF6, 0xE9,
+ 0xAD, 0xAF, 0xF6, 0xE9, 0xB7, 0xBA, 0xF6, 0xE7,
+ 0xA2, 0x8C, 0xF6, 0xE7, 0xA5, 0xBF, 0xF6, 0xE7,
+ 0xB6, 0xA0, 0xF6, 0xE8, 0x8F, 0x89, 0xF6, 0xE9,
+ 0x8C, 0x84, 0xF6, 0xE9, 0xB9, 0xBF, 0xF6, 0xE8,
+ 0xAB, 0x96, 0xF6, 0xE5, 0xA3, 0x9F, 0xF6, 0xE5,
+ 0xBC, 0x84, 0xF6, 0xE7, 0xB1, 0xA0, 0xF6, 0xE8,
+ 0x81, 0xBE, 0xF6, 0xE7, 0x89, 0xA2, 0xF6, 0xE7,
+ 0xA3, 0x8A, 0xF6, 0xE8, 0xB3, 0x82, 0xF6, 0xE9,
+ 0x9B, 0xB7, 0xF6, 0xE5, 0xA3, 0x98, 0xF6, 0xE5,
+ 0xB1, 0xA2, 0xF6, 0xE6, 0xA8, 0x93, 0xF6, 0xE6,
+ 0xB7, 0x9A, 0xF6, 0xE6, 0xBC, 0x8F, 0xF6, 0xE7,
+ 0xB4, 0xAF, 0xF6, 0xE7, 0xB8, 0xB7, 0xF6, 0xE9,
+ 0x99, 0x8B, 0xF6, 0xE5, 0x8B, 0x92, 0xF6, 0xE8,
+ 0x82, 0x8B, 0xF6, 0xE5, 0x87, 0x9C, 0xF6, 0xE5,
+ 0x87, 0x8C, 0xF6, 0xE7, 0xA8, 0x9C, 0xF6, 0xE7,
+ 0xB6, 0xBE, 0xF6, 0xE8, 0x8F, 0xB1, 0xF6, 0xE9,
+ 0x99, 0xB5, 0xF6, 0xE8, 0xAE, 0x80, 0xF6, 0xE6,
+ 0x8B, 0x8F, 0xF6, 0xE6, 0xA8, 0x82, 0xF6, 0xE8,
+ 0xAB, 0xBE, 0xF6, 0xE4, 0xB8, 0xB9, 0xF6, 0xE5,
+ 0xAF, 0xA7, 0xF6, 0xE6, 0x80, 0x92, 0xF6, 0xE7,
+ 0x8E, 0x87, 0xF6, 0xE7, 0x95, 0xB0, 0xF6, 0xE5,
+ 0x8C, 0x97, 0xF6, 0xE7, 0xA3, 0xBB, 0xF6, 0xE4,
+ 0xBE, 0xBF, 0xF6, 0xE5, 0xBE, 0xA9, 0xF6, 0xE4,
+ 0xB8, 0x8D, 0xF6, 0xE6, 0xB3, 0x8C, 0xF6, 0xE6,
+ 0x95, 0xB8, 0xF6, 0xE7, 0xB4, 0xA2, 0xF6, 0xE5,
+ 0x8F, 0x83, 0xF6, 0xE5, 0xA1, 0x9E, 0xF6, 0xE7,
+ 0x9C, 0x81, 0xF6, 0xE8, 0x91, 0x89, 0xF6, 0xE8,
+ 0xAA, 0xAA, 0xF6, 0xE6, 0xAE, 0xBA, 0xF6, 0xE8,
+ 0xBE, 0xB0, 0xF6, 0xE6, 0xB2, 0x88, 0xF6, 0xE6,
+ 0x8B, 0xBE, 0xF6, 0xE8, 0x8B, 0xA5, 0xF6, 0xE6,
+ 0x8E, 0xA0, 0xF6, 0xE7, 0x95, 0xA5, 0xF6, 0xE4,
+ 0xBA, 0xAE, 0xF6, 0xE5, 0x85, 0xA9, 0xF6, 0xE5,
+ 0x87, 0x89, 0xF6, 0xE6, 0xA2, 0x81, 0xF6, 0xE7,
+ 0xB3, 0xA7, 0xF6, 0xE8, 0x89, 0xAF, 0xF6, 0xE8,
+ 0xAB, 0x92, 0xF6, 0xE9, 0x87, 0x8F, 0xF6, 0xE5,
+ 0x8B, 0xB5, 0xF6, 0xE5, 0x91, 0x82, 0xF6, 0xE5,
+ 0xA5, 0xB3, 0xF6, 0xE5, 0xBB, 0xAC, 0xF6, 0xE6,
+ 0x97, 0x85, 0xF6, 0xE6, 0xBF, 0xBE, 0xF6, 0xE7,
+ 0xA4, 0xAA, 0xF6, 0xE9, 0x96, 0xAD, 0xF6, 0xE9,
+ 0xA9, 0xAA, 0xF6, 0xE9, 0xBA, 0x97, 0xF6, 0xE9,
+ 0xBB, 0x8E, 0xF6, 0xE5, 0x8A, 0x9B, 0xF6, 0xE6,
+ 0x9B, 0x86, 0xF6, 0xE6, 0xAD, 0xB7, 0xF6, 0xE8,
+ 0xBD, 0xA2, 0xF6, 0xE5, 0xB9, 0xB4, 0xF6, 0xE6,
+ 0x86, 0x90, 0xF6, 0xE6, 0x88, 0x80, 0xF6, 0xE6,
+ 0x92, 0x9A, 0xF6, 0xE6, 0xBC, 0xA3, 0xF6, 0xE7,
+ 0x85, 0x89, 0xF6, 0xE7, 0x92, 0x89, 0xF6, 0xE7,
+ 0xA7, 0x8A, 0xF6, 0xE7, 0xB7, 0xB4, 0xF6, 0xE8,
+ 0x81, 0xAF, 0xF6, 0xE8, 0xBC, 0xA6, 0xF6, 0xE8,
+ 0x93, 0xAE, 0xF6, 0xE9, 0x80, 0xA3, 0xF6, 0xE9,
+ 0x8D, 0x8A, 0xF6, 0xE5, 0x88, 0x97, 0xF6, 0xE5,
+ 0x8A, 0xA3, 0xF6, 0xE5, 0x92, 0xBD, 0xF6, 0xE7,
+ 0x83, 0x88, 0xF6, 0xE8, 0xA3, 0x82, 0xF6, 0xE8,
+ 0xAA, 0xAA, 0xF6, 0xE5, 0xBB, 0x89, 0xF6, 0xE5,
+ 0xBF, 0xB5, 0xF6, 0xE6, 0x8D, 0xBB, 0xF6, 0xE6,
+ 0xAE, 0xAE, 0xF6, 0xE7, 0xB0, 0xBE, 0xF6, 0xE7,
+ 0x8D, 0xB5, 0xF6, 0xE4, 0xBB, 0xA4, 0xF6, 0xE5,
+ 0x9B, 0xB9, 0xF6, 0xE5, 0xAF, 0xA7, 0xF6, 0xE5,
+ 0xB6, 0xBA, 0xF6, 0xE6, 0x80, 0x9C, 0xF6, 0xE7,
+ 0x8E, 0xB2, 0xF6, 0xE7, 0x91, 0xA9, 0xF6, 0xE7,
+ 0xBE, 0x9A, 0xF6, 0xE8, 0x81, 0x86, 0xF6, 0xE9,
+ 0x88, 0xB4, 0xF6, 0xE9, 0x9B, 0xB6, 0xF6, 0xE9,
+ 0x9D, 0x88, 0xF6, 0xE9, 0xA0, 0x98, 0xF6, 0xE4,
+ 0xBE, 0x8B, 0xF6, 0xE7, 0xA6, 0xAE, 0xF6, 0xE9,
+ 0x86, 0xB4, 0xF6, 0xE9, 0x9A, 0xB8, 0xF6, 0xE6,
+ 0x83, 0xA1, 0xF6, 0xE4, 0xBA, 0x86, 0xF6, 0xE5,
+ 0x83, 0x9A, 0xF6, 0xE5, 0xAF, 0xAE, 0xF6, 0xE5,
+ 0xB0, 0xBF, 0xF6, 0xE6, 0x96, 0x99, 0xF6, 0xE6,
+ 0xA8, 0x82, 0xF6, 0xE7, 0x87, 0x8E, 0xF6, 0xE7,
+ 0x99, 0x82, 0xF6, 0xE8, 0x93, 0xBC, 0xF6, 0xE9,
+ 0x81, 0xBC, 0xF6, 0xE9, 0xBE, 0x8D, 0xF6, 0xE6,
+ 0x9A, 0x88, 0xF6, 0xE9, 0x98, 0xAE, 0xF6, 0xE5,
+ 0x8A, 0x89, 0xF6, 0xE6, 0x9D, 0xBB, 0xF6, 0xE6,
+ 0x9F, 0xB3, 0xF6, 0xE6, 0xB5, 0x81, 0xF6, 0xE6,
+ 0xBA, 0x9C, 0xF6, 0xE7, 0x90, 0x89, 0xF6, 0xE7,
+ 0x95, 0x99, 0xF6, 0xE7, 0xA1, 0xAB, 0xF6, 0xE7,
+ 0xB4, 0x90, 0xF6, 0xE9, 0xA1, 0x9E, 0xF6, 0xE5,
+ 0x85, 0xAD, 0xF6, 0xE6, 0x88, 0xAE, 0xF6, 0xE9,
+ 0x99, 0xB8, 0xF6, 0xE5, 0x80, 0xAB, 0xF6, 0xE5,
+ 0xB4, 0x99, 0xF6, 0xE6, 0xB7, 0xAA, 0xF6, 0xE8,
+ 0xBC, 0xAA, 0xF6, 0xE5, 0xBE, 0x8B, 0xF6, 0xE6,
+ 0x85, 0x84, 0xF6, 0xE6, 0xA0, 0x97, 0xF6, 0xE7,
+ 0x8E, 0x87, 0xF6, 0xE9, 0x9A, 0x86, 0xF6, 0xE5,
+ 0x88, 0xA9, 0xF6, 0xE5, 0x90, 0x8F, 0xF6, 0xE5,
+ 0xB1, 0xA5, 0xF6, 0xE6, 0x98, 0x93, 0xF6, 0xE6,
+ 0x9D, 0x8E, 0xF6, 0xE6, 0xA2, 0xA8, 0xF6, 0xE6,
+ 0xB3, 0xA5, 0xF6, 0xE7, 0x90, 0x86, 0xF6, 0xE7,
+ 0x97, 0xA2, 0xF6, 0xE7, 0xBD, 0xB9, 0xF6, 0xE8,
+ 0xA3, 0x8F, 0xF6, 0xE8, 0xA3, 0xA1, 0xF6, 0xE9,
+ 0x87, 0x8C, 0xF6, 0xE9, 0x9B, 0xA2, 0xF6, 0xE5,
+ 0x8C, 0xBF, 0xF6, 0xE6, 0xBA, 0xBA, 0xF6, 0xE5,
+ 0x90, 0x9D, 0xF6, 0xE7, 0x87, 0x90, 0xF6, 0xE7,
+ 0x92, 0x98, 0xF6, 0xE8, 0x97, 0xBA, 0xF6, 0xE9,
+ 0x9A, 0xA3, 0xF6, 0xE9, 0xB1, 0x97, 0xF6, 0xE9,
+ 0xBA, 0x9F, 0xF6, 0xE6, 0x9E, 0x97, 0xF6, 0xE6,
+ 0xB7, 0x8B, 0xF6, 0xE8, 0x87, 0xA8, 0xF6, 0xE7,
+ 0xAB, 0x8B, 0xF6, 0xE7, 0xAC, 0xA0, 0xF6, 0xE7,
+ 0xB2, 0x92, 0xF6, 0xE7, 0x8B, 0x80, 0xF6, 0xE7,
+ 0x82, 0x99, 0xF6, 0xE8, 0xAD, 0x98, 0xF6, 0xE4,
+ 0xBB, 0x80, 0xF6, 0xE8, 0x8C, 0xB6, 0xF6, 0xE5,
+ 0x88, 0xBA, 0xF6, 0xE5, 0x88, 0x87, 0xF6, 0xE5,
+ 0xBA, 0xA6, 0xF6, 0xE6, 0x8B, 0x93, 0xF6, 0xE7,
+ 0xB3, 0x96, 0xF6, 0xE5, 0xAE, 0x85, 0xF6, 0xE6,
+ 0xB4, 0x9E, 0xF6, 0xE6, 0x9A, 0xB4, 0xF6, 0xE8,
+ 0xBC, 0xBB, 0xF6, 0xE8, 0xA1, 0x8C, 0xF6, 0xE9,
+ 0x99, 0x8D, 0xF6, 0xE8, 0xA6, 0x8B, 0xF6, 0xE5,
+ 0xBB, 0x93, 0xF6, 0xE5, 0x85, 0x80, 0xF6, 0xE5,
+ 0x97, 0x80, 0xF6, 0xE5, 0xA1, 0x9A, 0xF6, 0xE6,
+ 0x99, 0xB4, 0xF6, 0xE5, 0x87, 0x9E, 0xF6, 0xE7,
+ 0x8C, 0xAA, 0xF6, 0xE7, 0x9B, 0x8A, 0xF6, 0xE7,
+ 0xA4, 0xBC, 0xF6, 0xE7, 0xA5, 0x9E, 0xF6, 0xE7,
+ 0xA5, 0xA5, 0xF6, 0xE7, 0xA6, 0x8F, 0xF6, 0xE9,
+ 0x9D, 0x96, 0xF6, 0xE7, 0xB2, 0xBE, 0xF6, 0xE7,
+ 0xBE, 0xBD, 0xF6, 0xE8, 0x98, 0x92, 0xF6, 0xE8,
+ 0xAB, 0xB8, 0xF6, 0xE9, 0x80, 0xB8, 0xF6, 0xE9,
+ 0x83, 0xBD, 0xF6, 0xE9, 0xA3, 0xAF, 0xF6, 0xE9,
+ 0xA3, 0xBC, 0xF6, 0xE9, 0xA4, 0xA8, 0xF6, 0xE9,
+ 0xB6, 0xB4, 0xF6, 0xE4, 0xBE, 0xAE, 0xF6, 0xE5,
+ 0x83, 0xA7, 0xF6, 0xE5, 0x85, 0x8D, 0xF6, 0xE5,
+ 0x8B, 0x89, 0xF6, 0xE5, 0x8B, 0xA4, 0xF6, 0xE5,
+ 0x8D, 0x91, 0xF6, 0xE5, 0x96, 0x9D, 0xF6, 0xE5,
+ 0x98, 0x86, 0xF6, 0xE5, 0x99, 0xA8, 0xF6, 0xE5,
+ 0xA1, 0x80, 0xF6, 0xE5, 0xA2, 0xA8, 0xF6, 0xE5,
+ 0xB1, 0xA4, 0xF6, 0xE5, 0xB1, 0xAE, 0xF6, 0xE6,
+ 0x82, 0x94, 0xF6, 0xE6, 0x85, 0xA8, 0xF6, 0xE6,
+ 0x86, 0x8E, 0xF6, 0xE6, 0x87, 0xB2, 0xF6, 0xE6,
+ 0x95, 0x8F, 0xF6, 0xE6, 0x97, 0xA2, 0xF6, 0xE6,
+ 0x9A, 0x91, 0xF6, 0xE6, 0xA2, 0x85, 0xF6, 0xE6,
+ 0xB5, 0xB7, 0xF6, 0xE6, 0xB8, 0x9A, 0xF6, 0xE6,
+ 0xBC, 0xA2, 0xF6, 0xE7, 0x85, 0xAE, 0xF6, 0xE7,
+ 0x88, 0xAB, 0xF6, 0xE7, 0x90, 0xA2, 0xF6, 0xE7,
+ 0xA2, 0x91, 0xF6, 0xE7, 0xA4, 0xBE, 0xF6, 0xE7,
+ 0xA5, 0x89, 0xF6, 0xE7, 0xA5, 0x88, 0xF6, 0xE7,
+ 0xA5, 0x90, 0xF6, 0xE7, 0xA5, 0x96, 0xF6, 0xE7,
+ 0xA5, 0x9D, 0xF6, 0xE7, 0xA6, 0x8D, 0xF6, 0xE7,
+ 0xA6, 0x8E, 0xF6, 0xE7, 0xA9, 0x80, 0xF6, 0xE7,
+ 0xAA, 0x81, 0xF6, 0xE7, 0xAF, 0x80, 0xF6, 0xE7,
+ 0xB7, 0xB4, 0xF6, 0xE7, 0xB8, 0x89, 0xF6, 0xE7,
+ 0xB9, 0x81, 0xF6, 0xE7, 0xBD, 0xB2, 0xF6, 0xE8,
+ 0x80, 0x85, 0xF6, 0xE8, 0x87, 0xAD, 0xF6, 0xE8,
+ 0x89, 0xB9, 0xF6, 0xE8, 0x89, 0xB9, 0xF6, 0xE8,
+ 0x91, 0x97, 0xF6, 0xE8, 0xA4, 0x90, 0xF6, 0xE8,
+ 0xA6, 0x96, 0xF6, 0xE8, 0xAC, 0x81, 0xF6, 0xE8,
+ 0xAC, 0xB9, 0xF6, 0xE8, 0xB3, 0x93, 0xF6, 0xE8,
+ 0xB4, 0x88, 0xF6, 0xE8, 0xBE, 0xB6, 0xF6, 0xE9,
+ 0x80, 0xB8, 0xF6, 0xE9, 0x9B, 0xA3, 0xF6, 0xE9,
+ 0x9F, 0xBF, 0xF6, 0xE9, 0xA0, 0xBB, 0xF6, 0xE4,
+ 0xB8, 0xA6, 0xF6, 0xE5, 0x86, 0xB5, 0xF6, 0xE5,
+ 0x85, 0xA8, 0xF6, 0xE4, 0xBE, 0x80, 0xF6, 0xE5,
+ 0x85, 0x85, 0xF6, 0xE5, 0x86, 0x80, 0xF6, 0xE5,
+ 0x8B, 0x87, 0xF6, 0xE5, 0x8B, 0xBA, 0xF6, 0xE5,
+ 0x96, 0x9D, 0xF6, 0xE5, 0x95, 0x95, 0xF6, 0xE5,
+ 0x96, 0x99, 0xF6, 0xE5, 0x97, 0xA2, 0xF6, 0xE5,
+ 0xA1, 0x9A, 0xF6, 0xE5, 0xA2, 0xB3, 0xF6, 0xE5,
+ 0xA5, 0x84, 0xF6, 0xE5, 0xA5, 0x94, 0xF6, 0xE5,
+ 0xA9, 0xA2, 0xF6, 0xE5, 0xAC, 0xA8, 0xF6, 0xE5,
+ 0xBB, 0x92, 0xF6, 0xE5, 0xBB, 0x99, 0xF6, 0xE5,
+ 0xBD, 0xA9, 0xF6, 0xE5, 0xBE, 0xAD, 0xF6, 0xE6,
+ 0x83, 0x98, 0xF6, 0xE6, 0x85, 0x8E, 0xF6, 0xE6,
+ 0x84, 0x88, 0xF6, 0xE6, 0x86, 0x8E, 0xF6, 0xE6,
+ 0x85, 0xA0, 0xF6, 0xE6, 0x87, 0xB2, 0xF6, 0xE6,
+ 0x88, 0xB4, 0xF6, 0xE6, 0x8F, 0x84, 0xF6, 0xE6,
+ 0x90, 0x9C, 0xF6, 0xE6, 0x91, 0x92, 0xF6, 0xE6,
+ 0x95, 0x96, 0xF6, 0xE6, 0x99, 0xB4, 0xF6, 0xE6,
+ 0x9C, 0x97, 0xF6, 0xE6, 0x9C, 0x9B, 0xF6, 0xE6,
+ 0x9D, 0x96, 0xF6, 0xE6, 0xAD, 0xB9, 0xF6, 0xE6,
+ 0xAE, 0xBA, 0xF6, 0xE6, 0xB5, 0x81, 0xF6, 0xE6,
+ 0xBB, 0x9B, 0xF6, 0xE6, 0xBB, 0x8B, 0xF6, 0xE6,
+ 0xBC, 0xA2, 0xF6, 0xE7, 0x80, 0x9E, 0xF6, 0xE7,
+ 0x85, 0xAE, 0xF6, 0xE7, 0x9E, 0xA7, 0xF6, 0xE7,
+ 0x88, 0xB5, 0xF6, 0xE7, 0x8A, 0xAF, 0xF6, 0xE7,
+ 0x8C, 0xAA, 0xF6, 0xE7, 0x91, 0xB1, 0xF6, 0xE7,
+ 0x94, 0x86, 0xF6, 0xE7, 0x94, 0xBB, 0xF6, 0xE7,
+ 0x98, 0x9D, 0xF6, 0xE7, 0x98, 0x9F, 0xF6, 0xE7,
+ 0x9B, 0x8A, 0xF6, 0xE7, 0x9B, 0x9B, 0xF6, 0xE7,
+ 0x9B, 0xB4, 0xF6, 0xE7, 0x9D, 0x8A, 0xF6, 0xE7,
+ 0x9D, 0x80, 0xF6, 0xE7, 0xA3, 0x8C, 0xF6, 0xE7,
+ 0xAA, 0xB1, 0xF6, 0xE7, 0xAF, 0x80, 0xF6, 0xE7,
+ 0xB1, 0xBB, 0xF6, 0xE7, 0xB5, 0x9B, 0xF6, 0xE7,
+ 0xB7, 0xB4, 0xF6, 0xE7, 0xBC, 0xBE, 0xF6, 0xE8,
+ 0x80, 0x85, 0xF6, 0xE8, 0x8D, 0x92, 0xF6, 0xE8,
+ 0x8F, 0xAF, 0xF6, 0xE8, 0x9D, 0xB9, 0xF6, 0xE8,
+ 0xA5, 0x81, 0xF6, 0xE8, 0xA6, 0x86, 0xF6, 0xE8,
+ 0xA6, 0x96, 0xF6, 0xE8, 0xAA, 0xBF, 0xF6, 0xE8,
+ 0xAB, 0xB8, 0xF6, 0xE8, 0xAB, 0x8B, 0xF6, 0xE8,
+ 0xAC, 0x81, 0xF6, 0xE8, 0xAB, 0xBE, 0xF6, 0xE8,
+ 0xAB, 0xAD, 0xF6, 0xE8, 0xAC, 0xB9, 0xF6, 0xE8,
+ 0xAE, 0x8A, 0xF6, 0xE8, 0xB4, 0x88, 0xF6, 0xE8,
+ 0xBC, 0xB8, 0xF6, 0xE9, 0x81, 0xB2, 0xF6, 0xE9,
+ 0x86, 0x99, 0xF6, 0xE9, 0x89, 0xB6, 0xF6, 0xE9,
+ 0x99, 0xBC, 0xF6, 0xE9, 0x9B, 0xA3, 0xF6, 0xE9,
+ 0x9D, 0x96, 0xF6, 0xE9, 0x9F, 0x9B, 0xF6, 0xE9,
+ 0x9F, 0xBF, 0xF6, 0xE9, 0xA0, 0x8B, 0xF6, 0xE9,
+ 0xA0, 0xBB, 0xF6, 0xE9, 0xAC, 0x92, 0xF6, 0xE9,
+ 0xBE, 0x9C, 0xF6, 0xF0, 0xA2, 0xA1, 0x8A, 0xF6,
+ 0xF0, 0xA2, 0xA1, 0x84, 0xF6, 0xF0, 0xA3, 0x8F,
+ 0x95, 0xF6, 0xE3, 0xAE, 0x9D, 0xF6, 0xE4, 0x80,
+ 0x98, 0xF6, 0xE4, 0x80, 0xB9, 0xF6, 0xF0, 0xA5,
+ 0x89, 0x89, 0xF6, 0xF0, 0xA5, 0xB3, 0x90, 0xF6,
+ 0xF0, 0xA7, 0xBB, 0x93, 0xF6, 0xE9, 0xBD, 0x83,
+ 0xF6, 0xE9, 0xBE, 0x8E, 0x66, 0x66, 0x66, 0x69,
+ 0x66, 0x6C, 0x66, 0x66, 0x69, 0x66, 0x66, 0x6C,
+ 0x73, 0x74, 0x73, 0x74, 0xD5, 0xB4, 0xD5, 0xB6,
+ 0xD5, 0xB4, 0xD5, 0xA5, 0xD5, 0xB4, 0xD5, 0xAB,
+ 0xD5, 0xBE, 0xD5, 0xB6, 0xD5, 0xB4, 0xD5, 0xAD,
+ 0xF6, 0xD7, 0x99, 0xD6, 0xB4, 0xF6, 0xD7, 0xB2,
+ 0xD6, 0xB7, 0xD7, 0xA2, 0xD7, 0x90, 0xD7, 0x93,
+ 0xD7, 0x94, 0xD7, 0x9B, 0xD7, 0x9C, 0xD7, 0x9D,
+ 0xD7, 0xA8, 0xD7, 0xAA, 0x2B, 0xF6, 0xD7, 0xA9,
+ 0xD7, 0x81, 0xF6, 0xD7, 0xA9, 0xD7, 0x82, 0xF6,
+ 0xD7, 0xA9, 0xD6, 0xBC, 0xD7, 0x81, 0xF6, 0xD7,
+ 0xA9, 0xD6, 0xBC, 0xD7, 0x82, 0xF6, 0xD7, 0x90,
+ 0xD6, 0xB7, 0xF6, 0xD7, 0x90, 0xD6, 0xB8, 0xF6,
+ 0xD7, 0x90, 0xD6, 0xBC, 0xF6, 0xD7, 0x91, 0xD6,
+ 0xBC, 0xF6, 0xD7, 0x92, 0xD6, 0xBC, 0xF6, 0xD7,
+ 0x93, 0xD6, 0xBC, 0xF6, 0xD7, 0x94, 0xD6, 0xBC,
+ 0xF6, 0xD7, 0x95, 0xD6, 0xBC, 0xF6, 0xD7, 0x96,
+ 0xD6, 0xBC, 0xF6, 0xD7, 0x98, 0xD6, 0xBC, 0xF6,
+ 0xD7, 0x99, 0xD6, 0xBC, 0xF6, 0xD7, 0x9A, 0xD6,
+ 0xBC, 0xF6, 0xD7, 0x9B, 0xD6, 0xBC, 0xF6, 0xD7,
+ 0x9C, 0xD6, 0xBC, 0xF6, 0xD7, 0x9E, 0xD6, 0xBC,
+ 0xF6, 0xD7, 0xA0, 0xD6, 0xBC, 0xF6, 0xD7, 0xA1,
+ 0xD6, 0xBC, 0xF6, 0xD7, 0xA3, 0xD6, 0xBC, 0xF6,
+ 0xD7, 0xA4, 0xD6, 0xBC, 0xF6, 0xD7, 0xA6, 0xD6,
+ 0xBC, 0xF6, 0xD7, 0xA7, 0xD6, 0xBC, 0xF6, 0xD7,
+ 0xA8, 0xD6, 0xBC, 0xF6, 0xD7, 0xA9, 0xD6, 0xBC,
+ 0xF6, 0xD7, 0xAA, 0xD6, 0xBC, 0xF6, 0xD7, 0x95,
+ 0xD6, 0xB9, 0xF6, 0xD7, 0x91, 0xD6, 0xBF, 0xF6,
+ 0xD7, 0x9B, 0xD6, 0xBF, 0xF6, 0xD7, 0xA4, 0xD6,
+ 0xBF, 0xD7, 0x90, 0xD7, 0x9C, 0xD9, 0xB1, 0xD9,
+ 0xB1, 0xD9, 0xBB, 0xD9, 0xBB, 0xD9, 0xBB, 0xD9,
+ 0xBB, 0xD9, 0xBE, 0xD9, 0xBE, 0xD9, 0xBE, 0xD9,
+ 0xBE, 0xDA, 0x80, 0xDA, 0x80, 0xDA, 0x80, 0xDA,
+ 0x80, 0xD9, 0xBA, 0xD9, 0xBA, 0xD9, 0xBA, 0xD9,
+ 0xBA, 0xD9, 0xBF, 0xD9, 0xBF, 0xD9, 0xBF, 0xD9,
+ 0xBF, 0xD9, 0xB9, 0xD9, 0xB9, 0xD9, 0xB9, 0xD9,
+ 0xB9, 0xDA, 0xA4, 0xDA, 0xA4, 0xDA, 0xA4, 0xDA,
+ 0xA4, 0xDA, 0xA6, 0xDA, 0xA6, 0xDA, 0xA6, 0xDA,
+ 0xA6, 0xDA, 0x84, 0xDA, 0x84, 0xDA, 0x84, 0xDA,
+ 0x84, 0xDA, 0x83, 0xDA, 0x83, 0xDA, 0x83, 0xDA,
+ 0x83, 0xDA, 0x86, 0xDA, 0x86, 0xDA, 0x86, 0xDA,
+ 0x86, 0xDA, 0x87, 0xDA, 0x87, 0xDA, 0x87, 0xDA,
+ 0x87, 0xDA, 0x8D, 0xDA, 0x8D, 0xDA, 0x8C, 0xDA,
+ 0x8C, 0xDA, 0x8E, 0xDA, 0x8E, 0xDA, 0x88, 0xDA,
+ 0x88, 0xDA, 0x98, 0xDA, 0x98, 0xDA, 0x91, 0xDA,
+ 0x91, 0xDA, 0xA9, 0xDA, 0xA9, 0xDA, 0xA9, 0xDA,
+ 0xA9, 0xDA, 0xAF, 0xDA, 0xAF, 0xDA, 0xAF, 0xDA,
+ 0xAF, 0xDA, 0xB3, 0xDA, 0xB3, 0xDA, 0xB3, 0xDA,
+ 0xB3, 0xDA, 0xB1, 0xDA, 0xB1, 0xDA, 0xB1, 0xDA,
+ 0xB1, 0xDA, 0xBA, 0xDA, 0xBA, 0xDA, 0xBB, 0xDA,
+ 0xBB, 0xDA, 0xBB, 0xDA, 0xBB, 0xDB, 0x95, 0xD9,
+ 0x94, 0xDB, 0x95, 0xD9, 0x94, 0xDB, 0x81, 0xDB,
+ 0x81, 0xDB, 0x81, 0xDB, 0x81, 0xDA, 0xBE, 0xDA,
+ 0xBE, 0xDA, 0xBE, 0xDA, 0xBE, 0xDB, 0x92, 0xDB,
+ 0x92, 0xDB, 0x92, 0xD9, 0x94, 0xDB, 0x92, 0xD9,
+ 0x94, 0xDA, 0xAD, 0xDA, 0xAD, 0xDA, 0xAD, 0xDA,
+ 0xAD, 0xDB, 0x87, 0xDB, 0x87, 0xDB, 0x86, 0xDB,
+ 0x86, 0xDB, 0x88, 0xDB, 0x88, 0xDB, 0x87, 0xD9,
+ 0xB4, 0xDB, 0x8B, 0xDB, 0x8B, 0xDB, 0x85, 0xDB,
+ 0x85, 0xDB, 0x89, 0xDB, 0x89, 0xDB, 0x90, 0xDB,
+ 0x90, 0xDB, 0x90, 0xDB, 0x90, 0xD9, 0x89, 0xD9,
+ 0x89, 0xD9, 0x8A, 0xD9, 0x94, 0xD8, 0xA7, 0xD9,
+ 0x8A, 0xD9, 0x94, 0xD8, 0xA7, 0xD9, 0x8A, 0xD9,
+ 0x94, 0xDB, 0x95, 0xD9, 0x8A, 0xD9, 0x94, 0xDB,
+ 0x95, 0xD9, 0x8A, 0xD9, 0x94, 0xD9, 0x88, 0xD9,
+ 0x8A, 0xD9, 0x94, 0xD9, 0x88, 0xD9, 0x8A, 0xD9,
+ 0x94, 0xDB, 0x87, 0xD9, 0x8A, 0xD9, 0x94, 0xDB,
+ 0x87, 0xD9, 0x8A, 0xD9, 0x94, 0xDB, 0x86, 0xD9,
+ 0x8A, 0xD9, 0x94, 0xDB, 0x86, 0xD9, 0x8A, 0xD9,
+ 0x94, 0xDB, 0x88, 0xD9, 0x8A, 0xD9, 0x94, 0xDB,
+ 0x88, 0xD9, 0x8A, 0xD9, 0x94, 0xDB, 0x90, 0xD9,
+ 0x8A, 0xD9, 0x94, 0xDB, 0x90, 0xD9, 0x8A, 0xD9,
+ 0x94, 0xDB, 0x90, 0xD9, 0x8A, 0xD9, 0x94, 0xD9,
+ 0x89, 0xD9, 0x8A, 0xD9, 0x94, 0xD9, 0x89, 0xD9,
+ 0x8A, 0xD9, 0x94, 0xD9, 0x89, 0xDB, 0x8C, 0xDB,
+ 0x8C, 0xDB, 0x8C, 0xDB, 0x8C, 0xD9, 0x8A, 0xD9,
+ 0x94, 0xD8, 0xAC, 0xD9, 0x8A, 0xD9, 0x94, 0xD8,
+ 0xAD, 0xD9, 0x8A, 0xD9, 0x94, 0xD9, 0x85, 0xD9,
+ 0x8A, 0xD9, 0x94, 0xD9, 0x89, 0xD9, 0x8A, 0xD9,
+ 0x94, 0xD9, 0x8A, 0xD8, 0xA8, 0xD8, 0xAC, 0xD8,
+ 0xA8, 0xD8, 0xAD, 0xD8, 0xA8, 0xD8, 0xAE, 0xD8,
+ 0xA8, 0xD9, 0x85, 0xD8, 0xA8, 0xD9, 0x89, 0xD8,
+ 0xA8, 0xD9, 0x8A, 0xD8, 0xAA, 0xD8, 0xAC, 0xD8,
+ 0xAA, 0xD8, 0xAD, 0xD8, 0xAA, 0xD8, 0xAE, 0xD8,
+ 0xAA, 0xD9, 0x85, 0xD8, 0xAA, 0xD9, 0x89, 0xD8,
+ 0xAA, 0xD9, 0x8A, 0xD8, 0xAB, 0xD8, 0xAC, 0xD8,
+ 0xAB, 0xD9, 0x85, 0xD8, 0xAB, 0xD9, 0x89, 0xD8,
+ 0xAB, 0xD9, 0x8A, 0xD8, 0xAC, 0xD8, 0xAD, 0xD8,
+ 0xAC, 0xD9, 0x85, 0xD8, 0xAD, 0xD8, 0xAC, 0xD8,
+ 0xAD, 0xD9, 0x85, 0xD8, 0xAE, 0xD8, 0xAC, 0xD8,
+ 0xAE, 0xD8, 0xAD, 0xD8, 0xAE, 0xD9, 0x85, 0xD8,
+ 0xB3, 0xD8, 0xAC, 0xD8, 0xB3, 0xD8, 0xAD, 0xD8,
+ 0xB3, 0xD8, 0xAE, 0xD8, 0xB3, 0xD9, 0x85, 0xD8,
+ 0xB5, 0xD8, 0xAD, 0xD8, 0xB5, 0xD9, 0x85, 0xD8,
+ 0xB6, 0xD8, 0xAC, 0xD8, 0xB6, 0xD8, 0xAD, 0xD8,
+ 0xB6, 0xD8, 0xAE, 0xD8, 0xB6, 0xD9, 0x85, 0xD8,
+ 0xB7, 0xD8, 0xAD, 0xD8, 0xB7, 0xD9, 0x85, 0xD8,
+ 0xB8, 0xD9, 0x85, 0xD8, 0xB9, 0xD8, 0xAC, 0xD8,
+ 0xB9, 0xD9, 0x85, 0xD8, 0xBA, 0xD8, 0xAC, 0xD8,
+ 0xBA, 0xD9, 0x85, 0xD9, 0x81, 0xD8, 0xAC, 0xD9,
+ 0x81, 0xD8, 0xAD, 0xD9, 0x81, 0xD8, 0xAE, 0xD9,
+ 0x81, 0xD9, 0x85, 0xD9, 0x81, 0xD9, 0x89, 0xD9,
+ 0x81, 0xD9, 0x8A, 0xD9, 0x82, 0xD8, 0xAD, 0xD9,
+ 0x82, 0xD9, 0x85, 0xD9, 0x82, 0xD9, 0x89, 0xD9,
+ 0x82, 0xD9, 0x8A, 0xD9, 0x83, 0xD8, 0xA7, 0xD9,
+ 0x83, 0xD8, 0xAC, 0xD9, 0x83, 0xD8, 0xAD, 0xD9,
+ 0x83, 0xD8, 0xAE, 0xD9, 0x83, 0xD9, 0x84, 0xD9,
+ 0x83, 0xD9, 0x85, 0xD9, 0x83, 0xD9, 0x89, 0xD9,
+ 0x83, 0xD9, 0x8A, 0xD9, 0x84, 0xD8, 0xAC, 0xD9,
+ 0x84, 0xD8, 0xAD, 0xD9, 0x84, 0xD8, 0xAE, 0xD9,
+ 0x84, 0xD9, 0x85, 0xD9, 0x84, 0xD9, 0x89, 0xD9,
+ 0x84, 0xD9, 0x8A, 0xD9, 0x85, 0xD8, 0xAC, 0xD9,
+ 0x85, 0xD8, 0xAD, 0xD9, 0x85, 0xD8, 0xAE, 0xD9,
+ 0x85, 0xD9, 0x85, 0xD9, 0x85, 0xD9, 0x89, 0xD9,
+ 0x85, 0xD9, 0x8A, 0xD9, 0x86, 0xD8, 0xAC, 0xD9,
+ 0x86, 0xD8, 0xAD, 0xD9, 0x86, 0xD8, 0xAE, 0xD9,
+ 0x86, 0xD9, 0x85, 0xD9, 0x86, 0xD9, 0x89, 0xD9,
+ 0x86, 0xD9, 0x8A, 0xD9, 0x87, 0xD8, 0xAC, 0xD9,
+ 0x87, 0xD9, 0x85, 0xD9, 0x87, 0xD9, 0x89, 0xD9,
+ 0x87, 0xD9, 0x8A, 0xD9, 0x8A, 0xD8, 0xAC, 0xD9,
+ 0x8A, 0xD8, 0xAD, 0xD9, 0x8A, 0xD8, 0xAE, 0xD9,
+ 0x8A, 0xD9, 0x85, 0xD9, 0x8A, 0xD9, 0x89, 0xD9,
+ 0x8A, 0xD9, 0x8A, 0xD8, 0xB0, 0xD9, 0xB0, 0xD8,
+ 0xB1, 0xD9, 0xB0, 0xD9, 0x89, 0xD9, 0xB0, 0x20,
+ 0xD9, 0x8C, 0xD9, 0x91, 0x20, 0xD9, 0x8D, 0xD9,
+ 0x91, 0x20, 0xD9, 0x8E, 0xD9, 0x91, 0x20, 0xD9,
+ 0x8F, 0xD9, 0x91, 0x20, 0xD9, 0x90, 0xD9, 0x91,
+ 0x20, 0xD9, 0x91, 0xD9, 0xB0, 0xD9, 0x8A, 0xD9,
+ 0x94, 0xD8, 0xB1, 0xD9, 0x8A, 0xD9, 0x94, 0xD8,
+ 0xB2, 0xD9, 0x8A, 0xD9, 0x94, 0xD9, 0x85, 0xD9,
+ 0x8A, 0xD9, 0x94, 0xD9, 0x86, 0xD9, 0x8A, 0xD9,
+ 0x94, 0xD9, 0x89, 0xD9, 0x8A, 0xD9, 0x94, 0xD9,
+ 0x8A, 0xD8, 0xA8, 0xD8, 0xB1, 0xD8, 0xA8, 0xD8,
+ 0xB2, 0xD8, 0xA8, 0xD9, 0x85, 0xD8, 0xA8, 0xD9,
+ 0x86, 0xD8, 0xA8, 0xD9, 0x89, 0xD8, 0xA8, 0xD9,
+ 0x8A, 0xD8, 0xAA, 0xD8, 0xB1, 0xD8, 0xAA, 0xD8,
+ 0xB2, 0xD8, 0xAA, 0xD9, 0x85, 0xD8, 0xAA, 0xD9,
+ 0x86, 0xD8, 0xAA, 0xD9, 0x89, 0xD8, 0xAA, 0xD9,
+ 0x8A, 0xD8, 0xAB, 0xD8, 0xB1, 0xD8, 0xAB, 0xD8,
+ 0xB2, 0xD8, 0xAB, 0xD9, 0x85, 0xD8, 0xAB, 0xD9,
+ 0x86, 0xD8, 0xAB, 0xD9, 0x89, 0xD8, 0xAB, 0xD9,
+ 0x8A, 0xD9, 0x81, 0xD9, 0x89, 0xD9, 0x81, 0xD9,
+ 0x8A, 0xD9, 0x82, 0xD9, 0x89, 0xD9, 0x82, 0xD9,
+ 0x8A, 0xD9, 0x83, 0xD8, 0xA7, 0xD9, 0x83, 0xD9,
+ 0x84, 0xD9, 0x83, 0xD9, 0x85, 0xD9, 0x83, 0xD9,
+ 0x89, 0xD9, 0x83, 0xD9, 0x8A, 0xD9, 0x84, 0xD9,
+ 0x85, 0xD9, 0x84, 0xD9, 0x89, 0xD9, 0x84, 0xD9,
+ 0x8A, 0xD9, 0x85, 0xD8, 0xA7, 0xD9, 0x85, 0xD9,
+ 0x85, 0xD9, 0x86, 0xD8, 0xB1, 0xD9, 0x86, 0xD8,
+ 0xB2, 0xD9, 0x86, 0xD9, 0x85, 0xD9, 0x86, 0xD9,
+ 0x86, 0xD9, 0x86, 0xD9, 0x89, 0xD9, 0x86, 0xD9,
+ 0x8A, 0xD9, 0x89, 0xD9, 0xB0, 0xD9, 0x8A, 0xD8,
+ 0xB1, 0xD9, 0x8A, 0xD8, 0xB2, 0xD9, 0x8A, 0xD9,
+ 0x85, 0xD9, 0x8A, 0xD9, 0x86, 0xD9, 0x8A, 0xD9,
+ 0x89, 0xD9, 0x8A, 0xD9, 0x8A, 0xD9, 0x8A, 0xD9,
+ 0x94, 0xD8, 0xAC, 0xD9, 0x8A, 0xD9, 0x94, 0xD8,
+ 0xAD, 0xD9, 0x8A, 0xD9, 0x94, 0xD8, 0xAE, 0xD9,
+ 0x8A, 0xD9, 0x94, 0xD9, 0x85, 0xD9, 0x8A, 0xD9,
+ 0x94, 0xD9, 0x87, 0xD8, 0xA8, 0xD8, 0xAC, 0xD8,
+ 0xA8, 0xD8, 0xAD, 0xD8, 0xA8, 0xD8, 0xAE, 0xD8,
+ 0xA8, 0xD9, 0x85, 0xD8, 0xA8, 0xD9, 0x87, 0xD8,
+ 0xAA, 0xD8, 0xAC, 0xD8, 0xAA, 0xD8, 0xAD, 0xD8,
+ 0xAA, 0xD8, 0xAE, 0xD8, 0xAA, 0xD9, 0x85, 0xD8,
+ 0xAA, 0xD9, 0x87, 0xD8, 0xAB, 0xD9, 0x85, 0xD8,
+ 0xAC, 0xD8, 0xAD, 0xD8, 0xAC, 0xD9, 0x85, 0xD8,
+ 0xAD, 0xD8, 0xAC, 0xD8, 0xAD, 0xD9, 0x85, 0xD8,
+ 0xAE, 0xD8, 0xAC, 0xD8, 0xAE, 0xD9, 0x85, 0xD8,
+ 0xB3, 0xD8, 0xAC, 0xD8, 0xB3, 0xD8, 0xAD, 0xD8,
+ 0xB3, 0xD8, 0xAE, 0xD8, 0xB3, 0xD9, 0x85, 0xD8,
+ 0xB5, 0xD8, 0xAD, 0xD8, 0xB5, 0xD8, 0xAE, 0xD8,
+ 0xB5, 0xD9, 0x85, 0xD8, 0xB6, 0xD8, 0xAC, 0xD8,
+ 0xB6, 0xD8, 0xAD, 0xD8, 0xB6, 0xD8, 0xAE, 0xD8,
+ 0xB6, 0xD9, 0x85, 0xD8, 0xB7, 0xD8, 0xAD, 0xD8,
+ 0xB8, 0xD9, 0x85, 0xD8, 0xB9, 0xD8, 0xAC, 0xD8,
+ 0xB9, 0xD9, 0x85, 0xD8, 0xBA, 0xD8, 0xAC, 0xD8,
+ 0xBA, 0xD9, 0x85, 0xD9, 0x81, 0xD8, 0xAC, 0xD9,
+ 0x81, 0xD8, 0xAD, 0xD9, 0x81, 0xD8, 0xAE, 0xD9,
+ 0x81, 0xD9, 0x85, 0xD9, 0x82, 0xD8, 0xAD, 0xD9,
+ 0x82, 0xD9, 0x85, 0xD9, 0x83, 0xD8, 0xAC, 0xD9,
+ 0x83, 0xD8, 0xAD, 0xD9, 0x83, 0xD8, 0xAE, 0xD9,
+ 0x83, 0xD9, 0x84, 0xD9, 0x83, 0xD9, 0x85, 0xD9,
+ 0x84, 0xD8, 0xAC, 0xD9, 0x84, 0xD8, 0xAD, 0xD9,
+ 0x84, 0xD8, 0xAE, 0xD9, 0x84, 0xD9, 0x85, 0xD9,
+ 0x84, 0xD9, 0x87, 0xD9, 0x85, 0xD8, 0xAC, 0xD9,
+ 0x85, 0xD8, 0xAD, 0xD9, 0x85, 0xD8, 0xAE, 0xD9,
+ 0x85, 0xD9, 0x85, 0xD9, 0x86, 0xD8, 0xAC, 0xD9,
+ 0x86, 0xD8, 0xAD, 0xD9, 0x86, 0xD8, 0xAE, 0xD9,
+ 0x86, 0xD9, 0x85, 0xD9, 0x86, 0xD9, 0x87, 0xD9,
+ 0x87, 0xD8, 0xAC, 0xD9, 0x87, 0xD9, 0x85, 0xD9,
+ 0x87, 0xD9, 0xB0, 0xD9, 0x8A, 0xD8, 0xAC, 0xD9,
+ 0x8A, 0xD8, 0xAD, 0xD9, 0x8A, 0xD8, 0xAE, 0xD9,
+ 0x8A, 0xD9, 0x85, 0xD9, 0x8A, 0xD9, 0x87, 0xD9,
+ 0x8A, 0xD9, 0x94, 0xD9, 0x85, 0xD9, 0x8A, 0xD9,
+ 0x94, 0xD9, 0x87, 0xD8, 0xA8, 0xD9, 0x85, 0xD8,
+ 0xA8, 0xD9, 0x87, 0xD8, 0xAA, 0xD9, 0x85, 0xD8,
+ 0xAA, 0xD9, 0x87, 0xD8, 0xAB, 0xD9, 0x85, 0xD8,
+ 0xAB, 0xD9, 0x87, 0xD8, 0xB3, 0xD9, 0x85, 0xD8,
+ 0xB3, 0xD9, 0x87, 0xD8, 0xB4, 0xD9, 0x85, 0xD8,
+ 0xB4, 0xD9, 0x87, 0xD9, 0x83, 0xD9, 0x84, 0xD9,
+ 0x83, 0xD9, 0x85, 0xD9, 0x84, 0xD9, 0x85, 0xD9,
+ 0x86, 0xD9, 0x85, 0xD9, 0x86, 0xD9, 0x87, 0xD9,
+ 0x8A, 0xD9, 0x85, 0xD9, 0x8A, 0xD9, 0x87, 0xD9,
+ 0x80, 0xD9, 0x8E, 0xD9, 0x91, 0xD9, 0x80, 0xD9,
+ 0x8F, 0xD9, 0x91, 0xD9, 0x80, 0xD9, 0x90, 0xD9,
+ 0x91, 0xD8, 0xB7, 0xD9, 0x89, 0xD8, 0xB7, 0xD9,
+ 0x8A, 0xD8, 0xB9, 0xD9, 0x89, 0xD8, 0xB9, 0xD9,
+ 0x8A, 0xD8, 0xBA, 0xD9, 0x89, 0xD8, 0xBA, 0xD9,
+ 0x8A, 0xD8, 0xB3, 0xD9, 0x89, 0xD8, 0xB3, 0xD9,
+ 0x8A, 0xD8, 0xB4, 0xD9, 0x89, 0xD8, 0xB4, 0xD9,
+ 0x8A, 0xD8, 0xAD, 0xD9, 0x89, 0xD8, 0xAD, 0xD9,
+ 0x8A, 0xD8, 0xAC, 0xD9, 0x89, 0xD8, 0xAC, 0xD9,
+ 0x8A, 0xD8, 0xAE, 0xD9, 0x89, 0xD8, 0xAE, 0xD9,
+ 0x8A, 0xD8, 0xB5, 0xD9, 0x89, 0xD8, 0xB5, 0xD9,
+ 0x8A, 0xD8, 0xB6, 0xD9, 0x89, 0xD8, 0xB6, 0xD9,
+ 0x8A, 0xD8, 0xB4, 0xD8, 0xAC, 0xD8, 0xB4, 0xD8,
+ 0xAD, 0xD8, 0xB4, 0xD8, 0xAE, 0xD8, 0xB4, 0xD9,
+ 0x85, 0xD8, 0xB4, 0xD8, 0xB1, 0xD8, 0xB3, 0xD8,
+ 0xB1, 0xD8, 0xB5, 0xD8, 0xB1, 0xD8, 0xB6, 0xD8,
+ 0xB1, 0xD8, 0xB7, 0xD9, 0x89, 0xD8, 0xB7, 0xD9,
+ 0x8A, 0xD8, 0xB9, 0xD9, 0x89, 0xD8, 0xB9, 0xD9,
+ 0x8A, 0xD8, 0xBA, 0xD9, 0x89, 0xD8, 0xBA, 0xD9,
+ 0x8A, 0xD8, 0xB3, 0xD9, 0x89, 0xD8, 0xB3, 0xD9,
+ 0x8A, 0xD8, 0xB4, 0xD9, 0x89, 0xD8, 0xB4, 0xD9,
+ 0x8A, 0xD8, 0xAD, 0xD9, 0x89, 0xD8, 0xAD, 0xD9,
+ 0x8A, 0xD8, 0xAC, 0xD9, 0x89, 0xD8, 0xAC, 0xD9,
+ 0x8A, 0xD8, 0xAE, 0xD9, 0x89, 0xD8, 0xAE, 0xD9,
+ 0x8A, 0xD8, 0xB5, 0xD9, 0x89, 0xD8, 0xB5, 0xD9,
+ 0x8A, 0xD8, 0xB6, 0xD9, 0x89, 0xD8, 0xB6, 0xD9,
+ 0x8A, 0xD8, 0xB4, 0xD8, 0xAC, 0xD8, 0xB4, 0xD8,
+ 0xAD, 0xD8, 0xB4, 0xD8, 0xAE, 0xD8, 0xB4, 0xD9,
+ 0x85, 0xD8, 0xB4, 0xD8, 0xB1, 0xD8, 0xB3, 0xD8,
+ 0xB1, 0xD8, 0xB5, 0xD8, 0xB1, 0xD8, 0xB6, 0xD8,
+ 0xB1, 0xD8, 0xB4, 0xD8, 0xAC, 0xD8, 0xB4, 0xD8,
+ 0xAD, 0xD8, 0xB4, 0xD8, 0xAE, 0xD8, 0xB4, 0xD9,
+ 0x85, 0xD8, 0xB3, 0xD9, 0x87, 0xD8, 0xB4, 0xD9,
+ 0x87, 0xD8, 0xB7, 0xD9, 0x85, 0xD8, 0xB3, 0xD8,
+ 0xAC, 0xD8, 0xB3, 0xD8, 0xAD, 0xD8, 0xB3, 0xD8,
+ 0xAE, 0xD8, 0xB4, 0xD8, 0xAC, 0xD8, 0xB4, 0xD8,
+ 0xAD, 0xD8, 0xB4, 0xD8, 0xAE, 0xD8, 0xB7, 0xD9,
+ 0x85, 0xD8, 0xB8, 0xD9, 0x85, 0xD8, 0xA7, 0xD9,
+ 0x8B, 0xD8, 0xA7, 0xD9, 0x8B, 0xD8, 0xAA, 0xD8,
+ 0xAC, 0xD9, 0x85, 0xD8, 0xAA, 0xD8, 0xAD, 0xD8,
+ 0xAC, 0xD8, 0xAA, 0xD8, 0xAD, 0xD8, 0xAC, 0xD8,
+ 0xAA, 0xD8, 0xAD, 0xD9, 0x85, 0xD8, 0xAA, 0xD8,
+ 0xAE, 0xD9, 0x85, 0xD8, 0xAA, 0xD9, 0x85, 0xD8,
+ 0xAC, 0xD8, 0xAA, 0xD9, 0x85, 0xD8, 0xAD, 0xD8,
+ 0xAA, 0xD9, 0x85, 0xD8, 0xAE, 0xD8, 0xAC, 0xD9,
+ 0x85, 0xD8, 0xAD, 0xD8, 0xAC, 0xD9, 0x85, 0xD8,
+ 0xAD, 0xD8, 0xAD, 0xD9, 0x85, 0xD9, 0x8A, 0xD8,
+ 0xAD, 0xD9, 0x85, 0xD9, 0x89, 0xD8, 0xB3, 0xD8,
+ 0xAD, 0xD8, 0xAC, 0xD8, 0xB3, 0xD8, 0xAC, 0xD8,
+ 0xAD, 0xD8, 0xB3, 0xD8, 0xAC, 0xD9, 0x89, 0xD8,
+ 0xB3, 0xD9, 0x85, 0xD8, 0xAD, 0xD8, 0xB3, 0xD9,
+ 0x85, 0xD8, 0xAD, 0xD8, 0xB3, 0xD9, 0x85, 0xD8,
+ 0xAC, 0xD8, 0xB3, 0xD9, 0x85, 0xD9, 0x85, 0xD8,
+ 0xB3, 0xD9, 0x85, 0xD9, 0x85, 0xD8, 0xB5, 0xD8,
+ 0xAD, 0xD8, 0xAD, 0xD8, 0xB5, 0xD8, 0xAD, 0xD8,
+ 0xAD, 0xD8, 0xB5, 0xD9, 0x85, 0xD9, 0x85, 0xD8,
+ 0xB4, 0xD8, 0xAD, 0xD9, 0x85, 0xD8, 0xB4, 0xD8,
+ 0xAD, 0xD9, 0x85, 0xD8, 0xB4, 0xD8, 0xAC, 0xD9,
+ 0x8A, 0xD8, 0xB4, 0xD9, 0x85, 0xD8, 0xAE, 0xD8,
+ 0xB4, 0xD9, 0x85, 0xD8, 0xAE, 0xD8, 0xB4, 0xD9,
+ 0x85, 0xD9, 0x85, 0xD8, 0xB4, 0xD9, 0x85, 0xD9,
+ 0x85, 0xD8, 0xB6, 0xD8, 0xAD, 0xD9, 0x89, 0xD8,
+ 0xB6, 0xD8, 0xAE, 0xD9, 0x85, 0xD8, 0xB6, 0xD8,
+ 0xAE, 0xD9, 0x85, 0xD8, 0xB7, 0xD9, 0x85, 0xD8,
+ 0xAD, 0xD8, 0xB7, 0xD9, 0x85, 0xD8, 0xAD, 0xD8,
+ 0xB7, 0xD9, 0x85, 0xD9, 0x85, 0xD8, 0xB7, 0xD9,
+ 0x85, 0xD9, 0x8A, 0xD8, 0xB9, 0xD8, 0xAC, 0xD9,
+ 0x85, 0xD8, 0xB9, 0xD9, 0x85, 0xD9, 0x85, 0xD8,
+ 0xB9, 0xD9, 0x85, 0xD9, 0x85, 0xD8, 0xB9, 0xD9,
+ 0x85, 0xD9, 0x89, 0xD8, 0xBA, 0xD9, 0x85, 0xD9,
+ 0x85, 0xD8, 0xBA, 0xD9, 0x85, 0xD9, 0x8A, 0xD8,
+ 0xBA, 0xD9, 0x85, 0xD9, 0x89, 0xD9, 0x81, 0xD8,
+ 0xAE, 0xD9, 0x85, 0xD9, 0x81, 0xD8, 0xAE, 0xD9,
+ 0x85, 0xD9, 0x82, 0xD9, 0x85, 0xD8, 0xAD, 0xD9,
+ 0x82, 0xD9, 0x85, 0xD9, 0x85, 0xD9, 0x84, 0xD8,
+ 0xAD, 0xD9, 0x85, 0xD9, 0x84, 0xD8, 0xAD, 0xD9,
+ 0x8A, 0xD9, 0x84, 0xD8, 0xAD, 0xD9, 0x89, 0xD9,
+ 0x84, 0xD8, 0xAC, 0xD8, 0xAC, 0xD9, 0x84, 0xD8,
+ 0xAC, 0xD8, 0xAC, 0xD9, 0x84, 0xD8, 0xAE, 0xD9,
+ 0x85, 0xD9, 0x84, 0xD8, 0xAE, 0xD9, 0x85, 0xD9,
+ 0x84, 0xD9, 0x85, 0xD8, 0xAD, 0xD9, 0x84, 0xD9,
+ 0x85, 0xD8, 0xAD, 0xD9, 0x85, 0xD8, 0xAD, 0xD8,
+ 0xAC, 0xD9, 0x85, 0xD8, 0xAD, 0xD9, 0x85, 0xD9,
+ 0x85, 0xD8, 0xAD, 0xD9, 0x8A, 0xD9, 0x85, 0xD8,
+ 0xAC, 0xD8, 0xAD, 0xD9, 0x85, 0xD8, 0xAC, 0xD9,
+ 0x85, 0xD9, 0x85, 0xD8, 0xAE, 0xD8, 0xAC, 0xD9,
+ 0x85, 0xD8, 0xAE, 0xD9, 0x85, 0xD9, 0x85, 0xD8,
+ 0xAC, 0xD8, 0xAE, 0xD9, 0x87, 0xD9, 0x85, 0xD8,
+ 0xAC, 0xD9, 0x87, 0xD9, 0x85, 0xD9, 0x85, 0xD9,
+ 0x86, 0xD8, 0xAD, 0xD9, 0x85, 0xD9, 0x86, 0xD8,
+ 0xAD, 0xD9, 0x89, 0xD9, 0x86, 0xD8, 0xAC, 0xD9,
+ 0x85, 0xD9, 0x86, 0xD8, 0xAC, 0xD9, 0x85, 0xD9,
+ 0x86, 0xD8, 0xAC, 0xD9, 0x89, 0xD9, 0x86, 0xD9,
+ 0x85, 0xD9, 0x8A, 0xD9, 0x86, 0xD9, 0x85, 0xD9,
+ 0x89, 0xD9, 0x8A, 0xD9, 0x85, 0xD9, 0x85, 0xD9,
+ 0x8A, 0xD9, 0x85, 0xD9, 0x85, 0xD8, 0xA8, 0xD8,
+ 0xAE, 0xD9, 0x8A, 0xD8, 0xAA, 0xD8, 0xAC, 0xD9,
+ 0x8A, 0xD8, 0xAA, 0xD8, 0xAC, 0xD9, 0x89, 0xD8,
+ 0xAA, 0xD8, 0xAE, 0xD9, 0x8A, 0xD8, 0xAA, 0xD8,
+ 0xAE, 0xD9, 0x89, 0xD8, 0xAA, 0xD9, 0x85, 0xD9,
+ 0x8A, 0xD8, 0xAA, 0xD9, 0x85, 0xD9, 0x89, 0xD8,
+ 0xAC, 0xD9, 0x85, 0xD9, 0x8A, 0xD8, 0xAC, 0xD8,
+ 0xAD, 0xD9, 0x89, 0xD8, 0xAC, 0xD9, 0x85, 0xD9,
+ 0x89, 0xD8, 0xB3, 0xD8, 0xAE, 0xD9, 0x89, 0xD8,
+ 0xB5, 0xD8, 0xAD, 0xD9, 0x8A, 0xD8, 0xB4, 0xD8,
+ 0xAD, 0xD9, 0x8A, 0xD8, 0xB6, 0xD8, 0xAD, 0xD9,
+ 0x8A, 0xD9, 0x84, 0xD8, 0xAC, 0xD9, 0x8A, 0xD9,
+ 0x84, 0xD9, 0x85, 0xD9, 0x8A, 0xD9, 0x8A, 0xD8,
+ 0xAD, 0xD9, 0x8A, 0xD9, 0x8A, 0xD8, 0xAC, 0xD9,
+ 0x8A, 0xD9, 0x8A, 0xD9, 0x85, 0xD9, 0x8A, 0xD9,
+ 0x85, 0xD9, 0x85, 0xD9, 0x8A, 0xD9, 0x82, 0xD9,
+ 0x85, 0xD9, 0x8A, 0xD9, 0x86, 0xD8, 0xAD, 0xD9,
+ 0x8A, 0xD9, 0x82, 0xD9, 0x85, 0xD8, 0xAD, 0xD9,
+ 0x84, 0xD8, 0xAD, 0xD9, 0x85, 0xD8, 0xB9, 0xD9,
+ 0x85, 0xD9, 0x8A, 0xD9, 0x83, 0xD9, 0x85, 0xD9,
+ 0x8A, 0xD9, 0x86, 0xD8, 0xAC, 0xD8, 0xAD, 0xD9,
+ 0x85, 0xD8, 0xAE, 0xD9, 0x8A, 0xD9, 0x84, 0xD8,
+ 0xAC, 0xD9, 0x85, 0xD9, 0x83, 0xD9, 0x85, 0xD9,
+ 0x85, 0xD9, 0x84, 0xD8, 0xAC, 0xD9, 0x85, 0xD9,
+ 0x86, 0xD8, 0xAC, 0xD8, 0xAD, 0xD8, 0xAC, 0xD8,
+ 0xAD, 0xD9, 0x8A, 0xD8, 0xAD, 0xD8, 0xAC, 0xD9,
+ 0x8A, 0xD9, 0x85, 0xD8, 0xAC, 0xD9, 0x8A, 0xD9,
+ 0x81, 0xD9, 0x85, 0xD9, 0x8A, 0xD8, 0xA8, 0xD8,
+ 0xAD, 0xD9, 0x8A, 0xD9, 0x83, 0xD9, 0x85, 0xD9,
+ 0x85, 0xD8, 0xB9, 0xD8, 0xAC, 0xD9, 0x85, 0xD8,
+ 0xB5, 0xD9, 0x85, 0xD9, 0x85, 0xD8, 0xB3, 0xD8,
+ 0xAE, 0xD9, 0x8A, 0xD9, 0x86, 0xD8, 0xAC, 0xD9,
+ 0x8A, 0xD8, 0xB5, 0xD9, 0x84, 0xDB, 0x92, 0xD9,
+ 0x82, 0xD9, 0x84, 0xDB, 0x92, 0xD8, 0xA7, 0xD9,
+ 0x84, 0xD9, 0x84, 0xD9, 0x87, 0xD8, 0xA7, 0xD9,
+ 0x83, 0xD8, 0xA8, 0xD8, 0xB1, 0xD9, 0x85, 0xD8,
+ 0xAD, 0xD9, 0x85, 0xD8, 0xAF, 0xD8, 0xB5, 0xD9,
+ 0x84, 0xD8, 0xB9, 0xD9, 0x85, 0xD8, 0xB1, 0xD8,
+ 0xB3, 0xD9, 0x88, 0xD9, 0x84, 0xD8, 0xB9, 0xD9,
+ 0x84, 0xD9, 0x8A, 0xD9, 0x87, 0xD9, 0x88, 0xD8,
+ 0xB3, 0xD9, 0x84, 0xD9, 0x85, 0xD8, 0xB5, 0xD9,
+ 0x84, 0xD9, 0x89, 0xD8, 0xB5, 0xD9, 0x84, 0xD9,
+ 0x89, 0x20, 0xD8, 0xA7, 0xD9, 0x84, 0xD9, 0x84,
+ 0xD9, 0x87, 0x20, 0xD8, 0xB9, 0xD9, 0x84, 0xD9,
+ 0x8A, 0xD9, 0x87, 0x20, 0xD9, 0x88, 0xD8, 0xB3,
+ 0xD9, 0x84, 0xD9, 0x85, 0xD8, 0xAC, 0xD9, 0x84,
+ 0x20, 0xD8, 0xAC, 0xD9, 0x84, 0xD8, 0xA7, 0xD9,
+ 0x84, 0xD9, 0x87, 0xD8, 0xB1, 0xDB, 0x8C, 0xD8,
+ 0xA7, 0xD9, 0x84, 0x2C, 0xE3, 0x80, 0x81, 0xE3,
+ 0x80, 0x82, 0x3A, 0x3B, 0x21, 0x3F, 0xE3, 0x80,
+ 0x96, 0xE3, 0x80, 0x97, 0x2E, 0x2E, 0x2E, 0x2E,
+ 0x2E, 0xE2, 0x80, 0x94, 0xE2, 0x80, 0x93, 0x5F,
+ 0x5F, 0x28, 0x29, 0x7B, 0x7D, 0xE3, 0x80, 0x94,
+ 0xE3, 0x80, 0x95, 0xE3, 0x80, 0x90, 0xE3, 0x80,
+ 0x91, 0xE3, 0x80, 0x8A, 0xE3, 0x80, 0x8B, 0xE3,
+ 0x80, 0x88, 0xE3, 0x80, 0x89, 0xE3, 0x80, 0x8C,
+ 0xE3, 0x80, 0x8D, 0xE3, 0x80, 0x8E, 0xE3, 0x80,
+ 0x8F, 0x5B, 0x5D, 0x20, 0xCC, 0x85, 0x20, 0xCC,
+ 0x85, 0x20, 0xCC, 0x85, 0x20, 0xCC, 0x85, 0x5F,
+ 0x5F, 0x5F, 0x2C, 0xE3, 0x80, 0x81, 0x2E, 0x3B,
+ 0x3A, 0x3F, 0x21, 0xE2, 0x80, 0x94, 0x28, 0x29,
+ 0x7B, 0x7D, 0xE3, 0x80, 0x94, 0xE3, 0x80, 0x95,
+ 0x23, 0x26, 0x2A, 0x2B, 0x2D, 0x3C, 0x3E, 0x3D,
+ 0x5C, 0x24, 0x25, 0x40, 0x20, 0xD9, 0x8B, 0xD9,
+ 0x80, 0xD9, 0x8B, 0x20, 0xD9, 0x8C, 0x20, 0xD9,
+ 0x8D, 0x20, 0xD9, 0x8E, 0xD9, 0x80, 0xD9, 0x8E,
+ 0x20, 0xD9, 0x8F, 0xD9, 0x80, 0xD9, 0x8F, 0x20,
+ 0xD9, 0x90, 0xD9, 0x80, 0xD9, 0x90, 0x20, 0xD9,
+ 0x91, 0xD9, 0x80, 0xD9, 0x91, 0x20, 0xD9, 0x92,
+ 0xD9, 0x80, 0xD9, 0x92, 0xD8, 0xA1, 0xD8, 0xA7,
+ 0xD9, 0x93, 0xD8, 0xA7, 0xD9, 0x93, 0xD8, 0xA7,
+ 0xD9, 0x94, 0xD8, 0xA7, 0xD9, 0x94, 0xD9, 0x88,
+ 0xD9, 0x94, 0xD9, 0x88, 0xD9, 0x94, 0xD8, 0xA7,
+ 0xD9, 0x95, 0xD8, 0xA7, 0xD9, 0x95, 0xD9, 0x8A,
+ 0xD9, 0x94, 0xD9, 0x8A, 0xD9, 0x94, 0xD9, 0x8A,
+ 0xD9, 0x94, 0xD9, 0x8A, 0xD9, 0x94, 0xD8, 0xA7,
+ 0xD8, 0xA7, 0xD8, 0xA8, 0xD8, 0xA8, 0xD8, 0xA8,
+ 0xD8, 0xA8, 0xD8, 0xA9, 0xD8, 0xA9, 0xD8, 0xAA,
+ 0xD8, 0xAA, 0xD8, 0xAA, 0xD8, 0xAA, 0xD8, 0xAB,
+ 0xD8, 0xAB, 0xD8, 0xAB, 0xD8, 0xAB, 0xD8, 0xAC,
+ 0xD8, 0xAC, 0xD8, 0xAC, 0xD8, 0xAC, 0xD8, 0xAD,
+ 0xD8, 0xAD, 0xD8, 0xAD, 0xD8, 0xAD, 0xD8, 0xAE,
+ 0xD8, 0xAE, 0xD8, 0xAE, 0xD8, 0xAE, 0xD8, 0xAF,
+ 0xD8, 0xAF, 0xD8, 0xB0, 0xD8, 0xB0, 0xD8, 0xB1,
+ 0xD8, 0xB1, 0xD8, 0xB2, 0xD8, 0xB2, 0xD8, 0xB3,
+ 0xD8, 0xB3, 0xD8, 0xB3, 0xD8, 0xB3, 0xD8, 0xB4,
+ 0xD8, 0xB4, 0xD8, 0xB4, 0xD8, 0xB4, 0xD8, 0xB5,
+ 0xD8, 0xB5, 0xD8, 0xB5, 0xD8, 0xB5, 0xD8, 0xB6,
+ 0xD8, 0xB6, 0xD8, 0xB6, 0xD8, 0xB6, 0xD8, 0xB7,
+ 0xD8, 0xB7, 0xD8, 0xB7, 0xD8, 0xB7, 0xD8, 0xB8,
+ 0xD8, 0xB8, 0xD8, 0xB8, 0xD8, 0xB8, 0xD8, 0xB9,
+ 0xD8, 0xB9, 0xD8, 0xB9, 0xD8, 0xB9, 0xD8, 0xBA,
+ 0xD8, 0xBA, 0xD8, 0xBA, 0xD8, 0xBA, 0xD9, 0x81,
+ 0xD9, 0x81, 0xD9, 0x81, 0xD9, 0x81, 0xD9, 0x82,
+ 0xD9, 0x82, 0xD9, 0x82, 0xD9, 0x82, 0xD9, 0x83,
+ 0xD9, 0x83, 0xD9, 0x83, 0xD9, 0x83, 0xD9, 0x84,
+ 0xD9, 0x84, 0xD9, 0x84, 0xD9, 0x84, 0xD9, 0x85,
+ 0xD9, 0x85, 0xD9, 0x85, 0xD9, 0x85, 0xD9, 0x86,
+ 0xD9, 0x86, 0xD9, 0x86, 0xD9, 0x86, 0xD9, 0x87,
+ 0xD9, 0x87, 0xD9, 0x87, 0xD9, 0x87, 0xD9, 0x88,
+ 0xD9, 0x88, 0xD9, 0x89, 0xD9, 0x89, 0xD9, 0x8A,
+ 0xD9, 0x8A, 0xD9, 0x8A, 0xD9, 0x8A, 0xD9, 0x84,
+ 0xD8, 0xA7, 0xD9, 0x93, 0xD9, 0x84, 0xD8, 0xA7,
+ 0xD9, 0x93, 0xD9, 0x84, 0xD8, 0xA7, 0xD9, 0x94,
+ 0xD9, 0x84, 0xD8, 0xA7, 0xD9, 0x94, 0xD9, 0x84,
+ 0xD8, 0xA7, 0xD9, 0x95, 0xD9, 0x84, 0xD8, 0xA7,
+ 0xD9, 0x95, 0xD9, 0x84, 0xD8, 0xA7, 0xD9, 0x84,
+ 0xD8, 0xA7, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,
+ 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E,
+ 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
+ 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E,
+ 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
+ 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E,
+ 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56,
+ 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E,
+ 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
+ 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E,
+ 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E,
+ 0xE2, 0xA6, 0x85, 0xE2, 0xA6, 0x86, 0xE3, 0x80,
+ 0x82, 0xE3, 0x80, 0x8C, 0xE3, 0x80, 0x8D, 0xE3,
+ 0x80, 0x81, 0xE3, 0x83, 0xBB, 0xE3, 0x83, 0xB2,
+ 0xE3, 0x82, 0xA1, 0xE3, 0x82, 0xA3, 0xE3, 0x82,
+ 0xA5, 0xE3, 0x82, 0xA7, 0xE3, 0x82, 0xA9, 0xE3,
+ 0x83, 0xA3, 0xE3, 0x83, 0xA5, 0xE3, 0x83, 0xA7,
+ 0xE3, 0x83, 0x83, 0xE3, 0x83, 0xBC, 0xE3, 0x82,
+ 0xA2, 0xE3, 0x82, 0xA4, 0xE3, 0x82, 0xA6, 0xE3,
+ 0x82, 0xA8, 0xE3, 0x82, 0xAA, 0xE3, 0x82, 0xAB,
+ 0xE3, 0x82, 0xAD, 0xE3, 0x82, 0xAF, 0xE3, 0x82,
+ 0xB1, 0xE3, 0x82, 0xB3, 0xE3, 0x82, 0xB5, 0xE3,
+ 0x82, 0xB7, 0xE3, 0x82, 0xB9, 0xE3, 0x82, 0xBB,
+ 0xE3, 0x82, 0xBD, 0xE3, 0x82, 0xBF, 0xE3, 0x83,
+ 0x81, 0xE3, 0x83, 0x84, 0xE3, 0x83, 0x86, 0xE3,
+ 0x83, 0x88, 0xE3, 0x83, 0x8A, 0xE3, 0x83, 0x8B,
+ 0xE3, 0x83, 0x8C, 0xE3, 0x83, 0x8D, 0xE3, 0x83,
+ 0x8E, 0xE3, 0x83, 0x8F, 0xE3, 0x83, 0x92, 0xE3,
+ 0x83, 0x95, 0xE3, 0x83, 0x98, 0xE3, 0x83, 0x9B,
+ 0xE3, 0x83, 0x9E, 0xE3, 0x83, 0x9F, 0xE3, 0x83,
+ 0xA0, 0xE3, 0x83, 0xA1, 0xE3, 0x83, 0xA2, 0xE3,
+ 0x83, 0xA4, 0xE3, 0x83, 0xA6, 0xE3, 0x83, 0xA8,
+ 0xE3, 0x83, 0xA9, 0xE3, 0x83, 0xAA, 0xE3, 0x83,
+ 0xAB, 0xE3, 0x83, 0xAC, 0xE3, 0x83, 0xAD, 0xE3,
+ 0x83, 0xAF, 0xE3, 0x83, 0xB3, 0xE3, 0x82, 0x99,
+ 0xE3, 0x82, 0x9A, 0xE1, 0x85, 0xA0, 0xE1, 0x84,
+ 0x80, 0xE1, 0x84, 0x81, 0xE1, 0x86, 0xAA, 0xE1,
+ 0x84, 0x82, 0xE1, 0x86, 0xAC, 0xE1, 0x86, 0xAD,
+ 0xE1, 0x84, 0x83, 0xE1, 0x84, 0x84, 0xE1, 0x84,
+ 0x85, 0xE1, 0x86, 0xB0, 0xE1, 0x86, 0xB1, 0xE1,
+ 0x86, 0xB2, 0xE1, 0x86, 0xB3, 0xE1, 0x86, 0xB4,
+ 0xE1, 0x86, 0xB5, 0xE1, 0x84, 0x9A, 0xE1, 0x84,
+ 0x86, 0xE1, 0x84, 0x87, 0xE1, 0x84, 0x88, 0xE1,
+ 0x84, 0xA1, 0xE1, 0x84, 0x89, 0xE1, 0x84, 0x8A,
+ 0xE1, 0x84, 0x8B, 0xE1, 0x84, 0x8C, 0xE1, 0x84,
+ 0x8D, 0xE1, 0x84, 0x8E, 0xE1, 0x84, 0x8F, 0xE1,
+ 0x84, 0x90, 0xE1, 0x84, 0x91, 0xE1, 0x84, 0x92,
+ 0xE1, 0x85, 0xA1, 0xE1, 0x85, 0xA2, 0xE1, 0x85,
+ 0xA3, 0xE1, 0x85, 0xA4, 0xE1, 0x85, 0xA5, 0xE1,
+ 0x85, 0xA6, 0xE1, 0x85, 0xA7, 0xE1, 0x85, 0xA8,
+ 0xE1, 0x85, 0xA9, 0xE1, 0x85, 0xAA, 0xE1, 0x85,
+ 0xAB, 0xE1, 0x85, 0xAC, 0xE1, 0x85, 0xAD, 0xE1,
+ 0x85, 0xAE, 0xE1, 0x85, 0xAF, 0xE1, 0x85, 0xB0,
+ 0xE1, 0x85, 0xB1, 0xE1, 0x85, 0xB2, 0xE1, 0x85,
+ 0xB3, 0xE1, 0x85, 0xB4, 0xE1, 0x85, 0xB5, 0xC2,
+ 0xA2, 0xC2, 0xA3, 0xC2, 0xAC, 0x20, 0xCC, 0x84,
+ 0xC2, 0xA6, 0xC2, 0xA5, 0xE2, 0x82, 0xA9, 0xE2,
+ 0x94, 0x82, 0xE2, 0x86, 0x90, 0xE2, 0x86, 0x91,
+ 0xE2, 0x86, 0x92, 0xE2, 0x86, 0x93, 0xE2, 0x96,
+ 0xA0, 0xE2, 0x97, 0x8B, 0xF6, 0xF0, 0x9D, 0x85,
+ 0x97, 0xF0, 0x9D, 0x85, 0xA5, 0xF6, 0xF0, 0x9D,
+ 0x85, 0x98, 0xF0, 0x9D, 0x85, 0xA5, 0xF6, 0xF0,
+ 0x9D, 0x85, 0x98, 0xF0, 0x9D, 0x85, 0xA5, 0xF0,
+ 0x9D, 0x85, 0xAE, 0xF6, 0xF0, 0x9D, 0x85, 0x98,
+ 0xF0, 0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85, 0xAF,
+ 0xF6, 0xF0, 0x9D, 0x85, 0x98, 0xF0, 0x9D, 0x85,
+ 0xA5, 0xF0, 0x9D, 0x85, 0xB0, 0xF6, 0xF0, 0x9D,
+ 0x85, 0x98, 0xF0, 0x9D, 0x85, 0xA5, 0xF0, 0x9D,
+ 0x85, 0xB1, 0xF6, 0xF0, 0x9D, 0x85, 0x98, 0xF0,
+ 0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85, 0xB2, 0xF6,
+ 0xF0, 0x9D, 0x86, 0xB9, 0xF0, 0x9D, 0x85, 0xA5,
+ 0xF6, 0xF0, 0x9D, 0x86, 0xBA, 0xF0, 0x9D, 0x85,
+ 0xA5, 0xF6, 0xF0, 0x9D, 0x86, 0xB9, 0xF0, 0x9D,
+ 0x85, 0xA5, 0xF0, 0x9D, 0x85, 0xAE, 0xF6, 0xF0,
+ 0x9D, 0x86, 0xBA, 0xF0, 0x9D, 0x85, 0xA5, 0xF0,
+ 0x9D, 0x85, 0xAE, 0xF6, 0xF0, 0x9D, 0x86, 0xB9,
+ 0xF0, 0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85, 0xAF,
+ 0xF6, 0xF0, 0x9D, 0x86, 0xBA, 0xF0, 0x9D, 0x85,
+ 0xA5, 0xF0, 0x9D, 0x85, 0xAF, 0x41, 0x42, 0x43,
+ 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B,
+ 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53,
+ 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61,
+ 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7A, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E,
+ 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x79, 0x7A, 0x41, 0x42, 0x43, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C,
+ 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54,
+ 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62,
+ 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A,
+ 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72,
+ 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A,
+ 0x41, 0x43, 0x44, 0x47, 0x4A, 0x4B, 0x4E, 0x4F,
+ 0x50, 0x51, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+ 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x66, 0x68,
+ 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7A, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D,
+ 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7A, 0x41, 0x42, 0x44,
+ 0x45, 0x46, 0x47, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E,
+ 0x4F, 0x50, 0x51, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
+ 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E,
+ 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x79, 0x7A, 0x41, 0x42, 0x44, 0x45,
+ 0x46, 0x47, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4F,
+ 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x61,
+ 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7A, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D,
+ 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7A, 0x41, 0x42, 0x43,
+ 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B,
+ 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53,
+ 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61,
+ 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7A, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D,
+ 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7A, 0x41, 0x42, 0x43,
+ 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B,
+ 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53,
+ 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61,
+ 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7A, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D,
+ 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7A, 0x41, 0x42, 0x43,
+ 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B,
+ 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53,
+ 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61,
+ 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7A, 0xC4, 0xB1, 0xC8, 0xB7, 0xCE, 0x91, 0xCE,
+ 0x92, 0xCE, 0x93, 0xCE, 0x94, 0xCE, 0x95, 0xCE,
+ 0x96, 0xCE, 0x97, 0xCE, 0x98, 0xCE, 0x99, 0xCE,
+ 0x9A, 0xCE, 0x9B, 0xCE, 0x9C, 0xCE, 0x9D, 0xCE,
+ 0x9E, 0xCE, 0x9F, 0xCE, 0xA0, 0xCE, 0xA1, 0xCE,
+ 0x98, 0xCE, 0xA3, 0xCE, 0xA4, 0xCE, 0xA5, 0xCE,
+ 0xA6, 0xCE, 0xA7, 0xCE, 0xA8, 0xCE, 0xA9, 0xE2,
+ 0x88, 0x87, 0xCE, 0xB1, 0xCE, 0xB2, 0xCE, 0xB3,
+ 0xCE, 0xB4, 0xCE, 0xB5, 0xCE, 0xB6, 0xCE, 0xB7,
+ 0xCE, 0xB8, 0xCE, 0xB9, 0xCE, 0xBA, 0xCE, 0xBB,
+ 0xCE, 0xBC, 0xCE, 0xBD, 0xCE, 0xBE, 0xCE, 0xBF,
+ 0xCF, 0x80, 0xCF, 0x81, 0xCF, 0x82, 0xCF, 0x83,
+ 0xCF, 0x84, 0xCF, 0x85, 0xCF, 0x86, 0xCF, 0x87,
+ 0xCF, 0x88, 0xCF, 0x89, 0xE2, 0x88, 0x82, 0xCE,
+ 0xB5, 0xCE, 0xB8, 0xCE, 0xBA, 0xCF, 0x86, 0xCF,
+ 0x81, 0xCF, 0x80, 0xCE, 0x91, 0xCE, 0x92, 0xCE,
+ 0x93, 0xCE, 0x94, 0xCE, 0x95, 0xCE, 0x96, 0xCE,
+ 0x97, 0xCE, 0x98, 0xCE, 0x99, 0xCE, 0x9A, 0xCE,
+ 0x9B, 0xCE, 0x9C, 0xCE, 0x9D, 0xCE, 0x9E, 0xCE,
+ 0x9F, 0xCE, 0xA0, 0xCE, 0xA1, 0xCE, 0x98, 0xCE,
+ 0xA3, 0xCE, 0xA4, 0xCE, 0xA5, 0xCE, 0xA6, 0xCE,
+ 0xA7, 0xCE, 0xA8, 0xCE, 0xA9, 0xE2, 0x88, 0x87,
+ 0xCE, 0xB1, 0xCE, 0xB2, 0xCE, 0xB3, 0xCE, 0xB4,
+ 0xCE, 0xB5, 0xCE, 0xB6, 0xCE, 0xB7, 0xCE, 0xB8,
+ 0xCE, 0xB9, 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xBC,
+ 0xCE, 0xBD, 0xCE, 0xBE, 0xCE, 0xBF, 0xCF, 0x80,
+ 0xCF, 0x81, 0xCF, 0x82, 0xCF, 0x83, 0xCF, 0x84,
+ 0xCF, 0x85, 0xCF, 0x86, 0xCF, 0x87, 0xCF, 0x88,
+ 0xCF, 0x89, 0xE2, 0x88, 0x82, 0xCE, 0xB5, 0xCE,
+ 0xB8, 0xCE, 0xBA, 0xCF, 0x86, 0xCF, 0x81, 0xCF,
+ 0x80, 0xCE, 0x91, 0xCE, 0x92, 0xCE, 0x93, 0xCE,
+ 0x94, 0xCE, 0x95, 0xCE, 0x96, 0xCE, 0x97, 0xCE,
+ 0x98, 0xCE, 0x99, 0xCE, 0x9A, 0xCE, 0x9B, 0xCE,
+ 0x9C, 0xCE, 0x9D, 0xCE, 0x9E, 0xCE, 0x9F, 0xCE,
+ 0xA0, 0xCE, 0xA1, 0xCE, 0x98, 0xCE, 0xA3, 0xCE,
+ 0xA4, 0xCE, 0xA5, 0xCE, 0xA6, 0xCE, 0xA7, 0xCE,
+ 0xA8, 0xCE, 0xA9, 0xE2, 0x88, 0x87, 0xCE, 0xB1,
+ 0xCE, 0xB2, 0xCE, 0xB3, 0xCE, 0xB4, 0xCE, 0xB5,
+ 0xCE, 0xB6, 0xCE, 0xB7, 0xCE, 0xB8, 0xCE, 0xB9,
+ 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xBC, 0xCE, 0xBD,
+ 0xCE, 0xBE, 0xCE, 0xBF, 0xCF, 0x80, 0xCF, 0x81,
+ 0xCF, 0x82, 0xCF, 0x83, 0xCF, 0x84, 0xCF, 0x85,
+ 0xCF, 0x86, 0xCF, 0x87, 0xCF, 0x88, 0xCF, 0x89,
+ 0xE2, 0x88, 0x82, 0xCE, 0xB5, 0xCE, 0xB8, 0xCE,
+ 0xBA, 0xCF, 0x86, 0xCF, 0x81, 0xCF, 0x80, 0xCE,
+ 0x91, 0xCE, 0x92, 0xCE, 0x93, 0xCE, 0x94, 0xCE,
+ 0x95, 0xCE, 0x96, 0xCE, 0x97, 0xCE, 0x98, 0xCE,
+ 0x99, 0xCE, 0x9A, 0xCE, 0x9B, 0xCE, 0x9C, 0xCE,
+ 0x9D, 0xCE, 0x9E, 0xCE, 0x9F, 0xCE, 0xA0, 0xCE,
+ 0xA1, 0xCE, 0x98, 0xCE, 0xA3, 0xCE, 0xA4, 0xCE,
+ 0xA5, 0xCE, 0xA6, 0xCE, 0xA7, 0xCE, 0xA8, 0xCE,
+ 0xA9, 0xE2, 0x88, 0x87, 0xCE, 0xB1, 0xCE, 0xB2,
+ 0xCE, 0xB3, 0xCE, 0xB4, 0xCE, 0xB5, 0xCE, 0xB6,
+ 0xCE, 0xB7, 0xCE, 0xB8, 0xCE, 0xB9, 0xCE, 0xBA,
+ 0xCE, 0xBB, 0xCE, 0xBC, 0xCE, 0xBD, 0xCE, 0xBE,
+ 0xCE, 0xBF, 0xCF, 0x80, 0xCF, 0x81, 0xCF, 0x82,
+ 0xCF, 0x83, 0xCF, 0x84, 0xCF, 0x85, 0xCF, 0x86,
+ 0xCF, 0x87, 0xCF, 0x88, 0xCF, 0x89, 0xE2, 0x88,
+ 0x82, 0xCE, 0xB5, 0xCE, 0xB8, 0xCE, 0xBA, 0xCF,
+ 0x86, 0xCF, 0x81, 0xCF, 0x80, 0xCE, 0x91, 0xCE,
+ 0x92, 0xCE, 0x93, 0xCE, 0x94, 0xCE, 0x95, 0xCE,
+ 0x96, 0xCE, 0x97, 0xCE, 0x98, 0xCE, 0x99, 0xCE,
+ 0x9A, 0xCE, 0x9B, 0xCE, 0x9C, 0xCE, 0x9D, 0xCE,
+ 0x9E, 0xCE, 0x9F, 0xCE, 0xA0, 0xCE, 0xA1, 0xCE,
+ 0x98, 0xCE, 0xA3, 0xCE, 0xA4, 0xCE, 0xA5, 0xCE,
+ 0xA6, 0xCE, 0xA7, 0xCE, 0xA8, 0xCE, 0xA9, 0xE2,
+ 0x88, 0x87, 0xCE, 0xB1, 0xCE, 0xB2, 0xCE, 0xB3,
+ 0xCE, 0xB4, 0xCE, 0xB5, 0xCE, 0xB6, 0xCE, 0xB7,
+ 0xCE, 0xB8, 0xCE, 0xB9, 0xCE, 0xBA, 0xCE, 0xBB,
+ 0xCE, 0xBC, 0xCE, 0xBD, 0xCE, 0xBE, 0xCE, 0xBF,
+ 0xCF, 0x80, 0xCF, 0x81, 0xCF, 0x82, 0xCF, 0x83,
+ 0xCF, 0x84, 0xCF, 0x85, 0xCF, 0x86, 0xCF, 0x87,
+ 0xCF, 0x88, 0xCF, 0x89, 0xE2, 0x88, 0x82, 0xCE,
+ 0xB5, 0xCE, 0xB8, 0xCE, 0xBA, 0xCF, 0x86, 0xCF,
+ 0x81, 0xCF, 0x80, 0xCF, 0x9C, 0xCF, 0x9D, 0x30,
+ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+ 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
+ 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34,
+ 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32,
+ 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30,
+ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+ 0x39, 0xF6, 0xE4, 0xB8, 0xBD, 0xF6, 0xE4, 0xB8,
+ 0xB8, 0xF6, 0xE4, 0xB9, 0x81, 0xF6, 0xF0, 0xA0,
+ 0x84, 0xA2, 0xF6, 0xE4, 0xBD, 0xA0, 0xF6, 0xE4,
+ 0xBE, 0xAE, 0xF6, 0xE4, 0xBE, 0xBB, 0xF6, 0xE5,
+ 0x80, 0x82, 0xF6, 0xE5, 0x81, 0xBA, 0xF6, 0xE5,
+ 0x82, 0x99, 0xF6, 0xE5, 0x83, 0xA7, 0xF6, 0xE5,
+ 0x83, 0x8F, 0xF6, 0xE3, 0x92, 0x9E, 0xF6, 0xF0,
+ 0xA0, 0x98, 0xBA, 0xF6, 0xE5, 0x85, 0x8D, 0xF6,
+ 0xE5, 0x85, 0x94, 0xF6, 0xE5, 0x85, 0xA4, 0xF6,
+ 0xE5, 0x85, 0xB7, 0xF6, 0xF0, 0xA0, 0x94, 0x9C,
+ 0xF6, 0xE3, 0x92, 0xB9, 0xF6, 0xE5, 0x85, 0xA7,
+ 0xF6, 0xE5, 0x86, 0x8D, 0xF6, 0xF0, 0xA0, 0x95,
+ 0x8B, 0xF6, 0xE5, 0x86, 0x97, 0xF6, 0xE5, 0x86,
+ 0xA4, 0xF6, 0xE4, 0xBB, 0x8C, 0xF6, 0xE5, 0x86,
+ 0xAC, 0xF6, 0xE5, 0x86, 0xB5, 0xF6, 0xF0, 0xA9,
+ 0x87, 0x9F, 0xF6, 0xE5, 0x87, 0xB5, 0xF6, 0xE5,
+ 0x88, 0x83, 0xF6, 0xE3, 0x93, 0x9F, 0xF6, 0xE5,
+ 0x88, 0xBB, 0xF6, 0xE5, 0x89, 0x86, 0xF6, 0xE5,
+ 0x89, 0xB2, 0xF6, 0xE5, 0x89, 0xB7, 0xF6, 0xE3,
+ 0x94, 0x95, 0xF6, 0xE5, 0x8B, 0x87, 0xF6, 0xE5,
+ 0x8B, 0x89, 0xF6, 0xE5, 0x8B, 0xA4, 0xF6, 0xE5,
+ 0x8B, 0xBA, 0xF6, 0xE5, 0x8C, 0x85, 0xF6, 0xE5,
+ 0x8C, 0x86, 0xF6, 0xE5, 0x8C, 0x97, 0xF6, 0xE5,
+ 0x8D, 0x89, 0xF6, 0xE5, 0x8D, 0x91, 0xF6, 0xE5,
+ 0x8D, 0x9A, 0xF6, 0xE5, 0x8D, 0xB3, 0xF6, 0xE5,
+ 0x8D, 0xBD, 0xF6, 0xE5, 0x8D, 0xBF, 0xF6, 0xE5,
+ 0x8D, 0xBF, 0xF6, 0xE5, 0x8D, 0xBF, 0xF6, 0xF0,
+ 0xA0, 0xA8, 0xAC, 0xF6, 0xE7, 0x81, 0xB0, 0xF6,
+ 0xE5, 0x8F, 0x8A, 0xF6, 0xE5, 0x8F, 0x9F, 0xF6,
+ 0xF0, 0xA0, 0xAD, 0xA3, 0xF6, 0xE5, 0x8F, 0xAB,
+ 0xF6, 0xE5, 0x8F, 0xB1, 0xF6, 0xE5, 0x90, 0x86,
+ 0xF6, 0xE5, 0x92, 0x9E, 0xF6, 0xE5, 0x90, 0xB8,
+ 0xF6, 0xE5, 0x91, 0x88, 0xF6, 0xE5, 0x91, 0xA8,
+ 0xF6, 0xE5, 0x92, 0xA2, 0xF6, 0xE5, 0x93, 0xB6,
+ 0xF6, 0xE5, 0x94, 0x90, 0xF6, 0xE5, 0x95, 0x93,
+ 0xF6, 0xE5, 0x95, 0xA3, 0xF6, 0xE5, 0x96, 0x84,
+ 0xF6, 0xE5, 0x96, 0x84, 0xF6, 0xE5, 0x96, 0x99,
+ 0xF6, 0xE5, 0x96, 0xAB, 0xF6, 0xE5, 0x96, 0xB3,
+ 0xF6, 0xE5, 0x97, 0x82, 0xF6, 0xE5, 0x9C, 0x96,
+ 0xF6, 0xE5, 0x98, 0x86, 0xF6, 0xE5, 0x9C, 0x97,
+ 0xF6, 0xE5, 0x99, 0x91, 0xF6, 0xE5, 0x99, 0xB4,
+ 0xF6, 0xE5, 0x88, 0x87, 0xF6, 0xE5, 0xA3, 0xAE,
+ 0xF6, 0xE5, 0x9F, 0x8E, 0xF6, 0xE5, 0x9F, 0xB4,
+ 0xF6, 0xE5, 0xA0, 0x8D, 0xF6, 0xE5, 0x9E, 0x8B,
+ 0xF6, 0xE5, 0xA0, 0xB2, 0xF6, 0xE5, 0xA0, 0xB1,
+ 0xF6, 0xE5, 0xA2, 0xAC, 0xF6, 0xF0, 0xA1, 0x93,
+ 0xA4, 0xF6, 0xE5, 0xA3, 0xB2, 0xF6, 0xE5, 0xA3,
+ 0xB7, 0xF6, 0xE5, 0xA4, 0x86, 0xF6, 0xE5, 0xA4,
+ 0x9A, 0xF6, 0xE5, 0xA4, 0xA2, 0xF6, 0xE5, 0xA5,
+ 0xA2, 0xF6, 0xF0, 0xA1, 0x9A, 0xA8, 0xF6, 0xF0,
+ 0xA1, 0x9B, 0xAA, 0xF6, 0xE5, 0xA7, 0xAC, 0xF6,
+ 0xE5, 0xA8, 0x9B, 0xF6, 0xE5, 0xA8, 0xA7, 0xF6,
+ 0xE5, 0xA7, 0x98, 0xF6, 0xE5, 0xA9, 0xA6, 0xF6,
+ 0xE3, 0x9B, 0xAE, 0xF6, 0xE3, 0x9B, 0xBC, 0xF6,
+ 0xE5, 0xAC, 0x88, 0xF6, 0xE5, 0xAC, 0xBE, 0xF6,
+ 0xE5, 0xAC, 0xBE, 0xF6, 0xF0, 0xA1, 0xA7, 0x88,
+ 0xF6, 0xE5, 0xAF, 0x83, 0xF6, 0xE5, 0xAF, 0x98,
+ 0xF6, 0xE5, 0xAF, 0xA7, 0xF6, 0xE5, 0xAF, 0xB3,
+ 0xF6, 0xF0, 0xA1, 0xAC, 0x98, 0xF6, 0xE5, 0xAF,
+ 0xBF, 0xF6, 0xE5, 0xB0, 0x86, 0xF6, 0xE5, 0xBD,
+ 0x93, 0xF6, 0xE5, 0xB0, 0xA2, 0xF6, 0xE3, 0x9E,
+ 0x81, 0xF6, 0xE5, 0xB1, 0xA0, 0xF6, 0xE5, 0xB1,
+ 0xAE, 0xF6, 0xE5, 0xB3, 0x80, 0xF6, 0xE5, 0xB2,
+ 0x8D, 0xF6, 0xF0, 0xA1, 0xB7, 0xA4, 0xF6, 0xE5,
+ 0xB5, 0x83, 0xF6, 0xF0, 0xA1, 0xB7, 0xA6, 0xF6,
+ 0xE5, 0xB5, 0xAE, 0xF6, 0xE5, 0xB5, 0xAB, 0xF6,
+ 0xE5, 0xB5, 0xBC, 0xF6, 0xE5, 0xB7, 0xA1, 0xF6,
+ 0xE5, 0xB7, 0xA2, 0xF6, 0xE3, 0xA0, 0xAF, 0xF6,
+ 0xE5, 0xB7, 0xBD, 0xF6, 0xE5, 0xB8, 0xA8, 0xF6,
+ 0xE5, 0xB8, 0xBD, 0xF6, 0xE5, 0xB9, 0xA9, 0xF6,
+ 0xE3, 0xA1, 0xA2, 0xF6, 0xF0, 0xA2, 0x86, 0x83,
+ 0xF6, 0xE3, 0xA1, 0xBC, 0xF6, 0xE5, 0xBA, 0xB0,
+ 0xF6, 0xE5, 0xBA, 0xB3, 0xF6, 0xE5, 0xBA, 0xB6,
+ 0xF6, 0xE5, 0xBB, 0x8A, 0xF6, 0xF0, 0xAA, 0x8E,
+ 0x92, 0xF6, 0xE5, 0xBB, 0xBE, 0xF6, 0xF0, 0xA2,
+ 0x8C, 0xB1, 0xF6, 0xF0, 0xA2, 0x8C, 0xB1, 0xF6,
+ 0xE8, 0x88, 0x81, 0xF6, 0xE5, 0xBC, 0xA2, 0xF6,
+ 0xE5, 0xBC, 0xA2, 0xF6, 0xE3, 0xA3, 0x87, 0xF6,
+ 0xF0, 0xA3, 0x8A, 0xB8, 0xF6, 0xF0, 0xA6, 0x87,
+ 0x9A, 0xF6, 0xE5, 0xBD, 0xA2, 0xF6, 0xE5, 0xBD,
+ 0xAB, 0xF6, 0xE3, 0xA3, 0xA3, 0xF6, 0xE5, 0xBE,
+ 0x9A, 0xF6, 0xE5, 0xBF, 0x8D, 0xF6, 0xE5, 0xBF,
+ 0x97, 0xF6, 0xE5, 0xBF, 0xB9, 0xF6, 0xE6, 0x82,
+ 0x81, 0xF6, 0xE3, 0xA4, 0xBA, 0xF6, 0xE3, 0xA4,
+ 0x9C, 0xF6, 0xE6, 0x82, 0x94, 0xF6, 0xF0, 0xA2,
+ 0x9B, 0x94, 0xF6, 0xE6, 0x83, 0x87, 0xF6, 0xE6,
+ 0x85, 0x88, 0xF6, 0xE6, 0x85, 0x8C, 0xF6, 0xE6,
+ 0x85, 0x8E, 0xF6, 0xE6, 0x85, 0x8C, 0xF6, 0xE6,
+ 0x85, 0xBA, 0xF6, 0xE6, 0x86, 0x8E, 0xF6, 0xE6,
+ 0x86, 0xB2, 0xF6, 0xE6, 0x86, 0xA4, 0xF6, 0xE6,
+ 0x86, 0xAF, 0xF6, 0xE6, 0x87, 0x9E, 0xF6, 0xE6,
+ 0x87, 0xB2, 0xF6, 0xE6, 0x87, 0xB6, 0xF6, 0xE6,
+ 0x88, 0x90, 0xF6, 0xE6, 0x88, 0x9B, 0xF6, 0xE6,
+ 0x89, 0x9D, 0xF6, 0xE6, 0x8A, 0xB1, 0xF6, 0xE6,
+ 0x8B, 0x94, 0xF6, 0xE6, 0x8D, 0x90, 0xF6, 0xF0,
+ 0xA2, 0xAC, 0x8C, 0xF6, 0xE6, 0x8C, 0xBD, 0xF6,
+ 0xE6, 0x8B, 0xBC, 0xF6, 0xE6, 0x8D, 0xA8, 0xF6,
+ 0xE6, 0x8E, 0x83, 0xF6, 0xE6, 0x8F, 0xA4, 0xF6,
+ 0xF0, 0xA2, 0xAF, 0xB1, 0xF6, 0xE6, 0x90, 0xA2,
+ 0xF6, 0xE6, 0x8F, 0x85, 0xF6, 0xE6, 0x8E, 0xA9,
+ 0xF6, 0xE3, 0xA8, 0xAE, 0xF6, 0xE6, 0x91, 0xA9,
+ 0xF6, 0xE6, 0x91, 0xBE, 0xF6, 0xE6, 0x92, 0x9D,
+ 0xF6, 0xE6, 0x91, 0xB7, 0xF6, 0xE3, 0xA9, 0xAC,
+ 0xF6, 0xE6, 0x95, 0x8F, 0xF6, 0xE6, 0x95, 0xAC,
+ 0xF6, 0xF0, 0xA3, 0x80, 0x8A, 0xF6, 0xE6, 0x97,
+ 0xA3, 0xF6, 0xE6, 0x9B, 0xB8, 0xF6, 0xE6, 0x99,
+ 0x89, 0xF6, 0xE3, 0xAC, 0x99, 0xF6, 0xE6, 0x9A,
+ 0x91, 0xF6, 0xE3, 0xAC, 0x88, 0xF6, 0xE3, 0xAB,
+ 0xA4, 0xF6, 0xE5, 0x86, 0x92, 0xF6, 0xE5, 0x86,
+ 0x95, 0xF6, 0xE6, 0x9C, 0x80, 0xF6, 0xE6, 0x9A,
+ 0x9C, 0xF6, 0xE8, 0x82, 0xAD, 0xF6, 0xE4, 0x8F,
+ 0x99, 0xF6, 0xE6, 0x9C, 0x97, 0xF6, 0xE6, 0x9C,
+ 0x9B, 0xF6, 0xE6, 0x9C, 0xA1, 0xF6, 0xE6, 0x9D,
+ 0x9E, 0xF6, 0xE6, 0x9D, 0x93, 0xF6, 0xF0, 0xA3,
+ 0x8F, 0x83, 0xF6, 0xE3, 0xAD, 0x89, 0xF6, 0xE6,
+ 0x9F, 0xBA, 0xF6, 0xE6, 0x9E, 0x85, 0xF6, 0xE6,
+ 0xA1, 0x92, 0xF6, 0xE6, 0xA2, 0x85, 0xF6, 0xF0,
+ 0xA3, 0x91, 0xAD, 0xF6, 0xE6, 0xA2, 0x8E, 0xF6,
+ 0xE6, 0xA0, 0x9F, 0xF6, 0xE6, 0xA4, 0x94, 0xF6,
+ 0xE3, 0xAE, 0x9D, 0xF6, 0xE6, 0xA5, 0x82, 0xF6,
+ 0xE6, 0xA6, 0xA3, 0xF6, 0xE6, 0xA7, 0xAA, 0xF6,
+ 0xE6, 0xAA, 0xA8, 0xF6, 0xF0, 0xA3, 0x9A, 0xA3,
+ 0xF6, 0xE6, 0xAB, 0x9B, 0xF6, 0xE3, 0xB0, 0x98,
+ 0xF6, 0xE6, 0xAC, 0xA1, 0xF6, 0xF0, 0xA3, 0xA2,
+ 0xA7, 0xF6, 0xE6, 0xAD, 0x94, 0xF6, 0xE3, 0xB1,
+ 0x8E, 0xF6, 0xE6, 0xAD, 0xB2, 0xF6, 0xE6, 0xAE,
+ 0x9F, 0xF6, 0xE6, 0xAE, 0xBA, 0xF6, 0xE6, 0xAE,
+ 0xBB, 0xF6, 0xF0, 0xA3, 0xAA, 0x8D, 0xF6, 0xF0,
+ 0xA1, 0xB4, 0x8B, 0xF6, 0xF0, 0xA3, 0xAB, 0xBA,
+ 0xF6, 0xE6, 0xB1, 0x8E, 0xF6, 0xF0, 0xA3, 0xB2,
+ 0xBC, 0xF6, 0xE6, 0xB2, 0xBF, 0xF6, 0xE6, 0xB3,
+ 0x8D, 0xF6, 0xE6, 0xB1, 0xA7, 0xF6, 0xE6, 0xB4,
+ 0x96, 0xF6, 0xE6, 0xB4, 0xBE, 0xF6, 0xE6, 0xB5,
+ 0xB7, 0xF6, 0xE6, 0xB5, 0x81, 0xF6, 0xE6, 0xB5,
+ 0xA9, 0xF6, 0xE6, 0xB5, 0xB8, 0xF6, 0xE6, 0xB6,
+ 0x85, 0xF6, 0xF0, 0xA3, 0xB4, 0x9E, 0xF6, 0xE6,
+ 0xB4, 0xB4, 0xF6, 0xE6, 0xB8, 0xAF, 0xF6, 0xE6,
+ 0xB9, 0xAE, 0xF6, 0xE3, 0xB4, 0xB3, 0xF6, 0xE6,
+ 0xBB, 0x8B, 0xF6, 0xE6, 0xBB, 0x87, 0xF6, 0xF0,
+ 0xA3, 0xBB, 0x91, 0xF6, 0xE6, 0xB7, 0xB9, 0xF6,
+ 0xE6, 0xBD, 0xAE, 0xF6, 0xF0, 0xA3, 0xBD, 0x9E,
+ 0xF6, 0xF0, 0xA3, 0xBE, 0x8E, 0xF6, 0xE6, 0xBF,
+ 0x86, 0xF6, 0xE7, 0x80, 0xB9, 0xF6, 0xE7, 0x80,
+ 0x9E, 0xF6, 0xE7, 0x80, 0x9B, 0xF6, 0xE3, 0xB6,
+ 0x96, 0xF6, 0xE7, 0x81, 0x8A, 0xF6, 0xE7, 0x81,
+ 0xBD, 0xF6, 0xE7, 0x81, 0xB7, 0xF6, 0xE7, 0x82,
+ 0xAD, 0xF6, 0xF0, 0xA0, 0x94, 0xA5, 0xF6, 0xE7,
+ 0x85, 0x85, 0xF6, 0xF0, 0xA4, 0x89, 0xA3, 0xF6,
+ 0xE7, 0x86, 0x9C, 0xF6, 0xF0, 0xA4, 0x8E, 0xAB,
+ 0xF6, 0xE7, 0x88, 0xA8, 0xF6, 0xE7, 0x88, 0xB5,
+ 0xF6, 0xE7, 0x89, 0x90, 0xF6, 0xF0, 0xA4, 0x98,
+ 0x88, 0xF6, 0xE7, 0x8A, 0x80, 0xF6, 0xE7, 0x8A,
+ 0x95, 0xF6, 0xF0, 0xA4, 0x9C, 0xB5, 0xF6, 0xF0,
+ 0xA4, 0xA0, 0x94, 0xF6, 0xE7, 0x8D, 0xBA, 0xF6,
+ 0xE7, 0x8E, 0x8B, 0xF6, 0xE3, 0xBA, 0xAC, 0xF6,
+ 0xE7, 0x8E, 0xA5, 0xF6, 0xE3, 0xBA, 0xB8, 0xF6,
+ 0xE3, 0xBA, 0xB8, 0xF6, 0xE7, 0x91, 0x87, 0xF6,
+ 0xE7, 0x91, 0x9C, 0xF6, 0xE7, 0x91, 0xB1, 0xF6,
+ 0xE7, 0x92, 0x85, 0xF6, 0xE7, 0x93, 0x8A, 0xF6,
+ 0xE3, 0xBC, 0x9B, 0xF6, 0xE7, 0x94, 0xA4, 0xF6,
+ 0xF0, 0xA4, 0xB0, 0xB6, 0xF6, 0xE7, 0x94, 0xBE,
+ 0xF6, 0xF0, 0xA4, 0xB2, 0x92, 0xF6, 0xE7, 0x95,
+ 0xB0, 0xF6, 0xF0, 0xA2, 0x86, 0x9F, 0xF6, 0xE7,
+ 0x98, 0x90, 0xF6, 0xF0, 0xA4, 0xBE, 0xA1, 0xF6,
+ 0xF0, 0xA4, 0xBE, 0xB8, 0xF6, 0xF0, 0xA5, 0x81,
+ 0x84, 0xF6, 0xE3, 0xBF, 0xBC, 0xF6, 0xE4, 0x80,
+ 0x88, 0xF6, 0xE7, 0x9B, 0xB4, 0xF6, 0xF0, 0xA5,
+ 0x83, 0xB3, 0xF6, 0xF0, 0xA5, 0x83, 0xB2, 0xF6,
+ 0xF0, 0xA5, 0x84, 0x99, 0xF6, 0xF0, 0xA5, 0x84,
+ 0xB3, 0xF6, 0xE7, 0x9C, 0x9E, 0xF6, 0xE7, 0x9C,
+ 0x9F, 0xF6, 0xE7, 0x9C, 0x9F, 0xF6, 0xE7, 0x9D,
+ 0x8A, 0xF6, 0xE4, 0x80, 0xB9, 0xF6, 0xE7, 0x9E,
+ 0x8B, 0xF6, 0xE4, 0x81, 0x86, 0xF6, 0xE4, 0x82,
+ 0x96, 0xF6, 0xF0, 0xA5, 0x90, 0x9D, 0xF6, 0xE7,
+ 0xA1, 0x8E, 0xF6, 0xE7, 0xA2, 0x8C, 0xF6, 0xE7,
+ 0xA3, 0x8C, 0xF6, 0xE4, 0x83, 0xA3, 0xF6, 0xF0,
+ 0xA5, 0x98, 0xA6, 0xF6, 0xE7, 0xA5, 0x96, 0xF6,
+ 0xF0, 0xA5, 0x9A, 0x9A, 0xF6, 0xF0, 0xA5, 0x9B,
+ 0x85, 0xF6, 0xE7, 0xA6, 0x8F, 0xF6, 0xE7, 0xA7,
+ 0xAB, 0xF6, 0xE4, 0x84, 0xAF, 0xF6, 0xE7, 0xA9,
+ 0x80, 0xF6, 0xE7, 0xA9, 0x8A, 0xF6, 0xE7, 0xA9,
+ 0x8F, 0xF6, 0xF0, 0xA5, 0xA5, 0xBC, 0xF6, 0xF0,
+ 0xA5, 0xAA, 0xA7, 0xF6, 0xF0, 0xA5, 0xAA, 0xA7,
+ 0xF6, 0xE7, 0xAB, 0xAE, 0xF6, 0xE4, 0x88, 0x82,
+ 0xF6, 0xF0, 0xA5, 0xAE, 0xAB, 0xF6, 0xE7, 0xAF,
+ 0x86, 0xF6, 0xE7, 0xAF, 0x89, 0xF6, 0xE4, 0x88,
+ 0xA7, 0xF6, 0xF0, 0xA5, 0xB2, 0x80, 0xF6, 0xE7,
+ 0xB3, 0x92, 0xF6, 0xE4, 0x8A, 0xA0, 0xF6, 0xE7,
+ 0xB3, 0xA8, 0xF6, 0xE7, 0xB3, 0xA3, 0xF6, 0xE7,
+ 0xB4, 0x80, 0xF6, 0xF0, 0xA5, 0xBE, 0x86, 0xF6,
+ 0xE7, 0xB5, 0xA3, 0xF6, 0xE4, 0x8C, 0x81, 0xF6,
+ 0xE7, 0xB7, 0x87, 0xF6, 0xE7, 0xB8, 0x82, 0xF6,
+ 0xE7, 0xB9, 0x85, 0xF6, 0xE4, 0x8C, 0xB4, 0xF6,
+ 0xF0, 0xA6, 0x88, 0xA8, 0xF6, 0xF0, 0xA6, 0x89,
+ 0x87, 0xF6, 0xE4, 0x8D, 0x99, 0xF6, 0xF0, 0xA6,
+ 0x8B, 0x99, 0xF6, 0xE7, 0xBD, 0xBA, 0xF6, 0xF0,
+ 0xA6, 0x8C, 0xBE, 0xF6, 0xE7, 0xBE, 0x95, 0xF6,
+ 0xE7, 0xBF, 0xBA, 0xF6, 0xE8, 0x80, 0x85, 0xF6,
+ 0xF0, 0xA6, 0x93, 0x9A, 0xF6, 0xF0, 0xA6, 0x94,
+ 0xA3, 0xF6, 0xE8, 0x81, 0xA0, 0xF6, 0xF0, 0xA6,
+ 0x96, 0xA8, 0xF6, 0xE8, 0x81, 0xB0, 0xF6, 0xF0,
+ 0xA3, 0x8D, 0x9F, 0xF6, 0xE4, 0x8F, 0x95, 0xF6,
+ 0xE8, 0x82, 0xB2, 0xF6, 0xE8, 0x84, 0x83, 0xF6,
+ 0xE4, 0x90, 0x8B, 0xF6, 0xE8, 0x84, 0xBE, 0xF6,
+ 0xE5, 0xAA, 0xB5, 0xF6, 0xF0, 0xA6, 0x9E, 0xA7,
+ 0xF6, 0xF0, 0xA6, 0x9E, 0xB5, 0xF6, 0xF0, 0xA3,
+ 0x8E, 0x93, 0xF6, 0xF0, 0xA3, 0x8E, 0x9C, 0xF6,
+ 0xE8, 0x88, 0x81, 0xF6, 0xE8, 0x88, 0x84, 0xF6,
+ 0xE8, 0xBE, 0x9E, 0xF6, 0xE4, 0x91, 0xAB, 0xF6,
+ 0xE8, 0x8A, 0x91, 0xF6, 0xE8, 0x8A, 0x8B, 0xF6,
+ 0xE8, 0x8A, 0x9D, 0xF6, 0xE5, 0x8A, 0xB3, 0xF6,
+ 0xE8, 0x8A, 0xB1, 0xF6, 0xE8, 0x8A, 0xB3, 0xF6,
+ 0xE8, 0x8A, 0xBD, 0xF6, 0xE8, 0x8B, 0xA6, 0xF6,
+ 0xF0, 0xA6, 0xAC, 0xBC, 0xF6, 0xE8, 0x8B, 0xA5,
+ 0xF6, 0xE8, 0x8C, 0x9D, 0xF6, 0xE8, 0x8D, 0xA3,
+ 0xF6, 0xE8, 0x8E, 0xAD, 0xF6, 0xE8, 0x8C, 0xA3,
+ 0xF6, 0xE8, 0x8E, 0xBD, 0xF6, 0xE8, 0x8F, 0xA7,
+ 0xF6, 0xE8, 0x91, 0x97, 0xF6, 0xE8, 0x8D, 0x93,
+ 0xF6, 0xE8, 0x8F, 0x8A, 0xF6, 0xE8, 0x8F, 0x8C,
+ 0xF6, 0xE8, 0x8F, 0x9C, 0xF6, 0xF0, 0xA6, 0xB0,
+ 0xB6, 0xF6, 0xF0, 0xA6, 0xB5, 0xAB, 0xF6, 0xF0,
+ 0xA6, 0xB3, 0x95, 0xF6, 0xE4, 0x94, 0xAB, 0xF6,
+ 0xE8, 0x93, 0xB1, 0xF6, 0xE8, 0x93, 0xB3, 0xF6,
+ 0xE8, 0x94, 0x96, 0xF6, 0xF0, 0xA7, 0x8F, 0x8A,
+ 0xF6, 0xE8, 0x95, 0xA4, 0xF6, 0xF0, 0xA6, 0xBC,
+ 0xAC, 0xF6, 0xE4, 0x95, 0x9D, 0xF6, 0xE4, 0x95,
+ 0xA1, 0xF6, 0xF0, 0xA6, 0xBE, 0xB1, 0xF6, 0xF0,
+ 0xA7, 0x83, 0x92, 0xF6, 0xE4, 0x95, 0xAB, 0xF6,
+ 0xE8, 0x99, 0x90, 0xF6, 0xE8, 0x99, 0x9C, 0xF6,
+ 0xE8, 0x99, 0xA7, 0xF6, 0xE8, 0x99, 0xA9, 0xF6,
+ 0xE8, 0x9A, 0xA9, 0xF6, 0xE8, 0x9A, 0x88, 0xF6,
+ 0xE8, 0x9C, 0x8E, 0xF6, 0xE8, 0x9B, 0xA2, 0xF6,
+ 0xE8, 0x9D, 0xB9, 0xF6, 0xE8, 0x9C, 0xA8, 0xF6,
+ 0xE8, 0x9D, 0xAB, 0xF6, 0xE8, 0x9E, 0x86, 0xF6,
+ 0xE4, 0x97, 0x97, 0xF6, 0xE8, 0x9F, 0xA1, 0xF6,
+ 0xE8, 0xA0, 0x81, 0xF6, 0xE4, 0x97, 0xB9, 0xF6,
+ 0xE8, 0xA1, 0xA0, 0xF6, 0xE8, 0xA1, 0xA3, 0xF6,
+ 0xF0, 0xA7, 0x99, 0xA7, 0xF6, 0xE8, 0xA3, 0x97,
+ 0xF6, 0xE8, 0xA3, 0x9E, 0xF6, 0xE4, 0x98, 0xB5,
+ 0xF6, 0xE8, 0xA3, 0xBA, 0xF6, 0xE3, 0x92, 0xBB,
+ 0xF6, 0xF0, 0xA7, 0xA2, 0xAE, 0xF6, 0xF0, 0xA7,
+ 0xA5, 0xA6, 0xF6, 0xE4, 0x9A, 0xBE, 0xF6, 0xE4,
+ 0x9B, 0x87, 0xF6, 0xE8, 0xAA, 0xA0, 0xF6, 0xE8,
+ 0xAB, 0xAD, 0xF6, 0xE8, 0xAE, 0x8A, 0xF6, 0xE8,
+ 0xB1, 0x95, 0xF6, 0xF0, 0xA7, 0xB2, 0xA8, 0xF6,
+ 0xE8, 0xB2, 0xAB, 0xF6, 0xE8, 0xB3, 0x81, 0xF6,
+ 0xE8, 0xB4, 0x9B, 0xF6, 0xE8, 0xB5, 0xB7, 0xF6,
+ 0xF0, 0xA7, 0xBC, 0xAF, 0xF6, 0xF0, 0xA0, 0xA0,
+ 0x84, 0xF6, 0xE8, 0xB7, 0x8B, 0xF6, 0xE8, 0xB6,
+ 0xBC, 0xF6, 0xE8, 0xB7, 0xB0, 0xF6, 0xF0, 0xA0,
+ 0xA3, 0x9E, 0xF6, 0xE8, 0xBB, 0x94, 0xF6, 0xE8,
+ 0xBC, 0xB8, 0xF6, 0xF0, 0xA8, 0x97, 0x92, 0xF6,
+ 0xF0, 0xA8, 0x97, 0xAD, 0xF6, 0xE9, 0x82, 0x94,
+ 0xF6, 0xE9, 0x83, 0xB1, 0xF6, 0xE9, 0x84, 0x91,
+ 0xF6, 0xF0, 0xA8, 0x9C, 0xAE, 0xF6, 0xE9, 0x84,
+ 0x9B, 0xF6, 0xE9, 0x88, 0xB8, 0xF6, 0xE9, 0x8B,
+ 0x97, 0xF6, 0xE9, 0x8B, 0x98, 0xF6, 0xE9, 0x89,
+ 0xBC, 0xF6, 0xE9, 0x8F, 0xB9, 0xF6, 0xE9, 0x90,
+ 0x95, 0xF6, 0xF0, 0xA8, 0xAF, 0xBA, 0xF6, 0xE9,
+ 0x96, 0x8B, 0xF6, 0xE4, 0xA6, 0x95, 0xF6, 0xE9,
+ 0x96, 0xB7, 0xF6, 0xF0, 0xA8, 0xB5, 0xB7, 0xF6,
+ 0xE4, 0xA7, 0xA6, 0xF6, 0xE9, 0x9B, 0x83, 0xF6,
+ 0xE5, 0xB6, 0xB2, 0xF6, 0xE9, 0x9C, 0xA3, 0xF6,
+ 0xF0, 0xA9, 0x85, 0x85, 0xF6, 0xF0, 0xA9, 0x88,
+ 0x9A, 0xF6, 0xE4, 0xA9, 0xAE, 0xF6, 0xE4, 0xA9,
+ 0xB6, 0xF6, 0xE9, 0x9F, 0xA0, 0xF6, 0xF0, 0xA9,
+ 0x90, 0x8A, 0xF6, 0xE4, 0xAA, 0xB2, 0xF6, 0xF0,
+ 0xA9, 0x92, 0x96, 0xF6, 0xE9, 0xA0, 0x8B, 0xF6,
+ 0xE9, 0xA0, 0x8B, 0xF6, 0xE9, 0xA0, 0xA9, 0xF6,
+ 0xF0, 0xA9, 0x96, 0xB6, 0xF6, 0xE9, 0xA3, 0xA2,
+ 0xF6, 0xE4, 0xAC, 0xB3, 0xF6, 0xE9, 0xA4, 0xA9,
+ 0xF6, 0xE9, 0xA6, 0xA7, 0xF6, 0xE9, 0xA7, 0x82,
+ 0xF6, 0xE9, 0xA7, 0xBE, 0xF6, 0xE4, 0xAF, 0x8E,
+ 0xF6, 0xF0, 0xA9, 0xAC, 0xB0, 0xF6, 0xE9, 0xAC,
+ 0x92, 0xF6, 0xE9, 0xB1, 0x80, 0xF6, 0xE9, 0xB3,
+ 0xBD, 0xF6, 0xE4, 0xB3, 0x8E, 0xF6, 0xE4, 0xB3,
+ 0xAD, 0xF6, 0xE9, 0xB5, 0xA7, 0xF6, 0xF0, 0xAA,
+ 0x83, 0x8E, 0xF6, 0xE4, 0xB3, 0xB8, 0xF6, 0xF0,
+ 0xAA, 0x84, 0x85, 0xF6, 0xF0, 0xAA, 0x88, 0x8E,
+ 0xF6, 0xF0, 0xAA, 0x8A, 0x91, 0xF6, 0xE9, 0xBA,
+ 0xBB, 0xF6, 0xE4, 0xB5, 0x96, 0xF6, 0xE9, 0xBB,
+ 0xB9, 0xF6, 0xE9, 0xBB, 0xBE, 0xF6, 0xE9, 0xBC,
+ 0x85, 0xF6, 0xE9, 0xBC, 0x8F, 0xF6, 0xE9, 0xBC,
+ 0x96, 0xF6, 0xE9, 0xBC, 0xBB, 0xF6, 0xF0, 0xAA,
+ 0x98, 0x80,
+ },
+};
+
+static const uchar_t u8_case_common_b2_tbl[2][2][256] = {
+ {
+ {
+ 0, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, 1, 2, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, 3,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ {
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ 4, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+
+ },
+ {
+ {
+ 0, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, 1, 2, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, 3,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+ {
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ 4, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ N_, N_, N_, N_, N_, N_, N_, N_,
+ },
+
+ },
+
+};
+
+static const u8_displacement_t u8_tolower_b3_tbl[2][5][256] = {
+ {
+ { /* Third byte table 0. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { 0, 0 },
+ { 1, 60 }, { 2, 123 }, { 3, 185 }, { 4, 257 },
+ { 5, 321 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 6, 373 }, { 7, 439 },
+ { 8, 465 }, { 9, 561 }, { 10, 593 }, { 11, 649 },
+ { 12, 703 }, { 13, 749 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ },
+ { /* Third byte table 1. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 14, 795 }, { 15, 891 }, { 16, 987 }, { 17, 1068 },
+ { 18, 1155 }, { 19, 1245 }, { 20, 1299 }, { 21, 1386 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ },
+ { /* Third byte table 2. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 22, 1443 }, { 23, 1448 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 24, 1496 }, { 25, 1526 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ },
+ { /* Third byte table 3. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 26, 1574 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ },
+ { /* Third byte table 4. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 27, 1652 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ },
+ },
+ {
+ { /* Third byte table 0. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { 0, 0 },
+ { 1, 60 }, { 2, 123 }, { 3, 185 }, { 4, 257 },
+ { 5, 321 }, { 6, 383 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 7, 401 }, { 8, 467 },
+ { 9, 505 }, { 10, 601 }, { 11, 633 }, { 12, 689 },
+ { 13, 753 }, { 14, 803 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ },
+ { /* Third byte table 1. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 15, 849 }, { 16, 945 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 17, 963 }, { 18, 1059 }, { 19, 1155 }, { 20, 1236 },
+ { 21, 1323 }, { 22, 1413 }, { 23, 1467 }, { 24, 1554 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ },
+ { /* Third byte table 2. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 25, 1611 }, { 26, 1619 }, { 27, 1667 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 28, 1670 }, { 29, 1700 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 30, 1748 }, { 31, 1889 }, { 32, 1911 }, { 33, 2007 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ },
+ { /* Third byte table 3. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 34, 2061 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ },
+ { /* Third byte table 4. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 35, 2139 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ },
+ },
+};
+
+static const uchar_t u8_tolower_b4_tbl[2][36][257] = {
+ {
+ { /* Fourth byte table 0. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 46, 48, 50, 52, 54, 56, 58, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60,
+ },
+ { /* Fourth byte table 1. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 2, 4, 4, 6, 6, 8,
+ 8, 10, 10, 12, 12, 14, 14, 16,
+ 16, 18, 18, 20, 20, 22, 22, 24,
+ 24, 26, 26, 28, 28, 30, 30, 32,
+ 32, 34, 34, 36, 36, 38, 38, 40,
+ 40, 42, 42, 44, 44, 46, 46, 48,
+ 48, 49, 49, 51, 51, 53, 53, 55,
+ 55, 55, 57, 57, 59, 59, 61, 61,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63,
+ },
+ { /* Fourth byte table 2. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 2, 4, 4, 6, 6,
+ 8, 8, 8, 10, 10, 12, 12, 14,
+ 14, 16, 16, 18, 18, 20, 20, 22,
+ 22, 24, 24, 26, 26, 28, 28, 30,
+ 30, 32, 32, 34, 34, 36, 36, 38,
+ 38, 40, 40, 42, 42, 44, 44, 46,
+ 46, 48, 48, 50, 50, 52, 52, 54,
+ 54, 56, 58, 58, 60, 60, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62,
+ },
+ { /* Fourth byte table 3. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 4, 4, 6, 6, 8,
+ 10, 10, 12, 14, 16, 16, 16, 18,
+ 20, 22, 24, 24, 26, 28, 28, 30,
+ 32, 34, 34, 34, 34, 36, 38, 38,
+ 40, 42, 42, 44, 44, 46, 46, 48,
+ 50, 50, 52, 52, 52, 54, 54, 56,
+ 58, 58, 60, 62, 64, 64, 66, 66,
+ 68, 70, 70, 70, 70, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72,
+ },
+ { /* Fourth byte table 4. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 2, 4, 4,
+ 6, 8, 8, 10, 12, 12, 14, 14,
+ 16, 16, 18, 18, 20, 20, 22, 22,
+ 24, 24, 26, 26, 28, 28, 28, 30,
+ 30, 32, 32, 34, 34, 36, 36, 38,
+ 38, 40, 40, 42, 42, 44, 44, 46,
+ 46, 46, 48, 50, 50, 52, 52, 54,
+ 56, 58, 58, 60, 60, 62, 62, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64,
+ },
+ { /* Fourth byte table 5. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 2, 4, 4, 6, 6, 8,
+ 8, 10, 10, 12, 12, 14, 14, 16,
+ 16, 18, 18, 20, 20, 22, 22, 24,
+ 24, 26, 26, 28, 28, 30, 30, 32,
+ 32, 34, 34, 36, 36, 38, 38, 40,
+ 40, 42, 42, 44, 44, 46, 46, 48,
+ 48, 50, 50, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 52,
+ },
+ { /* Fourth byte table 6. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 2,
+ 2, 4, 6, 8, 8, 10, 10, 12,
+ 14, 14, 16, 18, 20, 22, 24, 26,
+ 28, 30, 32, 34, 36, 38, 40, 42,
+ 44, 46, 48, 48, 50, 52, 54, 56,
+ 58, 60, 62, 64, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66,
+ },
+ { /* Fourth byte table 7. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 2, 4, 4, 6, 6, 8,
+ 8, 10, 10, 12, 12, 14, 14, 16,
+ 16, 18, 18, 20, 20, 22, 22, 24,
+ 24, 24, 24, 24, 24, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26,
+ },
+ { /* Fourth byte table 8. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 48, 50, 52, 54, 56, 58, 60, 62,
+ 64, 66, 68, 70, 72, 74, 76, 78,
+ 80, 82, 84, 86, 88, 90, 92, 94,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96,
+ },
+ { /* Fourth byte table 9. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 2, 4, 4, 6, 6, 8,
+ 8, 10, 10, 12, 12, 14, 14, 16,
+ 16, 18, 18, 20, 20, 22, 22, 24,
+ 24, 26, 26, 28, 28, 30, 30, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32,
+ },
+ { /* Fourth byte table 10. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 4, 4, 6, 6, 8,
+ 8, 10, 10, 12, 12, 14, 14, 16,
+ 16, 18, 18, 20, 20, 22, 22, 24,
+ 24, 26, 26, 28, 28, 30, 30, 32,
+ 32, 34, 34, 36, 36, 38, 38, 40,
+ 40, 42, 42, 44, 44, 46, 46, 48,
+ 48, 50, 50, 52, 52, 54, 54, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56,
+ },
+ { /* Fourth byte table 11. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 2, 4, 4, 6, 6,
+ 8, 8, 10, 10, 12, 12, 14, 14,
+ 14, 16, 16, 18, 18, 20, 20, 22,
+ 22, 24, 24, 26, 26, 28, 28, 30,
+ 30, 32, 32, 34, 34, 36, 36, 38,
+ 38, 40, 40, 42, 42, 44, 44, 46,
+ 46, 48, 48, 50, 50, 52, 52, 52,
+ 52, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54,
+ },
+ { /* Fourth byte table 12. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 2, 4, 4, 6, 6, 8,
+ 8, 10, 10, 12, 12, 14, 14, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 18, 20, 22, 24, 26, 28,
+ 30, 32, 34, 36, 38, 40, 42, 44,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46,
+ },
+ { /* Fourth byte table 13. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46,
+ },
+ { /* Fourth byte table 14. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 6, 6, 9, 9, 12,
+ 12, 15, 15, 18, 18, 21, 21, 24,
+ 24, 27, 27, 30, 30, 33, 33, 36,
+ 36, 39, 39, 42, 42, 45, 45, 48,
+ 48, 51, 51, 54, 54, 57, 57, 60,
+ 60, 63, 63, 66, 66, 69, 69, 72,
+ 72, 75, 75, 78, 78, 81, 81, 84,
+ 84, 87, 87, 90, 90, 93, 93, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96,
+ },
+ { /* Fourth byte table 15. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 6, 6, 9, 9, 12,
+ 12, 15, 15, 18, 18, 21, 21, 24,
+ 24, 27, 27, 30, 30, 33, 33, 36,
+ 36, 39, 39, 42, 42, 45, 45, 48,
+ 48, 51, 51, 54, 54, 57, 57, 60,
+ 60, 63, 63, 66, 66, 69, 69, 72,
+ 72, 75, 75, 78, 78, 81, 81, 84,
+ 84, 87, 87, 90, 90, 93, 93, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96,
+ },
+ { /* Fourth byte table 16. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 6, 6, 9, 9, 12,
+ 12, 15, 15, 18, 18, 21, 21, 24,
+ 24, 27, 27, 30, 30, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 36, 36, 39, 39, 42, 42, 45,
+ 45, 48, 48, 51, 51, 54, 54, 57,
+ 57, 60, 60, 63, 63, 66, 66, 69,
+ 69, 72, 72, 75, 75, 78, 78, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81,
+ },
+ { /* Fourth byte table 17. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 6, 6, 9, 9, 12,
+ 12, 15, 15, 18, 18, 21, 21, 24,
+ 24, 27, 27, 30, 30, 33, 33, 36,
+ 36, 39, 39, 42, 42, 45, 45, 48,
+ 48, 51, 51, 54, 54, 57, 57, 60,
+ 60, 63, 63, 66, 66, 69, 69, 72,
+ 72, 75, 75, 78, 78, 81, 81, 84,
+ 84, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87,
+ },
+ { /* Fourth byte table 18. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 27, 30, 33, 36, 39, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 45, 48, 51, 54, 57, 60, 63,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 69, 72, 75, 78, 81, 84, 87,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90,
+ },
+ { /* Fourth byte table 19. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 21, 21, 24, 24, 27, 27,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 33, 36, 39, 42, 45, 48, 51,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54,
+ },
+ { /* Fourth byte table 20. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 51, 54, 57, 60, 63, 66, 69,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 75, 78, 81, 84, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87,
+ },
+ { /* Fourth byte table 21. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 18, 21, 24, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 30, 33, 36, 39, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 45, 48, 51, 54, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57,
+ },
+ { /* Fourth byte table 22. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 2,
+ 2, 2, 2, 3, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 5,
+ },
+ { /* Fourth byte table 23. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48,
+ },
+ { /* Fourth byte table 24. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 3,
+ 6, 9, 12, 15, 18, 21, 24, 27,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30,
+ },
+ { /* Fourth byte table 25. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48,
+ },
+ { /* Fourth byte table 26. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 6, 9, 12, 15, 18,
+ 21, 24, 27, 30, 33, 36, 39, 42,
+ 45, 48, 51, 54, 57, 60, 63, 66,
+ 69, 72, 75, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78,
+ },
+ { /* Fourth byte table 27. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 116, 120, 124,
+ 128, 132, 136, 140, 144, 148, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152,
+ },
+ { /* Fourth byte table 28. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ { /* Fourth byte table 29. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ { /* Fourth byte table 30. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ { /* Fourth byte table 31. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ { /* Fourth byte table 32. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ { /* Fourth byte table 33. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ { /* Fourth byte table 34. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ { /* Fourth byte table 35. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ },
+ {
+ { /* Fourth byte table 0. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 46, 48, 50, 52, 54, 56, 58, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60,
+ 60,
+ },
+ { /* Fourth byte table 1. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 2, 4, 4, 6, 6, 8,
+ 8, 10, 10, 12, 12, 14, 14, 16,
+ 16, 18, 18, 20, 20, 22, 22, 24,
+ 24, 26, 26, 28, 28, 30, 30, 32,
+ 32, 34, 34, 36, 36, 38, 38, 40,
+ 40, 42, 42, 44, 44, 46, 46, 48,
+ 48, 49, 49, 51, 51, 53, 53, 55,
+ 55, 55, 57, 57, 59, 59, 61, 61,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63,
+ },
+ { /* Fourth byte table 2. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 2, 4, 4, 6, 6,
+ 8, 8, 8, 10, 10, 12, 12, 14,
+ 14, 16, 16, 18, 18, 20, 20, 22,
+ 22, 24, 24, 26, 26, 28, 28, 30,
+ 30, 32, 32, 34, 34, 36, 36, 38,
+ 38, 40, 40, 42, 42, 44, 44, 46,
+ 46, 48, 48, 50, 50, 52, 52, 54,
+ 54, 56, 58, 58, 60, 60, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62,
+ },
+ { /* Fourth byte table 3. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 4, 4, 6, 6, 8,
+ 10, 10, 12, 14, 16, 16, 16, 18,
+ 20, 22, 24, 24, 26, 28, 28, 30,
+ 32, 34, 34, 34, 34, 36, 38, 38,
+ 40, 42, 42, 44, 44, 46, 46, 48,
+ 50, 50, 52, 52, 52, 54, 54, 56,
+ 58, 58, 60, 62, 64, 64, 66, 66,
+ 68, 70, 70, 70, 70, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72,
+ },
+ { /* Fourth byte table 4. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 2, 4, 4,
+ 6, 8, 8, 10, 12, 12, 14, 14,
+ 16, 16, 18, 18, 20, 20, 22, 22,
+ 24, 24, 26, 26, 28, 28, 28, 30,
+ 30, 32, 32, 34, 34, 36, 36, 38,
+ 38, 40, 40, 42, 42, 44, 44, 46,
+ 46, 46, 48, 50, 50, 52, 52, 54,
+ 56, 58, 58, 60, 60, 62, 62, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64,
+ },
+ { /* Fourth byte table 5. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 2, 4, 4, 6, 6, 8,
+ 8, 10, 10, 12, 12, 14, 14, 16,
+ 16, 18, 18, 20, 20, 22, 22, 24,
+ 24, 26, 26, 28, 28, 30, 30, 32,
+ 32, 34, 34, 36, 36, 38, 38, 40,
+ 40, 42, 42, 44, 44, 46, 46, 48,
+ 48, 50, 50, 52, 52, 52, 52, 52,
+ 52, 52, 52, 55, 57, 57, 59, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62,
+ },
+ { /* Fourth byte table 6. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 2, 4, 6, 8, 10,
+ 10, 12, 12, 14, 14, 16, 16, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18,
+ },
+ { /* Fourth byte table 7. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 2,
+ 2, 4, 6, 8, 8, 10, 10, 12,
+ 14, 14, 16, 18, 20, 22, 24, 26,
+ 28, 30, 32, 34, 36, 38, 40, 42,
+ 44, 46, 48, 48, 50, 52, 54, 56,
+ 58, 60, 62, 64, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66,
+ },
+ { /* Fourth byte table 8. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 2, 4, 4, 6, 6, 8,
+ 8, 10, 10, 12, 12, 14, 14, 16,
+ 16, 18, 18, 20, 20, 22, 22, 24,
+ 24, 24, 24, 24, 24, 26, 26, 26,
+ 28, 28, 30, 32, 32, 32, 34, 36,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38,
+ },
+ { /* Fourth byte table 9. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 48, 50, 52, 54, 56, 58, 60, 62,
+ 64, 66, 68, 70, 72, 74, 76, 78,
+ 80, 82, 84, 86, 88, 90, 92, 94,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96,
+ },
+ { /* Fourth byte table 10. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 2, 4, 4, 6, 6, 8,
+ 8, 10, 10, 12, 12, 14, 14, 16,
+ 16, 18, 18, 20, 20, 22, 22, 24,
+ 24, 26, 26, 28, 28, 30, 30, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32,
+ },
+ { /* Fourth byte table 11. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 4, 4, 6, 6, 8,
+ 8, 10, 10, 12, 12, 14, 14, 16,
+ 16, 18, 18, 20, 20, 22, 22, 24,
+ 24, 26, 26, 28, 28, 30, 30, 32,
+ 32, 34, 34, 36, 36, 38, 38, 40,
+ 40, 42, 42, 44, 44, 46, 46, 48,
+ 48, 50, 50, 52, 52, 54, 54, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56,
+ },
+ { /* Fourth byte table 12. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 4, 6, 6, 8, 8,
+ 10, 10, 12, 12, 14, 14, 16, 16,
+ 16, 18, 18, 20, 20, 22, 22, 24,
+ 24, 26, 26, 28, 28, 30, 30, 32,
+ 32, 34, 34, 36, 36, 38, 38, 40,
+ 40, 42, 42, 44, 44, 46, 46, 48,
+ 48, 50, 50, 52, 52, 54, 54, 56,
+ 56, 58, 58, 60, 60, 62, 62, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64,
+ },
+ { /* Fourth byte table 13. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 2, 4, 4, 6, 6, 8,
+ 8, 10, 10, 12, 12, 14, 14, 16,
+ 16, 18, 18, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 22, 24, 26, 28, 30, 32,
+ 34, 36, 38, 40, 42, 44, 46, 48,
+ 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50,
+ 50,
+ },
+ { /* Fourth byte table 14. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46,
+ },
+ { /* Fourth byte table 15. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 60, 63, 66, 69,
+ 72, 75, 78, 81, 84, 87, 90, 93,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96,
+ },
+ { /* Fourth byte table 16. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18,
+ },
+ { /* Fourth byte table 17. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 6, 6, 9, 9, 12,
+ 12, 15, 15, 18, 18, 21, 21, 24,
+ 24, 27, 27, 30, 30, 33, 33, 36,
+ 36, 39, 39, 42, 42, 45, 45, 48,
+ 48, 51, 51, 54, 54, 57, 57, 60,
+ 60, 63, 63, 66, 66, 69, 69, 72,
+ 72, 75, 75, 78, 78, 81, 81, 84,
+ 84, 87, 87, 90, 90, 93, 93, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96,
+ },
+ { /* Fourth byte table 18. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 6, 6, 9, 9, 12,
+ 12, 15, 15, 18, 18, 21, 21, 24,
+ 24, 27, 27, 30, 30, 33, 33, 36,
+ 36, 39, 39, 42, 42, 45, 45, 48,
+ 48, 51, 51, 54, 54, 57, 57, 60,
+ 60, 63, 63, 66, 66, 69, 69, 72,
+ 72, 75, 75, 78, 78, 81, 81, 84,
+ 84, 87, 87, 90, 90, 93, 93, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96,
+ },
+ { /* Fourth byte table 19. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 6, 6, 9, 9, 12,
+ 12, 15, 15, 18, 18, 21, 21, 24,
+ 24, 27, 27, 30, 30, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 36, 36, 39, 39, 42, 42, 45,
+ 45, 48, 48, 51, 51, 54, 54, 57,
+ 57, 60, 60, 63, 63, 66, 66, 69,
+ 69, 72, 72, 75, 75, 78, 78, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81,
+ },
+ { /* Fourth byte table 20. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 6, 6, 9, 9, 12,
+ 12, 15, 15, 18, 18, 21, 21, 24,
+ 24, 27, 27, 30, 30, 33, 33, 36,
+ 36, 39, 39, 42, 42, 45, 45, 48,
+ 48, 51, 51, 54, 54, 57, 57, 60,
+ 60, 63, 63, 66, 66, 69, 69, 72,
+ 72, 75, 75, 78, 78, 81, 81, 84,
+ 84, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87,
+ },
+ { /* Fourth byte table 21. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 27, 30, 33, 36, 39, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 45, 48, 51, 54, 57, 60, 63,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 69, 72, 75, 78, 81, 84, 87,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90,
+ },
+ { /* Fourth byte table 22. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 21, 21, 24, 24, 27, 27,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 33, 36, 39, 42, 45, 48, 51,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54,
+ },
+ { /* Fourth byte table 23. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 51, 54, 57, 60, 63, 66, 69,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 75, 78, 81, 84, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87,
+ },
+ { /* Fourth byte table 24. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 18, 21, 24, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 30, 33, 36, 39, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 45, 48, 51, 54, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57,
+ },
+ { /* Fourth byte table 25. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 2,
+ 2, 2, 2, 3, 5, 5, 5, 5,
+ 5, 5, 5, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8,
+ },
+ { /* Fourth byte table 26. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48,
+ },
+ { /* Fourth byte table 27. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3,
+ },
+ { /* Fourth byte table 28. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 3,
+ 6, 9, 12, 15, 18, 21, 24, 27,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30,
+ },
+ { /* Fourth byte table 29. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48,
+ },
+ { /* Fourth byte table 30. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 60, 63, 66, 69,
+ 72, 75, 78, 81, 84, 87, 90, 93,
+ 96, 99, 102, 105, 108, 111, 114, 117,
+ 120, 123, 126, 129, 132, 135, 138, 141,
+ 141, 141, 141, 141, 141, 141, 141, 141,
+ 141, 141, 141, 141, 141, 141, 141, 141,
+ 141, 141, 141, 141, 141, 141, 141, 141,
+ 141, 141, 141, 141, 141, 141, 141, 141,
+ 141, 141, 141, 141, 141, 141, 141, 141,
+ 141, 141, 141, 141, 141, 141, 141, 141,
+ 141, 141, 141, 141, 141, 141, 141, 141,
+ 141, 141, 141, 141, 141, 141, 141, 141,
+ 141, 141, 141, 141, 141, 141, 141, 141,
+ 141, 141, 141, 141, 141, 141, 141, 141,
+ 141,
+ },
+ { /* Fourth byte table 31. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 5, 8, 10, 10, 10,
+ 13, 13, 16, 16, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 22,
+ },
+ { /* Fourth byte table 32. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 6, 6, 9, 9, 12,
+ 12, 15, 15, 18, 18, 21, 21, 24,
+ 24, 27, 27, 30, 30, 33, 33, 36,
+ 36, 39, 39, 42, 42, 45, 45, 48,
+ 48, 51, 51, 54, 54, 57, 57, 60,
+ 60, 63, 63, 66, 66, 69, 69, 72,
+ 72, 75, 75, 78, 78, 81, 81, 84,
+ 84, 87, 87, 90, 90, 93, 93, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96,
+ },
+ { /* Fourth byte table 33. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 6, 6, 9, 9, 12,
+ 12, 15, 15, 18, 18, 21, 21, 24,
+ 24, 27, 27, 30, 30, 33, 33, 36,
+ 36, 39, 39, 42, 42, 45, 45, 48,
+ 48, 51, 51, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54,
+ },
+ { /* Fourth byte table 34. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 6, 9, 12, 15, 18,
+ 21, 24, 27, 30, 33, 36, 39, 42,
+ 45, 48, 51, 54, 57, 60, 63, 66,
+ 69, 72, 75, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78,
+ },
+ { /* Fourth byte table 35. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 100, 104, 108, 112, 116, 120, 124,
+ 128, 132, 136, 140, 144, 148, 152, 156,
+ 160, 160, 160, 160, 160, 160, 160, 160,
+ 160, 160, 160, 160, 160, 160, 160, 160,
+ 160, 160, 160, 160, 160, 160, 160, 160,
+ 160, 160, 160, 160, 160, 160, 160, 160,
+ 160, 160, 160, 160, 160, 160, 160, 160,
+ 160, 160, 160, 160, 160, 160, 160, 160,
+ 160, 160, 160, 160, 160, 160, 160, 160,
+ 160, 160, 160, 160, 160, 160, 160, 160,
+ 160, 160, 160, 160, 160, 160, 160, 160,
+ 160, 160, 160, 160, 160, 160, 160, 160,
+ 160, 160, 160, 160, 160, 160, 160, 160,
+ 160,
+ },
+ },
+};
+
+static const uchar_t u8_tolower_final_tbl[2][2299] = {
+ {
+ 0xC3, 0xA0, 0xC3, 0xA1, 0xC3, 0xA2, 0xC3, 0xA3,
+ 0xC3, 0xA4, 0xC3, 0xA5, 0xC3, 0xA6, 0xC3, 0xA7,
+ 0xC3, 0xA8, 0xC3, 0xA9, 0xC3, 0xAA, 0xC3, 0xAB,
+ 0xC3, 0xAC, 0xC3, 0xAD, 0xC3, 0xAE, 0xC3, 0xAF,
+ 0xC3, 0xB0, 0xC3, 0xB1, 0xC3, 0xB2, 0xC3, 0xB3,
+ 0xC3, 0xB4, 0xC3, 0xB5, 0xC3, 0xB6, 0xC3, 0xB8,
+ 0xC3, 0xB9, 0xC3, 0xBA, 0xC3, 0xBB, 0xC3, 0xBC,
+ 0xC3, 0xBD, 0xC3, 0xBE, 0xC4, 0x81, 0xC4, 0x83,
+ 0xC4, 0x85, 0xC4, 0x87, 0xC4, 0x89, 0xC4, 0x8B,
+ 0xC4, 0x8D, 0xC4, 0x8F, 0xC4, 0x91, 0xC4, 0x93,
+ 0xC4, 0x95, 0xC4, 0x97, 0xC4, 0x99, 0xC4, 0x9B,
+ 0xC4, 0x9D, 0xC4, 0x9F, 0xC4, 0xA1, 0xC4, 0xA3,
+ 0xC4, 0xA5, 0xC4, 0xA7, 0xC4, 0xA9, 0xC4, 0xAB,
+ 0xC4, 0xAD, 0xC4, 0xAF, 0x69, 0xC4, 0xB3, 0xC4,
+ 0xB5, 0xC4, 0xB7, 0xC4, 0xBA, 0xC4, 0xBC, 0xC4,
+ 0xBE, 0xC5, 0x80, 0xC5, 0x82, 0xC5, 0x84, 0xC5,
+ 0x86, 0xC5, 0x88, 0xC5, 0x8B, 0xC5, 0x8D, 0xC5,
+ 0x8F, 0xC5, 0x91, 0xC5, 0x93, 0xC5, 0x95, 0xC5,
+ 0x97, 0xC5, 0x99, 0xC5, 0x9B, 0xC5, 0x9D, 0xC5,
+ 0x9F, 0xC5, 0xA1, 0xC5, 0xA3, 0xC5, 0xA5, 0xC5,
+ 0xA7, 0xC5, 0xA9, 0xC5, 0xAB, 0xC5, 0xAD, 0xC5,
+ 0xAF, 0xC5, 0xB1, 0xC5, 0xB3, 0xC5, 0xB5, 0xC5,
+ 0xB7, 0xC3, 0xBF, 0xC5, 0xBA, 0xC5, 0xBC, 0xC5,
+ 0xBE, 0xC9, 0x93, 0xC6, 0x83, 0xC6, 0x85, 0xC9,
+ 0x94, 0xC6, 0x88, 0xC9, 0x96, 0xC9, 0x97, 0xC6,
+ 0x8C, 0xC7, 0x9D, 0xC9, 0x99, 0xC9, 0x9B, 0xC6,
+ 0x92, 0xC9, 0xA0, 0xC9, 0xA3, 0xC9, 0xA9, 0xC9,
+ 0xA8, 0xC6, 0x99, 0xC9, 0xAF, 0xC9, 0xB2, 0xC9,
+ 0xB5, 0xC6, 0xA1, 0xC6, 0xA3, 0xC6, 0xA5, 0xCA,
+ 0x80, 0xC6, 0xA8, 0xCA, 0x83, 0xC6, 0xAD, 0xCA,
+ 0x88, 0xC6, 0xB0, 0xCA, 0x8A, 0xCA, 0x8B, 0xC6,
+ 0xB4, 0xC6, 0xB6, 0xCA, 0x92, 0xC6, 0xB9, 0xC6,
+ 0xBD, 0xC7, 0x86, 0xC7, 0x86, 0xC7, 0x89, 0xC7,
+ 0x89, 0xC7, 0x8C, 0xC7, 0x8C, 0xC7, 0x8E, 0xC7,
+ 0x90, 0xC7, 0x92, 0xC7, 0x94, 0xC7, 0x96, 0xC7,
+ 0x98, 0xC7, 0x9A, 0xC7, 0x9C, 0xC7, 0x9F, 0xC7,
+ 0xA1, 0xC7, 0xA3, 0xC7, 0xA5, 0xC7, 0xA7, 0xC7,
+ 0xA9, 0xC7, 0xAB, 0xC7, 0xAD, 0xC7, 0xAF, 0xC7,
+ 0xB3, 0xC7, 0xB3, 0xC7, 0xB5, 0xC6, 0x95, 0xC6,
+ 0xBF, 0xC7, 0xB9, 0xC7, 0xBB, 0xC7, 0xBD, 0xC7,
+ 0xBF, 0xC8, 0x81, 0xC8, 0x83, 0xC8, 0x85, 0xC8,
+ 0x87, 0xC8, 0x89, 0xC8, 0x8B, 0xC8, 0x8D, 0xC8,
+ 0x8F, 0xC8, 0x91, 0xC8, 0x93, 0xC8, 0x95, 0xC8,
+ 0x97, 0xC8, 0x99, 0xC8, 0x9B, 0xC8, 0x9D, 0xC8,
+ 0x9F, 0xC6, 0x9E, 0xC8, 0xA3, 0xC8, 0xA5, 0xC8,
+ 0xA7, 0xC8, 0xA9, 0xC8, 0xAB, 0xC8, 0xAD, 0xC8,
+ 0xAF, 0xC8, 0xB1, 0xC8, 0xB3, 0xCE, 0xAC, 0xCE,
+ 0xAD, 0xCE, 0xAE, 0xCE, 0xAF, 0xCF, 0x8C, 0xCF,
+ 0x8D, 0xCF, 0x8E, 0xCE, 0xB1, 0xCE, 0xB2, 0xCE,
+ 0xB3, 0xCE, 0xB4, 0xCE, 0xB5, 0xCE, 0xB6, 0xCE,
+ 0xB7, 0xCE, 0xB8, 0xCE, 0xB9, 0xCE, 0xBA, 0xCE,
+ 0xBB, 0xCE, 0xBC, 0xCE, 0xBD, 0xCE, 0xBE, 0xCE,
+ 0xBF, 0xCF, 0x80, 0xCF, 0x81, 0xCF, 0x83, 0xCF,
+ 0x84, 0xCF, 0x85, 0xCF, 0x86, 0xCF, 0x87, 0xCF,
+ 0x88, 0xCF, 0x89, 0xCF, 0x8A, 0xCF, 0x8B, 0xCF,
+ 0x99, 0xCF, 0x9B, 0xCF, 0x9D, 0xCF, 0x9F, 0xCF,
+ 0xA1, 0xCF, 0xA3, 0xCF, 0xA5, 0xCF, 0xA7, 0xCF,
+ 0xA9, 0xCF, 0xAB, 0xCF, 0xAD, 0xCF, 0xAF, 0xCE,
+ 0xB8, 0xD1, 0x90, 0xD1, 0x91, 0xD1, 0x92, 0xD1,
+ 0x93, 0xD1, 0x94, 0xD1, 0x95, 0xD1, 0x96, 0xD1,
+ 0x97, 0xD1, 0x98, 0xD1, 0x99, 0xD1, 0x9A, 0xD1,
+ 0x9B, 0xD1, 0x9C, 0xD1, 0x9D, 0xD1, 0x9E, 0xD1,
+ 0x9F, 0xD0, 0xB0, 0xD0, 0xB1, 0xD0, 0xB2, 0xD0,
+ 0xB3, 0xD0, 0xB4, 0xD0, 0xB5, 0xD0, 0xB6, 0xD0,
+ 0xB7, 0xD0, 0xB8, 0xD0, 0xB9, 0xD0, 0xBA, 0xD0,
+ 0xBB, 0xD0, 0xBC, 0xD0, 0xBD, 0xD0, 0xBE, 0xD0,
+ 0xBF, 0xD1, 0x80, 0xD1, 0x81, 0xD1, 0x82, 0xD1,
+ 0x83, 0xD1, 0x84, 0xD1, 0x85, 0xD1, 0x86, 0xD1,
+ 0x87, 0xD1, 0x88, 0xD1, 0x89, 0xD1, 0x8A, 0xD1,
+ 0x8B, 0xD1, 0x8C, 0xD1, 0x8D, 0xD1, 0x8E, 0xD1,
+ 0x8F, 0xD1, 0xA1, 0xD1, 0xA3, 0xD1, 0xA5, 0xD1,
+ 0xA7, 0xD1, 0xA9, 0xD1, 0xAB, 0xD1, 0xAD, 0xD1,
+ 0xAF, 0xD1, 0xB1, 0xD1, 0xB3, 0xD1, 0xB5, 0xD1,
+ 0xB7, 0xD1, 0xB9, 0xD1, 0xBB, 0xD1, 0xBD, 0xD1,
+ 0xBF, 0xD2, 0x81, 0xD2, 0x8B, 0xD2, 0x8D, 0xD2,
+ 0x8F, 0xD2, 0x91, 0xD2, 0x93, 0xD2, 0x95, 0xD2,
+ 0x97, 0xD2, 0x99, 0xD2, 0x9B, 0xD2, 0x9D, 0xD2,
+ 0x9F, 0xD2, 0xA1, 0xD2, 0xA3, 0xD2, 0xA5, 0xD2,
+ 0xA7, 0xD2, 0xA9, 0xD2, 0xAB, 0xD2, 0xAD, 0xD2,
+ 0xAF, 0xD2, 0xB1, 0xD2, 0xB3, 0xD2, 0xB5, 0xD2,
+ 0xB7, 0xD2, 0xB9, 0xD2, 0xBB, 0xD2, 0xBD, 0xD2,
+ 0xBF, 0xD3, 0x82, 0xD3, 0x84, 0xD3, 0x86, 0xD3,
+ 0x88, 0xD3, 0x8A, 0xD3, 0x8C, 0xD3, 0x8E, 0xD3,
+ 0x91, 0xD3, 0x93, 0xD3, 0x95, 0xD3, 0x97, 0xD3,
+ 0x99, 0xD3, 0x9B, 0xD3, 0x9D, 0xD3, 0x9F, 0xD3,
+ 0xA1, 0xD3, 0xA3, 0xD3, 0xA5, 0xD3, 0xA7, 0xD3,
+ 0xA9, 0xD3, 0xAB, 0xD3, 0xAD, 0xD3, 0xAF, 0xD3,
+ 0xB1, 0xD3, 0xB3, 0xD3, 0xB5, 0xD3, 0xB9, 0xD4,
+ 0x81, 0xD4, 0x83, 0xD4, 0x85, 0xD4, 0x87, 0xD4,
+ 0x89, 0xD4, 0x8B, 0xD4, 0x8D, 0xD4, 0x8F, 0xD5,
+ 0xA1, 0xD5, 0xA2, 0xD5, 0xA3, 0xD5, 0xA4, 0xD5,
+ 0xA5, 0xD5, 0xA6, 0xD5, 0xA7, 0xD5, 0xA8, 0xD5,
+ 0xA9, 0xD5, 0xAA, 0xD5, 0xAB, 0xD5, 0xAC, 0xD5,
+ 0xAD, 0xD5, 0xAE, 0xD5, 0xAF, 0xD5, 0xB0, 0xD5,
+ 0xB1, 0xD5, 0xB2, 0xD5, 0xB3, 0xD5, 0xB4, 0xD5,
+ 0xB5, 0xD5, 0xB6, 0xD5, 0xB7, 0xD5, 0xB8, 0xD5,
+ 0xB9, 0xD5, 0xBA, 0xD5, 0xBB, 0xD5, 0xBC, 0xD5,
+ 0xBD, 0xD5, 0xBE, 0xD5, 0xBF, 0xD6, 0x80, 0xD6,
+ 0x81, 0xD6, 0x82, 0xD6, 0x83, 0xD6, 0x84, 0xD6,
+ 0x85, 0xD6, 0x86, 0xE1, 0xB8, 0x81, 0xE1, 0xB8,
+ 0x83, 0xE1, 0xB8, 0x85, 0xE1, 0xB8, 0x87, 0xE1,
+ 0xB8, 0x89, 0xE1, 0xB8, 0x8B, 0xE1, 0xB8, 0x8D,
+ 0xE1, 0xB8, 0x8F, 0xE1, 0xB8, 0x91, 0xE1, 0xB8,
+ 0x93, 0xE1, 0xB8, 0x95, 0xE1, 0xB8, 0x97, 0xE1,
+ 0xB8, 0x99, 0xE1, 0xB8, 0x9B, 0xE1, 0xB8, 0x9D,
+ 0xE1, 0xB8, 0x9F, 0xE1, 0xB8, 0xA1, 0xE1, 0xB8,
+ 0xA3, 0xE1, 0xB8, 0xA5, 0xE1, 0xB8, 0xA7, 0xE1,
+ 0xB8, 0xA9, 0xE1, 0xB8, 0xAB, 0xE1, 0xB8, 0xAD,
+ 0xE1, 0xB8, 0xAF, 0xE1, 0xB8, 0xB1, 0xE1, 0xB8,
+ 0xB3, 0xE1, 0xB8, 0xB5, 0xE1, 0xB8, 0xB7, 0xE1,
+ 0xB8, 0xB9, 0xE1, 0xB8, 0xBB, 0xE1, 0xB8, 0xBD,
+ 0xE1, 0xB8, 0xBF, 0xE1, 0xB9, 0x81, 0xE1, 0xB9,
+ 0x83, 0xE1, 0xB9, 0x85, 0xE1, 0xB9, 0x87, 0xE1,
+ 0xB9, 0x89, 0xE1, 0xB9, 0x8B, 0xE1, 0xB9, 0x8D,
+ 0xE1, 0xB9, 0x8F, 0xE1, 0xB9, 0x91, 0xE1, 0xB9,
+ 0x93, 0xE1, 0xB9, 0x95, 0xE1, 0xB9, 0x97, 0xE1,
+ 0xB9, 0x99, 0xE1, 0xB9, 0x9B, 0xE1, 0xB9, 0x9D,
+ 0xE1, 0xB9, 0x9F, 0xE1, 0xB9, 0xA1, 0xE1, 0xB9,
+ 0xA3, 0xE1, 0xB9, 0xA5, 0xE1, 0xB9, 0xA7, 0xE1,
+ 0xB9, 0xA9, 0xE1, 0xB9, 0xAB, 0xE1, 0xB9, 0xAD,
+ 0xE1, 0xB9, 0xAF, 0xE1, 0xB9, 0xB1, 0xE1, 0xB9,
+ 0xB3, 0xE1, 0xB9, 0xB5, 0xE1, 0xB9, 0xB7, 0xE1,
+ 0xB9, 0xB9, 0xE1, 0xB9, 0xBB, 0xE1, 0xB9, 0xBD,
+ 0xE1, 0xB9, 0xBF, 0xE1, 0xBA, 0x81, 0xE1, 0xBA,
+ 0x83, 0xE1, 0xBA, 0x85, 0xE1, 0xBA, 0x87, 0xE1,
+ 0xBA, 0x89, 0xE1, 0xBA, 0x8B, 0xE1, 0xBA, 0x8D,
+ 0xE1, 0xBA, 0x8F, 0xE1, 0xBA, 0x91, 0xE1, 0xBA,
+ 0x93, 0xE1, 0xBA, 0x95, 0xE1, 0xBA, 0xA1, 0xE1,
+ 0xBA, 0xA3, 0xE1, 0xBA, 0xA5, 0xE1, 0xBA, 0xA7,
+ 0xE1, 0xBA, 0xA9, 0xE1, 0xBA, 0xAB, 0xE1, 0xBA,
+ 0xAD, 0xE1, 0xBA, 0xAF, 0xE1, 0xBA, 0xB1, 0xE1,
+ 0xBA, 0xB3, 0xE1, 0xBA, 0xB5, 0xE1, 0xBA, 0xB7,
+ 0xE1, 0xBA, 0xB9, 0xE1, 0xBA, 0xBB, 0xE1, 0xBA,
+ 0xBD, 0xE1, 0xBA, 0xBF, 0xE1, 0xBB, 0x81, 0xE1,
+ 0xBB, 0x83, 0xE1, 0xBB, 0x85, 0xE1, 0xBB, 0x87,
+ 0xE1, 0xBB, 0x89, 0xE1, 0xBB, 0x8B, 0xE1, 0xBB,
+ 0x8D, 0xE1, 0xBB, 0x8F, 0xE1, 0xBB, 0x91, 0xE1,
+ 0xBB, 0x93, 0xE1, 0xBB, 0x95, 0xE1, 0xBB, 0x97,
+ 0xE1, 0xBB, 0x99, 0xE1, 0xBB, 0x9B, 0xE1, 0xBB,
+ 0x9D, 0xE1, 0xBB, 0x9F, 0xE1, 0xBB, 0xA1, 0xE1,
+ 0xBB, 0xA3, 0xE1, 0xBB, 0xA5, 0xE1, 0xBB, 0xA7,
+ 0xE1, 0xBB, 0xA9, 0xE1, 0xBB, 0xAB, 0xE1, 0xBB,
+ 0xAD, 0xE1, 0xBB, 0xAF, 0xE1, 0xBB, 0xB1, 0xE1,
+ 0xBB, 0xB3, 0xE1, 0xBB, 0xB5, 0xE1, 0xBB, 0xB7,
+ 0xE1, 0xBB, 0xB9, 0xE1, 0xBC, 0x80, 0xE1, 0xBC,
+ 0x81, 0xE1, 0xBC, 0x82, 0xE1, 0xBC, 0x83, 0xE1,
+ 0xBC, 0x84, 0xE1, 0xBC, 0x85, 0xE1, 0xBC, 0x86,
+ 0xE1, 0xBC, 0x87, 0xE1, 0xBC, 0x90, 0xE1, 0xBC,
+ 0x91, 0xE1, 0xBC, 0x92, 0xE1, 0xBC, 0x93, 0xE1,
+ 0xBC, 0x94, 0xE1, 0xBC, 0x95, 0xE1, 0xBC, 0xA0,
+ 0xE1, 0xBC, 0xA1, 0xE1, 0xBC, 0xA2, 0xE1, 0xBC,
+ 0xA3, 0xE1, 0xBC, 0xA4, 0xE1, 0xBC, 0xA5, 0xE1,
+ 0xBC, 0xA6, 0xE1, 0xBC, 0xA7, 0xE1, 0xBC, 0xB0,
+ 0xE1, 0xBC, 0xB1, 0xE1, 0xBC, 0xB2, 0xE1, 0xBC,
+ 0xB3, 0xE1, 0xBC, 0xB4, 0xE1, 0xBC, 0xB5, 0xE1,
+ 0xBC, 0xB6, 0xE1, 0xBC, 0xB7, 0xE1, 0xBD, 0x80,
+ 0xE1, 0xBD, 0x81, 0xE1, 0xBD, 0x82, 0xE1, 0xBD,
+ 0x83, 0xE1, 0xBD, 0x84, 0xE1, 0xBD, 0x85, 0xE1,
+ 0xBD, 0x91, 0xE1, 0xBD, 0x93, 0xE1, 0xBD, 0x95,
+ 0xE1, 0xBD, 0x97, 0xE1, 0xBD, 0xA0, 0xE1, 0xBD,
+ 0xA1, 0xE1, 0xBD, 0xA2, 0xE1, 0xBD, 0xA3, 0xE1,
+ 0xBD, 0xA4, 0xE1, 0xBD, 0xA5, 0xE1, 0xBD, 0xA6,
+ 0xE1, 0xBD, 0xA7, 0xE1, 0xBE, 0x80, 0xE1, 0xBE,
+ 0x81, 0xE1, 0xBE, 0x82, 0xE1, 0xBE, 0x83, 0xE1,
+ 0xBE, 0x84, 0xE1, 0xBE, 0x85, 0xE1, 0xBE, 0x86,
+ 0xE1, 0xBE, 0x87, 0xE1, 0xBE, 0x90, 0xE1, 0xBE,
+ 0x91, 0xE1, 0xBE, 0x92, 0xE1, 0xBE, 0x93, 0xE1,
+ 0xBE, 0x94, 0xE1, 0xBE, 0x95, 0xE1, 0xBE, 0x96,
+ 0xE1, 0xBE, 0x97, 0xE1, 0xBE, 0xA0, 0xE1, 0xBE,
+ 0xA1, 0xE1, 0xBE, 0xA2, 0xE1, 0xBE, 0xA3, 0xE1,
+ 0xBE, 0xA4, 0xE1, 0xBE, 0xA5, 0xE1, 0xBE, 0xA6,
+ 0xE1, 0xBE, 0xA7, 0xE1, 0xBE, 0xB0, 0xE1, 0xBE,
+ 0xB1, 0xE1, 0xBD, 0xB0, 0xE1, 0xBD, 0xB1, 0xE1,
+ 0xBE, 0xB3, 0xE1, 0xBD, 0xB2, 0xE1, 0xBD, 0xB3,
+ 0xE1, 0xBD, 0xB4, 0xE1, 0xBD, 0xB5, 0xE1, 0xBF,
+ 0x83, 0xE1, 0xBF, 0x90, 0xE1, 0xBF, 0x91, 0xE1,
+ 0xBD, 0xB6, 0xE1, 0xBD, 0xB7, 0xE1, 0xBF, 0xA0,
+ 0xE1, 0xBF, 0xA1, 0xE1, 0xBD, 0xBA, 0xE1, 0xBD,
+ 0xBB, 0xE1, 0xBF, 0xA5, 0xE1, 0xBD, 0xB8, 0xE1,
+ 0xBD, 0xB9, 0xE1, 0xBD, 0xBC, 0xE1, 0xBD, 0xBD,
+ 0xE1, 0xBF, 0xB3, 0xCF, 0x89, 0x6B, 0xC3, 0xA5,
+ 0xE2, 0x85, 0xB0, 0xE2, 0x85, 0xB1, 0xE2, 0x85,
+ 0xB2, 0xE2, 0x85, 0xB3, 0xE2, 0x85, 0xB4, 0xE2,
+ 0x85, 0xB5, 0xE2, 0x85, 0xB6, 0xE2, 0x85, 0xB7,
+ 0xE2, 0x85, 0xB8, 0xE2, 0x85, 0xB9, 0xE2, 0x85,
+ 0xBA, 0xE2, 0x85, 0xBB, 0xE2, 0x85, 0xBC, 0xE2,
+ 0x85, 0xBD, 0xE2, 0x85, 0xBE, 0xE2, 0x85, 0xBF,
+ 0xE2, 0x93, 0x90, 0xE2, 0x93, 0x91, 0xE2, 0x93,
+ 0x92, 0xE2, 0x93, 0x93, 0xE2, 0x93, 0x94, 0xE2,
+ 0x93, 0x95, 0xE2, 0x93, 0x96, 0xE2, 0x93, 0x97,
+ 0xE2, 0x93, 0x98, 0xE2, 0x93, 0x99, 0xE2, 0x93,
+ 0x9A, 0xE2, 0x93, 0x9B, 0xE2, 0x93, 0x9C, 0xE2,
+ 0x93, 0x9D, 0xE2, 0x93, 0x9E, 0xE2, 0x93, 0x9F,
+ 0xE2, 0x93, 0xA0, 0xE2, 0x93, 0xA1, 0xE2, 0x93,
+ 0xA2, 0xE2, 0x93, 0xA3, 0xE2, 0x93, 0xA4, 0xE2,
+ 0x93, 0xA5, 0xE2, 0x93, 0xA6, 0xE2, 0x93, 0xA7,
+ 0xE2, 0x93, 0xA8, 0xE2, 0x93, 0xA9, 0xEF, 0xBD,
+ 0x81, 0xEF, 0xBD, 0x82, 0xEF, 0xBD, 0x83, 0xEF,
+ 0xBD, 0x84, 0xEF, 0xBD, 0x85, 0xEF, 0xBD, 0x86,
+ 0xEF, 0xBD, 0x87, 0xEF, 0xBD, 0x88, 0xEF, 0xBD,
+ 0x89, 0xEF, 0xBD, 0x8A, 0xEF, 0xBD, 0x8B, 0xEF,
+ 0xBD, 0x8C, 0xEF, 0xBD, 0x8D, 0xEF, 0xBD, 0x8E,
+ 0xEF, 0xBD, 0x8F, 0xEF, 0xBD, 0x90, 0xEF, 0xBD,
+ 0x91, 0xEF, 0xBD, 0x92, 0xEF, 0xBD, 0x93, 0xEF,
+ 0xBD, 0x94, 0xEF, 0xBD, 0x95, 0xEF, 0xBD, 0x96,
+ 0xEF, 0xBD, 0x97, 0xEF, 0xBD, 0x98, 0xEF, 0xBD,
+ 0x99, 0xEF, 0xBD, 0x9A, 0xF0, 0x90, 0x90, 0xA8,
+ 0xF0, 0x90, 0x90, 0xA9, 0xF0, 0x90, 0x90, 0xAA,
+ 0xF0, 0x90, 0x90, 0xAB, 0xF0, 0x90, 0x90, 0xAC,
+ 0xF0, 0x90, 0x90, 0xAD, 0xF0, 0x90, 0x90, 0xAE,
+ 0xF0, 0x90, 0x90, 0xAF, 0xF0, 0x90, 0x90, 0xB0,
+ 0xF0, 0x90, 0x90, 0xB1, 0xF0, 0x90, 0x90, 0xB2,
+ 0xF0, 0x90, 0x90, 0xB3, 0xF0, 0x90, 0x90, 0xB4,
+ 0xF0, 0x90, 0x90, 0xB5, 0xF0, 0x90, 0x90, 0xB6,
+ 0xF0, 0x90, 0x90, 0xB7, 0xF0, 0x90, 0x90, 0xB8,
+ 0xF0, 0x90, 0x90, 0xB9, 0xF0, 0x90, 0x90, 0xBA,
+ 0xF0, 0x90, 0x90, 0xBB, 0xF0, 0x90, 0x90, 0xBC,
+ 0xF0, 0x90, 0x90, 0xBD, 0xF0, 0x90, 0x90, 0xBE,
+ 0xF0, 0x90, 0x90, 0xBF, 0xF0, 0x90, 0x91, 0x80,
+ 0xF0, 0x90, 0x91, 0x81, 0xF0, 0x90, 0x91, 0x82,
+ 0xF0, 0x90, 0x91, 0x83, 0xF0, 0x90, 0x91, 0x84,
+ 0xF0, 0x90, 0x91, 0x85, 0xF0, 0x90, 0x91, 0x86,
+ 0xF0, 0x90, 0x91, 0x87, 0xF0, 0x90, 0x91, 0x88,
+ 0xF0, 0x90, 0x91, 0x89, 0xF0, 0x90, 0x91, 0x8A,
+ 0xF0, 0x90, 0x91, 0x8B, 0xF0, 0x90, 0x91, 0x8C,
+ 0xF0, 0x90, 0x91, 0x8D, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0,
+ },
+ {
+ 0xC3, 0xA0, 0xC3, 0xA1, 0xC3, 0xA2, 0xC3, 0xA3,
+ 0xC3, 0xA4, 0xC3, 0xA5, 0xC3, 0xA6, 0xC3, 0xA7,
+ 0xC3, 0xA8, 0xC3, 0xA9, 0xC3, 0xAA, 0xC3, 0xAB,
+ 0xC3, 0xAC, 0xC3, 0xAD, 0xC3, 0xAE, 0xC3, 0xAF,
+ 0xC3, 0xB0, 0xC3, 0xB1, 0xC3, 0xB2, 0xC3, 0xB3,
+ 0xC3, 0xB4, 0xC3, 0xB5, 0xC3, 0xB6, 0xC3, 0xB8,
+ 0xC3, 0xB9, 0xC3, 0xBA, 0xC3, 0xBB, 0xC3, 0xBC,
+ 0xC3, 0xBD, 0xC3, 0xBE, 0xC4, 0x81, 0xC4, 0x83,
+ 0xC4, 0x85, 0xC4, 0x87, 0xC4, 0x89, 0xC4, 0x8B,
+ 0xC4, 0x8D, 0xC4, 0x8F, 0xC4, 0x91, 0xC4, 0x93,
+ 0xC4, 0x95, 0xC4, 0x97, 0xC4, 0x99, 0xC4, 0x9B,
+ 0xC4, 0x9D, 0xC4, 0x9F, 0xC4, 0xA1, 0xC4, 0xA3,
+ 0xC4, 0xA5, 0xC4, 0xA7, 0xC4, 0xA9, 0xC4, 0xAB,
+ 0xC4, 0xAD, 0xC4, 0xAF, 0x69, 0xC4, 0xB3, 0xC4,
+ 0xB5, 0xC4, 0xB7, 0xC4, 0xBA, 0xC4, 0xBC, 0xC4,
+ 0xBE, 0xC5, 0x80, 0xC5, 0x82, 0xC5, 0x84, 0xC5,
+ 0x86, 0xC5, 0x88, 0xC5, 0x8B, 0xC5, 0x8D, 0xC5,
+ 0x8F, 0xC5, 0x91, 0xC5, 0x93, 0xC5, 0x95, 0xC5,
+ 0x97, 0xC5, 0x99, 0xC5, 0x9B, 0xC5, 0x9D, 0xC5,
+ 0x9F, 0xC5, 0xA1, 0xC5, 0xA3, 0xC5, 0xA5, 0xC5,
+ 0xA7, 0xC5, 0xA9, 0xC5, 0xAB, 0xC5, 0xAD, 0xC5,
+ 0xAF, 0xC5, 0xB1, 0xC5, 0xB3, 0xC5, 0xB5, 0xC5,
+ 0xB7, 0xC3, 0xBF, 0xC5, 0xBA, 0xC5, 0xBC, 0xC5,
+ 0xBE, 0xC9, 0x93, 0xC6, 0x83, 0xC6, 0x85, 0xC9,
+ 0x94, 0xC6, 0x88, 0xC9, 0x96, 0xC9, 0x97, 0xC6,
+ 0x8C, 0xC7, 0x9D, 0xC9, 0x99, 0xC9, 0x9B, 0xC6,
+ 0x92, 0xC9, 0xA0, 0xC9, 0xA3, 0xC9, 0xA9, 0xC9,
+ 0xA8, 0xC6, 0x99, 0xC9, 0xAF, 0xC9, 0xB2, 0xC9,
+ 0xB5, 0xC6, 0xA1, 0xC6, 0xA3, 0xC6, 0xA5, 0xCA,
+ 0x80, 0xC6, 0xA8, 0xCA, 0x83, 0xC6, 0xAD, 0xCA,
+ 0x88, 0xC6, 0xB0, 0xCA, 0x8A, 0xCA, 0x8B, 0xC6,
+ 0xB4, 0xC6, 0xB6, 0xCA, 0x92, 0xC6, 0xB9, 0xC6,
+ 0xBD, 0xC7, 0x86, 0xC7, 0x86, 0xC7, 0x89, 0xC7,
+ 0x89, 0xC7, 0x8C, 0xC7, 0x8C, 0xC7, 0x8E, 0xC7,
+ 0x90, 0xC7, 0x92, 0xC7, 0x94, 0xC7, 0x96, 0xC7,
+ 0x98, 0xC7, 0x9A, 0xC7, 0x9C, 0xC7, 0x9F, 0xC7,
+ 0xA1, 0xC7, 0xA3, 0xC7, 0xA5, 0xC7, 0xA7, 0xC7,
+ 0xA9, 0xC7, 0xAB, 0xC7, 0xAD, 0xC7, 0xAF, 0xC7,
+ 0xB3, 0xC7, 0xB3, 0xC7, 0xB5, 0xC6, 0x95, 0xC6,
+ 0xBF, 0xC7, 0xB9, 0xC7, 0xBB, 0xC7, 0xBD, 0xC7,
+ 0xBF, 0xC8, 0x81, 0xC8, 0x83, 0xC8, 0x85, 0xC8,
+ 0x87, 0xC8, 0x89, 0xC8, 0x8B, 0xC8, 0x8D, 0xC8,
+ 0x8F, 0xC8, 0x91, 0xC8, 0x93, 0xC8, 0x95, 0xC8,
+ 0x97, 0xC8, 0x99, 0xC8, 0x9B, 0xC8, 0x9D, 0xC8,
+ 0x9F, 0xC6, 0x9E, 0xC8, 0xA3, 0xC8, 0xA5, 0xC8,
+ 0xA7, 0xC8, 0xA9, 0xC8, 0xAB, 0xC8, 0xAD, 0xC8,
+ 0xAF, 0xC8, 0xB1, 0xC8, 0xB3, 0xE2, 0xB1, 0xA5,
+ 0xC8, 0xBC, 0xC6, 0x9A, 0xE2, 0xB1, 0xA6, 0xC9,
+ 0x82, 0xC6, 0x80, 0xCA, 0x89, 0xCA, 0x8C, 0xC9,
+ 0x87, 0xC9, 0x89, 0xC9, 0x8B, 0xC9, 0x8D, 0xC9,
+ 0x8F, 0xCE, 0xAC, 0xCE, 0xAD, 0xCE, 0xAE, 0xCE,
+ 0xAF, 0xCF, 0x8C, 0xCF, 0x8D, 0xCF, 0x8E, 0xCE,
+ 0xB1, 0xCE, 0xB2, 0xCE, 0xB3, 0xCE, 0xB4, 0xCE,
+ 0xB5, 0xCE, 0xB6, 0xCE, 0xB7, 0xCE, 0xB8, 0xCE,
+ 0xB9, 0xCE, 0xBA, 0xCE, 0xBB, 0xCE, 0xBC, 0xCE,
+ 0xBD, 0xCE, 0xBE, 0xCE, 0xBF, 0xCF, 0x80, 0xCF,
+ 0x81, 0xCF, 0x83, 0xCF, 0x84, 0xCF, 0x85, 0xCF,
+ 0x86, 0xCF, 0x87, 0xCF, 0x88, 0xCF, 0x89, 0xCF,
+ 0x8A, 0xCF, 0x8B, 0xCF, 0x99, 0xCF, 0x9B, 0xCF,
+ 0x9D, 0xCF, 0x9F, 0xCF, 0xA1, 0xCF, 0xA3, 0xCF,
+ 0xA5, 0xCF, 0xA7, 0xCF, 0xA9, 0xCF, 0xAB, 0xCF,
+ 0xAD, 0xCF, 0xAF, 0xCE, 0xB8, 0xCF, 0xB8, 0xCF,
+ 0xB2, 0xCF, 0xBB, 0xCD, 0xBB, 0xCD, 0xBC, 0xCD,
+ 0xBD, 0xD1, 0x90, 0xD1, 0x91, 0xD1, 0x92, 0xD1,
+ 0x93, 0xD1, 0x94, 0xD1, 0x95, 0xD1, 0x96, 0xD1,
+ 0x97, 0xD1, 0x98, 0xD1, 0x99, 0xD1, 0x9A, 0xD1,
+ 0x9B, 0xD1, 0x9C, 0xD1, 0x9D, 0xD1, 0x9E, 0xD1,
+ 0x9F, 0xD0, 0xB0, 0xD0, 0xB1, 0xD0, 0xB2, 0xD0,
+ 0xB3, 0xD0, 0xB4, 0xD0, 0xB5, 0xD0, 0xB6, 0xD0,
+ 0xB7, 0xD0, 0xB8, 0xD0, 0xB9, 0xD0, 0xBA, 0xD0,
+ 0xBB, 0xD0, 0xBC, 0xD0, 0xBD, 0xD0, 0xBE, 0xD0,
+ 0xBF, 0xD1, 0x80, 0xD1, 0x81, 0xD1, 0x82, 0xD1,
+ 0x83, 0xD1, 0x84, 0xD1, 0x85, 0xD1, 0x86, 0xD1,
+ 0x87, 0xD1, 0x88, 0xD1, 0x89, 0xD1, 0x8A, 0xD1,
+ 0x8B, 0xD1, 0x8C, 0xD1, 0x8D, 0xD1, 0x8E, 0xD1,
+ 0x8F, 0xD1, 0xA1, 0xD1, 0xA3, 0xD1, 0xA5, 0xD1,
+ 0xA7, 0xD1, 0xA9, 0xD1, 0xAB, 0xD1, 0xAD, 0xD1,
+ 0xAF, 0xD1, 0xB1, 0xD1, 0xB3, 0xD1, 0xB5, 0xD1,
+ 0xB7, 0xD1, 0xB9, 0xD1, 0xBB, 0xD1, 0xBD, 0xD1,
+ 0xBF, 0xD2, 0x81, 0xD2, 0x8B, 0xD2, 0x8D, 0xD2,
+ 0x8F, 0xD2, 0x91, 0xD2, 0x93, 0xD2, 0x95, 0xD2,
+ 0x97, 0xD2, 0x99, 0xD2, 0x9B, 0xD2, 0x9D, 0xD2,
+ 0x9F, 0xD2, 0xA1, 0xD2, 0xA3, 0xD2, 0xA5, 0xD2,
+ 0xA7, 0xD2, 0xA9, 0xD2, 0xAB, 0xD2, 0xAD, 0xD2,
+ 0xAF, 0xD2, 0xB1, 0xD2, 0xB3, 0xD2, 0xB5, 0xD2,
+ 0xB7, 0xD2, 0xB9, 0xD2, 0xBB, 0xD2, 0xBD, 0xD2,
+ 0xBF, 0xD3, 0x8F, 0xD3, 0x82, 0xD3, 0x84, 0xD3,
+ 0x86, 0xD3, 0x88, 0xD3, 0x8A, 0xD3, 0x8C, 0xD3,
+ 0x8E, 0xD3, 0x91, 0xD3, 0x93, 0xD3, 0x95, 0xD3,
+ 0x97, 0xD3, 0x99, 0xD3, 0x9B, 0xD3, 0x9D, 0xD3,
+ 0x9F, 0xD3, 0xA1, 0xD3, 0xA3, 0xD3, 0xA5, 0xD3,
+ 0xA7, 0xD3, 0xA9, 0xD3, 0xAB, 0xD3, 0xAD, 0xD3,
+ 0xAF, 0xD3, 0xB1, 0xD3, 0xB3, 0xD3, 0xB5, 0xD3,
+ 0xB7, 0xD3, 0xB9, 0xD3, 0xBB, 0xD3, 0xBD, 0xD3,
+ 0xBF, 0xD4, 0x81, 0xD4, 0x83, 0xD4, 0x85, 0xD4,
+ 0x87, 0xD4, 0x89, 0xD4, 0x8B, 0xD4, 0x8D, 0xD4,
+ 0x8F, 0xD4, 0x91, 0xD4, 0x93, 0xD5, 0xA1, 0xD5,
+ 0xA2, 0xD5, 0xA3, 0xD5, 0xA4, 0xD5, 0xA5, 0xD5,
+ 0xA6, 0xD5, 0xA7, 0xD5, 0xA8, 0xD5, 0xA9, 0xD5,
+ 0xAA, 0xD5, 0xAB, 0xD5, 0xAC, 0xD5, 0xAD, 0xD5,
+ 0xAE, 0xD5, 0xAF, 0xD5, 0xB0, 0xD5, 0xB1, 0xD5,
+ 0xB2, 0xD5, 0xB3, 0xD5, 0xB4, 0xD5, 0xB5, 0xD5,
+ 0xB6, 0xD5, 0xB7, 0xD5, 0xB8, 0xD5, 0xB9, 0xD5,
+ 0xBA, 0xD5, 0xBB, 0xD5, 0xBC, 0xD5, 0xBD, 0xD5,
+ 0xBE, 0xD5, 0xBF, 0xD6, 0x80, 0xD6, 0x81, 0xD6,
+ 0x82, 0xD6, 0x83, 0xD6, 0x84, 0xD6, 0x85, 0xD6,
+ 0x86, 0xE2, 0xB4, 0x80, 0xE2, 0xB4, 0x81, 0xE2,
+ 0xB4, 0x82, 0xE2, 0xB4, 0x83, 0xE2, 0xB4, 0x84,
+ 0xE2, 0xB4, 0x85, 0xE2, 0xB4, 0x86, 0xE2, 0xB4,
+ 0x87, 0xE2, 0xB4, 0x88, 0xE2, 0xB4, 0x89, 0xE2,
+ 0xB4, 0x8A, 0xE2, 0xB4, 0x8B, 0xE2, 0xB4, 0x8C,
+ 0xE2, 0xB4, 0x8D, 0xE2, 0xB4, 0x8E, 0xE2, 0xB4,
+ 0x8F, 0xE2, 0xB4, 0x90, 0xE2, 0xB4, 0x91, 0xE2,
+ 0xB4, 0x92, 0xE2, 0xB4, 0x93, 0xE2, 0xB4, 0x94,
+ 0xE2, 0xB4, 0x95, 0xE2, 0xB4, 0x96, 0xE2, 0xB4,
+ 0x97, 0xE2, 0xB4, 0x98, 0xE2, 0xB4, 0x99, 0xE2,
+ 0xB4, 0x9A, 0xE2, 0xB4, 0x9B, 0xE2, 0xB4, 0x9C,
+ 0xE2, 0xB4, 0x9D, 0xE2, 0xB4, 0x9E, 0xE2, 0xB4,
+ 0x9F, 0xE2, 0xB4, 0xA0, 0xE2, 0xB4, 0xA1, 0xE2,
+ 0xB4, 0xA2, 0xE2, 0xB4, 0xA3, 0xE2, 0xB4, 0xA4,
+ 0xE2, 0xB4, 0xA5, 0xE1, 0xB8, 0x81, 0xE1, 0xB8,
+ 0x83, 0xE1, 0xB8, 0x85, 0xE1, 0xB8, 0x87, 0xE1,
+ 0xB8, 0x89, 0xE1, 0xB8, 0x8B, 0xE1, 0xB8, 0x8D,
+ 0xE1, 0xB8, 0x8F, 0xE1, 0xB8, 0x91, 0xE1, 0xB8,
+ 0x93, 0xE1, 0xB8, 0x95, 0xE1, 0xB8, 0x97, 0xE1,
+ 0xB8, 0x99, 0xE1, 0xB8, 0x9B, 0xE1, 0xB8, 0x9D,
+ 0xE1, 0xB8, 0x9F, 0xE1, 0xB8, 0xA1, 0xE1, 0xB8,
+ 0xA3, 0xE1, 0xB8, 0xA5, 0xE1, 0xB8, 0xA7, 0xE1,
+ 0xB8, 0xA9, 0xE1, 0xB8, 0xAB, 0xE1, 0xB8, 0xAD,
+ 0xE1, 0xB8, 0xAF, 0xE1, 0xB8, 0xB1, 0xE1, 0xB8,
+ 0xB3, 0xE1, 0xB8, 0xB5, 0xE1, 0xB8, 0xB7, 0xE1,
+ 0xB8, 0xB9, 0xE1, 0xB8, 0xBB, 0xE1, 0xB8, 0xBD,
+ 0xE1, 0xB8, 0xBF, 0xE1, 0xB9, 0x81, 0xE1, 0xB9,
+ 0x83, 0xE1, 0xB9, 0x85, 0xE1, 0xB9, 0x87, 0xE1,
+ 0xB9, 0x89, 0xE1, 0xB9, 0x8B, 0xE1, 0xB9, 0x8D,
+ 0xE1, 0xB9, 0x8F, 0xE1, 0xB9, 0x91, 0xE1, 0xB9,
+ 0x93, 0xE1, 0xB9, 0x95, 0xE1, 0xB9, 0x97, 0xE1,
+ 0xB9, 0x99, 0xE1, 0xB9, 0x9B, 0xE1, 0xB9, 0x9D,
+ 0xE1, 0xB9, 0x9F, 0xE1, 0xB9, 0xA1, 0xE1, 0xB9,
+ 0xA3, 0xE1, 0xB9, 0xA5, 0xE1, 0xB9, 0xA7, 0xE1,
+ 0xB9, 0xA9, 0xE1, 0xB9, 0xAB, 0xE1, 0xB9, 0xAD,
+ 0xE1, 0xB9, 0xAF, 0xE1, 0xB9, 0xB1, 0xE1, 0xB9,
+ 0xB3, 0xE1, 0xB9, 0xB5, 0xE1, 0xB9, 0xB7, 0xE1,
+ 0xB9, 0xB9, 0xE1, 0xB9, 0xBB, 0xE1, 0xB9, 0xBD,
+ 0xE1, 0xB9, 0xBF, 0xE1, 0xBA, 0x81, 0xE1, 0xBA,
+ 0x83, 0xE1, 0xBA, 0x85, 0xE1, 0xBA, 0x87, 0xE1,
+ 0xBA, 0x89, 0xE1, 0xBA, 0x8B, 0xE1, 0xBA, 0x8D,
+ 0xE1, 0xBA, 0x8F, 0xE1, 0xBA, 0x91, 0xE1, 0xBA,
+ 0x93, 0xE1, 0xBA, 0x95, 0xE1, 0xBA, 0xA1, 0xE1,
+ 0xBA, 0xA3, 0xE1, 0xBA, 0xA5, 0xE1, 0xBA, 0xA7,
+ 0xE1, 0xBA, 0xA9, 0xE1, 0xBA, 0xAB, 0xE1, 0xBA,
+ 0xAD, 0xE1, 0xBA, 0xAF, 0xE1, 0xBA, 0xB1, 0xE1,
+ 0xBA, 0xB3, 0xE1, 0xBA, 0xB5, 0xE1, 0xBA, 0xB7,
+ 0xE1, 0xBA, 0xB9, 0xE1, 0xBA, 0xBB, 0xE1, 0xBA,
+ 0xBD, 0xE1, 0xBA, 0xBF, 0xE1, 0xBB, 0x81, 0xE1,
+ 0xBB, 0x83, 0xE1, 0xBB, 0x85, 0xE1, 0xBB, 0x87,
+ 0xE1, 0xBB, 0x89, 0xE1, 0xBB, 0x8B, 0xE1, 0xBB,
+ 0x8D, 0xE1, 0xBB, 0x8F, 0xE1, 0xBB, 0x91, 0xE1,
+ 0xBB, 0x93, 0xE1, 0xBB, 0x95, 0xE1, 0xBB, 0x97,
+ 0xE1, 0xBB, 0x99, 0xE1, 0xBB, 0x9B, 0xE1, 0xBB,
+ 0x9D, 0xE1, 0xBB, 0x9F, 0xE1, 0xBB, 0xA1, 0xE1,
+ 0xBB, 0xA3, 0xE1, 0xBB, 0xA5, 0xE1, 0xBB, 0xA7,
+ 0xE1, 0xBB, 0xA9, 0xE1, 0xBB, 0xAB, 0xE1, 0xBB,
+ 0xAD, 0xE1, 0xBB, 0xAF, 0xE1, 0xBB, 0xB1, 0xE1,
+ 0xBB, 0xB3, 0xE1, 0xBB, 0xB5, 0xE1, 0xBB, 0xB7,
+ 0xE1, 0xBB, 0xB9, 0xE1, 0xBC, 0x80, 0xE1, 0xBC,
+ 0x81, 0xE1, 0xBC, 0x82, 0xE1, 0xBC, 0x83, 0xE1,
+ 0xBC, 0x84, 0xE1, 0xBC, 0x85, 0xE1, 0xBC, 0x86,
+ 0xE1, 0xBC, 0x87, 0xE1, 0xBC, 0x90, 0xE1, 0xBC,
+ 0x91, 0xE1, 0xBC, 0x92, 0xE1, 0xBC, 0x93, 0xE1,
+ 0xBC, 0x94, 0xE1, 0xBC, 0x95, 0xE1, 0xBC, 0xA0,
+ 0xE1, 0xBC, 0xA1, 0xE1, 0xBC, 0xA2, 0xE1, 0xBC,
+ 0xA3, 0xE1, 0xBC, 0xA4, 0xE1, 0xBC, 0xA5, 0xE1,
+ 0xBC, 0xA6, 0xE1, 0xBC, 0xA7, 0xE1, 0xBC, 0xB0,
+ 0xE1, 0xBC, 0xB1, 0xE1, 0xBC, 0xB2, 0xE1, 0xBC,
+ 0xB3, 0xE1, 0xBC, 0xB4, 0xE1, 0xBC, 0xB5, 0xE1,
+ 0xBC, 0xB6, 0xE1, 0xBC, 0xB7, 0xE1, 0xBD, 0x80,
+ 0xE1, 0xBD, 0x81, 0xE1, 0xBD, 0x82, 0xE1, 0xBD,
+ 0x83, 0xE1, 0xBD, 0x84, 0xE1, 0xBD, 0x85, 0xE1,
+ 0xBD, 0x91, 0xE1, 0xBD, 0x93, 0xE1, 0xBD, 0x95,
+ 0xE1, 0xBD, 0x97, 0xE1, 0xBD, 0xA0, 0xE1, 0xBD,
+ 0xA1, 0xE1, 0xBD, 0xA2, 0xE1, 0xBD, 0xA3, 0xE1,
+ 0xBD, 0xA4, 0xE1, 0xBD, 0xA5, 0xE1, 0xBD, 0xA6,
+ 0xE1, 0xBD, 0xA7, 0xE1, 0xBE, 0x80, 0xE1, 0xBE,
+ 0x81, 0xE1, 0xBE, 0x82, 0xE1, 0xBE, 0x83, 0xE1,
+ 0xBE, 0x84, 0xE1, 0xBE, 0x85, 0xE1, 0xBE, 0x86,
+ 0xE1, 0xBE, 0x87, 0xE1, 0xBE, 0x90, 0xE1, 0xBE,
+ 0x91, 0xE1, 0xBE, 0x92, 0xE1, 0xBE, 0x93, 0xE1,
+ 0xBE, 0x94, 0xE1, 0xBE, 0x95, 0xE1, 0xBE, 0x96,
+ 0xE1, 0xBE, 0x97, 0xE1, 0xBE, 0xA0, 0xE1, 0xBE,
+ 0xA1, 0xE1, 0xBE, 0xA2, 0xE1, 0xBE, 0xA3, 0xE1,
+ 0xBE, 0xA4, 0xE1, 0xBE, 0xA5, 0xE1, 0xBE, 0xA6,
+ 0xE1, 0xBE, 0xA7, 0xE1, 0xBE, 0xB0, 0xE1, 0xBE,
+ 0xB1, 0xE1, 0xBD, 0xB0, 0xE1, 0xBD, 0xB1, 0xE1,
+ 0xBE, 0xB3, 0xE1, 0xBD, 0xB2, 0xE1, 0xBD, 0xB3,
+ 0xE1, 0xBD, 0xB4, 0xE1, 0xBD, 0xB5, 0xE1, 0xBF,
+ 0x83, 0xE1, 0xBF, 0x90, 0xE1, 0xBF, 0x91, 0xE1,
+ 0xBD, 0xB6, 0xE1, 0xBD, 0xB7, 0xE1, 0xBF, 0xA0,
+ 0xE1, 0xBF, 0xA1, 0xE1, 0xBD, 0xBA, 0xE1, 0xBD,
+ 0xBB, 0xE1, 0xBF, 0xA5, 0xE1, 0xBD, 0xB8, 0xE1,
+ 0xBD, 0xB9, 0xE1, 0xBD, 0xBC, 0xE1, 0xBD, 0xBD,
+ 0xE1, 0xBF, 0xB3, 0xCF, 0x89, 0x6B, 0xC3, 0xA5,
+ 0xE2, 0x85, 0x8E, 0xE2, 0x85, 0xB0, 0xE2, 0x85,
+ 0xB1, 0xE2, 0x85, 0xB2, 0xE2, 0x85, 0xB3, 0xE2,
+ 0x85, 0xB4, 0xE2, 0x85, 0xB5, 0xE2, 0x85, 0xB6,
+ 0xE2, 0x85, 0xB7, 0xE2, 0x85, 0xB8, 0xE2, 0x85,
+ 0xB9, 0xE2, 0x85, 0xBA, 0xE2, 0x85, 0xBB, 0xE2,
+ 0x85, 0xBC, 0xE2, 0x85, 0xBD, 0xE2, 0x85, 0xBE,
+ 0xE2, 0x85, 0xBF, 0xE2, 0x86, 0x84, 0xE2, 0x93,
+ 0x90, 0xE2, 0x93, 0x91, 0xE2, 0x93, 0x92, 0xE2,
+ 0x93, 0x93, 0xE2, 0x93, 0x94, 0xE2, 0x93, 0x95,
+ 0xE2, 0x93, 0x96, 0xE2, 0x93, 0x97, 0xE2, 0x93,
+ 0x98, 0xE2, 0x93, 0x99, 0xE2, 0x93, 0x9A, 0xE2,
+ 0x93, 0x9B, 0xE2, 0x93, 0x9C, 0xE2, 0x93, 0x9D,
+ 0xE2, 0x93, 0x9E, 0xE2, 0x93, 0x9F, 0xE2, 0x93,
+ 0xA0, 0xE2, 0x93, 0xA1, 0xE2, 0x93, 0xA2, 0xE2,
+ 0x93, 0xA3, 0xE2, 0x93, 0xA4, 0xE2, 0x93, 0xA5,
+ 0xE2, 0x93, 0xA6, 0xE2, 0x93, 0xA7, 0xE2, 0x93,
+ 0xA8, 0xE2, 0x93, 0xA9, 0xE2, 0xB0, 0xB0, 0xE2,
+ 0xB0, 0xB1, 0xE2, 0xB0, 0xB2, 0xE2, 0xB0, 0xB3,
+ 0xE2, 0xB0, 0xB4, 0xE2, 0xB0, 0xB5, 0xE2, 0xB0,
+ 0xB6, 0xE2, 0xB0, 0xB7, 0xE2, 0xB0, 0xB8, 0xE2,
+ 0xB0, 0xB9, 0xE2, 0xB0, 0xBA, 0xE2, 0xB0, 0xBB,
+ 0xE2, 0xB0, 0xBC, 0xE2, 0xB0, 0xBD, 0xE2, 0xB0,
+ 0xBE, 0xE2, 0xB0, 0xBF, 0xE2, 0xB1, 0x80, 0xE2,
+ 0xB1, 0x81, 0xE2, 0xB1, 0x82, 0xE2, 0xB1, 0x83,
+ 0xE2, 0xB1, 0x84, 0xE2, 0xB1, 0x85, 0xE2, 0xB1,
+ 0x86, 0xE2, 0xB1, 0x87, 0xE2, 0xB1, 0x88, 0xE2,
+ 0xB1, 0x89, 0xE2, 0xB1, 0x8A, 0xE2, 0xB1, 0x8B,
+ 0xE2, 0xB1, 0x8C, 0xE2, 0xB1, 0x8D, 0xE2, 0xB1,
+ 0x8E, 0xE2, 0xB1, 0x8F, 0xE2, 0xB1, 0x90, 0xE2,
+ 0xB1, 0x91, 0xE2, 0xB1, 0x92, 0xE2, 0xB1, 0x93,
+ 0xE2, 0xB1, 0x94, 0xE2, 0xB1, 0x95, 0xE2, 0xB1,
+ 0x96, 0xE2, 0xB1, 0x97, 0xE2, 0xB1, 0x98, 0xE2,
+ 0xB1, 0x99, 0xE2, 0xB1, 0x9A, 0xE2, 0xB1, 0x9B,
+ 0xE2, 0xB1, 0x9C, 0xE2, 0xB1, 0x9D, 0xE2, 0xB1,
+ 0x9E, 0xE2, 0xB1, 0xA1, 0xC9, 0xAB, 0xE1, 0xB5,
+ 0xBD, 0xC9, 0xBD, 0xE2, 0xB1, 0xA8, 0xE2, 0xB1,
+ 0xAA, 0xE2, 0xB1, 0xAC, 0xE2, 0xB1, 0xB6, 0xE2,
+ 0xB2, 0x81, 0xE2, 0xB2, 0x83, 0xE2, 0xB2, 0x85,
+ 0xE2, 0xB2, 0x87, 0xE2, 0xB2, 0x89, 0xE2, 0xB2,
+ 0x8B, 0xE2, 0xB2, 0x8D, 0xE2, 0xB2, 0x8F, 0xE2,
+ 0xB2, 0x91, 0xE2, 0xB2, 0x93, 0xE2, 0xB2, 0x95,
+ 0xE2, 0xB2, 0x97, 0xE2, 0xB2, 0x99, 0xE2, 0xB2,
+ 0x9B, 0xE2, 0xB2, 0x9D, 0xE2, 0xB2, 0x9F, 0xE2,
+ 0xB2, 0xA1, 0xE2, 0xB2, 0xA3, 0xE2, 0xB2, 0xA5,
+ 0xE2, 0xB2, 0xA7, 0xE2, 0xB2, 0xA9, 0xE2, 0xB2,
+ 0xAB, 0xE2, 0xB2, 0xAD, 0xE2, 0xB2, 0xAF, 0xE2,
+ 0xB2, 0xB1, 0xE2, 0xB2, 0xB3, 0xE2, 0xB2, 0xB5,
+ 0xE2, 0xB2, 0xB7, 0xE2, 0xB2, 0xB9, 0xE2, 0xB2,
+ 0xBB, 0xE2, 0xB2, 0xBD, 0xE2, 0xB2, 0xBF, 0xE2,
+ 0xB3, 0x81, 0xE2, 0xB3, 0x83, 0xE2, 0xB3, 0x85,
+ 0xE2, 0xB3, 0x87, 0xE2, 0xB3, 0x89, 0xE2, 0xB3,
+ 0x8B, 0xE2, 0xB3, 0x8D, 0xE2, 0xB3, 0x8F, 0xE2,
+ 0xB3, 0x91, 0xE2, 0xB3, 0x93, 0xE2, 0xB3, 0x95,
+ 0xE2, 0xB3, 0x97, 0xE2, 0xB3, 0x99, 0xE2, 0xB3,
+ 0x9B, 0xE2, 0xB3, 0x9D, 0xE2, 0xB3, 0x9F, 0xE2,
+ 0xB3, 0xA1, 0xE2, 0xB3, 0xA3, 0xEF, 0xBD, 0x81,
+ 0xEF, 0xBD, 0x82, 0xEF, 0xBD, 0x83, 0xEF, 0xBD,
+ 0x84, 0xEF, 0xBD, 0x85, 0xEF, 0xBD, 0x86, 0xEF,
+ 0xBD, 0x87, 0xEF, 0xBD, 0x88, 0xEF, 0xBD, 0x89,
+ 0xEF, 0xBD, 0x8A, 0xEF, 0xBD, 0x8B, 0xEF, 0xBD,
+ 0x8C, 0xEF, 0xBD, 0x8D, 0xEF, 0xBD, 0x8E, 0xEF,
+ 0xBD, 0x8F, 0xEF, 0xBD, 0x90, 0xEF, 0xBD, 0x91,
+ 0xEF, 0xBD, 0x92, 0xEF, 0xBD, 0x93, 0xEF, 0xBD,
+ 0x94, 0xEF, 0xBD, 0x95, 0xEF, 0xBD, 0x96, 0xEF,
+ 0xBD, 0x97, 0xEF, 0xBD, 0x98, 0xEF, 0xBD, 0x99,
+ 0xEF, 0xBD, 0x9A, 0xF0, 0x90, 0x90, 0xA8, 0xF0,
+ 0x90, 0x90, 0xA9, 0xF0, 0x90, 0x90, 0xAA, 0xF0,
+ 0x90, 0x90, 0xAB, 0xF0, 0x90, 0x90, 0xAC, 0xF0,
+ 0x90, 0x90, 0xAD, 0xF0, 0x90, 0x90, 0xAE, 0xF0,
+ 0x90, 0x90, 0xAF, 0xF0, 0x90, 0x90, 0xB0, 0xF0,
+ 0x90, 0x90, 0xB1, 0xF0, 0x90, 0x90, 0xB2, 0xF0,
+ 0x90, 0x90, 0xB3, 0xF0, 0x90, 0x90, 0xB4, 0xF0,
+ 0x90, 0x90, 0xB5, 0xF0, 0x90, 0x90, 0xB6, 0xF0,
+ 0x90, 0x90, 0xB7, 0xF0, 0x90, 0x90, 0xB8, 0xF0,
+ 0x90, 0x90, 0xB9, 0xF0, 0x90, 0x90, 0xBA, 0xF0,
+ 0x90, 0x90, 0xBB, 0xF0, 0x90, 0x90, 0xBC, 0xF0,
+ 0x90, 0x90, 0xBD, 0xF0, 0x90, 0x90, 0xBE, 0xF0,
+ 0x90, 0x90, 0xBF, 0xF0, 0x90, 0x91, 0x80, 0xF0,
+ 0x90, 0x91, 0x81, 0xF0, 0x90, 0x91, 0x82, 0xF0,
+ 0x90, 0x91, 0x83, 0xF0, 0x90, 0x91, 0x84, 0xF0,
+ 0x90, 0x91, 0x85, 0xF0, 0x90, 0x91, 0x86, 0xF0,
+ 0x90, 0x91, 0x87, 0xF0, 0x90, 0x91, 0x88, 0xF0,
+ 0x90, 0x91, 0x89, 0xF0, 0x90, 0x91, 0x8A, 0xF0,
+ 0x90, 0x91, 0x8B, 0xF0, 0x90, 0x91, 0x8C, 0xF0,
+ 0x90, 0x91, 0x8D, 0xF0, 0x90, 0x91, 0x8E, 0xF0,
+ 0x90, 0x91, 0x8F,
+ },
+};
+
+static const u8_displacement_t u8_toupper_b3_tbl[2][5][256] = {
+ {
+ { /* Third byte table 0. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 0, 0 }, { 1, 2 },
+ { 2, 64 }, { 3, 125 }, { 4, 188 }, { 5, 226 },
+ { 6, 288 }, { 7, 338 }, { 8, 364 }, { N_, 0 },
+ { N_, 0 }, { 9, 376 }, { 10, 378 }, { 11, 416 },
+ { 12, 486 }, { 13, 518 }, { 14, 614 }, { 15, 670 },
+ { 16, 724 }, { 17, 740 }, { 18, 802 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ },
+ { /* Third byte table 1. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 19, 816 }, { 20, 912 }, { 21, 1008 }, { 22, 1092 },
+ { 23, 1179 }, { 24, 1269 }, { 25, 1365 }, { 26, 1448 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ },
+ { /* Third byte table 2. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { 27, 1469 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { 28, 1517 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ },
+ { /* Third byte table 3. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { 29, 1595 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ },
+ { /* Third byte table 4. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 30, 1673 }, { 31, 1769 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ },
+ },
+ {
+ { /* Third byte table 0. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { 0, 0 }, { 1, 2 },
+ { 2, 64 }, { 3, 125 }, { 4, 188 }, { 5, 230 },
+ { 6, 292 }, { 7, 344 }, { 8, 388 }, { N_, 0 },
+ { N_, 0 }, { 9, 404 }, { 10, 412 }, { 11, 450 },
+ { 12, 524 }, { 13, 556 }, { 14, 652 }, { 15, 708 },
+ { 16, 772 }, { 17, 792 }, { 18, 854 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ },
+ { /* Third byte table 1. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { 19, 868 }, { N_, 0 }, { N_, 0 },
+ { 20, 871 }, { 21, 967 }, { 22, 1063 }, { 23, 1147 },
+ { 24, 1234 }, { 25, 1324 }, { 26, 1420 }, { 27, 1503 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ },
+ { /* Third byte table 2. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { 28, 1524 }, { 29, 1575 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { 30, 1578 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 31, 1656 }, { 32, 1704 }, { 33, 1816 }, { 34, 1912 },
+ { 35, 1966 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ },
+ { /* Third byte table 3. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { 36, 2080 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ },
+ { /* Third byte table 4. */
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { 37, 2158 }, { 38, 2254 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ { N_, 0 }, { N_, 0 }, { N_, 0 }, { N_, 0 },
+ },
+ },
+};
+
+static const uchar_t u8_toupper_b4_tbl[2][39][257] = {
+ {
+ { /* Fourth byte table 0. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2,
+ },
+ { /* Fourth byte table 1. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 46, 48, 50, 52, 54, 56, 58, 60,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62,
+ },
+ { /* Fourth byte table 2. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 2, 4, 4, 6, 6,
+ 8, 8, 10, 10, 12, 12, 14, 14,
+ 16, 16, 18, 18, 20, 20, 22, 22,
+ 24, 24, 26, 26, 28, 28, 30, 30,
+ 32, 32, 34, 34, 36, 36, 38, 38,
+ 40, 40, 42, 42, 44, 44, 46, 46,
+ 48, 48, 49, 49, 51, 51, 53, 53,
+ 55, 55, 55, 57, 57, 59, 59, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61,
+ 61,
+ },
+ { /* Fourth byte table 3. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 2, 4, 4, 6, 6, 8,
+ 8, 10, 10, 10, 12, 12, 14, 14,
+ 16, 16, 18, 18, 20, 20, 22, 22,
+ 24, 24, 26, 26, 28, 28, 30, 30,
+ 32, 32, 34, 34, 36, 36, 38, 38,
+ 40, 40, 42, 42, 44, 44, 46, 46,
+ 48, 48, 50, 50, 52, 52, 54, 54,
+ 56, 56, 56, 58, 58, 60, 60, 62,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63,
+ },
+ { /* Fourth byte table 4. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 2, 4, 4,
+ 4, 6, 6, 6, 6, 8, 8, 8,
+ 8, 8, 8, 10, 10, 10, 12, 12,
+ 12, 12, 14, 14, 14, 14, 14, 16,
+ 16, 16, 18, 18, 20, 20, 22, 22,
+ 22, 24, 24, 24, 24, 24, 26, 26,
+ 26, 28, 28, 28, 28, 30, 30, 32,
+ 32, 32, 34, 34, 34, 34, 36, 36,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38,
+ },
+ { /* Fourth byte table 5. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 2, 4,
+ 4, 6, 8, 8, 10, 12, 12, 14,
+ 14, 16, 16, 18, 18, 20, 20, 22,
+ 22, 24, 24, 26, 26, 28, 30, 30,
+ 32, 32, 34, 34, 36, 36, 38, 38,
+ 40, 40, 42, 42, 44, 44, 46, 46,
+ 48, 48, 48, 50, 52, 52, 54, 54,
+ 54, 54, 56, 56, 58, 58, 60, 60,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62,
+ },
+ { /* Fourth byte table 6. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 2, 4, 4, 6, 6,
+ 8, 8, 10, 10, 12, 12, 14, 14,
+ 16, 16, 18, 18, 20, 20, 22, 22,
+ 24, 24, 26, 26, 28, 28, 30, 30,
+ 32, 32, 32, 32, 34, 34, 36, 36,
+ 38, 38, 40, 40, 42, 42, 44, 44,
+ 46, 46, 48, 48, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50,
+ 50,
+ },
+ { /* Fourth byte table 7. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 4, 4, 6,
+ 8, 8, 10, 10, 12, 12, 12, 12,
+ 12, 14, 14, 14, 16, 16, 16, 16,
+ 16, 18, 20, 20, 20, 20, 20, 20,
+ 22, 22, 22, 24, 24, 24, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 26,
+ },
+ { /* Fourth byte table 8. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 2, 2, 4, 4, 4, 4,
+ 4, 6, 6, 8, 10, 10, 10, 10,
+ 10, 10, 10, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 12,
+ },
+ { /* Fourth byte table 9. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2,
+ },
+ { /* Fourth byte table 10. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 2, 4, 6,
+ 8, 8, 10, 12, 14, 16, 18, 20,
+ 22, 24, 26, 28, 30, 32, 34, 36,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38,
+ },
+ { /* Fourth byte table 11. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 30, 32, 34, 34, 34, 34, 36, 38,
+ 38, 38, 40, 40, 42, 42, 44, 44,
+ 46, 46, 48, 48, 50, 50, 52, 52,
+ 54, 54, 56, 56, 58, 58, 60, 60,
+ 62, 64, 66, 68, 68, 68, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 70,
+ },
+ { /* Fourth byte table 12. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32,
+ },
+ { /* Fourth byte table 13. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 48, 50, 52, 54, 56, 58, 60, 62,
+ 64, 64, 66, 66, 68, 68, 70, 70,
+ 72, 72, 74, 74, 76, 76, 78, 78,
+ 80, 80, 82, 82, 84, 84, 86, 86,
+ 88, 88, 90, 90, 92, 92, 94, 94,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96,
+ },
+ { /* Fourth byte table 14. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 4, 4, 6, 6,
+ 8, 8, 10, 10, 12, 12, 14, 14,
+ 16, 16, 18, 18, 20, 20, 22, 22,
+ 24, 24, 26, 26, 28, 28, 30, 30,
+ 32, 32, 34, 34, 36, 36, 38, 38,
+ 40, 40, 42, 42, 44, 44, 46, 46,
+ 48, 48, 50, 50, 52, 52, 54, 54,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56,
+ },
+ { /* Fourth byte table 15. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 2, 2, 4, 4, 6,
+ 6, 8, 8, 10, 10, 12, 12, 14,
+ 14, 14, 16, 16, 18, 18, 20, 20,
+ 22, 22, 24, 24, 26, 26, 28, 28,
+ 30, 30, 32, 32, 34, 34, 36, 36,
+ 38, 38, 40, 40, 42, 42, 44, 44,
+ 46, 46, 48, 48, 50, 50, 52, 52,
+ 52, 52, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54,
+ },
+ { /* Fourth byte table 16. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 2, 4, 4, 6, 6,
+ 8, 8, 10, 10, 12, 12, 14, 14,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16,
+ },
+ { /* Fourth byte table 17. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 4, 6, 8, 10, 12,
+ 14, 16, 18, 20, 22, 24, 26, 28,
+ 30, 32, 34, 36, 38, 40, 42, 44,
+ 46, 48, 50, 52, 54, 56, 58, 60,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62,
+ },
+ { /* Fourth byte table 18. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14,
+ },
+ { /* Fourth byte table 19. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 3, 6, 6, 9, 9,
+ 12, 12, 15, 15, 18, 18, 21, 21,
+ 24, 24, 27, 27, 30, 30, 33, 33,
+ 36, 36, 39, 39, 42, 42, 45, 45,
+ 48, 48, 51, 51, 54, 54, 57, 57,
+ 60, 60, 63, 63, 66, 66, 69, 69,
+ 72, 72, 75, 75, 78, 78, 81, 81,
+ 84, 84, 87, 87, 90, 90, 93, 93,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96,
+ },
+ { /* Fourth byte table 20. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 3, 6, 6, 9, 9,
+ 12, 12, 15, 15, 18, 18, 21, 21,
+ 24, 24, 27, 27, 30, 30, 33, 33,
+ 36, 36, 39, 39, 42, 42, 45, 45,
+ 48, 48, 51, 51, 54, 54, 57, 57,
+ 60, 60, 63, 63, 66, 66, 69, 69,
+ 72, 72, 75, 75, 78, 78, 81, 81,
+ 84, 84, 87, 87, 90, 90, 93, 93,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96,
+ },
+ { /* Fourth byte table 21. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 3, 6, 6, 9, 9,
+ 12, 12, 15, 15, 18, 18, 21, 21,
+ 24, 24, 27, 27, 30, 30, 33, 33,
+ 33, 33, 33, 33, 36, 36, 36, 36,
+ 36, 36, 39, 39, 42, 42, 45, 45,
+ 48, 48, 51, 51, 54, 54, 57, 57,
+ 60, 60, 63, 63, 66, 66, 69, 69,
+ 72, 72, 75, 75, 78, 78, 81, 81,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84,
+ },
+ { /* Fourth byte table 22. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 3, 6, 6, 9, 9,
+ 12, 12, 15, 15, 18, 18, 21, 21,
+ 24, 24, 27, 27, 30, 30, 33, 33,
+ 36, 36, 39, 39, 42, 42, 45, 45,
+ 48, 48, 51, 51, 54, 54, 57, 57,
+ 60, 60, 63, 63, 66, 66, 69, 69,
+ 72, 72, 75, 75, 78, 78, 81, 81,
+ 84, 84, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87,
+ },
+ { /* Fourth byte table 23. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 27, 30, 33, 36, 39, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 45, 48, 51, 54, 57, 60, 63,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 69, 72, 75, 78, 81, 84, 87,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90,
+ },
+ { /* Fourth byte table 24. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 21, 21, 24, 24, 27, 27,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 33, 36, 39, 42, 45, 48, 51,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 57, 60, 63, 66, 69, 72, 75,
+ 78, 81, 84, 87, 90, 93, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96,
+ },
+ { /* Fourth byte table 25. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 51, 54, 57, 60, 63, 66, 69,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 75, 78, 78, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83,
+ },
+ { /* Fourth byte table 26. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 6, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 12, 15, 15, 15, 15, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21,
+ },
+ { /* Fourth byte table 27. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48,
+ },
+ { /* Fourth byte table 28. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 60, 63, 66, 69,
+ 72, 75, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78,
+ },
+ { /* Fourth byte table 29. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 6, 9, 12, 15, 18,
+ 21, 24, 27, 30, 33, 36, 39, 42,
+ 45, 48, 51, 54, 57, 60, 63, 66,
+ 69, 72, 75, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78,
+ },
+ { /* Fourth byte table 30. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96,
+ },
+ { /* Fourth byte table 31. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56,
+ },
+ { /* Fourth byte table 32. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ { /* Fourth byte table 33. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ { /* Fourth byte table 34. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ { /* Fourth byte table 35. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ { /* Fourth byte table 36. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ { /* Fourth byte table 37. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ { /* Fourth byte table 38. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ },
+ },
+ {
+ { /* Fourth byte table 0. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2,
+ },
+ { /* Fourth byte table 1. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 46, 48, 50, 52, 54, 56, 58, 60,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62,
+ },
+ { /* Fourth byte table 2. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 2, 4, 4, 6, 6,
+ 8, 8, 10, 10, 12, 12, 14, 14,
+ 16, 16, 18, 18, 20, 20, 22, 22,
+ 24, 24, 26, 26, 28, 28, 30, 30,
+ 32, 32, 34, 34, 36, 36, 38, 38,
+ 40, 40, 42, 42, 44, 44, 46, 46,
+ 48, 48, 49, 49, 51, 51, 53, 53,
+ 55, 55, 55, 57, 57, 59, 59, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61,
+ 61,
+ },
+ { /* Fourth byte table 3. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 2, 4, 4, 6, 6, 8,
+ 8, 10, 10, 10, 12, 12, 14, 14,
+ 16, 16, 18, 18, 20, 20, 22, 22,
+ 24, 24, 26, 26, 28, 28, 30, 30,
+ 32, 32, 34, 34, 36, 36, 38, 38,
+ 40, 40, 42, 42, 44, 44, 46, 46,
+ 48, 48, 50, 50, 52, 52, 54, 54,
+ 56, 56, 56, 58, 58, 60, 60, 62,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63,
+ },
+ { /* Fourth byte table 4. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 2, 2, 4, 4, 6, 6,
+ 6, 8, 8, 8, 8, 10, 10, 10,
+ 10, 10, 10, 12, 12, 12, 14, 14,
+ 14, 14, 16, 18, 18, 18, 18, 20,
+ 20, 20, 22, 22, 24, 24, 26, 26,
+ 26, 28, 28, 28, 28, 28, 30, 30,
+ 30, 32, 32, 32, 32, 34, 34, 36,
+ 36, 36, 38, 38, 38, 38, 40, 40,
+ 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42,
+ 42,
+ },
+ { /* Fourth byte table 5. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 2, 4,
+ 4, 6, 8, 8, 10, 12, 12, 14,
+ 14, 16, 16, 18, 18, 20, 20, 22,
+ 22, 24, 24, 26, 26, 28, 30, 30,
+ 32, 32, 34, 34, 36, 36, 38, 38,
+ 40, 40, 42, 42, 44, 44, 46, 46,
+ 48, 48, 48, 50, 52, 52, 54, 54,
+ 54, 54, 56, 56, 58, 58, 60, 60,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62,
+ },
+ { /* Fourth byte table 6. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 2, 4, 4, 6, 6,
+ 8, 8, 10, 10, 12, 12, 14, 14,
+ 16, 16, 18, 18, 20, 20, 22, 22,
+ 24, 24, 26, 26, 28, 28, 30, 30,
+ 32, 32, 32, 32, 34, 34, 36, 36,
+ 38, 38, 40, 40, 42, 42, 44, 44,
+ 46, 46, 48, 48, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 52,
+ },
+ { /* Fourth byte table 7. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 2, 2, 2, 2, 2,
+ 4, 4, 6, 6, 8, 8, 10, 10,
+ 12, 12, 12, 12, 14, 16, 16, 18,
+ 20, 20, 22, 22, 24, 24, 24, 24,
+ 24, 26, 26, 26, 28, 28, 28, 28,
+ 28, 30, 32, 32, 35, 35, 35, 35,
+ 37, 37, 37, 39, 39, 39, 41, 41,
+ 41, 41, 41, 41, 41, 41, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 44,
+ },
+ { /* Fourth byte table 8. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 2, 2, 4, 4, 4, 4,
+ 4, 6, 8, 10, 12, 14, 14, 14,
+ 14, 14, 14, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16,
+ },
+ { /* Fourth byte table 9. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 4, 6, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8,
+ },
+ { /* Fourth byte table 10. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 2, 4, 6,
+ 8, 8, 10, 12, 14, 16, 18, 20,
+ 22, 24, 26, 28, 30, 32, 34, 36,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38,
+ },
+ { /* Fourth byte table 11. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 30, 32, 34, 34, 34, 34, 36, 38,
+ 38, 38, 40, 40, 42, 42, 44, 44,
+ 46, 46, 48, 48, 50, 50, 52, 52,
+ 54, 54, 56, 56, 58, 58, 60, 60,
+ 62, 64, 66, 68, 68, 68, 70, 70,
+ 70, 72, 72, 72, 74, 74, 74, 74,
+ 74, 74, 74, 74, 74, 74, 74, 74,
+ 74, 74, 74, 74, 74, 74, 74, 74,
+ 74, 74, 74, 74, 74, 74, 74, 74,
+ 74, 74, 74, 74, 74, 74, 74, 74,
+ 74, 74, 74, 74, 74, 74, 74, 74,
+ 74, 74, 74, 74, 74, 74, 74, 74,
+ 74, 74, 74, 74, 74, 74, 74, 74,
+ 74, 74, 74, 74, 74, 74, 74, 74,
+ 74,
+ },
+ { /* Fourth byte table 12. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32,
+ },
+ { /* Fourth byte table 13. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 48, 50, 52, 54, 56, 58, 60, 62,
+ 64, 64, 66, 66, 68, 68, 70, 70,
+ 72, 72, 74, 74, 76, 76, 78, 78,
+ 80, 80, 82, 82, 84, 84, 86, 86,
+ 88, 88, 90, 90, 92, 92, 94, 94,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96,
+ },
+ { /* Fourth byte table 14. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 4, 4, 6, 6,
+ 8, 8, 10, 10, 12, 12, 14, 14,
+ 16, 16, 18, 18, 20, 20, 22, 22,
+ 24, 24, 26, 26, 28, 28, 30, 30,
+ 32, 32, 34, 34, 36, 36, 38, 38,
+ 40, 40, 42, 42, 44, 44, 46, 46,
+ 48, 48, 50, 50, 52, 52, 54, 54,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56,
+ 56,
+ },
+ { /* Fourth byte table 15. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 2, 2, 4, 4, 6,
+ 6, 8, 8, 10, 10, 12, 12, 14,
+ 16, 16, 18, 18, 20, 20, 22, 22,
+ 24, 24, 26, 26, 28, 28, 30, 30,
+ 32, 32, 34, 34, 36, 36, 38, 38,
+ 40, 40, 42, 42, 44, 44, 46, 46,
+ 48, 48, 50, 50, 52, 52, 54, 54,
+ 56, 56, 58, 58, 60, 60, 62, 62,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64,
+ },
+ { /* Fourth byte table 16. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 2, 4, 4, 6, 6,
+ 8, 8, 10, 10, 12, 12, 14, 14,
+ 16, 16, 18, 18, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20,
+ 20,
+ },
+ { /* Fourth byte table 17. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 4, 6, 8, 10, 12,
+ 14, 16, 18, 20, 22, 24, 26, 28,
+ 30, 32, 34, 36, 38, 40, 42, 44,
+ 46, 48, 50, 52, 54, 56, 58, 60,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62,
+ 62,
+ },
+ { /* Fourth byte table 18. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14,
+ },
+ { /* Fourth byte table 19. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3,
+ },
+ { /* Fourth byte table 20. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 3, 6, 6, 9, 9,
+ 12, 12, 15, 15, 18, 18, 21, 21,
+ 24, 24, 27, 27, 30, 30, 33, 33,
+ 36, 36, 39, 39, 42, 42, 45, 45,
+ 48, 48, 51, 51, 54, 54, 57, 57,
+ 60, 60, 63, 63, 66, 66, 69, 69,
+ 72, 72, 75, 75, 78, 78, 81, 81,
+ 84, 84, 87, 87, 90, 90, 93, 93,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96,
+ },
+ { /* Fourth byte table 21. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 3, 6, 6, 9, 9,
+ 12, 12, 15, 15, 18, 18, 21, 21,
+ 24, 24, 27, 27, 30, 30, 33, 33,
+ 36, 36, 39, 39, 42, 42, 45, 45,
+ 48, 48, 51, 51, 54, 54, 57, 57,
+ 60, 60, 63, 63, 66, 66, 69, 69,
+ 72, 72, 75, 75, 78, 78, 81, 81,
+ 84, 84, 87, 87, 90, 90, 93, 93,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96,
+ },
+ { /* Fourth byte table 22. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 3, 6, 6, 9, 9,
+ 12, 12, 15, 15, 18, 18, 21, 21,
+ 24, 24, 27, 27, 30, 30, 33, 33,
+ 33, 33, 33, 33, 36, 36, 36, 36,
+ 36, 36, 39, 39, 42, 42, 45, 45,
+ 48, 48, 51, 51, 54, 54, 57, 57,
+ 60, 60, 63, 63, 66, 66, 69, 69,
+ 72, 72, 75, 75, 78, 78, 81, 81,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84,
+ 84,
+ },
+ { /* Fourth byte table 23. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 3, 6, 6, 9, 9,
+ 12, 12, 15, 15, 18, 18, 21, 21,
+ 24, 24, 27, 27, 30, 30, 33, 33,
+ 36, 36, 39, 39, 42, 42, 45, 45,
+ 48, 48, 51, 51, 54, 54, 57, 57,
+ 60, 60, 63, 63, 66, 66, 69, 69,
+ 72, 72, 75, 75, 78, 78, 81, 81,
+ 84, 84, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87,
+ 87,
+ },
+ { /* Fourth byte table 24. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 27, 30, 33, 36, 39, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 45, 48, 51, 54, 57, 60, 63,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 69, 72, 75, 78, 81, 84, 87,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 90, 90, 90,
+ 90,
+ },
+ { /* Fourth byte table 25. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 21, 21, 24, 24, 27, 27,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 33, 36, 39, 42, 45, 48, 51,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 57, 60, 63, 66, 69, 72, 75,
+ 78, 81, 84, 87, 90, 93, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96,
+ },
+ { /* Fourth byte table 26. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 51, 54, 57, 60, 63, 66, 69,
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 75, 78, 78, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 83, 83, 83, 83,
+ 83,
+ },
+ { /* Fourth byte table 27. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 6, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 12, 15, 15, 15, 15, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21,
+ },
+ { /* Fourth byte table 28. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 6, 9, 12, 15, 18, 21, 24,
+ 27, 30, 33, 36, 39, 42, 45, 48,
+ 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51,
+ 51,
+ },
+ { /* Fourth byte table 29. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3,
+ },
+ { /* Fourth byte table 30. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 60, 63, 66, 69,
+ 72, 75, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78,
+ },
+ { /* Fourth byte table 31. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48,
+ 48,
+ },
+ { /* Fourth byte table 32. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 60, 63, 66, 69,
+ 72, 75, 78, 81, 84, 87, 90, 93,
+ 93, 93, 96, 96, 96, 96, 98, 100,
+ 100, 103, 103, 106, 106, 109, 109, 109,
+ 109, 109, 109, 109, 109, 109, 109, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112,
+ 112,
+ },
+ { /* Fourth byte table 33. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 3, 6, 6, 9, 9,
+ 12, 12, 15, 15, 18, 18, 21, 21,
+ 24, 24, 27, 27, 30, 30, 33, 33,
+ 36, 36, 39, 39, 42, 42, 45, 45,
+ 48, 48, 51, 51, 54, 54, 57, 57,
+ 60, 60, 63, 63, 66, 66, 69, 69,
+ 72, 72, 75, 75, 78, 78, 81, 81,
+ 84, 84, 87, 87, 90, 90, 93, 93,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96,
+ },
+ { /* Fourth byte table 34. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 3, 6, 6, 9, 9,
+ 12, 12, 15, 15, 18, 18, 21, 21,
+ 24, 24, 27, 27, 30, 30, 33, 33,
+ 36, 36, 39, 39, 42, 42, 45, 45,
+ 48, 48, 51, 51, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54,
+ 54,
+ },
+ { /* Fourth byte table 35. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 60, 63, 66, 69,
+ 72, 75, 78, 81, 84, 87, 90, 93,
+ 96, 99, 102, 105, 108, 111, 114, 114,
+ 114, 114, 114, 114, 114, 114, 114, 114,
+ 114, 114, 114, 114, 114, 114, 114, 114,
+ 114, 114, 114, 114, 114, 114, 114, 114,
+ 114, 114, 114, 114, 114, 114, 114, 114,
+ 114, 114, 114, 114, 114, 114, 114, 114,
+ 114, 114, 114, 114, 114, 114, 114, 114,
+ 114, 114, 114, 114, 114, 114, 114, 114,
+ 114, 114, 114, 114, 114, 114, 114, 114,
+ 114, 114, 114, 114, 114, 114, 114, 114,
+ 114, 114, 114, 114, 114, 114, 114, 114,
+ 114, 114, 114, 114, 114, 114, 114, 114,
+ 114,
+ },
+ { /* Fourth byte table 36. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 6, 9, 12, 15, 18,
+ 21, 24, 27, 30, 33, 36, 39, 42,
+ 45, 48, 51, 54, 57, 60, 63, 66,
+ 69, 72, 75, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 78, 78, 78, 78, 78,
+ 78,
+ },
+ { /* Fourth byte table 37. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 68, 72, 76, 80, 84, 88, 92,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 96,
+ },
+ { /* Fourth byte table 38. */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64,
+ },
+ },
+};
+
+static const uchar_t u8_toupper_final_tbl[2][2318] = {
+ {
+ 0xCE, 0x9C, 0xC3, 0x80, 0xC3, 0x81, 0xC3, 0x82,
+ 0xC3, 0x83, 0xC3, 0x84, 0xC3, 0x85, 0xC3, 0x86,
+ 0xC3, 0x87, 0xC3, 0x88, 0xC3, 0x89, 0xC3, 0x8A,
+ 0xC3, 0x8B, 0xC3, 0x8C, 0xC3, 0x8D, 0xC3, 0x8E,
+ 0xC3, 0x8F, 0xC3, 0x90, 0xC3, 0x91, 0xC3, 0x92,
+ 0xC3, 0x93, 0xC3, 0x94, 0xC3, 0x95, 0xC3, 0x96,
+ 0xC3, 0x98, 0xC3, 0x99, 0xC3, 0x9A, 0xC3, 0x9B,
+ 0xC3, 0x9C, 0xC3, 0x9D, 0xC3, 0x9E, 0xC5, 0xB8,
+ 0xC4, 0x80, 0xC4, 0x82, 0xC4, 0x84, 0xC4, 0x86,
+ 0xC4, 0x88, 0xC4, 0x8A, 0xC4, 0x8C, 0xC4, 0x8E,
+ 0xC4, 0x90, 0xC4, 0x92, 0xC4, 0x94, 0xC4, 0x96,
+ 0xC4, 0x98, 0xC4, 0x9A, 0xC4, 0x9C, 0xC4, 0x9E,
+ 0xC4, 0xA0, 0xC4, 0xA2, 0xC4, 0xA4, 0xC4, 0xA6,
+ 0xC4, 0xA8, 0xC4, 0xAA, 0xC4, 0xAC, 0xC4, 0xAE,
+ 0x49, 0xC4, 0xB2, 0xC4, 0xB4, 0xC4, 0xB6, 0xC4,
+ 0xB9, 0xC4, 0xBB, 0xC4, 0xBD, 0xC4, 0xBF, 0xC5,
+ 0x81, 0xC5, 0x83, 0xC5, 0x85, 0xC5, 0x87, 0xC5,
+ 0x8A, 0xC5, 0x8C, 0xC5, 0x8E, 0xC5, 0x90, 0xC5,
+ 0x92, 0xC5, 0x94, 0xC5, 0x96, 0xC5, 0x98, 0xC5,
+ 0x9A, 0xC5, 0x9C, 0xC5, 0x9E, 0xC5, 0xA0, 0xC5,
+ 0xA2, 0xC5, 0xA4, 0xC5, 0xA6, 0xC5, 0xA8, 0xC5,
+ 0xAA, 0xC5, 0xAC, 0xC5, 0xAE, 0xC5, 0xB0, 0xC5,
+ 0xB2, 0xC5, 0xB4, 0xC5, 0xB6, 0xC5, 0xB9, 0xC5,
+ 0xBB, 0xC5, 0xBD, 0x53, 0xC6, 0x82, 0xC6, 0x84,
+ 0xC6, 0x87, 0xC6, 0x8B, 0xC6, 0x91, 0xC7, 0xB6,
+ 0xC6, 0x98, 0xC8, 0xA0, 0xC6, 0xA0, 0xC6, 0xA2,
+ 0xC6, 0xA4, 0xC6, 0xA7, 0xC6, 0xAC, 0xC6, 0xAF,
+ 0xC6, 0xB3, 0xC6, 0xB5, 0xC6, 0xB8, 0xC6, 0xBC,
+ 0xC7, 0xB7, 0xC7, 0x84, 0xC7, 0x84, 0xC7, 0x87,
+ 0xC7, 0x87, 0xC7, 0x8A, 0xC7, 0x8A, 0xC7, 0x8D,
+ 0xC7, 0x8F, 0xC7, 0x91, 0xC7, 0x93, 0xC7, 0x95,
+ 0xC7, 0x97, 0xC7, 0x99, 0xC7, 0x9B, 0xC6, 0x8E,
+ 0xC7, 0x9E, 0xC7, 0xA0, 0xC7, 0xA2, 0xC7, 0xA4,
+ 0xC7, 0xA6, 0xC7, 0xA8, 0xC7, 0xAA, 0xC7, 0xAC,
+ 0xC7, 0xAE, 0xC7, 0xB1, 0xC7, 0xB1, 0xC7, 0xB4,
+ 0xC7, 0xB8, 0xC7, 0xBA, 0xC7, 0xBC, 0xC7, 0xBE,
+ 0xC8, 0x80, 0xC8, 0x82, 0xC8, 0x84, 0xC8, 0x86,
+ 0xC8, 0x88, 0xC8, 0x8A, 0xC8, 0x8C, 0xC8, 0x8E,
+ 0xC8, 0x90, 0xC8, 0x92, 0xC8, 0x94, 0xC8, 0x96,
+ 0xC8, 0x98, 0xC8, 0x9A, 0xC8, 0x9C, 0xC8, 0x9E,
+ 0xC8, 0xA2, 0xC8, 0xA4, 0xC8, 0xA6, 0xC8, 0xA8,
+ 0xC8, 0xAA, 0xC8, 0xAC, 0xC8, 0xAE, 0xC8, 0xB0,
+ 0xC8, 0xB2, 0xC6, 0x81, 0xC6, 0x86, 0xC6, 0x89,
+ 0xC6, 0x8A, 0xC6, 0x8F, 0xC6, 0x90, 0xC6, 0x93,
+ 0xC6, 0x94, 0xC6, 0x97, 0xC6, 0x96, 0xC6, 0x9C,
+ 0xC6, 0x9D, 0xC6, 0x9F, 0xC6, 0xA6, 0xC6, 0xA9,
+ 0xC6, 0xAE, 0xC6, 0xB1, 0xC6, 0xB2, 0xC6, 0xB7,
+ 0xCE, 0x99, 0xCE, 0x86, 0xCE, 0x88, 0xCE, 0x89,
+ 0xCE, 0x8A, 0xCE, 0x91, 0xCE, 0x92, 0xCE, 0x93,
+ 0xCE, 0x94, 0xCE, 0x95, 0xCE, 0x96, 0xCE, 0x97,
+ 0xCE, 0x98, 0xCE, 0x99, 0xCE, 0x9A, 0xCE, 0x9B,
+ 0xCE, 0x9C, 0xCE, 0x9D, 0xCE, 0x9E, 0xCE, 0x9F,
+ 0xCE, 0xA0, 0xCE, 0xA1, 0xCE, 0xA3, 0xCE, 0xA3,
+ 0xCE, 0xA4, 0xCE, 0xA5, 0xCE, 0xA6, 0xCE, 0xA7,
+ 0xCE, 0xA8, 0xCE, 0xA9, 0xCE, 0xAA, 0xCE, 0xAB,
+ 0xCE, 0x8C, 0xCE, 0x8E, 0xCE, 0x8F, 0xCE, 0x92,
+ 0xCE, 0x98, 0xCE, 0xA6, 0xCE, 0xA0, 0xCF, 0x98,
+ 0xCF, 0x9A, 0xCF, 0x9C, 0xCF, 0x9E, 0xCF, 0xA0,
+ 0xCF, 0xA2, 0xCF, 0xA4, 0xCF, 0xA6, 0xCF, 0xA8,
+ 0xCF, 0xAA, 0xCF, 0xAC, 0xCF, 0xAE, 0xCE, 0x9A,
+ 0xCE, 0xA1, 0xCE, 0xA3, 0xCE, 0x95, 0xD0, 0x90,
+ 0xD0, 0x91, 0xD0, 0x92, 0xD0, 0x93, 0xD0, 0x94,
+ 0xD0, 0x95, 0xD0, 0x96, 0xD0, 0x97, 0xD0, 0x98,
+ 0xD0, 0x99, 0xD0, 0x9A, 0xD0, 0x9B, 0xD0, 0x9C,
+ 0xD0, 0x9D, 0xD0, 0x9E, 0xD0, 0x9F, 0xD0, 0xA0,
+ 0xD0, 0xA1, 0xD0, 0xA2, 0xD0, 0xA3, 0xD0, 0xA4,
+ 0xD0, 0xA5, 0xD0, 0xA6, 0xD0, 0xA7, 0xD0, 0xA8,
+ 0xD0, 0xA9, 0xD0, 0xAA, 0xD0, 0xAB, 0xD0, 0xAC,
+ 0xD0, 0xAD, 0xD0, 0xAE, 0xD0, 0xAF, 0xD0, 0x80,
+ 0xD0, 0x81, 0xD0, 0x82, 0xD0, 0x83, 0xD0, 0x84,
+ 0xD0, 0x85, 0xD0, 0x86, 0xD0, 0x87, 0xD0, 0x88,
+ 0xD0, 0x89, 0xD0, 0x8A, 0xD0, 0x8B, 0xD0, 0x8C,
+ 0xD0, 0x8D, 0xD0, 0x8E, 0xD0, 0x8F, 0xD1, 0xA0,
+ 0xD1, 0xA2, 0xD1, 0xA4, 0xD1, 0xA6, 0xD1, 0xA8,
+ 0xD1, 0xAA, 0xD1, 0xAC, 0xD1, 0xAE, 0xD1, 0xB0,
+ 0xD1, 0xB2, 0xD1, 0xB4, 0xD1, 0xB6, 0xD1, 0xB8,
+ 0xD1, 0xBA, 0xD1, 0xBC, 0xD1, 0xBE, 0xD2, 0x80,
+ 0xD2, 0x8A, 0xD2, 0x8C, 0xD2, 0x8E, 0xD2, 0x90,
+ 0xD2, 0x92, 0xD2, 0x94, 0xD2, 0x96, 0xD2, 0x98,
+ 0xD2, 0x9A, 0xD2, 0x9C, 0xD2, 0x9E, 0xD2, 0xA0,
+ 0xD2, 0xA2, 0xD2, 0xA4, 0xD2, 0xA6, 0xD2, 0xA8,
+ 0xD2, 0xAA, 0xD2, 0xAC, 0xD2, 0xAE, 0xD2, 0xB0,
+ 0xD2, 0xB2, 0xD2, 0xB4, 0xD2, 0xB6, 0xD2, 0xB8,
+ 0xD2, 0xBA, 0xD2, 0xBC, 0xD2, 0xBE, 0xD3, 0x81,
+ 0xD3, 0x83, 0xD3, 0x85, 0xD3, 0x87, 0xD3, 0x89,
+ 0xD3, 0x8B, 0xD3, 0x8D, 0xD3, 0x90, 0xD3, 0x92,
+ 0xD3, 0x94, 0xD3, 0x96, 0xD3, 0x98, 0xD3, 0x9A,
+ 0xD3, 0x9C, 0xD3, 0x9E, 0xD3, 0xA0, 0xD3, 0xA2,
+ 0xD3, 0xA4, 0xD3, 0xA6, 0xD3, 0xA8, 0xD3, 0xAA,
+ 0xD3, 0xAC, 0xD3, 0xAE, 0xD3, 0xB0, 0xD3, 0xB2,
+ 0xD3, 0xB4, 0xD3, 0xB8, 0xD4, 0x80, 0xD4, 0x82,
+ 0xD4, 0x84, 0xD4, 0x86, 0xD4, 0x88, 0xD4, 0x8A,
+ 0xD4, 0x8C, 0xD4, 0x8E, 0xD4, 0xB1, 0xD4, 0xB2,
+ 0xD4, 0xB3, 0xD4, 0xB4, 0xD4, 0xB5, 0xD4, 0xB6,
+ 0xD4, 0xB7, 0xD4, 0xB8, 0xD4, 0xB9, 0xD4, 0xBA,
+ 0xD4, 0xBB, 0xD4, 0xBC, 0xD4, 0xBD, 0xD4, 0xBE,
+ 0xD4, 0xBF, 0xD5, 0x80, 0xD5, 0x81, 0xD5, 0x82,
+ 0xD5, 0x83, 0xD5, 0x84, 0xD5, 0x85, 0xD5, 0x86,
+ 0xD5, 0x87, 0xD5, 0x88, 0xD5, 0x89, 0xD5, 0x8A,
+ 0xD5, 0x8B, 0xD5, 0x8C, 0xD5, 0x8D, 0xD5, 0x8E,
+ 0xD5, 0x8F, 0xD5, 0x90, 0xD5, 0x91, 0xD5, 0x92,
+ 0xD5, 0x93, 0xD5, 0x94, 0xD5, 0x95, 0xD5, 0x96,
+ 0xE1, 0xB8, 0x80, 0xE1, 0xB8, 0x82, 0xE1, 0xB8,
+ 0x84, 0xE1, 0xB8, 0x86, 0xE1, 0xB8, 0x88, 0xE1,
+ 0xB8, 0x8A, 0xE1, 0xB8, 0x8C, 0xE1, 0xB8, 0x8E,
+ 0xE1, 0xB8, 0x90, 0xE1, 0xB8, 0x92, 0xE1, 0xB8,
+ 0x94, 0xE1, 0xB8, 0x96, 0xE1, 0xB8, 0x98, 0xE1,
+ 0xB8, 0x9A, 0xE1, 0xB8, 0x9C, 0xE1, 0xB8, 0x9E,
+ 0xE1, 0xB8, 0xA0, 0xE1, 0xB8, 0xA2, 0xE1, 0xB8,
+ 0xA4, 0xE1, 0xB8, 0xA6, 0xE1, 0xB8, 0xA8, 0xE1,
+ 0xB8, 0xAA, 0xE1, 0xB8, 0xAC, 0xE1, 0xB8, 0xAE,
+ 0xE1, 0xB8, 0xB0, 0xE1, 0xB8, 0xB2, 0xE1, 0xB8,
+ 0xB4, 0xE1, 0xB8, 0xB6, 0xE1, 0xB8, 0xB8, 0xE1,
+ 0xB8, 0xBA, 0xE1, 0xB8, 0xBC, 0xE1, 0xB8, 0xBE,
+ 0xE1, 0xB9, 0x80, 0xE1, 0xB9, 0x82, 0xE1, 0xB9,
+ 0x84, 0xE1, 0xB9, 0x86, 0xE1, 0xB9, 0x88, 0xE1,
+ 0xB9, 0x8A, 0xE1, 0xB9, 0x8C, 0xE1, 0xB9, 0x8E,
+ 0xE1, 0xB9, 0x90, 0xE1, 0xB9, 0x92, 0xE1, 0xB9,
+ 0x94, 0xE1, 0xB9, 0x96, 0xE1, 0xB9, 0x98, 0xE1,
+ 0xB9, 0x9A, 0xE1, 0xB9, 0x9C, 0xE1, 0xB9, 0x9E,
+ 0xE1, 0xB9, 0xA0, 0xE1, 0xB9, 0xA2, 0xE1, 0xB9,
+ 0xA4, 0xE1, 0xB9, 0xA6, 0xE1, 0xB9, 0xA8, 0xE1,
+ 0xB9, 0xAA, 0xE1, 0xB9, 0xAC, 0xE1, 0xB9, 0xAE,
+ 0xE1, 0xB9, 0xB0, 0xE1, 0xB9, 0xB2, 0xE1, 0xB9,
+ 0xB4, 0xE1, 0xB9, 0xB6, 0xE1, 0xB9, 0xB8, 0xE1,
+ 0xB9, 0xBA, 0xE1, 0xB9, 0xBC, 0xE1, 0xB9, 0xBE,
+ 0xE1, 0xBA, 0x80, 0xE1, 0xBA, 0x82, 0xE1, 0xBA,
+ 0x84, 0xE1, 0xBA, 0x86, 0xE1, 0xBA, 0x88, 0xE1,
+ 0xBA, 0x8A, 0xE1, 0xBA, 0x8C, 0xE1, 0xBA, 0x8E,
+ 0xE1, 0xBA, 0x90, 0xE1, 0xBA, 0x92, 0xE1, 0xBA,
+ 0x94, 0xE1, 0xB9, 0xA0, 0xE1, 0xBA, 0xA0, 0xE1,
+ 0xBA, 0xA2, 0xE1, 0xBA, 0xA4, 0xE1, 0xBA, 0xA6,
+ 0xE1, 0xBA, 0xA8, 0xE1, 0xBA, 0xAA, 0xE1, 0xBA,
+ 0xAC, 0xE1, 0xBA, 0xAE, 0xE1, 0xBA, 0xB0, 0xE1,
+ 0xBA, 0xB2, 0xE1, 0xBA, 0xB4, 0xE1, 0xBA, 0xB6,
+ 0xE1, 0xBA, 0xB8, 0xE1, 0xBA, 0xBA, 0xE1, 0xBA,
+ 0xBC, 0xE1, 0xBA, 0xBE, 0xE1, 0xBB, 0x80, 0xE1,
+ 0xBB, 0x82, 0xE1, 0xBB, 0x84, 0xE1, 0xBB, 0x86,
+ 0xE1, 0xBB, 0x88, 0xE1, 0xBB, 0x8A, 0xE1, 0xBB,
+ 0x8C, 0xE1, 0xBB, 0x8E, 0xE1, 0xBB, 0x90, 0xE1,
+ 0xBB, 0x92, 0xE1, 0xBB, 0x94, 0xE1, 0xBB, 0x96,
+ 0xE1, 0xBB, 0x98, 0xE1, 0xBB, 0x9A, 0xE1, 0xBB,
+ 0x9C, 0xE1, 0xBB, 0x9E, 0xE1, 0xBB, 0xA0, 0xE1,
+ 0xBB, 0xA2, 0xE1, 0xBB, 0xA4, 0xE1, 0xBB, 0xA6,
+ 0xE1, 0xBB, 0xA8, 0xE1, 0xBB, 0xAA, 0xE1, 0xBB,
+ 0xAC, 0xE1, 0xBB, 0xAE, 0xE1, 0xBB, 0xB0, 0xE1,
+ 0xBB, 0xB2, 0xE1, 0xBB, 0xB4, 0xE1, 0xBB, 0xB6,
+ 0xE1, 0xBB, 0xB8, 0xE1, 0xBC, 0x88, 0xE1, 0xBC,
+ 0x89, 0xE1, 0xBC, 0x8A, 0xE1, 0xBC, 0x8B, 0xE1,
+ 0xBC, 0x8C, 0xE1, 0xBC, 0x8D, 0xE1, 0xBC, 0x8E,
+ 0xE1, 0xBC, 0x8F, 0xE1, 0xBC, 0x98, 0xE1, 0xBC,
+ 0x99, 0xE1, 0xBC, 0x9A, 0xE1, 0xBC, 0x9B, 0xE1,
+ 0xBC, 0x9C, 0xE1, 0xBC, 0x9D, 0xE1, 0xBC, 0xA8,
+ 0xE1, 0xBC, 0xA9, 0xE1, 0xBC, 0xAA, 0xE1, 0xBC,
+ 0xAB, 0xE1, 0xBC, 0xAC, 0xE1, 0xBC, 0xAD, 0xE1,
+ 0xBC, 0xAE, 0xE1, 0xBC, 0xAF, 0xE1, 0xBC, 0xB8,
+ 0xE1, 0xBC, 0xB9, 0xE1, 0xBC, 0xBA, 0xE1, 0xBC,
+ 0xBB, 0xE1, 0xBC, 0xBC, 0xE1, 0xBC, 0xBD, 0xE1,
+ 0xBC, 0xBE, 0xE1, 0xBC, 0xBF, 0xE1, 0xBD, 0x88,
+ 0xE1, 0xBD, 0x89, 0xE1, 0xBD, 0x8A, 0xE1, 0xBD,
+ 0x8B, 0xE1, 0xBD, 0x8C, 0xE1, 0xBD, 0x8D, 0xE1,
+ 0xBD, 0x99, 0xE1, 0xBD, 0x9B, 0xE1, 0xBD, 0x9D,
+ 0xE1, 0xBD, 0x9F, 0xE1, 0xBD, 0xA8, 0xE1, 0xBD,
+ 0xA9, 0xE1, 0xBD, 0xAA, 0xE1, 0xBD, 0xAB, 0xE1,
+ 0xBD, 0xAC, 0xE1, 0xBD, 0xAD, 0xE1, 0xBD, 0xAE,
+ 0xE1, 0xBD, 0xAF, 0xE1, 0xBE, 0xBA, 0xE1, 0xBE,
+ 0xBB, 0xE1, 0xBF, 0x88, 0xE1, 0xBF, 0x89, 0xE1,
+ 0xBF, 0x8A, 0xE1, 0xBF, 0x8B, 0xE1, 0xBF, 0x9A,
+ 0xE1, 0xBF, 0x9B, 0xE1, 0xBF, 0xB8, 0xE1, 0xBF,
+ 0xB9, 0xE1, 0xBF, 0xAA, 0xE1, 0xBF, 0xAB, 0xE1,
+ 0xBF, 0xBA, 0xE1, 0xBF, 0xBB, 0xE1, 0xBE, 0x88,
+ 0xE1, 0xBE, 0x89, 0xE1, 0xBE, 0x8A, 0xE1, 0xBE,
+ 0x8B, 0xE1, 0xBE, 0x8C, 0xE1, 0xBE, 0x8D, 0xE1,
+ 0xBE, 0x8E, 0xE1, 0xBE, 0x8F, 0xE1, 0xBE, 0x98,
+ 0xE1, 0xBE, 0x99, 0xE1, 0xBE, 0x9A, 0xE1, 0xBE,
+ 0x9B, 0xE1, 0xBE, 0x9C, 0xE1, 0xBE, 0x9D, 0xE1,
+ 0xBE, 0x9E, 0xE1, 0xBE, 0x9F, 0xE1, 0xBE, 0xA8,
+ 0xE1, 0xBE, 0xA9, 0xE1, 0xBE, 0xAA, 0xE1, 0xBE,
+ 0xAB, 0xE1, 0xBE, 0xAC, 0xE1, 0xBE, 0xAD, 0xE1,
+ 0xBE, 0xAE, 0xE1, 0xBE, 0xAF, 0xE1, 0xBE, 0xB8,
+ 0xE1, 0xBE, 0xB9, 0xE1, 0xBE, 0xBC, 0xCE, 0x99,
+ 0xE1, 0xBF, 0x8C, 0xE1, 0xBF, 0x98, 0xE1, 0xBF,
+ 0x99, 0xE1, 0xBF, 0xA8, 0xE1, 0xBF, 0xA9, 0xE1,
+ 0xBF, 0xAC, 0xE1, 0xBF, 0xBC, 0xE2, 0x85, 0xA0,
+ 0xE2, 0x85, 0xA1, 0xE2, 0x85, 0xA2, 0xE2, 0x85,
+ 0xA3, 0xE2, 0x85, 0xA4, 0xE2, 0x85, 0xA5, 0xE2,
+ 0x85, 0xA6, 0xE2, 0x85, 0xA7, 0xE2, 0x85, 0xA8,
+ 0xE2, 0x85, 0xA9, 0xE2, 0x85, 0xAA, 0xE2, 0x85,
+ 0xAB, 0xE2, 0x85, 0xAC, 0xE2, 0x85, 0xAD, 0xE2,
+ 0x85, 0xAE, 0xE2, 0x85, 0xAF, 0xE2, 0x92, 0xB6,
+ 0xE2, 0x92, 0xB7, 0xE2, 0x92, 0xB8, 0xE2, 0x92,
+ 0xB9, 0xE2, 0x92, 0xBA, 0xE2, 0x92, 0xBB, 0xE2,
+ 0x92, 0xBC, 0xE2, 0x92, 0xBD, 0xE2, 0x92, 0xBE,
+ 0xE2, 0x92, 0xBF, 0xE2, 0x93, 0x80, 0xE2, 0x93,
+ 0x81, 0xE2, 0x93, 0x82, 0xE2, 0x93, 0x83, 0xE2,
+ 0x93, 0x84, 0xE2, 0x93, 0x85, 0xE2, 0x93, 0x86,
+ 0xE2, 0x93, 0x87, 0xE2, 0x93, 0x88, 0xE2, 0x93,
+ 0x89, 0xE2, 0x93, 0x8A, 0xE2, 0x93, 0x8B, 0xE2,
+ 0x93, 0x8C, 0xE2, 0x93, 0x8D, 0xE2, 0x93, 0x8E,
+ 0xE2, 0x93, 0x8F, 0xEF, 0xBC, 0xA1, 0xEF, 0xBC,
+ 0xA2, 0xEF, 0xBC, 0xA3, 0xEF, 0xBC, 0xA4, 0xEF,
+ 0xBC, 0xA5, 0xEF, 0xBC, 0xA6, 0xEF, 0xBC, 0xA7,
+ 0xEF, 0xBC, 0xA8, 0xEF, 0xBC, 0xA9, 0xEF, 0xBC,
+ 0xAA, 0xEF, 0xBC, 0xAB, 0xEF, 0xBC, 0xAC, 0xEF,
+ 0xBC, 0xAD, 0xEF, 0xBC, 0xAE, 0xEF, 0xBC, 0xAF,
+ 0xEF, 0xBC, 0xB0, 0xEF, 0xBC, 0xB1, 0xEF, 0xBC,
+ 0xB2, 0xEF, 0xBC, 0xB3, 0xEF, 0xBC, 0xB4, 0xEF,
+ 0xBC, 0xB5, 0xEF, 0xBC, 0xB6, 0xEF, 0xBC, 0xB7,
+ 0xEF, 0xBC, 0xB8, 0xEF, 0xBC, 0xB9, 0xEF, 0xBC,
+ 0xBA, 0xF0, 0x90, 0x90, 0x80, 0xF0, 0x90, 0x90,
+ 0x81, 0xF0, 0x90, 0x90, 0x82, 0xF0, 0x90, 0x90,
+ 0x83, 0xF0, 0x90, 0x90, 0x84, 0xF0, 0x90, 0x90,
+ 0x85, 0xF0, 0x90, 0x90, 0x86, 0xF0, 0x90, 0x90,
+ 0x87, 0xF0, 0x90, 0x90, 0x88, 0xF0, 0x90, 0x90,
+ 0x89, 0xF0, 0x90, 0x90, 0x8A, 0xF0, 0x90, 0x90,
+ 0x8B, 0xF0, 0x90, 0x90, 0x8C, 0xF0, 0x90, 0x90,
+ 0x8D, 0xF0, 0x90, 0x90, 0x8E, 0xF0, 0x90, 0x90,
+ 0x8F, 0xF0, 0x90, 0x90, 0x90, 0xF0, 0x90, 0x90,
+ 0x91, 0xF0, 0x90, 0x90, 0x92, 0xF0, 0x90, 0x90,
+ 0x93, 0xF0, 0x90, 0x90, 0x94, 0xF0, 0x90, 0x90,
+ 0x95, 0xF0, 0x90, 0x90, 0x96, 0xF0, 0x90, 0x90,
+ 0x97, 0xF0, 0x90, 0x90, 0x98, 0xF0, 0x90, 0x90,
+ 0x99, 0xF0, 0x90, 0x90, 0x9A, 0xF0, 0x90, 0x90,
+ 0x9B, 0xF0, 0x90, 0x90, 0x9C, 0xF0, 0x90, 0x90,
+ 0x9D, 0xF0, 0x90, 0x90, 0x9E, 0xF0, 0x90, 0x90,
+ 0x9F, 0xF0, 0x90, 0x90, 0xA0, 0xF0, 0x90, 0x90,
+ 0xA1, 0xF0, 0x90, 0x90, 0xA2, 0xF0, 0x90, 0x90,
+ 0xA3, 0xF0, 0x90, 0x90, 0xA4, 0xF0, 0x90, 0x90,
+ 0xA5, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ },
+ {
+ 0xCE, 0x9C, 0xC3, 0x80, 0xC3, 0x81, 0xC3, 0x82,
+ 0xC3, 0x83, 0xC3, 0x84, 0xC3, 0x85, 0xC3, 0x86,
+ 0xC3, 0x87, 0xC3, 0x88, 0xC3, 0x89, 0xC3, 0x8A,
+ 0xC3, 0x8B, 0xC3, 0x8C, 0xC3, 0x8D, 0xC3, 0x8E,
+ 0xC3, 0x8F, 0xC3, 0x90, 0xC3, 0x91, 0xC3, 0x92,
+ 0xC3, 0x93, 0xC3, 0x94, 0xC3, 0x95, 0xC3, 0x96,
+ 0xC3, 0x98, 0xC3, 0x99, 0xC3, 0x9A, 0xC3, 0x9B,
+ 0xC3, 0x9C, 0xC3, 0x9D, 0xC3, 0x9E, 0xC5, 0xB8,
+ 0xC4, 0x80, 0xC4, 0x82, 0xC4, 0x84, 0xC4, 0x86,
+ 0xC4, 0x88, 0xC4, 0x8A, 0xC4, 0x8C, 0xC4, 0x8E,
+ 0xC4, 0x90, 0xC4, 0x92, 0xC4, 0x94, 0xC4, 0x96,
+ 0xC4, 0x98, 0xC4, 0x9A, 0xC4, 0x9C, 0xC4, 0x9E,
+ 0xC4, 0xA0, 0xC4, 0xA2, 0xC4, 0xA4, 0xC4, 0xA6,
+ 0xC4, 0xA8, 0xC4, 0xAA, 0xC4, 0xAC, 0xC4, 0xAE,
+ 0x49, 0xC4, 0xB2, 0xC4, 0xB4, 0xC4, 0xB6, 0xC4,
+ 0xB9, 0xC4, 0xBB, 0xC4, 0xBD, 0xC4, 0xBF, 0xC5,
+ 0x81, 0xC5, 0x83, 0xC5, 0x85, 0xC5, 0x87, 0xC5,
+ 0x8A, 0xC5, 0x8C, 0xC5, 0x8E, 0xC5, 0x90, 0xC5,
+ 0x92, 0xC5, 0x94, 0xC5, 0x96, 0xC5, 0x98, 0xC5,
+ 0x9A, 0xC5, 0x9C, 0xC5, 0x9E, 0xC5, 0xA0, 0xC5,
+ 0xA2, 0xC5, 0xA4, 0xC5, 0xA6, 0xC5, 0xA8, 0xC5,
+ 0xAA, 0xC5, 0xAC, 0xC5, 0xAE, 0xC5, 0xB0, 0xC5,
+ 0xB2, 0xC5, 0xB4, 0xC5, 0xB6, 0xC5, 0xB9, 0xC5,
+ 0xBB, 0xC5, 0xBD, 0x53, 0xC9, 0x83, 0xC6, 0x82,
+ 0xC6, 0x84, 0xC6, 0x87, 0xC6, 0x8B, 0xC6, 0x91,
+ 0xC7, 0xB6, 0xC6, 0x98, 0xC8, 0xBD, 0xC8, 0xA0,
+ 0xC6, 0xA0, 0xC6, 0xA2, 0xC6, 0xA4, 0xC6, 0xA7,
+ 0xC6, 0xAC, 0xC6, 0xAF, 0xC6, 0xB3, 0xC6, 0xB5,
+ 0xC6, 0xB8, 0xC6, 0xBC, 0xC7, 0xB7, 0xC7, 0x84,
+ 0xC7, 0x84, 0xC7, 0x87, 0xC7, 0x87, 0xC7, 0x8A,
+ 0xC7, 0x8A, 0xC7, 0x8D, 0xC7, 0x8F, 0xC7, 0x91,
+ 0xC7, 0x93, 0xC7, 0x95, 0xC7, 0x97, 0xC7, 0x99,
+ 0xC7, 0x9B, 0xC6, 0x8E, 0xC7, 0x9E, 0xC7, 0xA0,
+ 0xC7, 0xA2, 0xC7, 0xA4, 0xC7, 0xA6, 0xC7, 0xA8,
+ 0xC7, 0xAA, 0xC7, 0xAC, 0xC7, 0xAE, 0xC7, 0xB1,
+ 0xC7, 0xB1, 0xC7, 0xB4, 0xC7, 0xB8, 0xC7, 0xBA,
+ 0xC7, 0xBC, 0xC7, 0xBE, 0xC8, 0x80, 0xC8, 0x82,
+ 0xC8, 0x84, 0xC8, 0x86, 0xC8, 0x88, 0xC8, 0x8A,
+ 0xC8, 0x8C, 0xC8, 0x8E, 0xC8, 0x90, 0xC8, 0x92,
+ 0xC8, 0x94, 0xC8, 0x96, 0xC8, 0x98, 0xC8, 0x9A,
+ 0xC8, 0x9C, 0xC8, 0x9E, 0xC8, 0xA2, 0xC8, 0xA4,
+ 0xC8, 0xA6, 0xC8, 0xA8, 0xC8, 0xAA, 0xC8, 0xAC,
+ 0xC8, 0xAE, 0xC8, 0xB0, 0xC8, 0xB2, 0xC8, 0xBB,
+ 0xC9, 0x81, 0xC9, 0x86, 0xC9, 0x88, 0xC9, 0x8A,
+ 0xC9, 0x8C, 0xC9, 0x8E, 0xC6, 0x81, 0xC6, 0x86,
+ 0xC6, 0x89, 0xC6, 0x8A, 0xC6, 0x8F, 0xC6, 0x90,
+ 0xC6, 0x93, 0xC6, 0x94, 0xC6, 0x97, 0xC6, 0x96,
+ 0xE2, 0xB1, 0xA2, 0xC6, 0x9C, 0xC6, 0x9D, 0xC6,
+ 0x9F, 0xE2, 0xB1, 0xA4, 0xC6, 0xA6, 0xC6, 0xA9,
+ 0xC6, 0xAE, 0xC9, 0x84, 0xC6, 0xB1, 0xC6, 0xB2,
+ 0xC9, 0x85, 0xC6, 0xB7, 0xCE, 0x99, 0xCF, 0xBD,
+ 0xCF, 0xBE, 0xCF, 0xBF, 0xCE, 0x86, 0xCE, 0x88,
+ 0xCE, 0x89, 0xCE, 0x8A, 0xCE, 0x91, 0xCE, 0x92,
+ 0xCE, 0x93, 0xCE, 0x94, 0xCE, 0x95, 0xCE, 0x96,
+ 0xCE, 0x97, 0xCE, 0x98, 0xCE, 0x99, 0xCE, 0x9A,
+ 0xCE, 0x9B, 0xCE, 0x9C, 0xCE, 0x9D, 0xCE, 0x9E,
+ 0xCE, 0x9F, 0xCE, 0xA0, 0xCE, 0xA1, 0xCE, 0xA3,
+ 0xCE, 0xA3, 0xCE, 0xA4, 0xCE, 0xA5, 0xCE, 0xA6,
+ 0xCE, 0xA7, 0xCE, 0xA8, 0xCE, 0xA9, 0xCE, 0xAA,
+ 0xCE, 0xAB, 0xCE, 0x8C, 0xCE, 0x8E, 0xCE, 0x8F,
+ 0xCE, 0x92, 0xCE, 0x98, 0xCE, 0xA6, 0xCE, 0xA0,
+ 0xCF, 0x98, 0xCF, 0x9A, 0xCF, 0x9C, 0xCF, 0x9E,
+ 0xCF, 0xA0, 0xCF, 0xA2, 0xCF, 0xA4, 0xCF, 0xA6,
+ 0xCF, 0xA8, 0xCF, 0xAA, 0xCF, 0xAC, 0xCF, 0xAE,
+ 0xCE, 0x9A, 0xCE, 0xA1, 0xCF, 0xB9, 0xCE, 0x95,
+ 0xCF, 0xB7, 0xCF, 0xBA, 0xD0, 0x90, 0xD0, 0x91,
+ 0xD0, 0x92, 0xD0, 0x93, 0xD0, 0x94, 0xD0, 0x95,
+ 0xD0, 0x96, 0xD0, 0x97, 0xD0, 0x98, 0xD0, 0x99,
+ 0xD0, 0x9A, 0xD0, 0x9B, 0xD0, 0x9C, 0xD0, 0x9D,
+ 0xD0, 0x9E, 0xD0, 0x9F, 0xD0, 0xA0, 0xD0, 0xA1,
+ 0xD0, 0xA2, 0xD0, 0xA3, 0xD0, 0xA4, 0xD0, 0xA5,
+ 0xD0, 0xA6, 0xD0, 0xA7, 0xD0, 0xA8, 0xD0, 0xA9,
+ 0xD0, 0xAA, 0xD0, 0xAB, 0xD0, 0xAC, 0xD0, 0xAD,
+ 0xD0, 0xAE, 0xD0, 0xAF, 0xD0, 0x80, 0xD0, 0x81,
+ 0xD0, 0x82, 0xD0, 0x83, 0xD0, 0x84, 0xD0, 0x85,
+ 0xD0, 0x86, 0xD0, 0x87, 0xD0, 0x88, 0xD0, 0x89,
+ 0xD0, 0x8A, 0xD0, 0x8B, 0xD0, 0x8C, 0xD0, 0x8D,
+ 0xD0, 0x8E, 0xD0, 0x8F, 0xD1, 0xA0, 0xD1, 0xA2,
+ 0xD1, 0xA4, 0xD1, 0xA6, 0xD1, 0xA8, 0xD1, 0xAA,
+ 0xD1, 0xAC, 0xD1, 0xAE, 0xD1, 0xB0, 0xD1, 0xB2,
+ 0xD1, 0xB4, 0xD1, 0xB6, 0xD1, 0xB8, 0xD1, 0xBA,
+ 0xD1, 0xBC, 0xD1, 0xBE, 0xD2, 0x80, 0xD2, 0x8A,
+ 0xD2, 0x8C, 0xD2, 0x8E, 0xD2, 0x90, 0xD2, 0x92,
+ 0xD2, 0x94, 0xD2, 0x96, 0xD2, 0x98, 0xD2, 0x9A,
+ 0xD2, 0x9C, 0xD2, 0x9E, 0xD2, 0xA0, 0xD2, 0xA2,
+ 0xD2, 0xA4, 0xD2, 0xA6, 0xD2, 0xA8, 0xD2, 0xAA,
+ 0xD2, 0xAC, 0xD2, 0xAE, 0xD2, 0xB0, 0xD2, 0xB2,
+ 0xD2, 0xB4, 0xD2, 0xB6, 0xD2, 0xB8, 0xD2, 0xBA,
+ 0xD2, 0xBC, 0xD2, 0xBE, 0xD3, 0x81, 0xD3, 0x83,
+ 0xD3, 0x85, 0xD3, 0x87, 0xD3, 0x89, 0xD3, 0x8B,
+ 0xD3, 0x8D, 0xD3, 0x80, 0xD3, 0x90, 0xD3, 0x92,
+ 0xD3, 0x94, 0xD3, 0x96, 0xD3, 0x98, 0xD3, 0x9A,
+ 0xD3, 0x9C, 0xD3, 0x9E, 0xD3, 0xA0, 0xD3, 0xA2,
+ 0xD3, 0xA4, 0xD3, 0xA6, 0xD3, 0xA8, 0xD3, 0xAA,
+ 0xD3, 0xAC, 0xD3, 0xAE, 0xD3, 0xB0, 0xD3, 0xB2,
+ 0xD3, 0xB4, 0xD3, 0xB6, 0xD3, 0xB8, 0xD3, 0xBA,
+ 0xD3, 0xBC, 0xD3, 0xBE, 0xD4, 0x80, 0xD4, 0x82,
+ 0xD4, 0x84, 0xD4, 0x86, 0xD4, 0x88, 0xD4, 0x8A,
+ 0xD4, 0x8C, 0xD4, 0x8E, 0xD4, 0x90, 0xD4, 0x92,
+ 0xD4, 0xB1, 0xD4, 0xB2, 0xD4, 0xB3, 0xD4, 0xB4,
+ 0xD4, 0xB5, 0xD4, 0xB6, 0xD4, 0xB7, 0xD4, 0xB8,
+ 0xD4, 0xB9, 0xD4, 0xBA, 0xD4, 0xBB, 0xD4, 0xBC,
+ 0xD4, 0xBD, 0xD4, 0xBE, 0xD4, 0xBF, 0xD5, 0x80,
+ 0xD5, 0x81, 0xD5, 0x82, 0xD5, 0x83, 0xD5, 0x84,
+ 0xD5, 0x85, 0xD5, 0x86, 0xD5, 0x87, 0xD5, 0x88,
+ 0xD5, 0x89, 0xD5, 0x8A, 0xD5, 0x8B, 0xD5, 0x8C,
+ 0xD5, 0x8D, 0xD5, 0x8E, 0xD5, 0x8F, 0xD5, 0x90,
+ 0xD5, 0x91, 0xD5, 0x92, 0xD5, 0x93, 0xD5, 0x94,
+ 0xD5, 0x95, 0xD5, 0x96, 0xE2, 0xB1, 0xA3, 0xE1,
+ 0xB8, 0x80, 0xE1, 0xB8, 0x82, 0xE1, 0xB8, 0x84,
+ 0xE1, 0xB8, 0x86, 0xE1, 0xB8, 0x88, 0xE1, 0xB8,
+ 0x8A, 0xE1, 0xB8, 0x8C, 0xE1, 0xB8, 0x8E, 0xE1,
+ 0xB8, 0x90, 0xE1, 0xB8, 0x92, 0xE1, 0xB8, 0x94,
+ 0xE1, 0xB8, 0x96, 0xE1, 0xB8, 0x98, 0xE1, 0xB8,
+ 0x9A, 0xE1, 0xB8, 0x9C, 0xE1, 0xB8, 0x9E, 0xE1,
+ 0xB8, 0xA0, 0xE1, 0xB8, 0xA2, 0xE1, 0xB8, 0xA4,
+ 0xE1, 0xB8, 0xA6, 0xE1, 0xB8, 0xA8, 0xE1, 0xB8,
+ 0xAA, 0xE1, 0xB8, 0xAC, 0xE1, 0xB8, 0xAE, 0xE1,
+ 0xB8, 0xB0, 0xE1, 0xB8, 0xB2, 0xE1, 0xB8, 0xB4,
+ 0xE1, 0xB8, 0xB6, 0xE1, 0xB8, 0xB8, 0xE1, 0xB8,
+ 0xBA, 0xE1, 0xB8, 0xBC, 0xE1, 0xB8, 0xBE, 0xE1,
+ 0xB9, 0x80, 0xE1, 0xB9, 0x82, 0xE1, 0xB9, 0x84,
+ 0xE1, 0xB9, 0x86, 0xE1, 0xB9, 0x88, 0xE1, 0xB9,
+ 0x8A, 0xE1, 0xB9, 0x8C, 0xE1, 0xB9, 0x8E, 0xE1,
+ 0xB9, 0x90, 0xE1, 0xB9, 0x92, 0xE1, 0xB9, 0x94,
+ 0xE1, 0xB9, 0x96, 0xE1, 0xB9, 0x98, 0xE1, 0xB9,
+ 0x9A, 0xE1, 0xB9, 0x9C, 0xE1, 0xB9, 0x9E, 0xE1,
+ 0xB9, 0xA0, 0xE1, 0xB9, 0xA2, 0xE1, 0xB9, 0xA4,
+ 0xE1, 0xB9, 0xA6, 0xE1, 0xB9, 0xA8, 0xE1, 0xB9,
+ 0xAA, 0xE1, 0xB9, 0xAC, 0xE1, 0xB9, 0xAE, 0xE1,
+ 0xB9, 0xB0, 0xE1, 0xB9, 0xB2, 0xE1, 0xB9, 0xB4,
+ 0xE1, 0xB9, 0xB6, 0xE1, 0xB9, 0xB8, 0xE1, 0xB9,
+ 0xBA, 0xE1, 0xB9, 0xBC, 0xE1, 0xB9, 0xBE, 0xE1,
+ 0xBA, 0x80, 0xE1, 0xBA, 0x82, 0xE1, 0xBA, 0x84,
+ 0xE1, 0xBA, 0x86, 0xE1, 0xBA, 0x88, 0xE1, 0xBA,
+ 0x8A, 0xE1, 0xBA, 0x8C, 0xE1, 0xBA, 0x8E, 0xE1,
+ 0xBA, 0x90, 0xE1, 0xBA, 0x92, 0xE1, 0xBA, 0x94,
+ 0xE1, 0xB9, 0xA0, 0xE1, 0xBA, 0xA0, 0xE1, 0xBA,
+ 0xA2, 0xE1, 0xBA, 0xA4, 0xE1, 0xBA, 0xA6, 0xE1,
+ 0xBA, 0xA8, 0xE1, 0xBA, 0xAA, 0xE1, 0xBA, 0xAC,
+ 0xE1, 0xBA, 0xAE, 0xE1, 0xBA, 0xB0, 0xE1, 0xBA,
+ 0xB2, 0xE1, 0xBA, 0xB4, 0xE1, 0xBA, 0xB6, 0xE1,
+ 0xBA, 0xB8, 0xE1, 0xBA, 0xBA, 0xE1, 0xBA, 0xBC,
+ 0xE1, 0xBA, 0xBE, 0xE1, 0xBB, 0x80, 0xE1, 0xBB,
+ 0x82, 0xE1, 0xBB, 0x84, 0xE1, 0xBB, 0x86, 0xE1,
+ 0xBB, 0x88, 0xE1, 0xBB, 0x8A, 0xE1, 0xBB, 0x8C,
+ 0xE1, 0xBB, 0x8E, 0xE1, 0xBB, 0x90, 0xE1, 0xBB,
+ 0x92, 0xE1, 0xBB, 0x94, 0xE1, 0xBB, 0x96, 0xE1,
+ 0xBB, 0x98, 0xE1, 0xBB, 0x9A, 0xE1, 0xBB, 0x9C,
+ 0xE1, 0xBB, 0x9E, 0xE1, 0xBB, 0xA0, 0xE1, 0xBB,
+ 0xA2, 0xE1, 0xBB, 0xA4, 0xE1, 0xBB, 0xA6, 0xE1,
+ 0xBB, 0xA8, 0xE1, 0xBB, 0xAA, 0xE1, 0xBB, 0xAC,
+ 0xE1, 0xBB, 0xAE, 0xE1, 0xBB, 0xB0, 0xE1, 0xBB,
+ 0xB2, 0xE1, 0xBB, 0xB4, 0xE1, 0xBB, 0xB6, 0xE1,
+ 0xBB, 0xB8, 0xE1, 0xBC, 0x88, 0xE1, 0xBC, 0x89,
+ 0xE1, 0xBC, 0x8A, 0xE1, 0xBC, 0x8B, 0xE1, 0xBC,
+ 0x8C, 0xE1, 0xBC, 0x8D, 0xE1, 0xBC, 0x8E, 0xE1,
+ 0xBC, 0x8F, 0xE1, 0xBC, 0x98, 0xE1, 0xBC, 0x99,
+ 0xE1, 0xBC, 0x9A, 0xE1, 0xBC, 0x9B, 0xE1, 0xBC,
+ 0x9C, 0xE1, 0xBC, 0x9D, 0xE1, 0xBC, 0xA8, 0xE1,
+ 0xBC, 0xA9, 0xE1, 0xBC, 0xAA, 0xE1, 0xBC, 0xAB,
+ 0xE1, 0xBC, 0xAC, 0xE1, 0xBC, 0xAD, 0xE1, 0xBC,
+ 0xAE, 0xE1, 0xBC, 0xAF, 0xE1, 0xBC, 0xB8, 0xE1,
+ 0xBC, 0xB9, 0xE1, 0xBC, 0xBA, 0xE1, 0xBC, 0xBB,
+ 0xE1, 0xBC, 0xBC, 0xE1, 0xBC, 0xBD, 0xE1, 0xBC,
+ 0xBE, 0xE1, 0xBC, 0xBF, 0xE1, 0xBD, 0x88, 0xE1,
+ 0xBD, 0x89, 0xE1, 0xBD, 0x8A, 0xE1, 0xBD, 0x8B,
+ 0xE1, 0xBD, 0x8C, 0xE1, 0xBD, 0x8D, 0xE1, 0xBD,
+ 0x99, 0xE1, 0xBD, 0x9B, 0xE1, 0xBD, 0x9D, 0xE1,
+ 0xBD, 0x9F, 0xE1, 0xBD, 0xA8, 0xE1, 0xBD, 0xA9,
+ 0xE1, 0xBD, 0xAA, 0xE1, 0xBD, 0xAB, 0xE1, 0xBD,
+ 0xAC, 0xE1, 0xBD, 0xAD, 0xE1, 0xBD, 0xAE, 0xE1,
+ 0xBD, 0xAF, 0xE1, 0xBE, 0xBA, 0xE1, 0xBE, 0xBB,
+ 0xE1, 0xBF, 0x88, 0xE1, 0xBF, 0x89, 0xE1, 0xBF,
+ 0x8A, 0xE1, 0xBF, 0x8B, 0xE1, 0xBF, 0x9A, 0xE1,
+ 0xBF, 0x9B, 0xE1, 0xBF, 0xB8, 0xE1, 0xBF, 0xB9,
+ 0xE1, 0xBF, 0xAA, 0xE1, 0xBF, 0xAB, 0xE1, 0xBF,
+ 0xBA, 0xE1, 0xBF, 0xBB, 0xE1, 0xBE, 0x88, 0xE1,
+ 0xBE, 0x89, 0xE1, 0xBE, 0x8A, 0xE1, 0xBE, 0x8B,
+ 0xE1, 0xBE, 0x8C, 0xE1, 0xBE, 0x8D, 0xE1, 0xBE,
+ 0x8E, 0xE1, 0xBE, 0x8F, 0xE1, 0xBE, 0x98, 0xE1,
+ 0xBE, 0x99, 0xE1, 0xBE, 0x9A, 0xE1, 0xBE, 0x9B,
+ 0xE1, 0xBE, 0x9C, 0xE1, 0xBE, 0x9D, 0xE1, 0xBE,
+ 0x9E, 0xE1, 0xBE, 0x9F, 0xE1, 0xBE, 0xA8, 0xE1,
+ 0xBE, 0xA9, 0xE1, 0xBE, 0xAA, 0xE1, 0xBE, 0xAB,
+ 0xE1, 0xBE, 0xAC, 0xE1, 0xBE, 0xAD, 0xE1, 0xBE,
+ 0xAE, 0xE1, 0xBE, 0xAF, 0xE1, 0xBE, 0xB8, 0xE1,
+ 0xBE, 0xB9, 0xE1, 0xBE, 0xBC, 0xCE, 0x99, 0xE1,
+ 0xBF, 0x8C, 0xE1, 0xBF, 0x98, 0xE1, 0xBF, 0x99,
+ 0xE1, 0xBF, 0xA8, 0xE1, 0xBF, 0xA9, 0xE1, 0xBF,
+ 0xAC, 0xE1, 0xBF, 0xBC, 0xE2, 0x84, 0xB2, 0xE2,
+ 0x85, 0xA0, 0xE2, 0x85, 0xA1, 0xE2, 0x85, 0xA2,
+ 0xE2, 0x85, 0xA3, 0xE2, 0x85, 0xA4, 0xE2, 0x85,
+ 0xA5, 0xE2, 0x85, 0xA6, 0xE2, 0x85, 0xA7, 0xE2,
+ 0x85, 0xA8, 0xE2, 0x85, 0xA9, 0xE2, 0x85, 0xAA,
+ 0xE2, 0x85, 0xAB, 0xE2, 0x85, 0xAC, 0xE2, 0x85,
+ 0xAD, 0xE2, 0x85, 0xAE, 0xE2, 0x85, 0xAF, 0xE2,
+ 0x86, 0x83, 0xE2, 0x92, 0xB6, 0xE2, 0x92, 0xB7,
+ 0xE2, 0x92, 0xB8, 0xE2, 0x92, 0xB9, 0xE2, 0x92,
+ 0xBA, 0xE2, 0x92, 0xBB, 0xE2, 0x92, 0xBC, 0xE2,
+ 0x92, 0xBD, 0xE2, 0x92, 0xBE, 0xE2, 0x92, 0xBF,
+ 0xE2, 0x93, 0x80, 0xE2, 0x93, 0x81, 0xE2, 0x93,
+ 0x82, 0xE2, 0x93, 0x83, 0xE2, 0x93, 0x84, 0xE2,
+ 0x93, 0x85, 0xE2, 0x93, 0x86, 0xE2, 0x93, 0x87,
+ 0xE2, 0x93, 0x88, 0xE2, 0x93, 0x89, 0xE2, 0x93,
+ 0x8A, 0xE2, 0x93, 0x8B, 0xE2, 0x93, 0x8C, 0xE2,
+ 0x93, 0x8D, 0xE2, 0x93, 0x8E, 0xE2, 0x93, 0x8F,
+ 0xE2, 0xB0, 0x80, 0xE2, 0xB0, 0x81, 0xE2, 0xB0,
+ 0x82, 0xE2, 0xB0, 0x83, 0xE2, 0xB0, 0x84, 0xE2,
+ 0xB0, 0x85, 0xE2, 0xB0, 0x86, 0xE2, 0xB0, 0x87,
+ 0xE2, 0xB0, 0x88, 0xE2, 0xB0, 0x89, 0xE2, 0xB0,
+ 0x8A, 0xE2, 0xB0, 0x8B, 0xE2, 0xB0, 0x8C, 0xE2,
+ 0xB0, 0x8D, 0xE2, 0xB0, 0x8E, 0xE2, 0xB0, 0x8F,
+ 0xE2, 0xB0, 0x90, 0xE2, 0xB0, 0x91, 0xE2, 0xB0,
+ 0x92, 0xE2, 0xB0, 0x93, 0xE2, 0xB0, 0x94, 0xE2,
+ 0xB0, 0x95, 0xE2, 0xB0, 0x96, 0xE2, 0xB0, 0x97,
+ 0xE2, 0xB0, 0x98, 0xE2, 0xB0, 0x99, 0xE2, 0xB0,
+ 0x9A, 0xE2, 0xB0, 0x9B, 0xE2, 0xB0, 0x9C, 0xE2,
+ 0xB0, 0x9D, 0xE2, 0xB0, 0x9E, 0xE2, 0xB0, 0x9F,
+ 0xE2, 0xB0, 0xA0, 0xE2, 0xB0, 0xA1, 0xE2, 0xB0,
+ 0xA2, 0xE2, 0xB0, 0xA3, 0xE2, 0xB0, 0xA4, 0xE2,
+ 0xB0, 0xA5, 0xE2, 0xB0, 0xA6, 0xE2, 0xB0, 0xA7,
+ 0xE2, 0xB0, 0xA8, 0xE2, 0xB0, 0xA9, 0xE2, 0xB0,
+ 0xAA, 0xE2, 0xB0, 0xAB, 0xE2, 0xB0, 0xAC, 0xE2,
+ 0xB0, 0xAD, 0xE2, 0xB0, 0xAE, 0xE2, 0xB1, 0xA0,
+ 0xC8, 0xBA, 0xC8, 0xBE, 0xE2, 0xB1, 0xA7, 0xE2,
+ 0xB1, 0xA9, 0xE2, 0xB1, 0xAB, 0xE2, 0xB1, 0xB5,
+ 0xE2, 0xB2, 0x80, 0xE2, 0xB2, 0x82, 0xE2, 0xB2,
+ 0x84, 0xE2, 0xB2, 0x86, 0xE2, 0xB2, 0x88, 0xE2,
+ 0xB2, 0x8A, 0xE2, 0xB2, 0x8C, 0xE2, 0xB2, 0x8E,
+ 0xE2, 0xB2, 0x90, 0xE2, 0xB2, 0x92, 0xE2, 0xB2,
+ 0x94, 0xE2, 0xB2, 0x96, 0xE2, 0xB2, 0x98, 0xE2,
+ 0xB2, 0x9A, 0xE2, 0xB2, 0x9C, 0xE2, 0xB2, 0x9E,
+ 0xE2, 0xB2, 0xA0, 0xE2, 0xB2, 0xA2, 0xE2, 0xB2,
+ 0xA4, 0xE2, 0xB2, 0xA6, 0xE2, 0xB2, 0xA8, 0xE2,
+ 0xB2, 0xAA, 0xE2, 0xB2, 0xAC, 0xE2, 0xB2, 0xAE,
+ 0xE2, 0xB2, 0xB0, 0xE2, 0xB2, 0xB2, 0xE2, 0xB2,
+ 0xB4, 0xE2, 0xB2, 0xB6, 0xE2, 0xB2, 0xB8, 0xE2,
+ 0xB2, 0xBA, 0xE2, 0xB2, 0xBC, 0xE2, 0xB2, 0xBE,
+ 0xE2, 0xB3, 0x80, 0xE2, 0xB3, 0x82, 0xE2, 0xB3,
+ 0x84, 0xE2, 0xB3, 0x86, 0xE2, 0xB3, 0x88, 0xE2,
+ 0xB3, 0x8A, 0xE2, 0xB3, 0x8C, 0xE2, 0xB3, 0x8E,
+ 0xE2, 0xB3, 0x90, 0xE2, 0xB3, 0x92, 0xE2, 0xB3,
+ 0x94, 0xE2, 0xB3, 0x96, 0xE2, 0xB3, 0x98, 0xE2,
+ 0xB3, 0x9A, 0xE2, 0xB3, 0x9C, 0xE2, 0xB3, 0x9E,
+ 0xE2, 0xB3, 0xA0, 0xE2, 0xB3, 0xA2, 0xE1, 0x82,
+ 0xA0, 0xE1, 0x82, 0xA1, 0xE1, 0x82, 0xA2, 0xE1,
+ 0x82, 0xA3, 0xE1, 0x82, 0xA4, 0xE1, 0x82, 0xA5,
+ 0xE1, 0x82, 0xA6, 0xE1, 0x82, 0xA7, 0xE1, 0x82,
+ 0xA8, 0xE1, 0x82, 0xA9, 0xE1, 0x82, 0xAA, 0xE1,
+ 0x82, 0xAB, 0xE1, 0x82, 0xAC, 0xE1, 0x82, 0xAD,
+ 0xE1, 0x82, 0xAE, 0xE1, 0x82, 0xAF, 0xE1, 0x82,
+ 0xB0, 0xE1, 0x82, 0xB1, 0xE1, 0x82, 0xB2, 0xE1,
+ 0x82, 0xB3, 0xE1, 0x82, 0xB4, 0xE1, 0x82, 0xB5,
+ 0xE1, 0x82, 0xB6, 0xE1, 0x82, 0xB7, 0xE1, 0x82,
+ 0xB8, 0xE1, 0x82, 0xB9, 0xE1, 0x82, 0xBA, 0xE1,
+ 0x82, 0xBB, 0xE1, 0x82, 0xBC, 0xE1, 0x82, 0xBD,
+ 0xE1, 0x82, 0xBE, 0xE1, 0x82, 0xBF, 0xE1, 0x83,
+ 0x80, 0xE1, 0x83, 0x81, 0xE1, 0x83, 0x82, 0xE1,
+ 0x83, 0x83, 0xE1, 0x83, 0x84, 0xE1, 0x83, 0x85,
+ 0xEF, 0xBC, 0xA1, 0xEF, 0xBC, 0xA2, 0xEF, 0xBC,
+ 0xA3, 0xEF, 0xBC, 0xA4, 0xEF, 0xBC, 0xA5, 0xEF,
+ 0xBC, 0xA6, 0xEF, 0xBC, 0xA7, 0xEF, 0xBC, 0xA8,
+ 0xEF, 0xBC, 0xA9, 0xEF, 0xBC, 0xAA, 0xEF, 0xBC,
+ 0xAB, 0xEF, 0xBC, 0xAC, 0xEF, 0xBC, 0xAD, 0xEF,
+ 0xBC, 0xAE, 0xEF, 0xBC, 0xAF, 0xEF, 0xBC, 0xB0,
+ 0xEF, 0xBC, 0xB1, 0xEF, 0xBC, 0xB2, 0xEF, 0xBC,
+ 0xB3, 0xEF, 0xBC, 0xB4, 0xEF, 0xBC, 0xB5, 0xEF,
+ 0xBC, 0xB6, 0xEF, 0xBC, 0xB7, 0xEF, 0xBC, 0xB8,
+ 0xEF, 0xBC, 0xB9, 0xEF, 0xBC, 0xBA, 0xF0, 0x90,
+ 0x90, 0x80, 0xF0, 0x90, 0x90, 0x81, 0xF0, 0x90,
+ 0x90, 0x82, 0xF0, 0x90, 0x90, 0x83, 0xF0, 0x90,
+ 0x90, 0x84, 0xF0, 0x90, 0x90, 0x85, 0xF0, 0x90,
+ 0x90, 0x86, 0xF0, 0x90, 0x90, 0x87, 0xF0, 0x90,
+ 0x90, 0x88, 0xF0, 0x90, 0x90, 0x89, 0xF0, 0x90,
+ 0x90, 0x8A, 0xF0, 0x90, 0x90, 0x8B, 0xF0, 0x90,
+ 0x90, 0x8C, 0xF0, 0x90, 0x90, 0x8D, 0xF0, 0x90,
+ 0x90, 0x8E, 0xF0, 0x90, 0x90, 0x8F, 0xF0, 0x90,
+ 0x90, 0x90, 0xF0, 0x90, 0x90, 0x91, 0xF0, 0x90,
+ 0x90, 0x92, 0xF0, 0x90, 0x90, 0x93, 0xF0, 0x90,
+ 0x90, 0x94, 0xF0, 0x90, 0x90, 0x95, 0xF0, 0x90,
+ 0x90, 0x96, 0xF0, 0x90, 0x90, 0x97, 0xF0, 0x90,
+ 0x90, 0x98, 0xF0, 0x90, 0x90, 0x99, 0xF0, 0x90,
+ 0x90, 0x9A, 0xF0, 0x90, 0x90, 0x9B, 0xF0, 0x90,
+ 0x90, 0x9C, 0xF0, 0x90, 0x90, 0x9D, 0xF0, 0x90,
+ 0x90, 0x9E, 0xF0, 0x90, 0x90, 0x9F, 0xF0, 0x90,
+ 0x90, 0xA0, 0xF0, 0x90, 0x90, 0xA1, 0xF0, 0x90,
+ 0x90, 0xA2, 0xF0, 0x90, 0x90, 0xA3, 0xF0, 0x90,
+ 0x90, 0xA4, 0xF0, 0x90, 0x90, 0xA5, 0xF0, 0x90,
+ 0x90, 0xA6, 0xF0, 0x90, 0x90, 0xA7,
+ },
+};
+
+#undef N_
+#undef FIL_
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_U8_TEXTPREP_DATA_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/vnode.h b/sys/cddl/contrib/opensolaris/uts/common/sys/vnode.h
new file mode 100644
index 0000000..c0e5b1b
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/vnode.h
@@ -0,0 +1,395 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL 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 (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#ifndef _SYS_VNODE_H
+#define _SYS_VNODE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include_next <sys/vnode.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define IS_DEVVP(vp) \
+ ((vp)->v_type == VCHR || (vp)->v_type == VBLK || (vp)->v_type == VFIFO)
+
+#define V_XATTRDIR 0x0000 /* attribute unnamed directory */
+
+#define AV_SCANSTAMP_SZ 32 /* length of anti-virus scanstamp */
+
+/*
+ * Structure of all optional attributes.
+ */
+typedef struct xoptattr {
+ timestruc_t xoa_createtime; /* Create time of file */
+ uint8_t xoa_archive;
+ uint8_t xoa_system;
+ uint8_t xoa_readonly;
+ uint8_t xoa_hidden;
+ uint8_t xoa_nounlink;
+ uint8_t xoa_immutable;
+ uint8_t xoa_appendonly;
+ uint8_t xoa_nodump;
+ uint8_t xoa_opaque;
+ uint8_t xoa_av_quarantined;
+ uint8_t xoa_av_modified;
+ uint8_t xoa_av_scanstamp[AV_SCANSTAMP_SZ];
+} xoptattr_t;
+
+/*
+ * The xvattr structure is really a variable length structure that
+ * is made up of:
+ * - The classic vattr_t (xva_vattr)
+ * - a 32 bit quantity (xva_mapsize) that specifies the size of the
+ * attribute bitmaps in 32 bit words.
+ * - A pointer to the returned attribute bitmap (needed because the
+ * previous element, the requested attribute bitmap) is variable lenth.
+ * - The requested attribute bitmap, which is an array of 32 bit words.
+ * Callers use the XVA_SET_REQ() macro to set the bits corresponding to
+ * the attributes that are being requested.
+ * - The returned attribute bitmap, which is an array of 32 bit words.
+ * File systems that support optional attributes use the XVA_SET_RTN()
+ * macro to set the bits corresponding to the attributes that are being
+ * returned.
+ * - The xoptattr_t structure which contains the attribute values
+ *
+ * xva_mapsize determines how many words in the attribute bitmaps.
+ * Immediately following the attribute bitmaps is the xoptattr_t.
+ * xva_getxoptattr() is used to get the pointer to the xoptattr_t
+ * section.
+ */
+
+#define XVA_MAPSIZE 3 /* Size of attr bitmaps */
+#define XVA_MAGIC 0x78766174 /* Magic # for verification */
+
+/*
+ * The xvattr structure is an extensible structure which permits optional
+ * attributes to be requested/returned. File systems may or may not support
+ * optional attributes. They do so at their own discretion but if they do
+ * support optional attributes, they must register the VFSFT_XVATTR feature
+ * so that the optional attributes can be set/retrived.
+ *
+ * The fields of the xvattr structure are:
+ *
+ * xva_vattr - The first element of an xvattr is a legacy vattr structure
+ * which includes the common attributes. If AT_XVATTR is set in the va_mask
+ * then the entire structure is treated as an xvattr. If AT_XVATTR is not
+ * set, then only the xva_vattr structure can be used.
+ *
+ * xva_magic - 0x78766174 (hex for "xvat"). Magic number for verification.
+ *
+ * xva_mapsize - Size of requested and returned attribute bitmaps.
+ *
+ * xva_rtnattrmapp - Pointer to xva_rtnattrmap[]. We need this since the
+ * size of the array before it, xva_reqattrmap[], could change which means
+ * the location of xva_rtnattrmap[] could change. This will allow unbundled
+ * file systems to find the location of xva_rtnattrmap[] when the sizes change.
+ *
+ * xva_reqattrmap[] - Array of requested attributes. Attributes are
+ * represented by a specific bit in a specific element of the attribute
+ * map array. Callers set the bits corresponding to the attributes
+ * that the caller wants to get/set.
+ *
+ * xva_rtnattrmap[] - Array of attributes that the file system was able to
+ * process. Not all file systems support all optional attributes. This map
+ * informs the caller which attributes the underlying file system was able
+ * to set/get. (Same structure as the requested attributes array in terms
+ * of each attribute corresponding to specific bits and array elements.)
+ *
+ * xva_xoptattrs - Structure containing values of optional attributes.
+ * These values are only valid if the corresponding bits in xva_reqattrmap
+ * are set and the underlying file system supports those attributes.
+ */
+typedef struct xvattr {
+ vattr_t xva_vattr; /* Embedded vattr structure */
+ uint32_t xva_magic; /* Magic Number */
+ uint32_t xva_mapsize; /* Size of attr bitmap (32-bit words) */
+ uint32_t *xva_rtnattrmapp; /* Ptr to xva_rtnattrmap[] */
+ uint32_t xva_reqattrmap[XVA_MAPSIZE]; /* Requested attrs */
+ uint32_t xva_rtnattrmap[XVA_MAPSIZE]; /* Returned attrs */
+ xoptattr_t xva_xoptattrs; /* Optional attributes */
+} xvattr_t;
+
+/*
+ * Attributes of interest to the caller of setattr or getattr.
+ */
+#define AT_TYPE 0x00001
+#define AT_MODE 0x00002
+#define AT_UID 0x00004
+#define AT_GID 0x00008
+#define AT_FSID 0x00010
+#define AT_NODEID 0x00020
+#define AT_NLINK 0x00040
+#define AT_SIZE 0x00080
+#define AT_ATIME 0x00100
+#define AT_MTIME 0x00200
+#define AT_CTIME 0x00400
+#define AT_RDEV 0x00800
+#define AT_BLKSIZE 0x01000
+#define AT_NBLOCKS 0x02000
+/* 0x04000 */ /* unused */
+#define AT_SEQ 0x08000
+/*
+ * If AT_XVATTR is set then there are additional bits to process in
+ * the xvattr_t's attribute bitmap. If this is not set then the bitmap
+ * MUST be ignored. Note that this bit must be set/cleared explicitly.
+ * That is, setting AT_ALL will NOT set AT_XVATTR.
+ */
+#define AT_XVATTR 0x10000
+
+#define AT_ALL (AT_TYPE|AT_MODE|AT_UID|AT_GID|AT_FSID|AT_NODEID|\
+ AT_NLINK|AT_SIZE|AT_ATIME|AT_MTIME|AT_CTIME|\
+ AT_RDEV|AT_BLKSIZE|AT_NBLOCKS|AT_SEQ)
+
+#define AT_STAT (AT_MODE|AT_UID|AT_GID|AT_FSID|AT_NODEID|AT_NLINK|\
+ AT_SIZE|AT_ATIME|AT_MTIME|AT_CTIME|AT_RDEV|AT_TYPE)
+
+#define AT_TIMES (AT_ATIME|AT_MTIME|AT_CTIME)
+
+#define AT_NOSET (AT_NLINK|AT_RDEV|AT_FSID|AT_NODEID|AT_TYPE|\
+ AT_BLKSIZE|AT_NBLOCKS|AT_SEQ)
+
+/*
+ * Attribute bits used in the extensible attribute's (xva's) attribute
+ * bitmaps. Note that the bitmaps are made up of a variable length number
+ * of 32-bit words. The convention is to use XAT{n}_{attrname} where "n"
+ * is the element in the bitmap (starting at 1). This convention is for
+ * the convenience of the maintainer to keep track of which element each
+ * attribute belongs to.
+ *
+ * NOTE THAT CONSUMERS MUST *NOT* USE THE XATn_* DEFINES DIRECTLY. CONSUMERS
+ * MUST USE THE XAT_* DEFINES.
+ */
+#define XAT0_INDEX 0LL /* Index into bitmap for XAT0 attrs */
+#define XAT0_CREATETIME 0x00000001 /* Create time of file */
+#define XAT0_ARCHIVE 0x00000002 /* Archive */
+#define XAT0_SYSTEM 0x00000004 /* System */
+#define XAT0_READONLY 0x00000008 /* Readonly */
+#define XAT0_HIDDEN 0x00000010 /* Hidden */
+#define XAT0_NOUNLINK 0x00000020 /* Nounlink */
+#define XAT0_IMMUTABLE 0x00000040 /* immutable */
+#define XAT0_APPENDONLY 0x00000080 /* appendonly */
+#define XAT0_NODUMP 0x00000100 /* nodump */
+#define XAT0_OPAQUE 0x00000200 /* opaque */
+#define XAT0_AV_QUARANTINED 0x00000400 /* anti-virus quarantine */
+#define XAT0_AV_MODIFIED 0x00000800 /* anti-virus modified */
+#define XAT0_AV_SCANSTAMP 0x00001000 /* anti-virus scanstamp */
+
+#define XAT0_ALL_ATTRS (XAT0_CREATETIME|XAT0_ARCHIVE|XAT0_SYSTEM| \
+ XAT0_READONLY|XAT0_HIDDEN|XAT0_NOUNLINK|XAT0_IMMUTABLE|XAT0_APPENDONLY| \
+ XAT0_NODUMP|XAT0_OPAQUE|XAT0_AV_QUARANTINED| \
+ XAT0_AV_MODIFIED|XAT0_AV_SCANSTAMP)
+
+/* Support for XAT_* optional attributes */
+#define XVA_MASK 0xffffffff /* Used to mask off 32 bits */
+#define XVA_SHFT 32 /* Used to shift index */
+
+/*
+ * Used to pry out the index and attribute bits from the XAT_* attributes
+ * defined below. Note that we're masking things down to 32 bits then
+ * casting to uint32_t.
+ */
+#define XVA_INDEX(attr) ((uint32_t)(((attr) >> XVA_SHFT) & XVA_MASK))
+#define XVA_ATTRBIT(attr) ((uint32_t)((attr) & XVA_MASK))
+
+/*
+ * The following defines present a "flat namespace" so that consumers don't
+ * need to keep track of which element belongs to which bitmap entry.
+ *
+ * NOTE THAT THESE MUST NEVER BE OR-ed TOGETHER
+ */
+#define XAT_CREATETIME ((XAT0_INDEX << XVA_SHFT) | XAT0_CREATETIME)
+#define XAT_ARCHIVE ((XAT0_INDEX << XVA_SHFT) | XAT0_ARCHIVE)
+#define XAT_SYSTEM ((XAT0_INDEX << XVA_SHFT) | XAT0_SYSTEM)
+#define XAT_READONLY ((XAT0_INDEX << XVA_SHFT) | XAT0_READONLY)
+#define XAT_HIDDEN ((XAT0_INDEX << XVA_SHFT) | XAT0_HIDDEN)
+#define XAT_NOUNLINK ((XAT0_INDEX << XVA_SHFT) | XAT0_NOUNLINK)
+#define XAT_IMMUTABLE ((XAT0_INDEX << XVA_SHFT) | XAT0_IMMUTABLE)
+#define XAT_APPENDONLY ((XAT0_INDEX << XVA_SHFT) | XAT0_APPENDONLY)
+#define XAT_NODUMP ((XAT0_INDEX << XVA_SHFT) | XAT0_NODUMP)
+#define XAT_OPAQUE ((XAT0_INDEX << XVA_SHFT) | XAT0_OPAQUE)
+#define XAT_AV_QUARANTINED ((XAT0_INDEX << XVA_SHFT) | XAT0_AV_QUARANTINED)
+#define XAT_AV_MODIFIED ((XAT0_INDEX << XVA_SHFT) | XAT0_AV_MODIFIED)
+#define XAT_AV_SCANSTAMP ((XAT0_INDEX << XVA_SHFT) | XAT0_AV_SCANSTAMP)
+
+/*
+ * The returned attribute map array (xva_rtnattrmap[]) is located past the
+ * requested attribute map array (xva_reqattrmap[]). Its location changes
+ * when the array sizes change. We use a separate pointer in a known location
+ * (xva_rtnattrmapp) to hold the location of xva_rtnattrmap[]. This is
+ * set in xva_init()
+ */
+#define XVA_RTNATTRMAP(xvap) ((xvap)->xva_rtnattrmapp)
+
+/*
+ * XVA_SET_REQ() sets an attribute bit in the proper element in the bitmap
+ * of requested attributes (xva_reqattrmap[]).
+ */
+#define XVA_SET_REQ(xvap, attr) \
+ ASSERT((xvap)->xva_vattr.va_mask | AT_XVATTR); \
+ ASSERT((xvap)->xva_magic == XVA_MAGIC); \
+ (xvap)->xva_reqattrmap[XVA_INDEX(attr)] |= XVA_ATTRBIT(attr)
+
+/*
+ * XVA_SET_RTN() sets an attribute bit in the proper element in the bitmap
+ * of returned attributes (xva_rtnattrmap[]).
+ */
+#define XVA_SET_RTN(xvap, attr) \
+ ASSERT((xvap)->xva_vattr.va_mask | AT_XVATTR); \
+ ASSERT((xvap)->xva_magic == XVA_MAGIC); \
+ (XVA_RTNATTRMAP(xvap))[XVA_INDEX(attr)] |= XVA_ATTRBIT(attr)
+
+/*
+ * XVA_ISSET_REQ() checks the requested attribute bitmap (xva_reqattrmap[])
+ * to see of the corresponding attribute bit is set. If so, returns non-zero.
+ */
+#define XVA_ISSET_REQ(xvap, attr) \
+ ((((xvap)->xva_vattr.va_mask | AT_XVATTR) && \
+ ((xvap)->xva_magic == XVA_MAGIC) && \
+ ((xvap)->xva_mapsize > XVA_INDEX(attr))) ? \
+ ((xvap)->xva_reqattrmap[XVA_INDEX(attr)] & XVA_ATTRBIT(attr)) : 0)
+
+/*
+ * XVA_ISSET_RTN() checks the returned attribute bitmap (xva_rtnattrmap[])
+ * to see of the corresponding attribute bit is set. If so, returns non-zero.
+ */
+#define XVA_ISSET_RTN(xvap, attr) \
+ ((((xvap)->xva_vattr.va_mask | AT_XVATTR) && \
+ ((xvap)->xva_magic == XVA_MAGIC) && \
+ ((xvap)->xva_mapsize > XVA_INDEX(attr))) ? \
+ ((XVA_RTNATTRMAP(xvap))[XVA_INDEX(attr)] & XVA_ATTRBIT(attr)) : 0)
+
+#define MODEMASK 07777 /* mode bits plus permission bits */
+#define PERMMASK 00777 /* permission bits */
+
+
+/*
+ * VOP_ACCESS flags
+ */
+#define V_ACE_MASK 0x1 /* mask represents NFSv4 ACE permissions */
+#define V_APPEND 0x2 /* want to do append only check */
+
+/*
+ * Flags for vnode operations.
+ */
+enum rm { RMFILE, RMDIRECTORY }; /* rm or rmdir (remove) */
+enum create { CRCREAT, CRMKNOD, CRMKDIR }; /* reason for create */
+
+/*
+ * Structure used on VOP_GETSECATTR and VOP_SETSECATTR operations
+ */
+
+typedef struct vsecattr {
+ uint_t vsa_mask; /* See below */
+ int vsa_aclcnt; /* ACL entry count */
+ void *vsa_aclentp; /* pointer to ACL entries */
+ int vsa_dfaclcnt; /* default ACL entry count */
+ void *vsa_dfaclentp; /* pointer to default ACL entries */
+ size_t vsa_aclentsz; /* ACE size in bytes of vsa_aclentp */
+ uint_t vsa_aclflags; /* ACE ACL flags */
+} vsecattr_t;
+
+/* vsa_mask values */
+#define VSA_ACL 0x0001
+#define VSA_ACLCNT 0x0002
+#define VSA_DFACL 0x0004
+#define VSA_DFACLCNT 0x0008
+#define VSA_ACE 0x0010
+#define VSA_ACECNT 0x0020
+#define VSA_ACE_ALLTYPES 0x0040
+#define VSA_ACE_ACLFLAGS 0x0080 /* get/set ACE ACL flags */
+
+/*
+ * Structure used by various vnode operations to determine
+ * the context (pid, host, identity) of a caller.
+ *
+ * The cc_caller_id is used to identify one or more callers who invoke
+ * operations, possibly on behalf of others. For example, the NFS
+ * server could have it's own cc_caller_id which can be detected by
+ * vnode/vfs operations or (FEM) monitors on those operations. New
+ * caller IDs are generated by fs_new_caller_id().
+ */
+typedef struct caller_context {
+ pid_t cc_pid; /* Process ID of the caller */
+ int cc_sysid; /* System ID, used for remote calls */
+ u_longlong_t cc_caller_id; /* Identifier for (set of) caller(s) */
+ ulong_t cc_flags;
+} caller_context_t;
+
+/*
+ * Flags for VOP_LOOKUP
+ *
+ * Defined in file.h, but also possible, FIGNORECASE
+ *
+ */
+#define LOOKUP_DIR 0x01 /* want parent dir vp */
+#define LOOKUP_XATTR 0x02 /* lookup up extended attr dir */
+#define CREATE_XATTR_DIR 0x04 /* Create extended attr dir */
+#define LOOKUP_HAVE_SYSATTR_DIR 0x08 /* Already created virtual GFS dir */
+
+/*
+ * Flags for VOP_READDIR
+ */
+#define V_RDDIR_ENTFLAGS 0x01 /* request dirent flags */
+
+/*
+ * Extensible vnode attribute (xva) routines:
+ * xva_init() initializes an xvattr_t (zero struct, init mapsize, set AT_XATTR)
+ * xva_getxoptattr() returns a ponter to the xoptattr_t section of xvattr_t
+ */
+void xva_init(xvattr_t *);
+xoptattr_t *xva_getxoptattr(xvattr_t *); /* Get ptr to xoptattr_t */
+
+/*
+ * Flags to VOP_SETATTR/VOP_GETATTR.
+ */
+#define ATTR_UTIME 0x01 /* non-default utime(2) request */
+#define ATTR_EXEC 0x02 /* invocation from exec(2) */
+#define ATTR_COMM 0x04 /* yield common vp attributes */
+#define ATTR_HINT 0x08 /* information returned will be `hint' */
+#define ATTR_REAL 0x10 /* yield attributes of the real vp */
+#define ATTR_NOACLCHECK 0x20 /* Don't check ACL when checking permissions */
+#define ATTR_TRIGGER 0x40 /* Mount first if vnode is a trigger mount */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_VNODE_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/zmod/zmod.c b/sys/cddl/contrib/opensolaris/uts/common/zmod/zmod.c
index 2627239..a251331 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/zmod/zmod.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/zmod/zmod.c
@@ -20,16 +20,15 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#include <sys/zmod.h>
#include "zlib.h"
+#include "zutil.h"
/*
* Uncompress the buffer 'src' into the buffer 'dst'. The caller must store
@@ -49,7 +48,12 @@ z_uncompress(void *dst, size_t *dstlen, const void *src, size_t srclen)
zs.next_out = dst;
zs.avail_out = *dstlen;
- if ((err = inflateInit(&zs)) != Z_OK)
+ /*
+ * Call inflateInit2() specifying a window size of DEF_WBITS
+ * with the 6th bit set to indicate that the compression format
+ * type (zlib or gzip) should be automatically detected.
+ */
+ if ((err = inflateInit2(&zs, DEF_WBITS | 0x20)) != Z_OK)
return (err);
if ((err = inflate(&zs, Z_FINISH)) != Z_STREAM_END) {
diff --git a/sys/conf/files b/sys/conf/files
index a8f5e41..6ab4068 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1764,6 +1764,7 @@ kern/kern_module.c standard
kern/kern_mtxpool.c standard
kern/kern_mutex.c standard
kern/kern_ntptime.c standard
+kern/kern_osd.c standard
kern/kern_physio.c standard
kern/kern_pmc.c standard
kern/kern_poll.c optional device_polling
diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c
index 3afbc91..cee1ca6 100644
--- a/sys/kern/kern_jail.c
+++ b/sys/kern/kern_jail.c
@@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$");
#include <sys/sysctl.h>
#include <sys/vnode.h>
#include <sys/vimage.h>
+#include <sys/osd.h>
#include <net/if.h>
#include <netinet/in.h>
@@ -86,22 +87,6 @@ struct sx allprison_lock;
int lastprid = 0;
int prisoncount = 0;
-/*
- * List of jail services. Protected by allprison_lock.
- */
-TAILQ_HEAD(prison_services_head, prison_service);
-static struct prison_services_head prison_services =
- TAILQ_HEAD_INITIALIZER(prison_services);
-static int prison_service_slots = 0;
-
-struct prison_service {
- prison_create_t ps_create;
- prison_destroy_t ps_destroy;
- int ps_slotno;
- TAILQ_ENTRY(prison_service) ps_next;
- char ps_name[0];
-};
-
static void init_prison(void *);
static void prison_complete(void *context, int pending);
static int sysctl_jail_list(SYSCTL_HANDLER_ARGS);
@@ -126,7 +111,6 @@ jail(struct thread *td, struct jail_args *uap)
{
struct nameidata nd;
struct prison *pr, *tpr;
- struct prison_service *psrv;
struct jail j;
struct jail_attach_args jaa;
int vfslocked, error, tryprid;
@@ -159,12 +143,7 @@ jail(struct thread *td, struct jail_args *uap)
pr->pr_ip = j.ip_number;
pr->pr_linux = NULL;
pr->pr_securelevel = securelevel;
- if (prison_service_slots == 0)
- pr->pr_slots = NULL;
- else {
- pr->pr_slots = malloc(sizeof(*pr->pr_slots) * prison_service_slots,
- M_PRISON, M_ZERO | M_WAITOK);
- }
+ bzero(&pr->pr_osd, sizeof(pr->pr_osd));
/* Determine next pr_id and add prison to allprison list. */
sx_xlock(&allprison_lock);
@@ -186,11 +165,7 @@ next:
pr->pr_id = jaa.jid = lastprid = tryprid;
LIST_INSERT_HEAD(&allprison, pr, pr_list);
prisoncount++;
- sx_downgrade(&allprison_lock);
- TAILQ_FOREACH(psrv, &prison_services, ps_next) {
- psrv->ps_create(psrv, pr);
- }
- sx_sunlock(&allprison_lock);
+ sx_xunlock(&allprison_lock);
error = jail_attach(td, &jaa);
if (error)
@@ -204,14 +179,8 @@ e_dropprref:
sx_xlock(&allprison_lock);
LIST_REMOVE(pr, pr_list);
prisoncount--;
- sx_downgrade(&allprison_lock);
- TAILQ_FOREACH(psrv, &prison_services, ps_next) {
- psrv->ps_destroy(psrv, pr);
- }
- sx_sunlock(&allprison_lock);
+ sx_xunlock(&allprison_lock);
e_dropvnref:
- if (pr->pr_slots != NULL)
- free(pr->pr_slots, M_PRISON);
vfslocked = VFS_LOCK_GIANT(pr->pr_root->v_mount);
vrele(pr->pr_root);
VFS_UNLOCK_GIANT(vfslocked);
@@ -311,10 +280,10 @@ prison_find(int prid)
}
void
-prison_free(struct prison *pr)
+prison_free_locked(struct prison *pr)
{
- mtx_lock(&pr->pr_mtx);
+ mtx_assert(&pr->pr_mtx, MA_OWNED);
pr->pr_ref--;
if (pr->pr_ref == 0) {
mtx_unlock(&pr->pr_mtx);
@@ -325,10 +294,17 @@ prison_free(struct prison *pr)
mtx_unlock(&pr->pr_mtx);
}
+void
+prison_free(struct prison *pr)
+{
+
+ mtx_lock(&pr->pr_mtx);
+ prison_free_locked(pr);
+}
+
static void
prison_complete(void *context, int pending)
{
- struct prison_service *psrv;
struct prison *pr;
int vfslocked;
@@ -337,13 +313,10 @@ prison_complete(void *context, int pending)
sx_xlock(&allprison_lock);
LIST_REMOVE(pr, pr_list);
prisoncount--;
- sx_downgrade(&allprison_lock);
- TAILQ_FOREACH(psrv, &prison_services, ps_next) {
- psrv->ps_destroy(psrv, pr);
- }
- sx_sunlock(&allprison_lock);
- if (pr->pr_slots != NULL)
- free(pr->pr_slots, M_PRISON);
+ sx_xunlock(&allprison_lock);
+
+ /* Free all OSD associated to this jail. */
+ osd_jail_exit(pr);
vfslocked = VFS_LOCK_GIANT(pr->pr_root->v_mount);
vrele(pr->pr_root);
@@ -356,13 +329,21 @@ prison_complete(void *context, int pending)
}
void
-prison_hold(struct prison *pr)
+prison_hold_locked(struct prison *pr)
{
- mtx_lock(&pr->pr_mtx);
+ mtx_assert(&pr->pr_mtx, MA_OWNED);
KASSERT(pr->pr_ref > 0,
("Trying to hold dead prison (id=%d).", pr->pr_id));
pr->pr_ref++;
+}
+
+void
+prison_hold(struct prison *pr)
+{
+
+ mtx_lock(&pr->pr_mtx);
+ prison_hold_locked(pr);
mtx_unlock(&pr->pr_mtx);
}
@@ -762,193 +743,6 @@ prison_priv_check(struct ucred *cred, int priv)
}
}
-/*
- * Register jail service. Provides 'create' and 'destroy' methods.
- * 'create' method will be called for every existing jail and all
- * jails in the future as they beeing created.
- * 'destroy' method will be called for every jail going away and
- * for all existing jails at the time of service deregistration.
- */
-struct prison_service *
-prison_service_register(const char *name, prison_create_t create,
- prison_destroy_t destroy)
-{
- struct prison_service *psrv, *psrv2;
- struct prison *pr;
- int reallocate = 1, slotno = 0;
- void **slots, **oldslots;
-
- psrv = malloc(sizeof(*psrv) + strlen(name) + 1, M_PRISON,
- M_WAITOK | M_ZERO);
- psrv->ps_create = create;
- psrv->ps_destroy = destroy;
- strcpy(psrv->ps_name, name);
- /*
- * Grab the allprison_lock here, so we won't miss any jail
- * creation/destruction.
- */
- sx_xlock(&allprison_lock);
-#ifdef INVARIANTS
- /*
- * Verify if service is not already registered.
- */
- TAILQ_FOREACH(psrv2, &prison_services, ps_next) {
- KASSERT(strcmp(psrv2->ps_name, name) != 0,
- ("jail service %s already registered", name));
- }
-#endif
- /*
- * Find free slot. When there is no existing free slot available,
- * allocate one at the end.
- */
- TAILQ_FOREACH(psrv2, &prison_services, ps_next) {
- if (psrv2->ps_slotno != slotno) {
- KASSERT(slotno < psrv2->ps_slotno,
- ("Invalid slotno (slotno=%d >= ps_slotno=%d",
- slotno, psrv2->ps_slotno));
- /* We found free slot. */
- reallocate = 0;
- break;
- }
- slotno++;
- }
- psrv->ps_slotno = slotno;
- /*
- * Keep the list sorted by slot number.
- */
- if (psrv2 != NULL) {
- KASSERT(reallocate == 0, ("psrv2 != NULL && reallocate != 0"));
- TAILQ_INSERT_BEFORE(psrv2, psrv, ps_next);
- } else {
- KASSERT(reallocate == 1, ("psrv2 == NULL && reallocate == 0"));
- TAILQ_INSERT_TAIL(&prison_services, psrv, ps_next);
- }
- prison_service_slots++;
- sx_downgrade(&allprison_lock);
- /*
- * Allocate memory for new slot if we didn't found empty one.
- * Do not use realloc(9), because pr_slots is protected with a mutex,
- * so we can't sleep.
- */
- LIST_FOREACH(pr, &allprison, pr_list) {
- if (reallocate) {
- /* First allocate memory with M_WAITOK. */
- slots = malloc(sizeof(*slots) * prison_service_slots,
- M_PRISON, M_WAITOK);
- /* Now grab the mutex and replace pr_slots. */
- mtx_lock(&pr->pr_mtx);
- oldslots = pr->pr_slots;
- if (psrv->ps_slotno > 0) {
- bcopy(oldslots, slots,
- sizeof(*slots) * (prison_service_slots - 1));
- }
- slots[psrv->ps_slotno] = NULL;
- pr->pr_slots = slots;
- mtx_unlock(&pr->pr_mtx);
- if (oldslots != NULL)
- free(oldslots, M_PRISON);
- }
- /*
- * Call 'create' method for each existing jail.
- */
- psrv->ps_create(psrv, pr);
- }
- sx_sunlock(&allprison_lock);
-
- return (psrv);
-}
-
-void
-prison_service_deregister(struct prison_service *psrv)
-{
- struct prison *pr;
- void **slots, **oldslots;
- int last = 0;
-
- sx_xlock(&allprison_lock);
- if (TAILQ_LAST(&prison_services, prison_services_head) == psrv)
- last = 1;
- TAILQ_REMOVE(&prison_services, psrv, ps_next);
- prison_service_slots--;
- sx_downgrade(&allprison_lock);
- LIST_FOREACH(pr, &allprison, pr_list) {
- /*
- * Call 'destroy' method for every currently existing jail.
- */
- psrv->ps_destroy(psrv, pr);
- /*
- * If this is the last slot, free the memory allocated for it.
- */
- if (last) {
- if (prison_service_slots == 0)
- slots = NULL;
- else {
- slots = malloc(sizeof(*slots) * prison_service_slots,
- M_PRISON, M_WAITOK);
- }
- mtx_lock(&pr->pr_mtx);
- oldslots = pr->pr_slots;
- /*
- * We require setting slot to NULL after freeing it,
- * this way we can check for memory leaks here.
- */
- KASSERT(oldslots[psrv->ps_slotno] == NULL,
- ("Slot %d (service %s, jailid=%d) still contains data?",
- psrv->ps_slotno, psrv->ps_name, pr->pr_id));
- if (psrv->ps_slotno > 0) {
- bcopy(oldslots, slots,
- sizeof(*slots) * prison_service_slots);
- }
- pr->pr_slots = slots;
- mtx_unlock(&pr->pr_mtx);
- KASSERT(oldslots != NULL, ("oldslots == NULL"));
- free(oldslots, M_PRISON);
- }
- }
- sx_sunlock(&allprison_lock);
- free(psrv, M_PRISON);
-}
-
-/*
- * Function sets data for the given jail in slot assigned for the given
- * jail service.
- */
-void
-prison_service_data_set(struct prison_service *psrv, struct prison *pr,
- void *data)
-{
-
- mtx_assert(&pr->pr_mtx, MA_OWNED);
- pr->pr_slots[psrv->ps_slotno] = data;
-}
-
-/*
- * Function clears slots assigned for the given jail service in the given
- * prison structure and returns current slot data.
- */
-void *
-prison_service_data_del(struct prison_service *psrv, struct prison *pr)
-{
- void *data;
-
- mtx_assert(&pr->pr_mtx, MA_OWNED);
- data = pr->pr_slots[psrv->ps_slotno];
- pr->pr_slots[psrv->ps_slotno] = NULL;
- return (data);
-}
-
-/*
- * Function returns current data from the slot assigned to the given jail
- * service for the given jail.
- */
-void *
-prison_service_data_get(struct prison_service *psrv, struct prison *pr)
-{
-
- mtx_assert(&pr->pr_mtx, MA_OWNED);
- return (pr->pr_slots[psrv->ps_slotno]);
-}
-
static int
sysctl_jail_list(SYSCTL_HANDLER_ARGS)
{
diff --git a/sys/kern/kern_osd.c b/sys/kern/kern_osd.c
new file mode 100644
index 0000000..d9563e6
--- /dev/null
+++ b/sys/kern/kern_osd.c
@@ -0,0 +1,301 @@
+/*-
+ * Copyright (c) 2007 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/proc.h>
+#include <sys/osd.h>
+
+/* OSD (Object Specific Data) */
+
+static MALLOC_DEFINE(M_OSD, "osd", "Object Specific Data");
+
+static int osd_debug = 0;
+TUNABLE_INT("debug.osd", &osd_debug);
+SYSCTL_INT(_debug, OID_AUTO, osd, CTLFLAG_RW, &osd_debug, 0, "OSD debug level");
+
+#define OSD_DEBUG(...) do { \
+ if (osd_debug) { \
+ printf("OSD (%s:%u): ", __func__, __LINE__); \
+ printf(__VA_ARGS__); \
+ printf("\n"); \
+ } \
+} while (0)
+
+/*
+ * Lists of objects with OSD.
+ */
+static LIST_HEAD(, osd) osd_list[OSD_LAST + 1];
+static osd_destructor_t *osd_destructors[OSD_LAST + 1];
+static u_int osd_nslots[OSD_LAST + 1];
+static struct mtx osd_lock[OSD_LAST + 1];
+
+static void
+osd_default_destructor(void *value __unused)
+{
+ /* Do nothing. */
+}
+
+int
+osd_register(u_int type, osd_destructor_t destructor)
+{
+ void *newptr;
+ u_int i;
+
+ KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
+
+ /*
+ * If no destructor is given, use default one. We need to use some
+ * destructor, because NULL destructor means unused slot.
+ */
+ if (destructor == NULL)
+ destructor = osd_default_destructor;
+
+ mtx_lock(&osd_lock[type]);
+ /*
+ * First, we try to find unused slot.
+ */
+ for (i = 0; i < osd_nslots[type]; i++) {
+ if (osd_destructors[type][i] == NULL) {
+ OSD_DEBUG("Unused slot found (type=%u, slot=%u).",
+ type, i);
+ break;
+ }
+ }
+ /*
+ * If no unused slot was found, allocate one.
+ */
+ if (i == osd_nslots[type]) {
+ osd_nslots[type]++;
+ newptr = realloc(osd_destructors[type],
+ sizeof(osd_destructor_t) * osd_nslots[type], M_OSD,
+ M_NOWAIT | M_ZERO);
+ if (newptr == NULL) {
+ mtx_unlock(&osd_lock[type]);
+ return (0);
+ }
+ osd_destructors[type] = newptr;
+ OSD_DEBUG("New slot allocated (type=%u, slot=%u).",
+ type, i + 1);
+ }
+ osd_destructors[type][i] = destructor;
+ mtx_unlock(&osd_lock[type]);
+ return (i + 1);
+}
+
+void
+osd_deregister(u_int type, u_int slot)
+{
+ struct osd *osd, *tosd;
+
+ KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
+ KASSERT(slot > 0, ("Invalid slot."));
+ KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot."));
+
+ mtx_lock(&osd_lock[type]);
+ /*
+ * Free all OSD for the given slot.
+ */
+ LIST_FOREACH_SAFE(osd, &osd_list[type], osd_next, tosd) {
+ osd_del(type, osd, slot);
+ }
+ /*
+ * Set destructor to NULL to free the slot.
+ */
+ osd_destructors[type][slot - 1] = NULL;
+ if (slot == osd_nslots[type]) {
+ osd_nslots[type]--;
+ osd_destructors[type] = realloc(osd_destructors[type],
+ sizeof(osd_destructor_t) * osd_nslots[type], M_OSD,
+ M_NOWAIT | M_ZERO);
+ /*
+ * We always reallocate to smaller size, so we assume it will
+ * always succeed.
+ */
+ KASSERT(osd_destructors[type] != NULL, ("realloc() failed"));
+ OSD_DEBUG("Deregistration of the last slot (type=%u, slot=%u).",
+ type, slot);
+ } else {
+ OSD_DEBUG("Slot deregistration (type=%u, slot=%u).",
+ type, slot);
+ }
+ mtx_unlock(&osd_lock[type]);
+}
+
+int
+osd_set(u_int type, struct osd *osd, u_int slot, void *value)
+{
+
+ KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
+ KASSERT(slot > 0, ("Invalid slot."));
+ KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot."));
+
+ if (osd->osd_nslots == 0) {
+ /*
+ * First OSD for this object, so we need to allocate space and
+ * put it onto the list.
+ */
+ osd->osd_slots = malloc(sizeof(void *) * slot, M_OSD,
+ M_NOWAIT | M_ZERO);
+ if (osd->osd_slots == NULL)
+ return (ENOMEM);
+ osd->osd_nslots = slot;
+ mtx_lock(&osd_lock[type]);
+ LIST_INSERT_HEAD(&osd_list[type], osd, osd_next);
+ mtx_unlock(&osd_lock[type]);
+ OSD_DEBUG("Setting first slot (type=%u).", type);
+ } else if (slot > osd->osd_nslots) {
+ void *newptr;
+
+ /*
+ * Too few slots allocated here, needs to extend the array.
+ */
+ newptr = realloc(osd->osd_slots, sizeof(void *) * slot, M_OSD,
+ M_NOWAIT | M_ZERO);
+ if (newptr == NULL)
+ return (ENOMEM);
+ osd->osd_slots = newptr;
+ osd->osd_nslots = slot;
+ OSD_DEBUG("Growing slots array (type=%u).", type);
+ }
+ OSD_DEBUG("Setting slot value (type=%u, slot=%u, value=%p).", type,
+ slot, value);
+ osd->osd_slots[slot - 1] = value;
+ return (0);
+}
+
+void *
+osd_get(u_int type, struct osd *osd, u_int slot)
+{
+
+ KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
+ KASSERT(slot > 0, ("Invalid slot."));
+ KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot."));
+
+ if (slot > osd->osd_nslots) {
+ OSD_DEBUG("Slot doesn't exist (type=%u, slot=%u).", type, slot);
+ return (NULL);
+ }
+
+ OSD_DEBUG("Returning slot value (type=%u, slot=%u, value=%p).", type,
+ slot, osd->osd_slots[slot - 1]);
+ return (osd->osd_slots[slot - 1]);
+}
+
+void
+osd_del(u_int type, struct osd *osd, u_int slot)
+{
+ int i;
+
+ KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
+ KASSERT(slot > 0, ("Invalid slot."));
+ KASSERT(osd_destructors[type][slot - 1] != NULL, ("Unused slot."));
+
+ OSD_DEBUG("Deleting slot (type=%u, slot=%u).", type, slot);
+
+ if (slot > osd->osd_nslots) {
+ OSD_DEBUG("Slot doesn't exist (type=%u, slot=%u).", type, slot);
+ return;
+ }
+ osd_destructors[type][slot - 1](osd->osd_slots[slot - 1]);
+ osd->osd_slots[slot - 1] = NULL;
+ for (i = osd->osd_nslots - 1; i >= 0; i--) {
+ if (osd->osd_slots[i] != NULL) {
+ OSD_DEBUG("Slot still has a value (type=%u, slot=%u).", type, i + 1);
+ break;
+ }
+ }
+ if (i == -1) {
+ int unlock;
+
+ /* No values left for this object. */
+ OSD_DEBUG("No more slots left (type=%u).", type);
+ if ((unlock = !mtx_owned(&osd_lock[type])))
+ mtx_lock(&osd_lock[type]);
+ LIST_REMOVE(osd, osd_next);
+ if (unlock)
+ mtx_unlock(&osd_lock[type]);
+ free(osd->osd_slots, M_OSD);
+ osd->osd_slots = NULL;
+ osd->osd_nslots = 0;
+ } else if (slot == osd->osd_nslots) {
+ /* This was the last slot. */
+ osd->osd_slots = realloc(osd->osd_slots,
+ sizeof(void *) * (i + 1), M_OSD, M_NOWAIT | M_ZERO);
+ /*
+ * We always reallocate to smaller size, so we assume it will
+ * always succeed.
+ */
+ KASSERT(osd->osd_slots != NULL, ("realloc() failed"));
+ osd->osd_nslots = i + 1;
+ OSD_DEBUG("Reducing slots array to %u (type=%u).",
+ osd->osd_nslots, type);
+ }
+}
+
+void
+osd_exit(u_int type, struct osd *osd)
+{
+ u_int i;
+
+ KASSERT(type >= OSD_FIRST && type <= OSD_LAST, ("Invalid type."));
+
+ if (osd->osd_nslots == 0) {
+ KASSERT(osd->osd_slots == NULL, ("Non-null osd_slots."));
+ /* No OSD attached, just leave. */
+ return;
+ }
+
+ mtx_lock(&osd_lock[type]);
+ for (i = 1; i <= osd->osd_nslots; i++)
+ osd_del(type, osd, i);
+ mtx_unlock(&osd_lock[type]);
+ OSD_DEBUG("Object exit (type=%u).", type);
+}
+
+static void
+osd_init(void *arg __unused)
+{
+ u_int i;
+
+ for (i = OSD_FIRST; i <= OSD_LAST; i++) {
+ osd_nslots[i] = 0;
+ LIST_INIT(&osd_list[i]);
+ mtx_init(&osd_lock[i], "osd", NULL, MTX_DEF);
+ osd_destructors[i] = NULL;
+ }
+}
+SYSINIT(osd, SI_SUB_LOCK, SI_ORDER_ANY, osd_init, NULL);
diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c
index f8f639c..a331a42 100644
--- a/sys/kern/kern_proc.c
+++ b/sys/kern/kern_proc.c
@@ -199,6 +199,9 @@ proc_dtor(void *mem, int size, void *arg)
("bad number of threads in exiting process"));
KASSERT(STAILQ_EMPTY(&p->p_ktr), ("proc_dtor: non-empty p_ktr"));
#endif
+ /* Free all OSD associated to this thread. */
+ osd_thread_exit(td);
+
/* Dispose of an alternate kstack, if it exists.
* XXX What if there are more than one thread in the proc?
* The first thread in the proc is special and not
diff --git a/sys/kern/kern_thread.c b/sys/kern/kern_thread.c
index 3bde08e..4606005 100644
--- a/sys/kern/kern_thread.c
+++ b/sys/kern/kern_thread.c
@@ -141,6 +141,9 @@ thread_dtor(void *mem, int size, void *arg)
#ifdef AUDIT
audit_thread_free(td);
#endif
+ /* Free all OSD associated to this thread. */
+ osd_thread_exit(td);
+
EVENTHANDLER_INVOKE(thread_dtor, td);
free_unr(tid_unrhdr, td->td_tid);
}
diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c
index c3e2e80..06682d5 100644
--- a/sys/kern/vfs_lookup.c
+++ b/sys/kern/vfs_lookup.c
@@ -189,14 +189,21 @@ namei(struct nameidata *ndp)
ndp->ni_rootdir = fdp->fd_rdir;
ndp->ni_topdir = fdp->fd_jdir;
- if (cnp->cn_pnbuf[0] != '/' && ndp->ni_dirfd != AT_FDCWD) {
- error = fgetvp(td, ndp->ni_dirfd, &dp);
- FILEDESC_SUNLOCK(fdp);
- if (error == 0 && dp->v_type != VDIR) {
- vfslocked = VFS_LOCK_GIANT(dp->v_mount);
- vrele(dp);
- VFS_UNLOCK_GIANT(vfslocked);
- error = ENOTDIR;
+ dp = NULL;
+ if (cnp->cn_pnbuf[0] != '/') {
+ if (ndp->ni_startdir != NULL) {
+ dp = ndp->ni_startdir;
+ error = 0;
+ } else if (ndp->ni_dirfd != AT_FDCWD)
+ error = fgetvp(td, ndp->ni_dirfd, &dp);
+ if (error != 0 || dp != NULL) {
+ FILEDESC_SUNLOCK(fdp);
+ if (error == 0 && dp->v_type != VDIR) {
+ vfslocked = VFS_LOCK_GIANT(dp->v_mount);
+ vrele(dp);
+ VFS_UNLOCK_GIANT(vfslocked);
+ error = ENOTDIR;
+ }
}
if (error) {
uma_zfree(namei_zone, cnp->cn_pnbuf);
@@ -206,10 +213,16 @@ namei(struct nameidata *ndp)
#endif
return (error);
}
- } else {
+ }
+ if (dp == NULL) {
dp = fdp->fd_cdir;
VREF(dp);
FILEDESC_SUNLOCK(fdp);
+ if (ndp->ni_startdir != NULL) {
+ vfslocked = VFS_LOCK_GIANT(ndp->ni_startdir->v_mount);
+ vrele(ndp->ni_startdir);
+ VFS_UNLOCK_GIANT(vfslocked);
+ }
}
vfslocked = VFS_LOCK_GIANT(dp->v_mount);
for (;;) {
diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c
index 327a9c5..32093c0 100644
--- a/sys/kern/vfs_subr.c
+++ b/sys/kern/vfs_subr.c
@@ -108,7 +108,6 @@ static void v_decr_useonly(struct vnode *);
static void v_upgrade_usecount(struct vnode *);
static void vfree(struct vnode *);
static void vnlru_free(int);
-static void vdestroy(struct vnode *);
static void vgonel(struct vnode *);
static void vfs_knllock(void *arg);
static void vfs_knlunlock(void *arg);
@@ -419,7 +418,7 @@ vfs_suser(struct mount *mp, struct thread *td)
* If the thread is jailed, but this is not a jail-friendly file
* system, deny immediately.
*/
- if (jailed(td->td_ucred) && !(mp->mnt_vfc->vfc_flags & VFCF_JAIL))
+ if (!(mp->mnt_vfc->vfc_flags & VFCF_JAIL) && jailed(td->td_ucred))
return (EPERM);
/*
@@ -438,7 +437,14 @@ vfs_suser(struct mount *mp, struct thread *td)
return (EPERM);
}
- if ((mp->mnt_flag & MNT_USER) == 0 ||
+ /*
+ * If file system supports delegated administration, we don't check
+ * for the PRIV_VFS_MOUNT_OWNER privilege - it will be better verified
+ * by the file system itself.
+ * If this is not the user that did original mount, we check for
+ * the PRIV_VFS_MOUNT_OWNER privilege.
+ */
+ if (!(mp->mnt_vfc->vfc_flags & VFCF_DELEGADMIN) &&
mp->mnt_cred->cr_uid != td->td_ucred->cr_uid) {
if ((error = priv_check(td, PRIV_VFS_MOUNT_OWNER)) != 0)
return (error);
@@ -793,7 +799,7 @@ SYSINIT(vnlru, SI_SUB_KTHREAD_UPDATE, SI_ORDER_FIRST, kproc_start,
* Routines having to do with the management of the vnode table.
*/
-static void
+void
vdestroy(struct vnode *vp)
{
struct bufobj *bo;
diff --git a/sys/modules/zfs/Makefile b/sys/modules/zfs/Makefile
index 0cd4361..7ac0237 100644
--- a/sys/modules/zfs/Makefile
+++ b/sys/modules/zfs/Makefile
@@ -12,11 +12,14 @@ SRCS+= acl_common.c
SRCS+= avl.c
.PATH: ${SUNW}/common/nvpair
SRCS+= nvpair.c
+.PATH: ${.CURDIR}/../../cddl/contrib/opensolaris/common/unicode
+SRCS+= u8_textprep.c
.PATH: ${.CURDIR}/../../cddl/compat/opensolaris/kern
SRCS+= opensolaris_kmem.c
SRCS+= opensolaris_kobj.c
SRCS+= opensolaris_kstat.c
+SRCS+= opensolaris_lookup.c
SRCS+= opensolaris_misc.c
SRCS+= opensolaris_policy.c
SRCS+= opensolaris_string.c
@@ -33,6 +36,7 @@ SRCS+= opensolaris_atomic.c
.PATH: ${SUNW}/uts/common/fs
SRCS+= gfs.c
+SRCS+= vnode.c
.PATH: ${SUNW}/uts/common/os
SRCS+= callb.c
diff --git a/sys/sys/conf.h b/sys/sys/conf.h
index 6939b86..3fc0777 100644
--- a/sys/sys/conf.h
+++ b/sys/sys/conf.h
@@ -288,6 +288,7 @@ void devfs_fpdrop(struct file *fp); /* XXX This is not public KPI */
#define UID_ROOT 0
#define UID_BIN 3
#define UID_UUCP 66
+#define UID_NOBODY 65534
#define GID_WHEEL 0
#define GID_KMEM 2
@@ -296,6 +297,7 @@ void devfs_fpdrop(struct file *fp); /* XXX This is not public KPI */
#define GID_BIN 7
#define GID_GAMES 13
#define GID_DIALER 68
+#define GID_NOBODY 65534
typedef void (*dev_clone_fn)(void *arg, struct ucred *cred, char *name,
int namelen, struct cdev **result);
diff --git a/sys/sys/jail.h b/sys/sys/jail.h
index b8972f8..08bc840 100644
--- a/sys/sys/jail.h
+++ b/sys/sys/jail.h
@@ -48,6 +48,10 @@ MALLOC_DECLARE(M_PRISON);
#endif
#endif /* _KERNEL */
+#if defined(_KERNEL) || defined(_WANT_PRISON)
+
+#include <sys/osd.h>
+
/*
* This structure describes a prison. It is pointed to by all struct
* ucreds's of the inmates. pr_ref keeps track of them and is used to
@@ -60,7 +64,6 @@ MALLOC_DECLARE(M_PRISON);
* required to read
* (d) set only during destruction of jail, no mutex needed
*/
-#if defined(_KERNEL) || defined(_WANT_PRISON)
struct prison {
LIST_ENTRY(prison) pr_list; /* (a) all prisons */
int pr_id; /* (c) prison id */
@@ -73,7 +76,7 @@ struct prison {
int pr_securelevel; /* (p) securelevel */
struct task pr_task; /* (d) destroy task */
struct mtx pr_mtx;
- void **pr_slots; /* (p) additional data */
+ struct osd pr_osd; /* (p) additional data */
};
#endif /* _KERNEL || _WANT_PRISON */
@@ -109,8 +112,10 @@ void prison_enforce_statfs(struct ucred *cred, struct mount *mp,
struct statfs *sp);
struct prison *prison_find(int prid);
void prison_free(struct prison *pr);
+void prison_free_locked(struct prison *pr);
u_int32_t prison_getip(struct ucred *cred);
void prison_hold(struct prison *pr);
+void prison_hold_locked(struct prison *pr);
int prison_if(struct ucred *cred, struct sockaddr *sa);
int prison_ip(struct ucred *cred, int flag, u_int32_t *ip);
int prison_priv_check(struct ucred *cred, int priv);
diff --git a/sys/sys/mount.h b/sys/sys/mount.h
index 038cbc7..3209753 100644
--- a/sys/sys/mount.h
+++ b/sys/sys/mount.h
@@ -453,6 +453,7 @@ struct ovfsconf {
#define VFCF_LOOPBACK 0x00100000 /* aliases some other mounted FS */
#define VFCF_UNICODE 0x00200000 /* stores file names as Unicode */
#define VFCF_JAIL 0x00400000 /* can be mounted from within a jail */
+#define VFCF_DELEGADMIN 0x00800000 /* supports delegated administration */
typedef uint32_t fsctlop_t;
diff --git a/sys/sys/namei.h b/sys/sys/namei.h
index 478ff33..0e044de 100644
--- a/sys/sys/namei.h
+++ b/sys/sys/namei.h
@@ -66,7 +66,7 @@ struct nameidata {
/*
* Arguments to lookup.
*/
- struct vnode *ni_startdir; /* starting directory */
+ struct vnode *ni_startdir; /* starting directory */
struct vnode *ni_rootdir; /* logical root directory */
struct vnode *ni_topdir; /* logical top directory */
int ni_dirfd; /* starting directory for *at functions */
@@ -149,15 +149,20 @@ struct nameidata {
/*
* Initialization of a nameidata structure.
*/
-#define NDINIT(ndp, op, flags, segflg, namep, td) \
- NDINIT_AT(ndp, op, flags, segflg, namep, AT_FDCWD, td)
+#define NDINIT(ndp, op, flags, segflg, namep, td) \
+ NDINIT_ALL(ndp, op, flags, segflg, namep, AT_FDCWD, NULL, td)
+#define NDINIT_AT(ndp, op, flags, segflg, namep, dirfd, td) \
+ NDINIT_ALL(ndp, op, flags, segflg, namep, dirfd, NULL, td)
+#define NDINIT_ATVP(ndp, op, flags, segflg, namep, vp, td) \
+ NDINIT_ALL(ndp, op, flags, segflg, namep, AT_FDCWD, vp, td)
static __inline void
-NDINIT_AT(struct nameidata *ndp,
+NDINIT_ALL(struct nameidata *ndp,
u_long op, u_long flags,
enum uio_seg segflg,
const char *namep,
int dirfd,
+ struct vnode *startdir,
struct thread *td)
{
ndp->ni_cnd.cn_nameiop = op;
@@ -165,6 +170,7 @@ NDINIT_AT(struct nameidata *ndp,
ndp->ni_segflg = segflg;
ndp->ni_dirp = namep;
ndp->ni_dirfd = dirfd;
+ ndp->ni_startdir = startdir;
ndp->ni_cnd.cn_thread = td;
}
diff --git a/sys/sys/osd.h b/sys/sys/osd.h
new file mode 100644
index 0000000..281f580
--- /dev/null
+++ b/sys/sys/osd.h
@@ -0,0 +1,89 @@
+/*-
+ * Copyright (c) 2007 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _SYS_OSD_H_
+#define _SYS_OSD_H_
+
+#include <sys/queue.h>
+
+struct osd {
+ u_int osd_nslots;
+ void **osd_slots;
+ LIST_ENTRY(osd) osd_next;
+};
+
+#ifdef _KERNEL
+
+#define OSD_THREAD 0
+#define OSD_JAIL 1
+
+#define OSD_FIRST OSD_THREAD
+#define OSD_LAST OSD_JAIL
+
+typedef void (*osd_destructor_t)(void *value);
+
+int osd_register(u_int type, osd_destructor_t destructor);
+void osd_deregister(u_int type, u_int slot);
+
+int osd_set(u_int type, struct osd *osd, u_int slot, void *value);
+void *osd_get(u_int type, struct osd *osd, u_int slot);
+void osd_del(u_int type, struct osd *osd, u_int slot);
+
+void osd_exit(u_int type, struct osd *osd);
+
+#define osd_thread_register(destructor) \
+ osd_register(OSD_THREAD, (destructor))
+#define osd_thread_deregister(slot) \
+ osd_deregister(OSD_THREAD, (slot))
+#define osd_thread_set(td, slot, value) \
+ osd_set(OSD_THREAD, &(td)->td_osd, (slot), (value))
+#define osd_thread_get(td, slot) \
+ osd_get(OSD_THREAD, &(td)->td_osd, (slot))
+#define osd_thread_del(td, slot) do { \
+ KASSERT((td) == curthread, ("Not curthread.")); \
+ osd_del(OSD_THREAD, &(td)->td_osd, (slot)); \
+} while (0)
+#define osd_thread_exit(td) \
+ osd_exit(OSD_THREAD, &(td)->td_osd)
+
+#define osd_jail_register(destructor) \
+ osd_register(OSD_JAIL, (destructor))
+#define osd_jail_deregister(slot) \
+ osd_deregister(OSD_JAIL, (slot))
+#define osd_jail_set(pr, slot, value) \
+ osd_set(OSD_JAIL, &(pr)->pr_osd, (slot), (value))
+#define osd_jail_get(pr, slot) \
+ osd_get(OSD_JAIL, &(pr)->pr_osd, (slot))
+#define osd_jail_del(pr, slot) \
+ osd_del(OSD_JAIL, &(pr)->pr_osd, (slot))
+#define osd_jail_exit(pr) \
+ osd_exit(OSD_JAIL, &(pr)->pr_osd)
+
+#endif /* _KERNEL */
+
+#endif /* !_SYS_OSD_H_ */
diff --git a/sys/sys/priv.h b/sys/sys/priv.h
index e517695..b242792 100644
--- a/sys/sys/priv.h
+++ b/sys/sys/priv.h
@@ -268,7 +268,7 @@
#define PRIV_VFS_MKNOD_DEV 331 /* Can mknod() to create dev nodes. */
#define PRIV_VFS_MKNOD_WHT 332 /* Can mknod() to create whiteout. */
#define PRIV_VFS_MOUNT 333 /* Can mount(). */
-#define PRIV_VFS_MOUNT_OWNER 334 /* Override owner on user mounts. */
+#define PRIV_VFS_MOUNT_OWNER 334 /* Can manage other users' file systems. */
#define PRIV_VFS_MOUNT_EXPORTED 335 /* Can set MNT_EXPORTED on mount. */
#define PRIV_VFS_MOUNT_PERM 336 /* Override dev node perms at mount. */
#define PRIV_VFS_MOUNT_SUIDDIR 337 /* Can set MNT_SUIDDIR on mount. */
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index b326ba7..fd490b0 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -47,6 +47,7 @@
#include <sys/_lock.h>
#include <sys/lock_profile.h>
#include <sys/_mutex.h>
+#include <sys/osd.h>
#include <sys/priority.h>
#include <sys/rtprio.h> /* XXX. */
#include <sys/runq.h>
@@ -233,6 +234,7 @@ struct thread {
char td_name[MAXCOMLEN + 1]; /* (*) Thread name. */
struct file *td_fpop; /* (k) file referencing cdev under op */
int td_dbgflags; /* (c) Userland debugger flags */
+ struct osd td_osd; /* (k) Object specific data. */
#define td_endzero td_base_pri
/* Copied during fork1() or thread_sched_upcall(). */
diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h
index 31b005d..9f59993 100644
--- a/sys/sys/vnode.h
+++ b/sys/sys/vnode.h
@@ -593,6 +593,7 @@ void vattr_null(struct vattr *vap);
int vcount(struct vnode *vp);
void vdrop(struct vnode *);
void vdropl(struct vnode *);
+void vdestroy(struct vnode *);
int vflush(struct mount *mp, int rootrefs, int flags, struct thread *td);
int vget(struct vnode *vp, int lockflag, struct thread *td);
void vgone(struct vnode *vp);
diff --git a/tools/regression/zfs/LICENSE b/tools/regression/zfs/LICENSE
new file mode 100644
index 0000000..45b9cac
--- /dev/null
+++ b/tools/regression/zfs/LICENSE
@@ -0,0 +1,27 @@
+$FreeBSD$
+
+License for all ZFS regression tests:
+
+Copyright (c) 2008 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/tools/regression/zfs/misc.sh b/tools/regression/zfs/misc.sh
new file mode 100755
index 0000000..d96aa22
--- /dev/null
+++ b/tools/regression/zfs/misc.sh
@@ -0,0 +1,436 @@
+# $FreeBSD$
+
+ntest=1
+os=`uname -s`
+
+echo ${dir} | egrep '^/' >/dev/null 2>&1
+if [ $? -eq 0 ]; then
+ maindir="${dir}/../.."
+else
+ maindir="`pwd`/${dir}/../.."
+fi
+
+# Set up correct command names and switches
+if [ -z "${LUSTRE}" ]; then
+ ZPOOL="zpool"
+ ZFS="zfs"
+ ZDB="zdb"
+ zpool_f_flag="-f"
+else
+ ZPOOL="lzpool"
+ ZFS="lzfs"
+ ZDB="lzdb"
+ zpool_f_flag="-F"
+ no_mountpoint=1
+fi
+
+# Use correct arguments to cmd line programs
+stat --version 2>/dev/null | grep GNU >/dev/null
+if [ $? -eq 0 ]; then
+ GNU_STAT="yes"
+fi
+if [ "${os}" = "SunOS" ]; then
+ import_flags="-d /dev/lofi"
+ mount_t_flag="-F"
+else
+ mount_t_flag="-t"
+fi
+
+die()
+{
+ echo "${1}" > /dev/stderr
+ exit 1
+}
+
+calcsum()
+{
+ dd if="${1}" bs=1M 2>/dev/null | openssl md5
+}
+
+create_file()
+{
+ name="${1}"
+ size="${2}"
+
+ dd if=/dev/urandom of=${name} bs=${size} count=1 >/dev/null 2>&1
+ sync
+}
+
+expect()
+{
+ eorig="${1}"
+ eexp=`echo "${eorig}" | egrep -v '^[ ]*$' | sed 's/^[ ][ ]*//g;s/[ ][ ]*$//g;s/[ ][ ]*/ /g;s/$/%EoL%/' | xargs`
+ shift
+ gorig=`sh -c "$*" 2>&1`
+ got=`echo "${gorig}" | egrep -v '^[ ]*$' | sed 's/^[ ][ ]*//g;s/[ ][ ]*$//g;s/[ ][ ]*/ /g;s/$/%EoL%/' | xargs`
+ echo "${got}" | egrep "${eexp}" >/dev/null
+ if [ $? -eq 0 ]; then
+ echo "ok ${ntest} ${add_msg}"
+ else
+ echo "not ok ${ntest} ${add_msg}"
+ echo "# ----- expected from: $*"
+ echo "${eorig}" | sed 's/^/# /'
+ echo "# ----- got:"
+ echo "${gorig}" | sed 's/^/# /'
+ echo "# ----- end"
+ fi
+ ntest=`expr $ntest + 1`
+}
+
+expect_ok()
+{
+ out=`$* 2>&1`
+ ec=$?
+ if [ $ec -eq 0 ]; then
+ echo "ok ${ntest} ${add_msg}"
+ echo "# ----- expected success from: $*"
+ if [ ! -z "${out}" ]; then
+ echo "# ----- output (exit code=${ec}):"
+ echo "${out}" | sed 's/^/# /'
+ echo "# ----- end"
+ fi
+ else
+ echo "not ok ${ntest} ${add_msg}"
+ echo "# ----- expected success from: $*"
+ echo "# ----- output (exit code=${ec}):"
+ echo "${out}" | sed 's/^/# /'
+ echo "# ----- end"
+ fi
+ ntest=`expr $ntest + 1`
+}
+
+expect_fl()
+{
+ out=`$* 2>&1`
+ ec=$?
+ if [ $ec -ne 0 ]; then
+ echo "ok ${ntest} ${add_msg}"
+ echo "# ----- expected failure from: $*"
+ if [ ! -z "${out}" ]; then
+ echo "# ----- output (exit code=${ec}):"
+ echo "${out}" | sed 's/^/# /'
+ echo "# ----- end"
+ fi
+ else
+ echo "not ok ${ntest} ${add_msg}"
+ echo "# ----- expected failure from: $*"
+ echo "# ----- output (exit code=${ec}):"
+ echo "${out}" | sed 's/^/# /'
+ echo "# ----- end"
+ fi
+ ntest=`expr $ntest + 1`
+}
+
+quick_exit()
+{
+ echo "1..1"
+ echo "ok 1"
+ exit 0
+}
+
+# Set up a scratch tmpfs directory (Linux only)
+setup_tmpfs()
+{
+ cmd="mktemp -d /tmp/zfs-regression.XXXXXXXXXX"
+ TMPDIR=`${cmd}` || die "failed: ${cmd}"
+ cmd="mount -t tmpfs none ${TMPDIR}"
+ ${cmd} || die "failed: ${cmd}"
+}
+
+# Clean up the tmpfs directory (Linux only)
+cleanup_tmpfs()
+{
+ if [ -n "${TMPDIR}" ]; then
+ cmd="umount ${TMPDIR} && rmdir ${TMPDIR}"
+ eval "${cmd}" || die "failed: ${cmd}"
+ fi
+}
+
+# Truncate a file
+truncate_cmd()
+{
+ size="${1}"
+ file="${2}"
+
+ cmd="dd if=/dev/null of=${file} bs=1 count=0 seek=${size}"
+ ${cmd} > /dev/null 2>&1 || die "failed: ${cmd}"
+}
+
+# Create a memory-backed block device
+create_memdisk()
+{
+ size="${1}"
+ devname="${2}"
+
+ if [ "${os}" = "FreeBSD" ]; then
+ if [ -n "${devname}" ]; then
+ devparam="-u ${devname}"
+ fi
+ cmd="mdconfig -a -t swap -s ${size} ${devparam} 2>/dev/null"
+ DISKNAME=`${cmd}` || die "failed: ${cmd}"
+ if [ -n "${devname}" ]; then
+ DISKNAME="${devname}"
+ fi
+ FDISKNAME="/dev/${DISKNAME}"
+ elif [ "${os}" = "SunOS" ]; then
+ cmd="mktemp /tmp/zfstest.XXXXXXXXXX"
+ fname=`${cmd}` || die "failed: ${cmd}"
+
+ truncate_cmd "${size}" "${fname}"
+
+ if [ -n "${devname}" ]; then
+ cmd="lofiadm -a ${fname} ${devname}"
+ ${cmd} || die "failed: ${cmd}"
+ DISKNAME="${devname}"
+ else
+ cmd="lofiadm -a ${fname}"
+ DISKNAME=`${cmd}` || die "failed: ${cmd}"
+ fi
+ FDISKNAME="${DISKNAME}"
+ elif [ "${os}" = "Linux" ]; then
+ if [ -z "${TMPDIR_DISKS}" ]; then
+ setup_tmpfs
+ TMPDIR_DISKS="${TMPDIR}"
+ fi
+
+ cmd="mktemp ${TMPDIR_DISKS}/disk.XXXXXXXXXX"
+ fname=`${cmd}` || die "failed: ${cmd}"
+
+ truncate_cmd "${size}" "${fname}"
+
+ if [ -n "${devname}" ]; then
+ devname=`echo ${devname} | cut -c 9-`
+ cmd="losetup /dev/${devname} ${fname} 2>&1"
+ eval ${cmd} || die "failed: ${cmd}"
+ DISKNAME="${devname}"
+ else
+ cmd="losetup -s -f ${fname} 2>&1"
+ diskname=`eval ${cmd}`
+
+ if [ "${diskname}" = "losetup: could not find any free loop device" ]; then
+ # If there are no free loopback devices, create one more
+ max=`echo /dev/loop* | awk 'BEGIN { RS=" "; FS="loop" } {if ($2 > max) max = $2} END {print max}'`
+ max=$((max + 1))
+ cmd="mknod /dev/loop${max} b 7 ${max}"
+ ${cmd} || die "failed: ${cmd}"
+
+ cmd="losetup -s -f ${fname}"
+ diskname=`${cmd}` || die "failed: ${cmd}"
+ fi
+ DISKNAME=`eval echo ${diskname} | sed 's/^\/dev\///'`
+ fi
+ ln /dev/${DISKNAME} /dev/zfstest_${DISKNAME}
+ DISKNAME="zfstest_${DISKNAME}"
+ FDISKNAME="/dev/${DISKNAME}"
+ else
+ die "Sorry, your OS is not supported"
+ fi
+}
+
+# Destroy a memory-backed block device
+destroy_memdisk()
+{
+ disk="${1}"
+
+ if [ "${os}" = "FreeBSD" ]; then
+ cmd="mdconfig -d -u ${disk}"
+ ${cmd} || die "failed: ${cmd}"
+ elif [ "${os}" = "SunOS" ]; then
+ cmd="lofiadm ${disk}"
+ fname=`${cmd}` || die "failed: ${cmd}"
+
+ cmd="lofiadm -d ${disk}"
+ ${cmd} || die "failed: ${cmd}"
+
+ cmd="rm ${fname}"
+ ${cmd} || die "failed: ${cmd}"
+ elif [ "${os}" = "Linux" ]; then
+ cmd="rm /dev/${disk}"
+ ${cmd} || die "failed: ${cmd}"
+ disk=`echo ${disk} | cut -c 9-`
+
+ cmd="losetup /dev/${disk} | awk '{print substr(\$3, 2, length(\$3)-2)}'"
+ fname=`eval ${cmd}` || die "failed: ${cmd}"
+
+ cmd="losetup -d /dev/${disk}"
+ ${cmd} || die "failed: ${cmd}"
+
+ cmd="rm ${fname}"
+ ${cmd} || die "failed: ${cmd}"
+ else
+ die "Sorry, your OS is not supported"
+ fi
+}
+
+disks_create()
+{
+ if [ -z "${ndisks}" ]; then
+ start=0
+ else
+ start=${ndisks}
+ fi
+ ndisks=$((start+$1))
+ n=$((ndisks-$start))
+ if [ -z "${2}" ]; then
+ size="96M"
+ else
+ size="${2}"
+ fi
+ for i in `nums $n $start`; do
+ create_memdisk ${size}
+ eval disk${i}="${DISKNAME}"
+ eval fdisk${i}="${FDISKNAME}"
+ done
+}
+
+disks_destroy()
+{
+ for i in `nums $ndisks 0`; do
+ eval disk=\$disk${i}
+ if [ ! -z "${disk}" ]; then
+ destroy_memdisk ${disk}
+ fi
+ done
+ [ -n "${TMPDIR_DISKS}" ] && TMPDIR="${TMPDIR_DISKS}" cleanup_tmpfs
+ return 0
+}
+
+disk_create()
+{
+ diskno=${1}
+ eval disk=\$disk${diskno}
+ if [ ! -z ${disk} ]; then
+ die "disk${diskno} is already set"
+ fi
+ dname=${2}
+ if [ -z "${3}" ]; then
+ size="96M"
+ else
+ size="${3}"
+ fi
+ create_memdisk ${size} ${dname}
+ [ "${DISKNAME}" = "${dname}" ] || die "${DISKNAME} != ${dname}"
+ eval disk${diskno}="${DISKNAME}"
+ eval fdisk${diskno}="${FDISKNAME}"
+}
+
+disk_destroy()
+{
+ eval disk=\$disk${1}
+ destroy_memdisk ${disk}
+ eval disk${1}=""
+}
+
+files_create()
+{
+ if [ -z "${nfiles}" ]; then
+ start=0
+ else
+ start=${nfiles}
+ fi
+ nfiles=$((start+$1))
+ n=$((nfiles-$start))
+ if [ -z "${2}" ]; then
+ size="96M"
+ else
+ size="${2}"
+ fi
+ for i in `nums $n $start`; do
+ if [ "${os}" = "Linux" ]; then
+ if [ -z "${TMPDIR_FILES}" ]; then
+ setup_tmpfs
+ TMPDIR_FILES="${TMPDIR}"
+ fi
+ file=`mktemp ${TMPDIR_FILES}/zfstest.XXXXXXXX`
+ else
+ file=`mktemp /tmp/zfstest.XXXXXXXX`
+ fi
+ truncate_cmd ${size} ${file}
+ eval file${i}=${file}
+ done
+}
+
+files_destroy()
+{
+ for i in `nums $nfiles 0`; do
+ eval file=\$file${i}
+ rm -f ${file}
+ done
+ nfiles=0
+ [ -n "${TMPDIR_FILES}" ] && TMPDIR="${TMPDIR_FILES}" cleanup_tmpfs
+ return 0
+}
+
+name_create()
+{
+ echo "zfstest_`dd if=/dev/urandom bs=1k count=1 2>/dev/null | openssl md5 | cut -b -8`"
+}
+
+names_create()
+{
+ nnames=$1
+ for i in `nums $nnames 0`; do
+ eval name${i}=`name_create`
+ done
+}
+
+is_mountpoint()
+{
+ dir="${1}"
+ if [ ! -d "${dir}" ]; then
+ return 1
+ fi
+ if [ -n "${GNU_STAT}" ]; then
+ statcmd="stat -c"
+ else
+ statcmd="stat -f"
+ fi
+ if [ "`${statcmd} '%d' ${dir} 2>/dev/null`" -eq "`${statcmd} '%d' ${dir}/.. 2>/dev/null`" ]; then
+ return 1
+ fi
+ return 0
+}
+
+nums()
+{
+ which jot >/dev/null 2>&1
+ if [ $? -eq 0 ]; then
+ jot ${1} ${2}
+ return $?
+ fi
+
+ start="${2}"
+ [ -z "${start}" ] && start="1";
+ end=$((${1}+${start}-1))
+
+ which seq >/dev/null 2>&1
+ if [ $? -eq 0 ]; then
+ seq ${start} ${end}
+ return $?
+ fi
+
+ i=1
+ while :; do
+ echo $i
+ if [ $i -eq ${1} ]; then
+ break
+ fi
+ i=$((i+1))
+ done
+}
+
+wait_for_resilver()
+{
+ for i in `nums 64`; do
+ ${ZPOOL} status ${1} | grep replacing >/dev/null
+ if [ $? -ne 0 ]; then
+ break
+ fi
+ sleep 1
+ done
+}
+
+get_guid()
+{
+ ${ZDB} -l ${1} | grep -B1 ${1} | grep guid | head -n1 | awk 'BEGIN {FS="="} {print $2}'
+}
diff --git a/tools/regression/zfs/zpool/add/cache.t b/tools/regression/zfs/zpool/add/cache.t
new file mode 100644
index 0000000..abaaf41
--- /dev/null
+++ b/tools/regression/zfs/zpool/add/cache.t
@@ -0,0 +1,126 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..33"
+
+disks_create 6
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_fl ${ZPOOL} add ${name0} cache ${disk0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} cache ${disk1}
+expect_fl ${ZPOOL} add ${name0} cache ${disk1}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} add ${name0} cache ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} cache ${disk1}
+expect_ok ${ZPOOL} add ${name0} cache ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} cache ${disk2} ${disk3}
+expect_ok ${ZPOOL} add ${name0} cache ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2} cache ${disk3}
+expect_ok ${ZPOOL} add ${name0} cache ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3} cache ${disk4}
+expect_ok ${ZPOOL} add ${name0} cache ${disk5}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/add/disks.t b/tools/regression/zfs/zpool/add/disks.t
new file mode 100644
index 0000000..e12f5fe
--- /dev/null
+++ b/tools/regression/zfs/zpool/add/disks.t
@@ -0,0 +1,60 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..19"
+
+disks_create 5
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_fl ${ZPOOL} add ${name0} ${disk0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} ${disk1}
+expect_fl ${ZPOOL} add ${name0} ${disk0}
+expect_fl ${ZPOOL} add ${name0} ${disk1}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} add ${name0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} add ${name0} ${disk3} ${disk4}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/add/doesnt_exist.t b/tools/regression/zfs/zpool/add/doesnt_exist.t
new file mode 100644
index 0000000..d099afc
--- /dev/null
+++ b/tools/regression/zfs/zpool/add/doesnt_exist.t
@@ -0,0 +1,20 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..6"
+
+disks_create 1
+names_create 1
+
+expect_fl ${ZPOOL} add ${name0} ${disk0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} add -f ${name0} ${disk0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/add/files.t b/tools/regression/zfs/zpool/add/files.t
new file mode 100644
index 0000000..59014aa
--- /dev/null
+++ b/tools/regression/zfs/zpool/add/files.t
@@ -0,0 +1,231 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..54"
+
+files_create 8
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${file0}
+expect_fl ${ZPOOL} add ${name0} ${file0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${file0} ${file1}
+expect_fl ${ZPOOL} add ${name0} ${file0}
+expect_fl ${ZPOOL} add ${name0} ${file1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${file0}
+expect_ok ${ZPOOL} add ${name0} ${file1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${file0} ${file1} ${file2}
+expect_ok ${ZPOOL} add ${name0} ${file3} ${file4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo " ${file3} ONLINE 0 0 0"
+ echo " ${file4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${file0} ${file1}
+expect_ok ${ZPOOL} add ${name0} mirror ${file2} ${file3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo " ${file3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${file0} ${file1} ${file2}
+expect_ok ${ZPOOL} add ${name0} raidz1 ${file3} ${file4} ${file5}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${file3} ONLINE 0 0 0"
+ echo " ${file4} ONLINE 0 0 0"
+ echo " ${file5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${file0} ${file1} ${file2} ${file3}
+expect_ok ${ZPOOL} add ${name0} raidz2 ${file4} ${file5} ${file6} ${file7}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo " ${file3} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${file4} ONLINE 0 0 0"
+ echo " ${file5} ONLINE 0 0 0"
+ echo " ${file6} ONLINE 0 0 0"
+ echo " ${file7} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${file0} ${file1}
+expect_ok ${ZPOOL} add ${name0} spare ${file2} ${file3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " spares"
+ echo " ${file2} AVAIL"
+ echo " ${file3} AVAIL"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${file0} ${file1}
+expect_ok ${ZPOOL} add ${name0} log ${file2} ${file3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo " ${file3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${file0} ${file1}
+expect_ok ${ZPOOL} add ${name0} log mirror ${file2} ${file3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo " ${file3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${file0} ${file1}
+expect_fl ${ZPOOL} add ${name0} cache ${file2} ${file3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+files_destroy
diff --git a/tools/regression/zfs/zpool/add/log.t b/tools/regression/zfs/zpool/add/log.t
new file mode 100644
index 0000000..516ab1b
--- /dev/null
+++ b/tools/regression/zfs/zpool/add/log.t
@@ -0,0 +1,284 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..66"
+
+disks_create 7
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} add ${name0} log ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} add ${name0} log mirror ${disk1} ${disk2} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} add ${name0} log ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} add ${name0} log mirror ${disk2} ${disk3} ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} add ${name0} log ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} add ${name0} log mirror ${disk3} ${disk4} ${disk5}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} add ${name0} log ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} add ${name0} log mirror ${disk4} ${disk5} ${disk6}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} add ${name0} log ${disk1}
+expect_ok ${ZPOOL} add ${name0} log ${disk2}
+expect_ok ${ZPOOL} add ${name0} log ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} add ${name0} log mirror ${disk1} ${disk2}
+expect_ok ${ZPOOL} add ${name0} log mirror ${disk3} ${disk4}
+expect_ok ${ZPOOL} add ${name0} log mirror ${disk5} ${disk6}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log ${disk1}
+expect_ok ${ZPOOL} add ${name0} log ${disk2}
+expect_ok ${ZPOOL} add ${name0} log ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2}
+expect_ok ${ZPOOL} add ${name0} log mirror ${disk3} ${disk4}
+expect_ok ${ZPOOL} add ${name0} log mirror ${disk5} ${disk6}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/add/mirror.t b/tools/regression/zfs/zpool/add/mirror.t
new file mode 100644
index 0000000..25ff59e
--- /dev/null
+++ b/tools/regression/zfs/zpool/add/mirror.t
@@ -0,0 +1,86 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..15"
+
+disks_create 10
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} add ${name0} mirror ${disk2} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} add ${name0} mirror ${disk3} ${disk4} ${disk5}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} mirror ${disk2} ${disk3}
+expect_ok ${ZPOOL} add ${name0} mirror ${disk4} ${disk5} mirror ${disk6} ${disk7} mirror ${disk8} ${disk9}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk8} ONLINE 0 0 0"
+ echo " ${disk9} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/add/option-f_inuse.t b/tools/regression/zfs/zpool/add/option-f_inuse.t
new file mode 100644
index 0000000..5cc86c3
--- /dev/null
+++ b/tools/regression/zfs/zpool/add/option-f_inuse.t
@@ -0,0 +1,1186 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..263"
+
+disks_create 11
+names_create 2
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} ${disk1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} add -f ${name1} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} ${disk1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} ${disk2} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} add -f ${name1} ${disk2} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} mirror ${disk2} ${disk3}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} mirror ${disk0} ${disk1}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} add -f ${name1} mirror ${disk0} ${disk1}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} mirror ${disk1} ${disk2}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} mirror ${disk3} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} add -f ${name1} mirror ${disk3} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} raidz1 ${disk3} ${disk4} ${disk5}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} raidz1 ${disk0} ${disk1} ${disk2}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} add -f ${name1} raidz1 ${disk0} ${disk1} ${disk2}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} raidz1 ${disk1} ${disk2} ${disk3}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} raidz1 ${disk4} ${disk5} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} add -f ${name1} raidz1 ${disk4} ${disk5} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} raidz2 ${disk4} ${disk5} ${disk6} ${disk7}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} add -f ${name1} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} raidz2 ${disk1} ${disk2} ${disk3} ${disk4}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} raidz2 ${disk5} ${disk6} ${disk0} ${disk7}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} add -f ${name1} raidz2 ${disk5} ${disk6} ${disk0} ${disk7}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} ${disk1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} log ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} add -f ${name1} log ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} ${disk1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} log ${disk2} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} add -f ${name1} log ${disk2} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log ${disk1}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} ${disk2}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk1} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} log mirror ${disk1} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} add -f ${name1} log mirror ${disk1} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} ${disk1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} log mirror ${disk2} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} add -f ${name1} log mirror ${disk2} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+
+expect_ok ${ZPOOL} create ${name0} ${disk1} cache ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} ${disk2}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+add_msg="# TODO It shouldn't be possible to use offlined cache vdevs."
+expect "${exp}" ${ZPOOL} add ${name1} cache ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} add -f ${name1} cache ${disk0}
+add_msg=""
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} import ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk1} cache ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} ${disk2}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+add_msg="# TODO It shouldn't be possible to use offlined cache vdevs."
+expect "${exp}" ${ZPOOL} add ${name1} cache ${disk3} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} add -f ${name1} cache ${disk3} ${disk0}
+add_msg=""
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} import ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} ${disk2}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of potentially active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} add -f ${name1} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} import ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} mirror ${disk2} ${disk3}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of potentially active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} mirror ${disk0} ${disk4}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} add -f ${name1} mirror ${disk0} ${disk4}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} import ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} raidz1 ${disk3} ${disk4} ${disk5}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of potentially active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} raidz1 ${disk0} ${disk6} ${disk7}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} add -f ${name1} raidz1 ${disk0} ${disk6} ${disk7}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} import ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} raidz2 ${disk4} ${disk5} ${disk6} ${disk7}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of potentially active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} raidz2 ${disk0} ${disk8} ${disk9} ${disk10}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} add -f ${name1} raidz2 ${disk0} ${disk8} ${disk9} ${disk10}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk8} ONLINE 0 0 0"
+ echo " ${disk9} ONLINE 0 0 0"
+ echo " ${disk10} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} import ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} ${disk2}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of potentially active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} log ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} add -f ${name1} log ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} import ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} ${disk3}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk1} is part of potentially active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} log mirror ${disk1} ${disk4}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} add -f ${name1} log mirror ${disk1} ${disk4}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} import ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk1} cache ${disk0}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} ${disk2}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of potentially active pool '${name0}'"
+)`
+add_msg="# TODO It shouldn't be possible to use offlined cache vdevs."
+expect "${exp}" ${ZPOOL} add ${name1} cache ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} add -f ${name1} cache ${disk0}
+add_msg=""
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} import ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_ok ${ZPOOL} create ${name1} ${disk2}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "the following errors must be manually repaired:"
+ echo "${fdisk0} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add -f ${name1} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} online ${name0} ${disk0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_ok ${ZPOOL} create ${name1} mirror ${disk2} ${disk3}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} mirror ${disk0} ${disk4}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "the following errors must be manually repaired:"
+ echo "${fdisk0} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add -f ${name1} mirror ${disk0} ${disk4}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} online ${name0} ${disk0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_ok ${ZPOOL} create ${name1} raidz1 ${disk3} ${disk4} ${disk5}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} raidz1 ${disk0} ${disk6} ${disk7}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "the following errors must be manually repaired:"
+ echo "${fdisk0} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add -f ${name1} raidz1 ${disk0} ${disk6} ${disk7}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} online ${name0} ${disk0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_ok ${ZPOOL} create ${name1} raidz2 ${disk4} ${disk5} ${disk6} ${disk7}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} raidz2 ${disk0} ${disk8} ${disk9} ${disk10}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "the following errors must be manually repaired:"
+ echo "${fdisk0} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add -f ${name1} raidz2 ${disk0} ${disk8} ${disk9} ${disk10}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} online ${name0} ${disk0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_ok ${ZPOOL} create ${name1} ${disk2}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} log ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "the following errors must be manually repaired:"
+ echo "${fdisk0} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add -f ${name1} log ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} online ${name0} ${disk0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+expect_ok ${ZPOOL} create ${name1} ${disk3}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk1} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add ${name1} log mirror ${disk1} ${disk4}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "the following errors must be manually repaired:"
+ echo "${fdisk1} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add -f ${name1} log mirror ${disk1} ${disk4}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} online ${name0} ${disk1}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk1} cache ${disk0}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_ok ${ZPOOL} create ${name1} ${disk2}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of active pool '${name0}'"
+)`
+add_msg="# TODO It reports that ${fdisk0} is part of unknown pool."
+expect "${exp}" ${ZPOOL} add ${name1} cache ${disk0}
+add_msg=""
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+add_msg="# TODO Invalid problem description."
+exp=`(
+ echo "invalid vdev specification"
+ echo "the following errors must be manually repaired:"
+ echo "${fdisk0} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} add -f ${name1} cache ${disk0}
+add_msg=""
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} online ${name0} ${disk0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/add/option-f_replication_level_mismatch_0.t b/tools/regression/zfs/zpool/add/option-f_replication_level_mismatch_0.t
new file mode 100644
index 0000000..83b0a595
--- /dev/null
+++ b/tools/regression/zfs/zpool/add/option-f_replication_level_mismatch_0.t
@@ -0,0 +1,161 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..40"
+
+disks_create 5
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_fl ${ZPOOL} add ${name0} mirror ${disk1} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} add -f ${name0} mirror ${disk1} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_fl ${ZPOOL} add ${name0} raidz ${disk1} ${disk2} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} add -f ${name0} raidz ${disk1} ${disk2} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_fl ${ZPOOL} add ${name0} raidz2 ${disk1} ${disk2} ${disk3} ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} add -f ${name0} raidz2 ${disk1} ${disk2} ${disk3} ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log ${disk1}
+add_msg="# TODO Sun CR 6726091, Lustre bug 16873"
+expect_fl ${ZPOOL} add ${name0} log mirror ${disk2} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg=""
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log ${disk1}
+expect_ok ${ZPOOL} add -f ${name0} log mirror ${disk2} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/add/option-f_replication_level_mismatch_1.t b/tools/regression/zfs/zpool/add/option-f_replication_level_mismatch_1.t
new file mode 100644
index 0000000..6b19b0a
--- /dev/null
+++ b/tools/regression/zfs/zpool/add/option-f_replication_level_mismatch_1.t
@@ -0,0 +1,785 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..182"
+
+disks_create 9
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_fl ${ZPOOL} add ${name0} mirror ${disk2} ${disk3} ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} add -f ${name0} mirror ${disk2} ${disk3} ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} ${disk2}
+expect_fl ${ZPOOL} add ${name0} mirror ${disk3} ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} add -f ${name0} mirror ${disk3} ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_fl ${ZPOOL} add ${name0} raidz1 ${disk3} ${disk4} ${disk5} ${disk6}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} add -f ${name0} raidz1 ${disk3} ${disk4} ${disk5} ${disk6}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_fl ${ZPOOL} add ${name0} raidz1 ${disk4} ${disk5} ${disk6}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} add -f ${name0} raidz1 ${disk4} ${disk5} ${disk6}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_fl ${ZPOOL} add ${name0} raidz2 ${disk4} ${disk5} ${disk6} ${disk7} ${disk8}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} add -f ${name0} raidz2 ${disk4} ${disk5} ${disk6} ${disk7} ${disk8}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo " ${disk8} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3} ${disk4}
+expect_fl ${ZPOOL} add ${name0} raidz2 ${disk5} ${disk6} ${disk7} ${disk8}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3} ${disk4}
+expect_ok ${ZPOOL} add -f ${name0} raidz2 ${disk5} ${disk6} ${disk7} ${disk8}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo " ${disk8} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+
+
+
+
+
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_fl ${ZPOOL} add ${name0} raidz ${disk2} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} add -f ${name0} raidz ${disk2} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz ${disk0} ${disk1}
+expect_fl ${ZPOOL} add ${name0} mirror ${disk2} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz ${disk0} ${disk1}
+expect_ok ${ZPOOL} add -f ${name0} mirror ${disk2} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} ${disk2}
+expect_fl ${ZPOOL} add ${name0} raidz ${disk3} ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} add -f ${name0} raidz ${disk3} ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_fl ${ZPOOL} add ${name0} raidz ${disk2} ${disk3} ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} add -f ${name0} raidz ${disk2} ${disk3} ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_fl ${ZPOOL} add ${name0} raidz2 ${disk2} ${disk3} ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} add -f ${name0} raidz2 ${disk2} ${disk3} ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} ${disk2}
+expect_fl ${ZPOOL} add ${name0} raidz2 ${disk3} ${disk4} ${disk5}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} add -f ${name0} raidz2 ${disk3} ${disk4} ${disk5}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} ${disk2} ${disk3}
+expect_fl ${ZPOOL} add ${name0} raidz2 ${disk4} ${disk5} ${disk6}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} add -f ${name0} raidz2 ${disk4} ${disk5} ${disk6}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1}
+expect_fl ${ZPOOL} add ${name0} raidz2 ${disk2} ${disk3} ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1}
+expect_ok ${ZPOOL} add -f ${name0} raidz2 ${disk2} ${disk3} ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_fl ${ZPOOL} add ${name0} raidz2 ${disk3} ${disk4} ${disk5}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} add -f ${name0} raidz2 ${disk3} ${disk4} ${disk5}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_fl ${ZPOOL} add ${name0} raidz2 ${disk4} ${disk5} ${disk6}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} add -f ${name0} raidz2 ${disk4} ${disk5} ${disk6}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2}
+add_msg="# TODO Sun CR 6726091, Lustre bug 16873"
+expect_fl ${ZPOOL} add ${name0} log mirror ${disk3} ${disk4} ${disk5}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg=""
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2}
+expect_ok ${ZPOOL} add -f ${name0} log mirror ${disk3} ${disk4} ${disk5}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2} ${disk3}
+add_msg="# TODO Sun CR 6726091, Lustre bug 16873"
+expect_fl ${ZPOOL} add ${name0} log mirror ${disk4} ${disk5}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg=""
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} add -f ${name0} log mirror ${disk4} ${disk5}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/add/option-f_size_mismatch.t b/tools/regression/zfs/zpool/add/option-f_size_mismatch.t
new file mode 100644
index 0000000..79281e5
--- /dev/null
+++ b/tools/regression/zfs/zpool/add/option-f_size_mismatch.t
@@ -0,0 +1,434 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..100"
+
+disks_create 7
+disks_create 1 64M
+files_create 7
+files_create 1 64M
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_fl ${ZPOOL} add ${name0} mirror ${disk7} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} add -f ${name0} mirror ${disk7} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${file0} ${file1}
+expect_fl ${ZPOOL} add ${name0} mirror ${file7} ${file2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${file0} ${file1}
+expect_ok ${ZPOOL} add -f ${name0} mirror ${file7} ${file2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file7} ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_fl ${ZPOOL} add ${name0} raidz1 ${disk3} ${disk7} ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} add -f ${name0} raidz1 ${disk3} ${disk7} ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${file0} ${file1} ${file2}
+expect_fl ${ZPOOL} add ${name0} raidz1 ${file3} ${file7} ${file4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${file0} ${file1} ${file2}
+expect_ok ${ZPOOL} add -f ${name0} raidz1 ${file3} ${file7} ${file4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${file3} ONLINE 0 0 0"
+ echo " ${file7} ONLINE 0 0 0"
+ echo " ${file4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_fl ${ZPOOL} add ${name0} raidz2 ${disk4} ${disk5} ${disk6} ${disk7}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} add -f ${name0} raidz2 ${disk4} ${disk5} ${disk6} ${disk7}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${file0} ${file1} ${file2} ${file3}
+expect_fl ${ZPOOL} add ${name0} raidz2 ${file4} ${file5} ${file6} ${file7}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo " ${file3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${file0} ${file1} ${file2} ${file3}
+expect_ok ${ZPOOL} add -f ${name0} raidz2 ${file4} ${file5} ${file6} ${file7}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo " ${file3} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${file4} ONLINE 0 0 0"
+ echo " ${file5} ONLINE 0 0 0"
+ echo " ${file6} ONLINE 0 0 0"
+ echo " ${file7} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+add_msg="# TODO Sun CR 6726091, Lustre bug 16873"
+expect_fl ${ZPOOL} add ${name0} log mirror ${disk1} ${disk7}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg=""
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} add -f ${name0} log mirror ${disk1} ${disk7}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${file0}
+add_msg="# TODO Sun CR 6726091, Lustre bug 16873"
+expect_fl ${ZPOOL} add ${name0} log mirror ${file1} ${file7}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg=""
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${file0}
+expect_ok ${ZPOOL} add -f ${name0} log mirror ${file1} ${file7}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " ${file7} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2}
+add_msg="# TODO Sun CR 6726091, Lustre bug 16873"
+expect_fl ${ZPOOL} add ${name0} log mirror ${disk3} ${disk7}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg=""
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2}
+expect_ok ${ZPOOL} add -f ${name0} log mirror ${disk3} ${disk7}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${file0} log mirror ${file1} ${file2}
+add_msg="# TODO Sun CR 6726091, Lustre bug 16873"
+expect_fl ${ZPOOL} add ${name0} log mirror ${file3} ${file7}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg=""
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${file0} log mirror ${file1} ${file2}
+expect_ok ${ZPOOL} add -f ${name0} log mirror ${file3} ${file7}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file3} ONLINE 0 0 0"
+ echo " ${file7} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
+files_destroy
diff --git a/tools/regression/zfs/zpool/add/option-f_type_mismatch.t b/tools/regression/zfs/zpool/add/option-f_type_mismatch.t
new file mode 100644
index 0000000..a79bce9
--- /dev/null
+++ b/tools/regression/zfs/zpool/add/option-f_type_mismatch.t
@@ -0,0 +1,408 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..100"
+
+disks_create 7
+files_create 7
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_fl ${ZPOOL} add ${name0} ${file0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} add -f ${name0} ${file0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${file0}
+expect_fl ${ZPOOL} add ${name0} ${disk0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${file0}
+expect_ok ${ZPOOL} add -f ${name0} ${disk0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_fl ${ZPOOL} add ${name0} mirror ${disk2} ${file0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} add -f ${name0} mirror ${disk2} ${file0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${file0} ${file1}
+expect_fl ${ZPOOL} add ${name0} mirror ${disk0} ${file2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${file0} ${file1}
+expect_ok ${ZPOOL} add -f ${name0} mirror ${disk0} ${file2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_fl ${ZPOOL} add ${name0} raidz1 ${disk3} ${file0} ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} add -f ${name0} raidz1 ${disk3} ${file0} ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${file0} ${file1} ${file2}
+expect_fl ${ZPOOL} add ${name0} raidz1 ${file3} ${disk0} ${file4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${file0} ${file1} ${file2}
+expect_ok ${ZPOOL} add -f ${name0} raidz1 ${file3} ${disk0} ${file4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${file3} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${file4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_fl ${ZPOOL} add ${name0} raidz2 ${disk4} ${file0} ${disk5} ${disk6}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} add -f ${name0} raidz2 ${disk4} ${file0} ${disk5} ${disk6}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${file0} ${file1} ${file2} ${file3}
+expect_fl ${ZPOOL} add ${name0} raidz2 ${file4} ${disk0} ${file5} ${file6}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo " ${file3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${file0} ${file1} ${file2} ${file3}
+expect_ok ${ZPOOL} add -f ${name0} raidz2 ${file4} ${disk0} ${file5} ${file6}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo " ${file3} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${file4} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${file5} ONLINE 0 0 0"
+ echo " ${file6} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+add_msg="# TODO Sun CR 6726091, Lustre bug 16873"
+expect_fl ${ZPOOL} add ${name0} log mirror ${disk1} ${file0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg=""
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} add -f ${name0} log mirror ${disk1} ${file0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${file0}
+add_msg="# TODO Sun CR 6726091, Lustre bug 16873"
+expect_fl ${ZPOOL} add ${name0} log mirror ${file1} ${disk0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg=""
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${file0}
+expect_ok ${ZPOOL} add -f ${name0} log mirror ${file1} ${disk0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
+files_destroy
diff --git a/tools/regression/zfs/zpool/add/option-n.t b/tools/regression/zfs/zpool/add/option-n.t
new file mode 100644
index 0000000..5f8b5c0
--- /dev/null
+++ b/tools/regression/zfs/zpool/add/option-n.t
@@ -0,0 +1,34 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..5"
+
+disks_create 2
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+exp=`(
+ echo "would update '${name0}' to the following configuration:"
+ echo " ${name0}"
+ echo " ${disk0}"
+ echo " ${disk1}"
+)`
+expect "${exp}" ${ZPOOL} add -n ${name0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/add/raidz1.t b/tools/regression/zfs/zpool/add/raidz1.t
new file mode 100644
index 0000000..cd004a2
--- /dev/null
+++ b/tools/regression/zfs/zpool/add/raidz1.t
@@ -0,0 +1,70 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..10"
+
+disks_create 15
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} add ${name0} raidz1 ${disk3} ${disk4} ${disk5}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2} raidz1 ${disk3} ${disk4} ${disk5}
+expect_ok ${ZPOOL} add ${name0} raidz1 ${disk6} ${disk7} ${disk8} raidz1 ${disk9} ${disk10} ${disk11} raidz1 ${disk12} ${disk13} ${disk14}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo " ${disk8} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk9} ONLINE 0 0 0"
+ echo " ${disk10} ONLINE 0 0 0"
+ echo " ${disk11} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk12} ONLINE 0 0 0"
+ echo " ${disk13} ONLINE 0 0 0"
+ echo " ${disk14} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/add/raidz2.t b/tools/regression/zfs/zpool/add/raidz2.t
new file mode 100644
index 0000000..20fca8a
--- /dev/null
+++ b/tools/regression/zfs/zpool/add/raidz2.t
@@ -0,0 +1,77 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..10"
+
+disks_create 20
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} add ${name0} raidz2 ${disk4} ${disk5} ${disk6} ${disk7}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3} raidz2 ${disk4} ${disk5} ${disk6} ${disk7}
+expect_ok ${ZPOOL} add ${name0} raidz2 ${disk8} ${disk9} ${disk10} ${disk11} raidz2 ${disk12} ${disk13} ${disk14} ${disk15} raidz2 ${disk16} ${disk17} ${disk18} ${disk19}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk8} ONLINE 0 0 0"
+ echo " ${disk9} ONLINE 0 0 0"
+ echo " ${disk10} ONLINE 0 0 0"
+ echo " ${disk11} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk12} ONLINE 0 0 0"
+ echo " ${disk13} ONLINE 0 0 0"
+ echo " ${disk14} ONLINE 0 0 0"
+ echo " ${disk15} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk16} ONLINE 0 0 0"
+ echo " ${disk17} ONLINE 0 0 0"
+ echo " ${disk18} ONLINE 0 0 0"
+ echo " ${disk19} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/add/spare.t b/tools/regression/zfs/zpool/add/spare.t
new file mode 100644
index 0000000..816e385
--- /dev/null
+++ b/tools/regression/zfs/zpool/add/spare.t
@@ -0,0 +1,137 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..31"
+
+disks_create 6
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} add ${name0} spare ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " spares"
+ echo " ${disk1} AVAIL"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} add ${name0} spare ${disk2} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " spares"
+ echo " ${disk2} AVAIL"
+ echo " ${disk3} AVAIL"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} add ${name0} spare ${disk3} ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " spares"
+ echo " ${disk3} AVAIL"
+ echo " ${disk4} AVAIL"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} add ${name0} spare ${disk4} ${disk5}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " spares"
+ echo " ${disk4} AVAIL"
+ echo " ${disk5} AVAIL"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} spare ${disk1}
+expect_ok ${ZPOOL} add ${name0} spare ${disk2} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " spares"
+ echo " ${disk1} AVAIL"
+ echo " ${disk2} AVAIL"
+ echo " ${disk3} AVAIL"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} add ${name0} spare ${disk1} ${disk2}
+expect_ok ${ZPOOL} add ${name0} spare ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " spares"
+ echo " ${disk1} AVAIL"
+ echo " ${disk2} AVAIL"
+ echo " ${disk3} AVAIL"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/attach/log.t b/tools/regression/zfs/zpool/attach/log.t
new file mode 100644
index 0000000..dde5995
--- /dev/null
+++ b/tools/regression/zfs/zpool/attach/log.t
@@ -0,0 +1,232 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..34"
+
+disks_create 6
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log ${disk1}
+expect_ok ${ZPOOL} attach ${name0} ${disk1} ${disk2}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} attach ${name0} ${disk1} ${disk3}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} attach ${name0} ${disk3} ${disk4}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} detach ${name0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} detach ${name0} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} detach ${name0} ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_fl ${ZPOOL} detach ${name0} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log ${disk1}
+expect_ok ${ZPOOL} attach ${name0} ${disk1} ${disk2}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log ${disk1} ${disk2}
+expect_ok ${ZPOOL} attach ${name0} ${disk1} ${disk3}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} attach ${name0} ${disk2} ${disk4}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} attach ${name0} ${disk1} ${disk5}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/attach/mirror.t b/tools/regression/zfs/zpool/attach/mirror.t
new file mode 100644
index 0000000..ac3fe2d
--- /dev/null
+++ b/tools/regression/zfs/zpool/attach/mirror.t
@@ -0,0 +1,208 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..34"
+
+disks_create 5
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} attach ${name0} ${disk0} ${disk1}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo " ${disk1} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} attach ${name0} ${disk0} ${disk2}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo " ${disk1} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo " ${disk2} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} attach ${name0} ${disk2} ${disk3}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo " ${disk1} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo " ${disk2} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo " ${disk3} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} detach ${name0} ${disk0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo " ${disk2} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo " ${disk3} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} detach ${name0} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo " ${disk3} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} detach ${name0} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_fl ${ZPOOL} detach ${name0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} attach ${name0} ${disk0} ${disk1}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo " ${disk1} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} ${disk1}
+expect_ok ${ZPOOL} attach ${name0} ${disk0} ${disk2}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo " ${disk2} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} attach ${name0} ${disk1} ${disk3}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo " ${disk3} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} attach ${name0} ${disk0} ${disk4}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo " ${disk2} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo " ${disk4} ONLINE 0 0 0 [0-9.]+[A-Z] resilvered"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/attach/option-f_inuse.t b/tools/regression/zfs/zpool/attach/option-f_inuse.t
new file mode 100644
index 0000000..a62d360
--- /dev/null
+++ b/tools/regression/zfs/zpool/attach/option-f_inuse.t
@@ -0,0 +1,670 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..141"
+
+disks_create 11
+names_create 2
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} ${disk1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} attach ${name1} ${disk1} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} attach -f ${name1} ${disk1} ${disk0}
+wait_for_resilver ${name1}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk0} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} mirror ${disk1} ${disk2}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} attach ${name1} ${disk1} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} attach -f ${name1} ${disk1} ${disk0}
+wait_for_resilver ${name1}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk0} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+
+expect_ok ${ZPOOL} create ${name0} ${disk1} log mirror ${disk2} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} ${disk3} log ${disk4}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} attach ${name1} ${disk4} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} attach -f ${name1} ${disk4} ${disk0}
+wait_for_resilver ${name1}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk0} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} import ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk1} log mirror ${disk2} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} ${disk3} log mirror ${disk4} ${disk5}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} attach ${name1} ${disk2} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} attach -f ${name1} ${disk4} ${disk0}
+wait_for_resilver ${name1}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk5} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk0} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} import ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk1} cache ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} ${disk2}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+add_msg="# TODO It shouldn't be possible to use offlined cache vdevs."
+expect "${exp}" ${ZPOOL} attach ${name1} ${disk2} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} attach -f ${name1} ${disk2} ${disk0}
+add_msg=""
+wait_for_resilver ${name1}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk0} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} import ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} ${disk2}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of potentially active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} attach ${name1} ${disk2} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} attach -f ${name1} ${disk2} ${disk0}
+wait_for_resilver ${name1}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk0} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} import ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} mirror ${disk2} ${disk3}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of potentially active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} attach ${name1} ${disk2} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} attach -f ${name1} ${disk2} ${disk0}
+wait_for_resilver ${name1}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk3} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk0} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} import ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk1} log mirror ${disk2} ${disk0}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} ${disk3} log ${disk4}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of potentially active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} attach ${name1} ${disk4} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} attach -f ${name1} ${disk4} ${disk0}
+wait_for_resilver ${name1}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk0} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} import ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk1} log mirror ${disk2} ${disk0}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} ${disk3} log mirror ${disk4} ${disk5}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of potentially active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} attach ${name1} ${disk2} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} attach -f ${name1} ${disk4} ${disk0}
+wait_for_resilver ${name1}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk5} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk0} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} import ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk1} cache ${disk0}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} create ${name1} ${disk2}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of potentially active pool '${name0}'"
+)`
+add_msg="# TODO It shouldn't be possible to use offlined cache vdevs."
+expect "${exp}" ${ZPOOL} attach ${name1} ${disk2} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} attach -f ${name1} ${disk2} ${disk0}
+add_msg=""
+wait_for_resilver ${name1}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk0} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} import ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} create ${name1} ${disk1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} attach ${name1} ${disk2} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "the following errors must be manually repaired:"
+ echo "${fdisk0} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} attach -f ${name1} ${disk2} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} create ${name1} mirror ${disk2} ${disk3}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} attach ${name1} ${disk2} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "the following errors must be manually repaired:"
+ echo "${fdisk0} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} attach -f ${name1} ${disk2} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk1} log ${disk0}
+expect_ok ${ZPOOL} create ${name1} ${disk2} log ${disk3}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} attach ${name1} ${disk3} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "the following errors must be manually repaired:"
+ echo "${fdisk0} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} attach -f ${name1} ${disk3} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk1} log mirror ${disk2} ${disk0}
+expect_ok ${ZPOOL} create ${name1} ${disk3} log mirror ${disk4} ${disk5}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} attach ${name1} ${disk2} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "the following errors must be manually repaired:"
+ echo "${fdisk0} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} attach -f ${name1} ${disk4} ${disk0}
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk1} cache ${disk0}
+expect_ok ${ZPOOL} create ${name1} ${disk2}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of active pool '${name0}'"
+)`
+add_msg="# TODO It shouldn't be possible to use offlined cache vdevs."
+expect "${exp}" ${ZPOOL} attach ${name1} ${disk2} ${disk0}
+add_msg=""
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+add_msg="# TODO It shouldn't be possible to use offlined cache vdevs."
+exp=`(
+ echo "invalid vdev specification"
+ echo "the following errors must be manually repaired:"
+ echo "${fdisk0} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} attach -f ${name1} ${disk2} ${disk0}
+add_msg=""
+exp=`(
+ echo " pool: ${name1}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} destroy ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/create/already_exists.t b/tools/regression/zfs/zpool/create/already_exists.t
new file mode 100644
index 0000000..f300580
--- /dev/null
+++ b/tools/regression/zfs/zpool/create/already_exists.t
@@ -0,0 +1,28 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..5"
+
+disks_create 2
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_fl ${ZPOOL} create ${name0} ${disk1}
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/create/automount.t b/tools/regression/zfs/zpool/create/automount.t
new file mode 100644
index 0000000..175f2ab
--- /dev/null
+++ b/tools/regression/zfs/zpool/create/automount.t
@@ -0,0 +1,22 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..5"
+
+disks_create 1
+names_create 1
+
+expect_fl is_mountpoint /${name0}
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+if [ -z "${no_mountpoint}" ]; then
+ expect_ok is_mountpoint /${name0}
+else
+ expect_fl is_mountpoint /${name0}
+fi
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl is_mountpoint /${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/create/cache.t b/tools/regression/zfs/zpool/create/cache.t
new file mode 100644
index 0000000..13af7f1
--- /dev/null
+++ b/tools/regression/zfs/zpool/create/cache.t
@@ -0,0 +1,126 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..35"
+
+disks_create 6
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} cache ${disk1}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} cache ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} cache ${disk2} ${disk3}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2} cache ${disk3} ${disk4}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3} cache ${disk4} ${disk5}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/create/disks.t b/tools/regression/zfs/zpool/create/disks.t
new file mode 100644
index 0000000..b6b722c
--- /dev/null
+++ b/tools/regression/zfs/zpool/create/disks.t
@@ -0,0 +1,52 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..14"
+
+disks_create 5
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} ${disk1} ${disk2} ${disk3} ${disk4}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/create/files.t b/tools/regression/zfs/zpool/create/files.t
new file mode 100644
index 0000000..06107b2
--- /dev/null
+++ b/tools/regression/zfs/zpool/create/files.t
@@ -0,0 +1,189 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..59"
+
+files_create 5
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${file0}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${file0} ${file1} ${file2} ${file3} ${file4}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo " ${file3} ONLINE 0 0 0"
+ echo " ${file4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${file0} ${file1}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${file0} ${file1} ${file2}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${file0} ${file1} ${file2} ${file3}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo " ${file3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${file0} ${file1} spare ${file2} ${file3}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " spares"
+ echo " ${file2} AVAIL "
+ echo " ${file3} AVAIL "
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${file0} ${file1} log ${file2} ${file3}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo " ${file3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${file0} ${file1} log mirror ${file2} ${file3}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo " ${file3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} mirror ${file0} ${file1} cache ${file2} ${file3}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+files_destroy
diff --git a/tools/regression/zfs/zpool/create/log.t b/tools/regression/zfs/zpool/create/log.t
new file mode 100644
index 0000000..18105fb
--- /dev/null
+++ b/tools/regression/zfs/zpool/create/log.t
@@ -0,0 +1,202 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..56"
+
+disks_create 7
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log ${disk1}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} log ${disk2}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} log mirror ${disk2} ${disk3} ${disk4}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz ${disk0} ${disk1} ${disk2} log ${disk3}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2} log mirror ${disk3} ${disk4} ${disk5}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3} log ${disk4}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3} log mirror ${disk4} ${disk5} ${disk6}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/create/mirror.t b/tools/regression/zfs/zpool/create/mirror.t
new file mode 100644
index 0000000..49572e8
--- /dev/null
+++ b/tools/regression/zfs/zpool/create/mirror.t
@@ -0,0 +1,83 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..22"
+
+disks_create 6
+names_create 1
+
+expect_fl ${ZPOOL} create ${name0} mirror ${disk0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} ${disk2} ${disk3} ${disk4}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} mirror ${disk2} ${disk3} mirror ${disk4} ${disk5}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/create/option-R.t b/tools/regression/zfs/zpool/create/option-R.t
new file mode 100644
index 0000000..75182a0
--- /dev/null
+++ b/tools/regression/zfs/zpool/create/option-R.t
@@ -0,0 +1,30 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..9"
+
+disks_create 1
+names_create 2
+
+expect_fl is_mountpoint /${name0}
+expect_fl is_mountpoint /${name1}
+expect_ok ${ZPOOL} create -R /${name1} ${name0} ${disk0}
+exp=`(
+ echo "NAME PROPERTY VALUE SOURCE"
+ echo "${name0} altroot /${name1} local"
+)`
+expect "${exp}" ${ZPOOL} get altroot ${name0}
+expect_fl is_mountpoint /${name0}
+if [ -z "${no_mountpoint}" ]; then
+ expect_ok is_mountpoint /${name1}
+else
+ expect_fl is_mountpoint /${name1}
+fi
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl is_mountpoint /${name0}
+expect_fl is_mountpoint /${name1}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/create/option-f_inuse.t b/tools/regression/zfs/zpool/create/option-f_inuse.t
new file mode 100644
index 0000000..344b9c1
--- /dev/null
+++ b/tools/regression/zfs/zpool/create/option-f_inuse.t
@@ -0,0 +1,297 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..146"
+
+disks_create 7
+names_create 2
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} create ${name1} ${disk0}
+expect_fl ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} create -f ${name1} ${disk0}
+expect_ok ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} export ${name0}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} create ${name1} mirror ${disk0} ${disk1}
+expect_fl ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} create -f ${name1} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} export ${name0}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} create ${name1} raidz1 ${disk0} ${disk1} ${disk2}
+expect_fl ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} create -f ${name1} raidz1 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} export ${name0}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} create ${name1} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_fl ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} create -f ${name1} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log ${disk1}
+expect_ok ${ZPOOL} export ${name0}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} create ${name1} ${disk0} log ${disk1}
+expect_fl ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} create -f ${name1} ${disk0} log ${disk1}
+expect_ok ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2}
+expect_ok ${ZPOOL} export ${name0}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} create ${name1} ${disk0} log mirror ${disk1} ${disk2}
+expect_fl ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} create -f ${name1} ${disk0} log mirror ${disk1} ${disk2}
+expect_ok ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} cache ${disk1}
+expect_ok ${ZPOOL} export ${name0}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} create ${name1} ${disk0} cache ${disk1}
+expect_fl ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} create -f ${name1} ${disk0} cache ${disk1}
+expect_ok ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+expect_ok ${ZPOOL} export ${name0}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk1} is part of potentially active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} create ${name1} mirror ${disk1} ${disk2}
+expect_fl ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} create -f ${name1} mirror ${disk1} ${disk2}
+expect_ok ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} import ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk2}
+expect_ok ${ZPOOL} export ${name0}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk2} is part of potentially active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} create ${name1} raidz1 ${disk2} ${disk3} ${disk4}
+expect_fl ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} create -f ${name1} raidz1 ${disk2} ${disk3} ${disk4}
+expect_ok ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} import ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline ${name0} ${disk3}
+expect_ok ${ZPOOL} export ${name0}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk3} is part of potentially active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} create ${name1} raidz2 ${disk3} ${disk4} ${disk5} ${disk6}
+expect_fl ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} create -f ${name1} raidz2 ${disk3} ${disk4} ${disk5} ${disk6}
+expect_ok ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} import ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk2}
+expect_ok ${ZPOOL} export ${name0}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk2} is part of potentially active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} create ${name1} ${disk3} log mirror ${disk2} ${disk4}
+expect_fl ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} create -f ${name1} ${disk3} log mirror ${disk2} ${disk4}
+expect_ok ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} import ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} cache ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+expect_ok ${ZPOOL} export ${name0}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk1} is part of potentially active pool '${name0}'"
+)`
+add_msg="# TODO It shouldn't be possible to use offlined cache vdev."
+expect "${exp}" ${ZPOOL} create ${name1} ${disk2} cache ${disk1}
+expect_fl ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} create -f ${name1} ${disk2} cache ${disk1}
+add_msg=""
+expect_ok ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} destroy ${name1}
+expect_ok ${ZPOOL} import ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk1} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} create ${name1} mirror ${disk1} ${disk2}
+expect_fl ${ZPOOL} status -x ${name1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "the following errors must be manually repaired:"
+ echo "${fdisk1} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} create -f ${name1} mirror ${disk1} ${disk2}
+expect_fl ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} online ${name0} ${disk1}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk2}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk2} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} create ${name1} raidz1 ${disk2} ${disk3} ${disk4}
+expect_fl ${ZPOOL} status -x ${name1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "the following errors must be manually repaired:"
+ echo "${fdisk2} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} create -f ${name1} raidz1 ${disk2} ${disk3} ${disk4}
+expect_fl ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} online ${name0} ${disk2}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline ${name0} ${disk3}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk3} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} create ${name1} raidz2 ${disk3} ${disk4} ${disk5} ${disk6}
+expect_fl ${ZPOOL} status -x ${name1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "the following errors must be manually repaired:"
+ echo "${fdisk3} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} create -f ${name1} raidz2 ${disk3} ${disk4} ${disk5} ${disk6}
+expect_fl ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} online ${name0} ${disk3}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk2}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk2} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} create ${name1} ${disk3} log mirror ${disk2} ${disk4}
+expect_fl ${ZPOOL} status -x ${name1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "the following errors must be manually repaired:"
+ echo "${fdisk2} is part of active pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} create -f ${name1} ${disk3} log mirror ${disk2} ${disk4}
+expect_fl ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} online ${name0} ${disk2}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} cache ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk1} is part of active pool '${name0}'"
+)`
+add_msg="# TODO It reports that ${fdisk1} is part of unknown pool."
+expect "${exp}" ${ZPOOL} create ${name1} ${disk2} cache ${disk1}
+add_msg=""
+expect_fl ${ZPOOL} status -x ${name1}
+exp=`(
+ echo "invalid vdev specification"
+ echo "the following errors must be manually repaired:"
+ echo "${fdisk1} is part of active pool '${name0}'"
+)`
+add_msg="# TODO It reports that ${fdisk1} is used twice."
+expect "${exp}" ${ZPOOL} create -f ${name1} ${disk2} cache ${disk1}
+add_msg=""
+expect_fl ${ZPOOL} status -x ${name1}
+expect_ok ${ZPOOL} online ${name0} ${disk1}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+exp=`(
+ echo "invalid vdev specification"
+ echo "use '-f' to override the following errors:"
+ echo "${fdisk0} is part of exported pool '${name0}'"
+)`
+expect "${exp}" ${ZPOOL} create ${name0} ${disk0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_ok ${ZPOOL} create -f ${name0} ${disk0}
+expect_ok ${ZPOOL} status -x ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/create/option-f_replication_level_mismatch_0.t b/tools/regression/zfs/zpool/create/option-f_replication_level_mismatch_0.t
new file mode 100644
index 0000000..27ffede
--- /dev/null
+++ b/tools/regression/zfs/zpool/create/option-f_replication_level_mismatch_0.t
@@ -0,0 +1,200 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..70"
+
+disks_create 6
+names_create 1
+
+expect_fl ${ZPOOL} create ${name0} ${disk0} mirror ${disk1} ${disk2}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} ${disk0} mirror ${disk1} ${disk2}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} ${disk0} ${disk1} mirror ${disk2} ${disk3}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} ${disk0} ${disk1} mirror ${disk2} ${disk3}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} ${disk0} raidz ${disk1} ${disk2} ${disk3}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} ${disk0} raidz ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} ${disk0} ${disk1} raidz1 ${disk2} ${disk3} ${disk4}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} ${disk0} ${disk1} raidz1 ${disk2} ${disk3} ${disk4}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} ${disk0} raidz2 ${disk1} ${disk2} ${disk3} ${disk4}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} ${disk0} raidz2 ${disk1} ${disk2} ${disk3} ${disk4}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} ${disk0} ${disk1} raidz2 ${disk2} ${disk3} ${disk4} ${disk5}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} ${disk0} ${disk1} raidz2 ${disk2} ${disk3} ${disk4} ${disk5}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+add_msg="# TODO Sun CR 6726091, Lustre bug 16873"
+expect_fl ${ZPOOL} create ${name0} ${disk0} log ${disk1} mirror ${disk2} ${disk3}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+add_msg=""
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} ${disk0} log ${disk1} mirror ${disk2} ${disk3}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/create/option-f_replication_level_mismatch_1.t b/tools/regression/zfs/zpool/create/option-f_replication_level_mismatch_1.t
new file mode 100644
index 0000000..d64db3a
--- /dev/null
+++ b/tools/regression/zfs/zpool/create/option-f_replication_level_mismatch_1.t
@@ -0,0 +1,540 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..180"
+
+disks_create 9
+names_create 1
+
+expect_fl ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} mirror ${disk2} ${disk3} ${disk4}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} mirror ${disk0} ${disk1} mirror ${disk2} ${disk3} ${disk4}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} ${disk2} mirror ${disk3} ${disk4}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} mirror ${disk0} ${disk1} ${disk2} mirror ${disk3} ${disk4}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} raidz ${disk0} ${disk1} ${disk2} raidz ${disk3} ${disk4} ${disk5} ${disk6}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz ${disk0} ${disk1} ${disk2} raidz ${disk3} ${disk4} ${disk5} ${disk6}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} raidz ${disk0} ${disk1} ${disk2} ${disk3} raidz ${disk4} ${disk5} ${disk6}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz ${disk0} ${disk1} ${disk2} ${disk3} raidz ${disk4} ${disk5} ${disk6}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3} raidz2 ${disk4} ${disk5} ${disk6} ${disk7} ${disk8}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3} raidz2 ${disk4} ${disk5} ${disk6} ${disk7} ${disk8}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo " ${disk8} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3} ${disk4} raidz2 ${disk5} ${disk6} ${disk7} ${disk8}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3} ${disk4} raidz2 ${disk5} ${disk6} ${disk7} ${disk8}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo " ${disk8} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} raidz ${disk2} ${disk3}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} mirror ${disk0} ${disk1} raidz ${disk2} ${disk3}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} raidz ${disk0} ${disk1} mirror ${disk2} ${disk3}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz ${disk0} ${disk1} mirror ${disk2} ${disk3}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} ${disk2} raidz ${disk3} ${disk4}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} mirror ${disk0} ${disk1} ${disk2} raidz ${disk3} ${disk4}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} raidz ${disk2} ${disk3} ${disk4}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} mirror ${disk0} ${disk1} raidz ${disk2} ${disk3} ${disk4}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} raidz2 ${disk2} ${disk3} ${disk4}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} mirror ${disk0} ${disk1} raidz2 ${disk2} ${disk3} ${disk4}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} ${disk2} raidz2 ${disk3} ${disk4} ${disk5}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} mirror ${disk0} ${disk1} ${disk2} raidz2 ${disk3} ${disk4} ${disk5}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} ${disk2} ${disk3} raidz2 ${disk4} ${disk5} ${disk6}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} mirror ${disk0} ${disk1} ${disk2} ${disk3} raidz2 ${disk4} ${disk5} ${disk6}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} raidz2 ${disk2} ${disk3} ${disk4}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz1 ${disk0} ${disk1} raidz2 ${disk2} ${disk3} ${disk4}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${dik2} raidz2 ${disk3} ${disk4} ${disk5}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz1 ${disk0} ${disk1} ${disk2} raidz2 ${disk3} ${disk4} ${disk5}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${dik2} ${disk3} raidz2 ${disk4} ${disk5} ${disk6}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz1 ${disk0} ${disk1} ${disk2} ${disk3} raidz2 ${disk4} ${disk5} ${disk6}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+add_msg="# TODO Sun CR 6726091, Lustre bug 16873"
+expect_fl ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2} mirror ${disk3} ${disk4} ${disk5}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+add_msg=""
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} ${disk0} log mirror ${disk1} ${disk2} mirror ${disk3} ${disk4} ${disk5}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+add_msg="# TODO Sun CR 6726091, Lustre bug 16873"
+expect_fl ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2} ${disk3} mirror ${disk4} ${disk5}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+add_msg=""
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} ${disk0} log mirror ${disk1} ${disk2} ${disk3} mirror ${disk4} ${disk5}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/create/option-f_size_mismatch.t b/tools/regression/zfs/zpool/create/option-f_size_mismatch.t
new file mode 100644
index 0000000..74af390
--- /dev/null
+++ b/tools/regression/zfs/zpool/create/option-f_size_mismatch.t
@@ -0,0 +1,256 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..104"
+
+disks_create 1 64M
+disks_create 4
+disks_create 3 64M
+files_create 1 64M
+files_create 4
+files_create 3 64M
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} ${disk1}
+expect_ok ${ZPOOL} status -x ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${file0} ${file1}
+expect_ok ${ZPOOL} status -x ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk1} ${disk2} mirror ${disk0} ${disk5}
+expect_ok ${ZPOOL} status -x ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${file1} ${file2} mirror ${file0} ${file5}
+expect_ok ${ZPOOL} status -x ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk1} ${disk2} ${disk3} raidz1 ${disk0} ${disk5} ${disk6}
+expect_ok ${ZPOOL} status -x ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${file1} ${file2} ${file3} raidz1 ${file0} ${file5} ${file6}
+expect_ok ${ZPOOL} status -x ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk1} ${disk2} ${disk3} ${disk4} raidz2 ${disk0} ${disk5} ${disk6} ${disk7}
+expect_ok ${ZPOOL} status -x ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${file1} ${file2} ${file3} ${file4} raidz2 ${file0} ${file5} ${file6} ${file7}
+expect_ok ${ZPOOL} status -x ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} mirror ${file0} ${file1}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} mirror ${file0} ${file1}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} raidz1 ${file0} ${file1} ${file2}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz1 ${file0} ${file1} ${file2}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} raidz2 ${file0} ${file1} ${file2} ${file3}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz2 ${file0} ${file1} ${file2} ${file3}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo " ${file3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+add_msg="# TODO Sun CR 6726091, Lustre bug 16873"
+expect_fl ${ZPOOL} create ${name0} ${disk1} log mirror ${disk0} ${disk2}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+add_msg=""
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} ${disk1} log mirror ${disk0} ${disk2}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+add_msg="# TODO Sun CR 6726091, Lustre bug 16873"
+expect_fl ${ZPOOL} create ${name0} ${file1} log mirror ${file0} ${file2}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+add_msg=""
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} ${file1} log mirror ${file0} ${file2}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+disks_destroy
+files_destroy
diff --git a/tools/regression/zfs/zpool/create/option-f_type_mismatch.t b/tools/regression/zfs/zpool/create/option-f_type_mismatch.t
new file mode 100644
index 0000000..7739eb8
--- /dev/null
+++ b/tools/regression/zfs/zpool/create/option-f_type_mismatch.t
@@ -0,0 +1,414 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..160"
+
+disks_create 6
+files_create 2
+names_create 1
+
+expect_fl ${ZPOOL} create ${name0} ${disk0} ${file0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} ${disk0} ${file0}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} ${file0} ${disk0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} ${file0} ${disk0}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} mirror ${disk0} ${file0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} mirror ${disk0} ${file0}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} mirror ${file0} ${disk0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} mirror ${file0} ${disk0}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} mirror ${disk0} ${file0} ${file1}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} mirror ${disk0} ${file0} ${file1}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} mirror ${file0} ${disk0} ${disk1}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} mirror ${file0} ${disk0} ${disk1}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} raidz1 ${disk0} ${file0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz1 ${disk0} ${file0}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} raidz1 ${file0} ${disk0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz1 ${file0} ${disk0}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} raidz1 ${disk0} ${file0} ${file1}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz1 ${disk0} ${file0} ${file1}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} raidz1 ${file0} ${disk0} ${disk1}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz1 ${file0} ${disk0} ${disk1}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} raidz2 ${disk0} ${file0} ${file1}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz2 ${disk0} ${file0} ${file1}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} raidz2 ${file0} ${disk0} ${disk1}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz2 ${file0} ${disk0} ${disk1}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${file0} ${file1}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz2 ${disk0} ${disk1} ${file0} ${file1}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create ${name0} raidz2 ${file0} ${disk0} ${file1} ${disk1}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz2 ${file0} ${disk0} ${file1} ${disk1}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+add_msg="# TODO Sun CR 6726091, Lustre bug 16873"
+expect_fl ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${file0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+add_msg=""
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} ${disk0} log mirror ${disk1} ${file0}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+add_msg="# TODO Sun CR 6726091, Lustre bug 16873"
+expect_fl ${ZPOOL} create ${name0} ${file0} log mirror ${file1} ${disk0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+add_msg=""
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} ${file0} log mirror ${file1} ${disk0}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${file1} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+disks_destroy
+files_destroy
diff --git a/tools/regression/zfs/zpool/create/option-m.t b/tools/regression/zfs/zpool/create/option-m.t
new file mode 100644
index 0000000..357959e
--- /dev/null
+++ b/tools/regression/zfs/zpool/create/option-m.t
@@ -0,0 +1,59 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..28"
+
+disks_create 1
+names_create 2
+
+expect_fl is_mountpoint /${name0}
+expect_fl is_mountpoint /${name1}
+expect_ok ${ZPOOL} create -m /${name1} ${name0} ${disk0}
+exp=`(
+ echo "NAME PROPERTY VALUE SOURCE"
+ echo "${name0} altroot - default"
+)`
+expect "${exp}" ${ZPOOL} get altroot ${name0}
+expect_fl is_mountpoint /${name0}
+if [ -z "${no_mountpoint}" ]; then
+ expect_ok is_mountpoint /${name1}
+else
+ expect_fl is_mountpoint /${name1}
+fi
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl is_mountpoint /${name0}
+expect_fl is_mountpoint /${name1}
+expect_ok rmdir /${name1}
+
+expect_ok mkdir /${name1}
+expect_ok ${ZPOOL} create -m legacy ${name0} ${disk0}
+expect_fl is_mountpoint /${name0}
+expect_ok mount ${mount_t_flag} zfs ${name0} /${name1}
+if [ -z "${no_mountpoint}" ]; then
+ expect_ok is_mountpoint /${name1}
+else
+ expect_fl is_mountpoint /${name1}
+fi
+expect_ok umount /${name1}
+expect_fl is_mountpoint /${name1}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_ok rmdir /${name1}
+
+expect_ok mkdir /${name1}
+expect_ok ${ZPOOL} create -m none ${name0} ${disk0}
+expect_fl is_mountpoint /${name0}
+expect_ok mount ${mount_t_flag} zfs ${name0} /${name1}
+if [ -z "${no_mountpoint}" ]; then
+ expect_ok is_mountpoint /${name1}
+else
+ expect_fl is_mountpoint /${name1}
+fi
+expect_ok umount /${name1}
+expect_fl is_mountpoint /${name1}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_ok rmdir /${name1}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/create/option-n.t b/tools/regression/zfs/zpool/create/option-n.t
new file mode 100644
index 0000000..0cb3e7f
--- /dev/null
+++ b/tools/regression/zfs/zpool/create/option-n.t
@@ -0,0 +1,23 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..5"
+
+disks_create 1
+names_create 1
+
+expect_fl is_mountpoint /${name0}
+exp=`(
+ echo "would create '${name0}' with the following layout:"
+ echo " ${name0}"
+ echo " ${disk0}"
+)`
+expect "${exp}" ${ZPOOL} create -n ${name0} ${disk0}
+expect_fl is_mountpoint /${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/create/option-o.t b/tools/regression/zfs/zpool/create/option-o.t
new file mode 100644
index 0000000..3305323
--- /dev/null
+++ b/tools/regression/zfs/zpool/create/option-o.t
@@ -0,0 +1,107 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..44"
+
+disks_create 1
+names_create 2
+
+expect_fl ${ZPOOL} create -o size=96M ${name0} ${disk0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create -o used=0 ${name0} ${disk0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create -o available=96M ${name0} ${disk0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create -o capacity=0% ${name0} ${disk0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl is_mountpoint /${name0}
+expect_fl is_mountpoint /${name1}
+expect_ok ${ZPOOL} create -o altroot=/${name1} ${name0} ${disk0}
+exp=`(
+ echo "NAME PROPERTY VALUE SOURCE"
+ echo "${name0} altroot /${name1} local"
+)`
+expect "${exp}" ${ZPOOL} get altroot ${name0}
+expect_fl is_mountpoint /${name0}
+if [ -z "${no_mountpoint}" ]; then
+ expect_ok is_mountpoint /${name1}
+else
+ expect_fl is_mountpoint /${name1}
+fi
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl is_mountpoint /${name0}
+expect_fl is_mountpoint /${name1}
+
+expect_fl ${ZPOOL} create -o health=ONLINE ${name0} ${disk0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create -o guid=13949667482126165574 ${name0} ${disk0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create -o version=9 ${name0} ${disk0}
+exp=`(
+ echo "NAME PROPERTY VALUE SOURCE"
+ echo "${name0} version 9 local"
+)`
+expect "${exp}" ${ZPOOL} get version ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_fl ${ZPOOL} create -o bootfs=${name0}/root ${name0} ${disk0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create -o delegation=off ${name0} ${disk0}
+exp=`(
+ echo "NAME PROPERTY VALUE SOURCE"
+ echo "${name0} delegation off local"
+)`
+expect "${exp}" ${ZPOOL} get delegation ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create -o autoreplace=on ${name0} ${disk0}
+exp=`(
+ echo "NAME PROPERTY VALUE SOURCE"
+ echo "${name0} autoreplace on local"
+)`
+expect "${exp}" ${ZPOOL} get autoreplace ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create -o cachefile=none ${name0} ${disk0}
+exp=`(
+ echo "NAME PROPERTY VALUE SOURCE"
+ echo "${name0} cachefile none local"
+)`
+expect "${exp}" ${ZPOOL} get cachefile ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create -o cachefile=/tmp/${name1} ${name0} ${disk0}
+exp=`(
+ echo "NAME PROPERTY VALUE SOURCE"
+ echo "${name0} cachefile /tmp/${name1} local"
+)`
+expect "${exp}" ${ZPOOL} get cachefile ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create -o failmode=continue ${name0} ${disk0}
+exp=`(
+ echo "NAME PROPERTY VALUE SOURCE"
+ echo "${name0} failmode continue local"
+)`
+expect "${exp}" ${ZPOOL} get failmode ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create -o failmode=panic ${name0} ${disk0}
+exp=`(
+ echo "NAME PROPERTY VALUE SOURCE"
+ echo "${name0} failmode panic local"
+)`
+expect "${exp}" ${ZPOOL} get failmode ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/create/raidz1.t b/tools/regression/zfs/zpool/create/raidz1.t
new file mode 100644
index 0000000..382f7e7
--- /dev/null
+++ b/tools/regression/zfs/zpool/create/raidz1.t
@@ -0,0 +1,129 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..37"
+
+disks_create 9
+names_create 1
+
+expect_fl ${ZPOOL} create ${name0} raidz ${disk0}
+expect_fl ${ZPOOL} create ${name0} raidz1 ${disk0}
+
+expect_ok ${ZPOOL} create ${name0} raidz ${disk0} ${disk1}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2} ${disk3} ${disk4}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2} raidz ${disk3} ${disk4} ${disk5} raidz1 ${disk6} ${disk7} ${disk8}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo " ${disk8} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/create/raidz2.t b/tools/regression/zfs/zpool/create/raidz2.t
new file mode 100644
index 0000000..587d3dc
--- /dev/null
+++ b/tools/regression/zfs/zpool/create/raidz2.t
@@ -0,0 +1,91 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..23"
+
+disks_create 12
+names_create 1
+
+expect_fl ${ZPOOL} create ${name0} raidz2 ${disk0}
+expect_fl ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3} ${disk4}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3} raidz2 ${disk4} ${disk5} ${disk6} ${disk7} raidz2 ${disk8} ${disk9} ${disk10} ${disk11}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk8} ONLINE 0 0 0"
+ echo " ${disk9} ONLINE 0 0 0"
+ echo " ${disk10} ONLINE 0 0 0"
+ echo " ${disk11} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/create/spare.t b/tools/regression/zfs/zpool/create/spare.t
new file mode 100644
index 0000000..46f47da
--- /dev/null
+++ b/tools/regression/zfs/zpool/create/spare.t
@@ -0,0 +1,104 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..28"
+
+disks_create 6
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} spare ${disk1}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " spares"
+ echo " ${disk1} AVAIL"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} spare ${disk2} ${disk3}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " spares"
+ echo " ${disk2} AVAIL"
+ echo " ${disk3} AVAIL"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz ${disk0} ${disk1} ${disk2} spare ${disk3} ${disk4}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " spares"
+ echo " ${disk3} AVAIL"
+ echo " ${disk4} AVAIL"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3} spare ${disk4} ${disk5}
+expect_ok ${ZPOOL} status -x ${name0}
+expect "pool '${name0}' is healthy" ${ZPOOL} status -x ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " spares"
+ echo " ${disk4} AVAIL"
+ echo " ${disk5} AVAIL"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+expect_fl ${ZPOOL} destroy ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/offline/io.t b/tools/regression/zfs/zpool/offline/io.t
new file mode 100644
index 0000000..8faeb8a2
--- /dev/null
+++ b/tools/regression/zfs/zpool/offline/io.t
@@ -0,0 +1,85 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..31"
+
+disks_create 4 128M
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+sum0_before=`calcsum ${fdisk0}`
+sum1_before=`calcsum ${fdisk1}`
+${ZFS} snapshot ${name0}@test
+sum0_after=`calcsum ${fdisk0}`
+sum1_after=`calcsum ${fdisk1}`
+expect_ok test "${sum0_before}" = "${sum0_after}"
+expect_fl test "${sum1_before}" = "${sum1_after}"
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} mirror ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+add_msg="# TODO Sun CR 6328632, Lustre bug 16878"
+expect_ok ${ZPOOL} offline ${name0} ${disk3}
+add_msg=""
+sum0_before=`calcsum ${fdisk0}`
+sum1_before=`calcsum ${fdisk1}`
+sum2_before=`calcsum ${fdisk2}`
+sum3_before=`calcsum ${fdisk3}`
+${ZFS} snapshot ${name0}@test
+sum0_after=`calcsum ${fdisk0}`
+sum1_after=`calcsum ${fdisk1}`
+sum2_after=`calcsum ${fdisk2}`
+sum3_after=`calcsum ${fdisk3}`
+expect_fl test "${sum0_before}" = "${sum0_after}"
+expect_ok test "${sum1_before}" = "${sum1_after}"
+expect_fl test "${sum2_before}" = "${sum2_after}"
+add_msg="# TODO Sun CR 6328632, Lustre bug 16878"
+expect_ok test "${sum3_before}" = "${sum3_after}"
+add_msg=""
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+sum0_before=`calcsum ${fdisk0}`
+sum1_before=`calcsum ${fdisk1}`
+sum2_before=`calcsum ${fdisk2}`
+${ZFS} snapshot ${name0}@test
+sum0_after=`calcsum ${fdisk0}`
+sum1_after=`calcsum ${fdisk1}`
+sum2_after=`calcsum ${fdisk2}`
+expect_fl test "${sum0_before}" = "${sum0_after}"
+expect_ok test "${sum1_before}" = "${sum1_after}"
+expect_fl test "${sum2_before}" = "${sum2_after}"
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+add_msg="# TODO Sun CR 6328632, Lustre bug 16878"
+expect_ok ${ZPOOL} offline ${name0} ${disk3}
+add_msg=""
+sum0_before=`calcsum ${fdisk0}`
+sum1_before=`calcsum ${fdisk1}`
+sum2_before=`calcsum ${fdisk2}`
+sum3_before=`calcsum ${fdisk3}`
+${ZFS} snapshot ${name0}@test
+sum0_after=`calcsum ${fdisk0}`
+sum1_after=`calcsum ${fdisk1}`
+sum2_after=`calcsum ${fdisk2}`
+sum3_after=`calcsum ${fdisk3}`
+expect_fl test "${sum0_before}" = "${sum0_after}"
+expect_ok test "${sum1_before}" = "${sum1_after}"
+expect_fl test "${sum2_before}" = "${sum2_after}"
+add_msg="# TODO Sun CR 6328632, Lustre bug 16878"
+expect_ok test "${sum3_before}" = "${sum3_after}"
+add_msg=""
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/offline/log.t b/tools/regression/zfs/zpool/offline/log.t
new file mode 100644
index 0000000..2a741ba
--- /dev/null
+++ b/tools/regression/zfs/zpool/offline/log.t
@@ -0,0 +1,254 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..67"
+
+disks_create 7
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} online ${name0} ${disk1}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} OFFLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} online ${name0} ${disk2}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+expect_fl ${ZPOOL} offline ${name0} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} online ${name0} ${disk1}
+expect_ok ${ZPOOL} online ${name0} ${disk2}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2} mirror ${disk3} ${disk4}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk4}
+expect_fl ${ZPOOL} offline ${name0} ${disk2}
+expect_fl ${ZPOOL} offline ${name0} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} OFFLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} online ${name0} ${disk1}
+expect_ok ${ZPOOL} online ${name0} ${disk4}
+expect_ok ${ZPOOL} online ${name0} ${disk2}
+expect_ok ${ZPOOL} online ${name0} ${disk3}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2} mirror ${disk3} ${disk4}
+expect_ok ${ZPOOL} offline ${name0} ${disk2} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} OFFLINE 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk3} OFFLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} online ${name0} ${disk2}
+expect_ok ${ZPOOL} online ${name0} ${disk3}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} OFFLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} online ${name0} ${disk1}
+expect_ok ${ZPOOL} online ${name0} ${disk2}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2} ${disk3} mirror ${disk4} ${disk5} ${disk6}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk4} ${disk6}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} OFFLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk4} OFFLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} OFFLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} online ${name0} ${disk1}
+expect_ok ${ZPOOL} online ${name0} ${disk2}
+expect_ok ${ZPOOL} online ${name0} ${disk4}
+expect_ok ${ZPOOL} online ${name0} ${disk6}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2} ${disk3} ${disk4} ${disk5}
+expect_ok ${ZPOOL} offline ${name0} ${disk1} ${disk3} ${disk4} ${disk5}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} OFFLINE 0 0 0"
+ echo " ${disk4} OFFLINE 0 0 0"
+ echo " ${disk5} OFFLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} online ${name0} ${disk1}
+expect_ok ${ZPOOL} online ${name0} ${disk3}
+expect_ok ${ZPOOL} online ${name0} ${disk4}
+expect_ok ${ZPOOL} online ${name0} ${disk5}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/offline/mirror.t b/tools/regression/zfs/zpool/offline/mirror.t
new file mode 100644
index 0000000..37778fc
--- /dev/null
+++ b/tools/regression/zfs/zpool/offline/mirror.t
@@ -0,0 +1,224 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..47"
+
+disks_create 6
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create -f ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create -f ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_fl ${ZPOOL} offline ${name0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create -f ${name0} mirror ${disk0} ${disk1} mirror ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_ok ${ZPOOL} offline ${name0} ${disk3}
+expect_fl ${ZPOOL} offline ${name0} ${disk1}
+expect_fl ${ZPOOL} offline ${name0} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} OFFLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create -f ${name0} mirror ${disk0} ${disk1} mirror ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline ${name0} ${disk1} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk2} OFFLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create -f ${name0} mirror ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+add_msg="# TODO Sun CR 6328632, Lustre bug 16878"
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg=""
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create -f ${name0} mirror ${disk0} ${disk1} ${disk2} mirror ${disk3} ${disk4} ${disk5}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+add_msg="# TODO Sun CR 6328632, Lustre bug 16878"
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk3} ${disk5}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk3} OFFLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} OFFLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg=""
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create -f ${name0} mirror ${disk0} ${disk1} ${disk2} ${disk3} ${disk4}
+add_msg="# TODO Sun CR 6328632, Lustre bug 16878"
+expect_ok ${ZPOOL} offline ${name0} ${disk0} ${disk2} ${disk3} ${disk4}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} OFFLINE 0 0 0"
+ echo " ${disk3} OFFLINE 0 0 0"
+ echo " ${disk4} OFFLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg=""
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/offline/option-t.t b/tools/regression/zfs/zpool/offline/option-t.t
new file mode 100644
index 0000000..c408796
--- /dev/null
+++ b/tools/regression/zfs/zpool/offline/option-t.t
@@ -0,0 +1,1110 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..219"
+
+disks_create 8
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} online ${name0} ${disk0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} offline -t ${name0} ${disk0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0} \| grep -v 'scrub:'
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} ${disk2}
+add_msg="# TODO Sun CR 6328632, Lustre bug 16878"
+expect_ok ${ZPOOL} offline ${name0} ${disk0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg=""
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+add_msg="# TODO Sun CR 6328632, Lustre bug 16878"
+expect "${exp}" ${ZPOOL} status ${name0} \| grep -v 'scrub:'
+add_msg=""
+expect_ok ${ZPOOL} online ${name0} ${disk0}
+expect_ok ${ZPOOL} online ${name0} ${disk1}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} ${disk2}
+add_msg="# TODO Sun CR 6328632, Lustre bug 16878"
+expect_ok ${ZPOOL} offline -t ${name0} ${disk0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg=""
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0} \| grep -v 'scrub:'
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+add_msg="# TODO Sun CR 6328632, Lustre bug 16878"
+expect_ok ${ZPOOL} offline -t ${name0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg=""
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0} \| grep -v 'scrub:'
+expect_ok ${ZPOOL} online ${name0} ${disk0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1} mirror ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_ok ${ZPOOL} offline -t ${name0} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} OFFLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0} \| grep -v 'scrub:'
+expect_ok ${ZPOOL} online ${name0} ${disk0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz1 DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz1 DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0} \| grep -v 'scrub:'
+expect_ok ${ZPOOL} online ${name0} ${disk0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline -t ${name0} ${disk0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz1 DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0} \| grep -v 'scrub:'
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2} raidz1 ${disk3} ${disk4} ${disk5}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+expect_ok ${ZPOOL} offline -t ${name0} ${disk5}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz1 DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " raidz1 DEGRADED 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} OFFLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz1 DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0} \| grep -v 'scrub:'
+expect_ok ${ZPOOL} online ${name0} ${disk1}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0} \| grep -v 'scrub:'
+expect_ok ${ZPOOL} online ${name0} ${disk0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline -t ${name0} ${disk0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0} \| grep -v 'scrub:'
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+add_msg="# TODO Sun CR 6328632, Lustre bug 16878"
+expect_ok ${ZPOOL} offline ${name0} ${disk0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg=""
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+add_msg="# TODO Sun CR 6328632, Lustre bug 16878"
+expect "${exp}" ${ZPOOL} status ${name0} \| grep -v 'scrub:'
+add_msg=""
+expect_ok ${ZPOOL} online ${name0} ${disk0}
+expect_ok ${ZPOOL} online ${name0} ${disk1}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+add_msg="# TODO Sun CR 6328632, Lustre bug 16878"
+expect_ok ${ZPOOL} offline -t ${name0} ${disk0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg=""
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0} \| grep -v 'scrub:'
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+add_msg="# TODO Sun CR 6328632, Lustre bug 16878"
+expect_ok ${ZPOOL} offline -t ${name0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg=""
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0} \| grep -v 'scrub:'
+expect_ok ${ZPOOL} online ${name0} ${disk0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3} raidz2 ${disk4} ${disk5} ${disk6} ${disk7}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+expect_ok ${ZPOOL} offline -t ${name0} ${disk6}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} OFFLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0} \| grep -v 'scrub:'
+expect_ok ${ZPOOL} online ${name0} ${disk1}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} online ${name0} ${disk1}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline -t ${name0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline ${name0} ${disk1} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} OFFLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} OFFLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} online ${name0} ${disk1}
+expect_ok ${ZPOOL} online ${name0} ${disk2}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline -t ${name0} ${disk1} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} OFFLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+expect_ok ${ZPOOL} offline -t ${name0} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} OFFLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} online ${name0} ${disk1}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2} mirror ${disk3} ${disk4}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+expect_ok ${ZPOOL} offline -t ${name0} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk3} OFFLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} online ${name0} ${disk1}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} cache ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} online ${name0} ${disk1}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} cache ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline -t ${name0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} cache ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+expect_ok ${ZPOOL} offline -t ${name0} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} OFFLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} export ${name0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} online ${name0} ${disk1}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/offline/raidz1.t b/tools/regression/zfs/zpool/offline/raidz1.t
new file mode 100644
index 0000000..d6827ca
--- /dev/null
+++ b/tools/regression/zfs/zpool/offline/raidz1.t
@@ -0,0 +1,184 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..35"
+
+disks_create 5
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz1 DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create -f ${name0} raidz1 ${disk0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz1 DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create -f ${name0} raidz1 ${disk0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_fl ${ZPOOL} offline ${name0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz1 DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create -f ${name0} raidz1 ${disk0} ${disk1} raidz1 ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz1 DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect_ok ${ZPOOL} offline ${name0} ${disk3}
+expect_fl ${ZPOOL} offline ${name0} ${disk1}
+expect_fl ${ZPOOL} offline ${name0} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz1 DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " raidz1 DEGRADED 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} OFFLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create -f ${name0} raidz1 ${disk0} ${disk1} raidz1 ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline ${name0} ${disk1} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz1 DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " raidz1 DEGRADED 0 0 0"
+ echo " ${disk2} OFFLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create -f ${name0} raidz1 ${disk0} ${disk1} ${disk2} ${disk3} ${disk4}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+expect_fl ${ZPOOL} offline ${name0} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz1 DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/offline/raidz2.t b/tools/regression/zfs/zpool/offline/raidz2.t
new file mode 100644
index 0000000..299649d
--- /dev/null
+++ b/tools/regression/zfs/zpool/offline/raidz2.t
@@ -0,0 +1,202 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..33"
+
+disks_create 8
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create -f ${name0} raidz2 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create -f ${name0} raidz2 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} OFFLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create -f ${name0} raidz2 ${disk0} ${disk1} ${disk2}
+add_msg="# TODO Sun CR 6328632, Lustre bug 16878"
+expect_ok ${ZPOOL} offline ${name0} ${disk0} ${disk1}
+add_msg=""
+expect_fl ${ZPOOL} offline ${name0} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+add_msg="# TODO Sun CR 6328632, Lustre bug 16878"
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg=""
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create -f ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3} raidz2 ${disk4} ${disk5} ${disk6} ${disk7}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg="# TODO Sun CR 6328632, Lustre bug 16878"
+expect_ok ${ZPOOL} offline ${name0} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} OFFLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk7} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} offline ${name0} ${disk4} ${disk7}
+add_msg=""
+expect_fl ${ZPOOL} offline ${name0} ${disk0}
+expect_fl ${ZPOOL} offline ${name0} ${disk2}
+expect_fl ${ZPOOL} offline ${name0} ${disk5} ${disk6}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} OFFLINE 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk4} OFFLINE 0 0 0"
+ echo " ${disk5} ONLINE 0 0 0"
+ echo " ${disk6} ONLINE 0 0 0"
+ echo " ${disk7} OFFLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+add_msg="# TODO Sun CR 6328632, Lustre bug 16878"
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg=""
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/remove/cache.t b/tools/regression/zfs/zpool/remove/cache.t
new file mode 100644
index 0000000..fd55451
--- /dev/null
+++ b/tools/regression/zfs/zpool/remove/cache.t
@@ -0,0 +1,58 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..9"
+
+disks_create 4
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} cache ${disk1}
+expect_ok ${ZPOOL} add ${name0} cache ${disk2} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} remove ${name0} ${disk1} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} remove ${name0} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/remove/spare.t b/tools/regression/zfs/zpool/remove/spare.t
new file mode 100644
index 0000000..c52f220
--- /dev/null
+++ b/tools/regression/zfs/zpool/remove/spare.t
@@ -0,0 +1,106 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..18"
+
+disks_create 4
+files_create 4
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} spare ${disk1}
+expect_ok ${ZPOOL} add ${name0} spare ${disk2} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " spares"
+ echo " ${disk1} AVAIL"
+ echo " ${disk2} AVAIL"
+ echo " ${disk3} AVAIL"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} remove ${name0} ${disk1} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " spares"
+ echo " ${disk3} AVAIL"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} remove ${name0} ${disk3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${file0} spare ${file1}
+expect_ok ${ZPOOL} add ${name0} spare ${file2} ${file3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " spares"
+ echo " ${file1} AVAIL"
+ echo " ${file2} AVAIL"
+ echo " ${file3} AVAIL"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} remove ${name0} ${file1} ${file2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo " spares"
+ echo " ${file3} AVAIL"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} remove ${name0} ${file3}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${file0} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+files_destroy
+disks_destroy
diff --git a/tools/regression/zfs/zpool/replace/cache.t b/tools/regression/zfs/zpool/replace/cache.t
new file mode 100644
index 0000000..23abc37
--- /dev/null
+++ b/tools/regression/zfs/zpool/replace/cache.t
@@ -0,0 +1,43 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..6"
+
+disks_create 4
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} cache ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_fl ${ZPOOL} replace ${name0} ${disk1} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " cache"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/replace/disk.t b/tools/regression/zfs/zpool/replace/disk.t
new file mode 100644
index 0000000..51029e6
--- /dev/null
+++ b/tools/regression/zfs/zpool/replace/disk.t
@@ -0,0 +1,48 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..10"
+
+disks_create 4
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${disk0}
+expect_ok ${ZPOOL} replace ${name0} ${disk0} ${disk1}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} replace ${name0} ${disk1} ${disk3}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/replace/log.t b/tools/regression/zfs/zpool/replace/log.t
new file mode 100644
index 0000000..6eed8bd
--- /dev/null
+++ b/tools/regression/zfs/zpool/replace/log.t
@@ -0,0 +1,152 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..27"
+
+disks_create 4
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2}
+expect_ok ${ZPOOL} export ${name0}
+dname1=${disk1}
+fdname1=${fdisk1}
+guid1=`get_guid ${fdisk1}`
+disk_destroy 1
+disk_create 1 ${dname1}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices could not be used because the label is missing or"
+ echo " invalid. Sufficient replicas exist for the pool to continue"
+ echo " functioning in a degraded state."
+ echo "action: Replace the device using 'zpool replace'."
+ echo " see: http://www.sun.com/msg/ZFS-8000-4J"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${guid1} UNAVAIL 0 0 0 was ${fdname1}"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+add_msg="# TODO Sun CR 6710376, Lustre bug 16912"
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg=""
+expect_ok ${ZPOOL} replace ${name0} ${disk1} ${disk3}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2}
+expect_ok ${ZPOOL} replace ${name0} ${disk2} ${disk3}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2}
+expect_ok ${ZPOOL} export ${name0}
+dname1=${disk1}
+fdname1=${fdisk1}
+guid1=`get_guid ${fdisk1}`
+disk_destroy 1
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices could not be opened. Sufficient replicas exist for"
+ echo " the pool to continue functioning in a degraded state."
+ echo "action: Attach the missing device and online it using 'zpool online'."
+ echo " see: http://www.sun.com/msg/ZFS-8000-2Q"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${guid1} UNAVAIL 0 0 0 was ${fdname1}"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} replace ${name0} ${dname1} ${disk3}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+disk_create 1 ${dname1}
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} log mirror ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+expect_ok ${ZPOOL} replace ${name0} ${disk1} ${disk3}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: resilver completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " logs ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/replace/mirror.t b/tools/regression/zfs/zpool/replace/mirror.t
new file mode 100644
index 0000000..6e9dafe
--- /dev/null
+++ b/tools/regression/zfs/zpool/replace/mirror.t
@@ -0,0 +1,134 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..27"
+
+disks_create 3
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} export ${name0}
+dname0=${disk0}
+fdname0=${fdisk0}
+guid0=`get_guid ${fdisk0}`
+disk_destroy 0
+disk_create 0 ${dname0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices could not be used because the label is missing or"
+ echo " invalid. Sufficient replicas exist for the pool to continue"
+ echo " functioning in a degraded state."
+ echo "action: Replace the device using 'zpool replace'."
+ echo " see: http://www.sun.com/msg/ZFS-8000-4J"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${guid0} UNAVAIL 0 0 0 was ${fdname0}"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} replace ${name0} ${disk0} ${disk2}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk1} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} replace ${name0} ${disk1} ${disk2}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} export ${name0}
+dname0=${disk0}
+fdname0=${fdisk0}
+guid0=`get_guid ${fdisk0}`
+disk_destroy 0
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " mirror DEGRADED 0 0 0"
+ echo " ${guid0} REMOVED 0 0 0 was ${fdname0}"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} replace ${name0} ${dname0} ${disk2}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk1} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+disk_create 0 ${dname0}
+
+expect_ok ${ZPOOL} create ${name0} mirror ${disk0} ${disk1}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+expect_ok ${ZPOOL} replace ${name0} ${disk1} ${disk2}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " mirror ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/replace/raidz1.t b/tools/regression/zfs/zpool/replace/raidz1.t
new file mode 100644
index 0000000..79bcf8e
--- /dev/null
+++ b/tools/regression/zfs/zpool/replace/raidz1.t
@@ -0,0 +1,140 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..27"
+
+disks_create 4
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} export ${name0}
+dname0=${disk0}
+fdname0=${fdisk0}
+guid0=`get_guid ${fdisk0}`
+disk_destroy 0
+disk_create 0 ${dname0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices could not be used because the label is missing or"
+ echo " invalid. Sufficient replicas exist for the pool to continue"
+ echo " functioning in a degraded state."
+ echo "action: Replace the device using 'zpool replace'."
+ echo " see: http://www.sun.com/msg/ZFS-8000-4J"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz1 DEGRADED 0 0 0"
+ echo " ${guid0} UNAVAIL 0 0 0 was ${fdname0}"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} replace ${name0} ${disk0} ${disk3}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk1} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} replace ${name0} ${disk1} ${disk3}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk3} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} export ${name0}
+dname0=${disk0}
+fdname0=${fdisk0}
+guid0=`get_guid ${fdisk0}`
+disk_destroy 0
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz1 DEGRADED 0 0 0"
+ echo " ${guid0} REMOVED 0 0 0 was ${fdname0}"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} replace ${name0} ${dname0} ${disk3}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk1} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+disk_create 0 ${dname0}
+
+expect_ok ${ZPOOL} create ${name0} raidz1 ${disk0} ${disk1} ${disk2}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+expect_ok ${ZPOOL} replace ${name0} ${disk1} ${disk3}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz1 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk3} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/replace/raidz2.t b/tools/regression/zfs/zpool/replace/raidz2.t
new file mode 100644
index 0000000..d961e3c
--- /dev/null
+++ b/tools/regression/zfs/zpool/replace/raidz2.t
@@ -0,0 +1,607 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..115"
+
+disks_create 6
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} export ${name0}
+dname0=${disk0}
+fdname0=${fdisk0}
+guid0=`get_guid ${fdisk0}`
+disk_destroy 0
+disk_create 0 ${dname0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices could not be used because the label is missing or"
+ echo " invalid. Sufficient replicas exist for the pool to continue"
+ echo " functioning in a degraded state."
+ echo "action: Replace the device using 'zpool replace'."
+ echo " see: http://www.sun.com/msg/ZFS-8000-4J"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${guid0} UNAVAIL 0 0 0 was ${fdname0}"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} replace ${name0} ${disk0} ${disk4}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk1} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk3} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} replace ${name0} ${disk1} ${disk4}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk4} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk3} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} export ${name0}
+dname0=${disk0}
+fdname0=${fdisk0}
+guid0=`get_guid ${fdisk0}`
+disk_destroy 0
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${guid0} REMOVED 0 0 0 was ${fdname0}"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} replace ${name0} ${dname0} ${disk4}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk1} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk3} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+disk_create 0 ${dname0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+expect_ok ${ZPOOL} replace ${name0} ${disk1} ${disk4}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk4} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk3} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} export ${name0}
+dname0=${disk0}
+fdname0=${fdisk0}
+guid0=`get_guid ${fdisk0}`
+disk_destroy 0
+disk_create 0 ${dname0}
+dname1=${disk1}
+fdname1=${fdisk1}
+guid1=`get_guid ${fdisk1}`
+disk_destroy 1
+disk_create 1 ${dname1}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices could not be used because the label is missing or"
+ echo " invalid. Sufficient replicas exist for the pool to continue"
+ echo " functioning in a degraded state."
+ echo "action: Replace the device using 'zpool replace'."
+ echo " see: http://www.sun.com/msg/ZFS-8000-4J"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${guid0} UNAVAIL 0 0 0 was ${fdname0}"
+ echo " ${guid1} UNAVAIL 0 0 0 was ${fdname1}"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} replace ${name0} ${disk0} ${disk4}
+expect_ok ${ZPOOL} replace ${name0} ${disk1} ${disk5}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk5} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk3} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} replace ${name0} ${disk1} ${disk4}
+expect_ok ${ZPOOL} replace ${name0} ${disk2} ${disk5}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk4} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk5} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk3} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} export ${name0}
+dname0=${disk0}
+fdname0=${fdisk0}
+guid0=`get_guid ${fdisk0}`
+disk_destroy 0
+dname1=${disk1}
+fdname1=${fdisk1}
+guid1=`get_guid ${fdisk1}`
+disk_destroy 1
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${guid0} REMOVED 0 0 0 was ${fdname0}"
+ echo " ${guid1} REMOVED 0 0 0 was ${fdname1}"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} replace ${name0} ${dname0} ${disk4}
+expect_ok ${ZPOOL} replace ${name0} ${dname1} ${disk5}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk5} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk3} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+disk_create 0 ${dname0}
+disk_create 1 ${dname1}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+add_msg="# TODO Sun CR 6328632, Lustre bug 16878"
+expect_ok ${ZPOOL} offline ${name0} ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} OFFLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+add_msg=""
+expect_ok ${ZPOOL} replace ${name0} ${disk0} ${disk4}
+expect_ok ${ZPOOL} replace ${name0} ${disk1} ${disk5}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk5} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk3} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} export ${name0}
+dname0=${disk0}
+fdname0=${fdisk0}
+guid0=`get_guid ${fdisk0}`
+disk_destroy 0
+disk_create 0 ${dname0}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices could not be used because the label is missing or"
+ echo " invalid. Sufficient replicas exist for the pool to continue"
+ echo " functioning in a degraded state."
+ echo "action: Replace the device using 'zpool replace'."
+ echo " see: http://www.sun.com/msg/ZFS-8000-4J"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${guid0} UNAVAIL 0 0 0 was ${fdname0}"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} replace ${name0} ${disk0} ${disk4}
+expect_ok ${ZPOOL} replace ${name0} ${disk1} ${disk5}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk5} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk3} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} export ${name0}
+dname0=${disk0}
+fdname0=${fdisk0}
+guid0=`get_guid ${fdisk0}`
+disk_destroy 0
+disk_create 0 ${dname0}
+dname1=${disk1}
+fdname1=${fdisk1}
+guid1=`get_guid ${fdisk1}`
+disk_destroy 1
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices could not be used because the label is missing or"
+ echo " invalid. Sufficient replicas exist for the pool to continue"
+ echo " functioning in a degraded state."
+ echo "action: Replace the device using 'zpool replace'."
+ echo " see: http://www.sun.com/msg/ZFS-8000-4J"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${guid0} UNAVAIL 0 0 0 was ${fdname0}"
+ echo " ${guid1} REMOVED 0 0 0 was ${fdname1}"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} replace ${name0} ${disk0} ${disk4}
+expect_ok ${ZPOOL} replace ${name0} ${dname1} ${disk5}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk5} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk3} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+disk_create 1 ${dname1}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} export ${name0}
+dname0=${disk0}
+fdname0=${fdisk0}
+guid0=`get_guid ${fdisk0}`
+disk_destroy 0
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${guid0} REMOVED 0 0 0 was ${fdname0}"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} replace ${name0} ${dname0} ${disk4}
+expect_ok ${ZPOOL} replace ${name0} ${disk1} ${disk5}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk5} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk3} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+disk_create 0 ${dname0}
+
+expect_ok ${ZPOOL} create ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${disk1} ONLINE 0 0 0"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} replace ${name0} ${disk0} ${disk4}
+expect_ok ${ZPOOL} replace ${name0} ${disk1} ${disk5}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk5} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk3} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+dname1=${disk1}
+fdname1=${fdisk1}
+guid1=`get_guid ${fdisk1}`
+disk_destroy 1
+disk_create 1 ${dname1}
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices could not be used because the label is missing or"
+ echo " invalid. Sufficient replicas exist for the pool to continue"
+ echo " functioning in a degraded state."
+ echo "action: Replace the device using 'zpool replace'."
+ echo " see: http://www.sun.com/msg/ZFS-8000-4J"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${guid1} UNAVAIL 0 0 0 was ${fdname1}"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} replace ${name0} ${disk0} ${disk4}
+expect_ok ${ZPOOL} replace ${name0} ${disk1} ${disk5}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk5} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk3} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+expect_ok ${ZPOOL} create ${zpool_f_flag} ${name0} raidz2 ${disk0} ${disk1} ${disk2} ${disk3}
+expect_ok ${ZPOOL} offline ${name0} ${disk0}
+expect_ok ${ZPOOL} export ${name0}
+dname1=${disk1}
+fdname1=${fdisk1}
+guid1=`get_guid ${fdisk1}`
+disk_destroy 1
+expect_ok ${ZPOOL} import ${import_flags} ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: DEGRADED"
+ echo "status: One or more devices has been taken offline by the administrator."
+ echo " Sufficient replicas exist for the pool to continue functioning in a"
+ echo " degraded state."
+ echo "action: Online the device using 'zpool online' or replace the device with"
+ echo " 'zpool replace'."
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} DEGRADED 0 0 0"
+ echo " raidz2 DEGRADED 0 0 0"
+ echo " ${disk0} OFFLINE 0 0 0"
+ echo " ${guid1} REMOVED 0 0 0 was ${fdname1}"
+ echo " ${disk2} ONLINE 0 0 0"
+ echo " ${disk3} ONLINE 0 0 0"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} replace ${name0} ${disk0} ${disk4}
+expect_ok ${ZPOOL} replace ${name0} ${dname1} ${disk5}
+wait_for_resilver ${name0}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: (scrub|resilver) completed after [0-9]+h[0-9]+m with 0 errors on .*"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " raidz2 ONLINE 0 0 0"
+ echo " ${disk4} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk5} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk2} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo " ${disk3} ONLINE 0 0 0( [0-9.]+[A-Z] resilvered)?"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+disk_create 1 ${dname1}
+
+disks_destroy
diff --git a/tools/regression/zfs/zpool/replace/spare.t b/tools/regression/zfs/zpool/replace/spare.t
new file mode 100644
index 0000000..5899305
--- /dev/null
+++ b/tools/regression/zfs/zpool/replace/spare.t
@@ -0,0 +1,43 @@
+#!/bin/sh
+# $FreeBSD$
+
+dir=`dirname $0`
+. ${dir}/../../misc.sh
+
+echo "1..6"
+
+disks_create 4
+names_create 1
+
+expect_ok ${ZPOOL} create ${name0} ${disk0} spare ${disk1}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " spares"
+ echo " ${disk1} AVAIL"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_fl ${ZPOOL} replace ${name0} ${disk1} ${disk2}
+exp=`(
+ echo " pool: ${name0}"
+ echo " state: ONLINE"
+ echo " scrub: none requested"
+ echo "config:"
+ echo " NAME STATE READ WRITE CKSUM"
+ echo " ${name0} ONLINE 0 0 0"
+ echo " ${disk0} ONLINE 0 0 0"
+ echo " spares"
+ echo " ${disk1} AVAIL"
+ echo "errors: No known data errors"
+)`
+expect "${exp}" ${ZPOOL} status ${name0}
+expect_ok ${ZPOOL} destroy ${name0}
+expect_fl ${ZPOOL} status -x ${name0}
+
+disks_destroy
diff --git a/usr.bin/lsvfs/lsvfs.c b/usr.bin/lsvfs/lsvfs.c
index a091282..3107971 100644
--- a/usr.bin/lsvfs/lsvfs.c
+++ b/usr.bin/lsvfs/lsvfs.c
@@ -71,44 +71,29 @@ fmt_flags(int flags)
*/
static char buf[sizeof
"static, network, read-only, synthetic, loopback, unicode, jail"];
- int comma = 0;
+ size_t len;
buf[0] = '\0';
- if(flags & VFCF_STATIC) {
- if(comma++) strcat(buf, ", ");
- strcat(buf, "static");
- }
-
- if(flags & VFCF_NETWORK) {
- if(comma++) strcat(buf, ", ");
- strcat(buf, "network");
- }
-
- if(flags & VFCF_READONLY) {
- if(comma++) strcat(buf, ", ");
- strcat(buf, "read-only");
- }
-
- if(flags & VFCF_SYNTHETIC) {
- if(comma++) strcat(buf, ", ");
- strcat(buf, "synthetic");
- }
-
- if(flags & VFCF_LOOPBACK) {
- if(comma++) strcat(buf, ", ");
- strcat(buf, "loopback");
- }
-
- if(flags & VFCF_UNICODE) {
- if(comma++) strcat(buf, ", ");
- strcat(buf, "unicode");
- }
-
- if(flags & VFCF_JAIL) {
- if(comma++) strcat(buf, ", ");
- strcat(buf, "jail");
- }
+ if(flags & VFCF_STATIC)
+ strlcat(buf, "static, ", sizeof(buf));
+ if(flags & VFCF_NETWORK)
+ strlcat(buf, "network, ", sizeof(buf));
+ if(flags & VFCF_READONLY)
+ strlcat(buf, "read-only, ", sizeof(buf));
+ if(flags & VFCF_SYNTHETIC)
+ strlcat(buf, "synthetic, ", sizeof(buf));
+ if(flags & VFCF_LOOPBACK)
+ strlcat(buf, "loopback, ", sizeof(buf));
+ if(flags & VFCF_UNICODE)
+ strlcat(buf, "unicode, ", sizeof(buf));
+ if(flags & VFCF_JAIL)
+ strlcat(buf, "jail, ", sizeof(buf));
+ if(flags & VFCF_DELEGADMIN)
+ strlcat(buf, "delegated-administration, ", sizeof(buf));
+ len = strlen(buf);
+ if (len > 2 && buf[len - 2] == ',')
+ buf[len - 2] = '\0';
return buf;
}
OpenPOWER on IntegriCloud