From 34dc7c2f2553220ebc6e29ca195fb6d57155f95f Mon Sep 17 00:00:00 2001 From: Brian Behlendorf Date: Thu, 20 Nov 2008 12:01:55 -0800 Subject: [PATCH] Initial Linux ZFS GIT Repo --- AUTHORS | 4 + ChangeLog | 114 + DISCLAIMER | 22 + GIT | 162 + META | 6 + Makefile.am | 25 + README | 74 + TODO | 30 + autoconf/Makefile.am | 1 + autoconf/zfs-build.m4 | 378 + autogen.sh | 10 + build/Makefile | 1 + configs/Makefile.am | 1 + configs/kernel | 11 + configs/lustre | 11 + configs/user | 9 + configure.ac | 223 + doc/LEGAL | 113 + doc/Makefile.am | 1 + doc/analysis-spl-0.3.1/zfs-report.doc | Bin 0 -> 54784 bytes doc/analysis-spl-0.3.1/zfs-report.odt | Bin 0 -> 33209 bytes doc/analysis-spl-0.3.1/zfs-report.pdf | Bin 0 -> 332105 bytes doc/analysis-spl-0.3.1/zfs-testing.ods | Bin 0 -> 124723 bytes doc/analysis-spl-0.3.1/zfs-testing.xls | Bin 0 -> 164352 bytes lib/libsolcompat/zu.c | 4 + lib/libumem/zu.c | 4 + lib/libuutil/zu.c | 4 + lib/libzfs/zu.c | 4 + patches/README | 17 + patches/lztest-lzdb.patch | 40 + patches/no-debug-userspace.patch | 184 + patches/no-events.patch | 44 + patches/port-no-zmod.patch | 112 + patches/port-pragma-init.patch | 52 + patches/pthreads.patch | 924 + patches/zap-cursor-move-to-key.patch | 115 + scripts/Makefile.am | 8 + scripts/check.sh | 17 + scripts/create-zpool.sh | 42 + scripts/load-zfs.sh | 58 + scripts/profile-kpios-disk.sh | 128 + scripts/profile-kpios-pids.sh | 130 + scripts/profile-kpios-post.sh | 67 + scripts/profile-kpios-pre.sh | 69 + scripts/profile-kpios.sh | 222 + scripts/survey.sh | 102 + scripts/unload-zfs.sh | 55 + scripts/update-zfs.sh | 59 + scripts/zpios-jbod.sh | 110 + scripts/zpios.sh | 139 + zfs/Makefile.in | 16 + zfs/lib/Makefile.in | 12 + zfs/lib/libavl/Makefile.in | 31 + zfs/lib/libavl/avl.c | 969 + zfs/lib/libavl/include/Makefile.in | 1 + zfs/lib/libavl/include/sys/Makefile.in | 1 + zfs/lib/libavl/include/sys/avl.h | 298 + zfs/lib/libavl/include/sys/avl_impl.h | 164 + zfs/lib/libdmu-ctl/Makefile.in | 28 + zfs/lib/libdmu-ctl/dctl_client.c | 263 + zfs/lib/libdmu-ctl/dctl_common.c | 109 + zfs/lib/libdmu-ctl/dctl_server.c | 476 + zfs/lib/libdmu-ctl/dctl_thrpool.c | 253 + zfs/lib/libdmu-ctl/dmu_send.c | 1249 + zfs/lib/libdmu-ctl/include/Makefile.in | 1 + zfs/lib/libdmu-ctl/include/sys/Makefile.in | 1 + zfs/lib/libdmu-ctl/include/sys/dmu_ctl.h | 71 + zfs/lib/libdmu-ctl/include/sys/dmu_ctl_impl.h | 144 + zfs/lib/libdmu-ctl/rrwlock.c | 249 + zfs/lib/libdmu-ctl/zfs_acl.c | 2641 ++ zfs/lib/libdmu-ctl/zfs_ctldir.c | 1147 + zfs/lib/libdmu-ctl/zfs_dir.c | 968 + zfs/lib/libdmu-ctl/zfs_fuid.c | 688 + zfs/lib/libdmu-ctl/zfs_ioctl.c | 3055 ++ zfs/lib/libdmu-ctl/zfs_log.c | 693 + zfs/lib/libdmu-ctl/zfs_replay.c | 876 + zfs/lib/libdmu-ctl/zfs_rlock.c | 602 + zfs/lib/libdmu-ctl/zfs_vfsops.c | 1671 + zfs/lib/libdmu-ctl/zfs_vnops.c | 4558 +++ zfs/lib/libdmu-ctl/zvol.c | 1830 + zfs/lib/libnvpair/Makefile.in | 34 + zfs/lib/libnvpair/include/Makefile.in | 2 + zfs/lib/libnvpair/include/libnvpair.h | 46 + zfs/lib/libnvpair/include/sys/Makefile.in | 1 + zfs/lib/libnvpair/include/sys/nvpair.h | 262 + zfs/lib/libnvpair/include/sys/nvpair_impl.h | 73 + zfs/lib/libnvpair/libnvpair.c | 266 + zfs/lib/libnvpair/nvpair.c | 3005 ++ zfs/lib/libnvpair/nvpair_alloc_fixed.c | 120 + zfs/lib/libnvpair/nvpair_alloc_system.c | 59 + zfs/lib/libport/Makefile.in | 37 + zfs/lib/libport/include/Makefile.in | 4 + zfs/lib/libport/include/fake_ioctl.h | 41 + zfs/lib/libport/include/libdiskmgt.h | 278 + zfs/lib/libport/include/libshare.h | 287 + zfs/lib/libport/include/mntent.h | 35 + zfs/lib/libport/include/stdlib.h | 38 + zfs/lib/libport/include/string.h | 46 + zfs/lib/libport/include/strings.h | 38 + zfs/lib/libport/include/stropts.h | 37 + zfs/lib/libport/include/sys/Makefile.in | 3 + zfs/lib/libport/include/sys/byteorder.h | 31 + zfs/lib/libport/include/sys/debug.h | 47 + zfs/lib/libport/include/sys/efi_partition.h | 37 + zfs/lib/libport/include/sys/ioctl.h | 37 + zfs/lib/libport/include/sys/isa_defs.h | 31 + zfs/lib/libport/include/sys/policy.h | 40 + zfs/lib/libport/include/sys/socket.h | 37 + zfs/lib/libport/include/sys/swap.h | 32 + zfs/lib/libport/include/sys/systeminfo.h | 40 + zfs/lib/libport/include/sys/systm.h | 40 + zfs/lib/libport/include/sys/time.h | 31 + zfs/lib/libport/include/sys/types.h | 49 + zfs/lib/libport/include/sys/u8_textprep.h | 119 + zfs/lib/libport/include/sys/u8_textprep_data.h | 35382 +++++++++++++++++++ zfs/lib/libport/include/unistd.h | 53 + zfs/lib/libport/port.c | 63 + zfs/lib/libport/strlcat.c | 61 + zfs/lib/libport/strlcpy.c | 57 + zfs/lib/libport/strnlen.c | 50 + zfs/lib/libport/u8_textprep.c | 2137 ++ zfs/lib/libsolcompat/Makefile.in | 22 + zfs/lib/libsolcompat/amd64/Makefile.in | 1 + zfs/lib/libsolcompat/amd64/atomic.S | 617 + zfs/lib/libsolcompat/atomic_asm_weak.h | 0 zfs/lib/libsolcompat/gen_synonyms.h | 0 zfs/lib/libsolcompat/getmntany.c | 94 + zfs/lib/libsolcompat/i386/Makefile.in | 1 + zfs/lib/libsolcompat/i386/atomic.S | 752 + zfs/lib/libsolcompat/include/Makefile.in | 4 + zfs/lib/libsolcompat/include/amd64/Makefile.in | 1 + zfs/lib/libsolcompat/include/amd64/sys/Makefile.in | 2 + .../libsolcompat/include/amd64/sys/asm_linkage.h | 5 + zfs/lib/libsolcompat/include/assert.h | 42 + zfs/lib/libsolcompat/include/atomic.h | 438 + zfs/lib/libsolcompat/include/devid.h | 48 + zfs/lib/libsolcompat/include/dirent.h | 36 + zfs/lib/libsolcompat/include/i386/Makefile.in | 1 + zfs/lib/libsolcompat/include/i386/sys/Makefile.in | 2 + .../libsolcompat/include/i386/sys/asm_linkage.h | 5 + zfs/lib/libsolcompat/include/ia32/Makefile.in | 1 + zfs/lib/libsolcompat/include/ia32/sys/Makefile.in | 2 + .../libsolcompat/include/ia32/sys/asm_linkage.h | 307 + zfs/lib/libsolcompat/include/libc.h | 0 zfs/lib/libsolcompat/include/libdevinfo.h | 0 zfs/lib/libsolcompat/include/libgen.h | 35 + zfs/lib/libsolcompat/include/mtlib.h | 0 zfs/lib/libsolcompat/include/priv.h | 37 + zfs/lib/libsolcompat/include/rpc/Makefile.in | 2 + zfs/lib/libsolcompat/include/rpc/xdr.h | 118 + zfs/lib/libsolcompat/include/sparc64/Makefile.in | 1 + .../libsolcompat/include/sparc64/sys/Makefile.in | 2 + .../libsolcompat/include/sparc64/sys/asm_linkage.h | 247 + zfs/lib/libsolcompat/include/stdarg.h | 37 + zfs/lib/libsolcompat/include/stdio_ext.h | 32 + zfs/lib/libsolcompat/include/strings.h | 33 + zfs/lib/libsolcompat/include/sys/Makefile.in | 8 + zfs/lib/libsolcompat/include/sys/acl.h | 289 + zfs/lib/libsolcompat/include/sys/acl_impl.h | 61 + zfs/lib/libsolcompat/include/sys/bitmap.h | 0 zfs/lib/libsolcompat/include/sys/byteorder.h | 199 + zfs/lib/libsolcompat/include/sys/callb.h | 0 zfs/lib/libsolcompat/include/sys/cmn_err.h | 0 zfs/lib/libsolcompat/include/sys/cred.h | 32 + zfs/lib/libsolcompat/include/sys/dkio.h | 484 + zfs/lib/libsolcompat/include/sys/dklabel.h | 268 + zfs/lib/libsolcompat/include/sys/feature_tests.h | 32 + zfs/lib/libsolcompat/include/sys/file.h | 50 + zfs/lib/libsolcompat/include/sys/fm/Makefile.in | 1 + zfs/lib/libsolcompat/include/sys/fm/protocol.h | 0 zfs/lib/libsolcompat/include/sys/fm/util.h | 0 zfs/lib/libsolcompat/include/sys/idmap.h | 87 + zfs/lib/libsolcompat/include/sys/int_limits.h | 0 zfs/lib/libsolcompat/include/sys/int_types.h | 1 + zfs/lib/libsolcompat/include/sys/inttypes.h | 34 + zfs/lib/libsolcompat/include/sys/isa_defs.h | 482 + zfs/lib/libsolcompat/include/sys/kmem.h | 0 zfs/lib/libsolcompat/include/sys/kstat.h | 820 + zfs/lib/libsolcompat/include/sys/mkdev.h | 0 zfs/lib/libsolcompat/include/sys/mntent.h | 142 + zfs/lib/libsolcompat/include/sys/mntio.h | 0 zfs/lib/libsolcompat/include/sys/mnttab.h | 86 + zfs/lib/libsolcompat/include/sys/modctl.h | 2 + zfs/lib/libsolcompat/include/sys/mount.h | 52 + zfs/lib/libsolcompat/include/sys/note.h | 56 + zfs/lib/libsolcompat/include/sys/param.h | 62 + zfs/lib/libsolcompat/include/sys/priv.h | 0 zfs/lib/libsolcompat/include/sys/processor.h | 32 + zfs/lib/libsolcompat/include/sys/rctl.h | 1 + zfs/lib/libsolcompat/include/sys/sdt.h | 35 + zfs/lib/libsolcompat/include/sys/stack.h | 0 zfs/lib/libsolcompat/include/sys/stat.h | 60 + zfs/lib/libsolcompat/include/sys/sunddi.h | 50 + zfs/lib/libsolcompat/include/sys/sysevent.h | 0 .../libsolcompat/include/sys/sysevent/Makefile.in | 1 + .../libsolcompat/include/sys/sysevent/eventdefs.h | 235 + zfs/lib/libsolcompat/include/sys/sysmacros.h | 95 + zfs/lib/libsolcompat/include/sys/time.h | 53 + zfs/lib/libsolcompat/include/sys/trap.h | 0 zfs/lib/libsolcompat/include/sys/types.h | 78 + zfs/lib/libsolcompat/include/sys/types32.h | 91 + zfs/lib/libsolcompat/include/sys/uio.h | 50 + zfs/lib/libsolcompat/include/sys/utsname.h | 35 + zfs/lib/libsolcompat/include/sys/uuid.h | 100 + zfs/lib/libsolcompat/include/sys/va_list.h | 32 + zfs/lib/libsolcompat/include/sys/varargs.h | 0 zfs/lib/libsolcompat/include/sys/vmem.h | 34 + zfs/lib/libsolcompat/include/sys/vtoc.h | 217 + zfs/lib/libsolcompat/include/sys/zone.h | 0 zfs/lib/libsolcompat/include/tsol/Makefile.in | 1 + zfs/lib/libsolcompat/include/tsol/label.h | 0 zfs/lib/libsolcompat/include/ucred.h | 32 + zfs/lib/libsolcompat/include/zone.h | 86 + zfs/lib/libsolcompat/mkdirp.c | 216 + zfs/lib/libsolcompat/sparc64/Makefile.in | 1 + zfs/lib/libsolcompat/sparc64/atomic.S | 813 + zfs/lib/libsolcompat/synonyms.h | 0 zfs/lib/libsolcompat/tsd.h | 0 zfs/lib/libsolcompat/zone.c | 60 + zfs/lib/libudmu/Makefile.in | 22 + zfs/lib/libudmu/include/Makefile.in | 1 + zfs/lib/libudmu/include/udmu.h | 241 + zfs/lib/libudmu/include/udmu_util.h | 59 + zfs/lib/libudmu/udmu.c | 811 + zfs/lib/libudmu/udmu_util.c | 241 + zfs/lib/libumem/COPYING | 2 + zfs/lib/libumem/COPYRIGHT | 6 + zfs/lib/libumem/Makefile.in | 40 + zfs/lib/libumem/OPENSOLARIS.LICENSE | 385 + zfs/lib/libumem/README | 23 + zfs/lib/libumem/README-alpha | 56 + zfs/lib/libumem/TODO | 19 + zfs/lib/libumem/config.h | 13 + zfs/lib/libumem/envvar.c | 693 + zfs/lib/libumem/getpcstack.c | 211 + zfs/lib/libumem/include/Makefile.in | 1 + zfs/lib/libumem/include/umem.h | 86 + zfs/lib/libumem/init_lib.c | 149 + zfs/lib/libumem/misc.c | 298 + zfs/lib/libumem/misc.h | 150 + zfs/lib/libumem/sol_compat.h | 202 + zfs/lib/libumem/sys/Makefile.in | 1 + zfs/lib/libumem/sys/vmem.h | 142 + zfs/lib/libumem/sys/vmem_impl_user.h | 165 + zfs/lib/libumem/umem.c | 3208 ++ zfs/lib/libumem/umem_agent_support.c | 50 + zfs/lib/libumem/umem_base.h | 143 + zfs/lib/libumem/umem_fail.c | 176 + zfs/lib/libumem/umem_fork.c | 214 + zfs/lib/libumem/umem_impl.h | 424 + zfs/lib/libumem/umem_update_thread.c | 153 + zfs/lib/libumem/vmem.c | 1807 + zfs/lib/libumem/vmem_base.c | 58 + zfs/lib/libumem/vmem_base.h | 85 + zfs/lib/libumem/vmem_mmap.c | 186 + zfs/lib/libumem/vmem_sbrk.c | 326 + zfs/lib/libumem/vmem_stand.h | 49 + zfs/lib/libuutil/Makefile.in | 35 + zfs/lib/libuutil/include/Makefile.in | 1 + zfs/lib/libuutil/include/libuutil.h | 383 + zfs/lib/libuutil/include/libuutil_common.h | 44 + zfs/lib/libuutil/include/libuutil_impl.h | 181 + zfs/lib/libuutil/uu_alloc.c | 85 + zfs/lib/libuutil/uu_avl.c | 567 + zfs/lib/libuutil/uu_dprintf.c | 128 + zfs/lib/libuutil/uu_ident.c | 122 + zfs/lib/libuutil/uu_list.c | 711 + zfs/lib/libuutil/uu_misc.c | 255 + zfs/lib/libuutil/uu_open.c | 70 + zfs/lib/libuutil/uu_pname.c | 207 + zfs/lib/libuutil/uu_strtoint.c | 300 + zfs/lib/libzcommon/Makefile.in | 55 + zfs/lib/libzcommon/compress.c | 228 + zfs/lib/libzcommon/include/Makefile.in | 2 + zfs/lib/libzcommon/include/sys/Makefile.in | 14 + zfs/lib/libzcommon/include/sys/arc.h | 123 + zfs/lib/libzcommon/include/sys/bplist.h | 89 + zfs/lib/libzcommon/include/sys/compress.h | 46 + zfs/lib/libzcommon/include/sys/dbuf.h | 334 + zfs/lib/libzcommon/include/sys/dmu.h | 620 + zfs/lib/libzcommon/include/sys/dmu_impl.h | 237 + zfs/lib/libzcommon/include/sys/dmu_objset.h | 129 + zfs/lib/libzcommon/include/sys/dmu_traverse.h | 121 + zfs/lib/libzcommon/include/sys/dmu_tx.h | 137 + zfs/lib/libzcommon/include/sys/dmu_zfetch.h | 75 + zfs/lib/libzcommon/include/sys/dnode.h | 270 + zfs/lib/libzcommon/include/sys/dsl_dataset.h | 228 + zfs/lib/libzcommon/include/sys/dsl_deleg.h | 73 + zfs/lib/libzcommon/include/sys/dsl_dir.h | 146 + zfs/lib/libzcommon/include/sys/dsl_pool.h | 92 + zfs/lib/libzcommon/include/sys/dsl_prop.h | 81 + zfs/lib/libzcommon/include/sys/dsl_synctask.h | 83 + zfs/lib/libzcommon/include/sys/fm/Makefile.in | 1 + zfs/lib/libzcommon/include/sys/fm/fs/Makefile.in | 1 + zfs/lib/libzcommon/include/sys/fm/fs/zfs.h | 77 + zfs/lib/libzcommon/include/sys/fs/Makefile.in | 1 + zfs/lib/libzcommon/include/sys/fs/zfs.h | 650 + zfs/lib/libzcommon/include/sys/list.h | 63 + zfs/lib/libzcommon/include/sys/list_impl.h | 53 + zfs/lib/libzcommon/include/sys/metaslab.h | 70 + zfs/lib/libzcommon/include/sys/metaslab_impl.h | 81 + zfs/lib/libzcommon/include/sys/refcount.h | 104 + zfs/lib/libzcommon/include/sys/rprwlock.h | 61 + zfs/lib/libzcommon/include/sys/rrwlock.h | 80 + zfs/lib/libzcommon/include/sys/spa.h | 538 + zfs/lib/libzcommon/include/sys/spa_boot.h | 46 + zfs/lib/libzcommon/include/sys/spa_impl.h | 178 + zfs/lib/libzcommon/include/sys/space_map.h | 162 + zfs/lib/libzcommon/include/sys/txg.h | 127 + zfs/lib/libzcommon/include/sys/txg_impl.h | 76 + zfs/lib/libzcommon/include/sys/uberblock.h | 50 + zfs/lib/libzcommon/include/sys/uberblock_impl.h | 63 + zfs/lib/libzcommon/include/sys/unique.h | 59 + zfs/lib/libzcommon/include/sys/vdev.h | 138 + zfs/lib/libzcommon/include/sys/vdev_file.h | 46 + zfs/lib/libzcommon/include/sys/vdev_impl.h | 306 + zfs/lib/libzcommon/include/sys/zap.h | 410 + zfs/lib/libzcommon/include/sys/zap_impl.h | 218 + zfs/lib/libzcommon/include/sys/zap_leaf.h | 244 + zfs/lib/libzcommon/include/sys/zfs_acl.h | 215 + zfs/lib/libzcommon/include/sys/zfs_context.h | 73 + zfs/lib/libzcommon/include/sys/zfs_context_user.h | 538 + zfs/lib/libzcommon/include/sys/zfs_ctldir.h | 74 + zfs/lib/libzcommon/include/sys/zfs_debug.h | 75 + zfs/lib/libzcommon/include/sys/zfs_dir.h | 76 + zfs/lib/libzcommon/include/sys/zfs_fuid.h | 125 + zfs/lib/libzcommon/include/sys/zfs_i18n.h | 71 + zfs/lib/libzcommon/include/sys/zfs_ioctl.h | 196 + zfs/lib/libzcommon/include/sys/zfs_rlock.h | 89 + zfs/lib/libzcommon/include/sys/zfs_vfsops.h | 140 + zfs/lib/libzcommon/include/sys/zfs_znode.h | 353 + zfs/lib/libzcommon/include/sys/zil.h | 380 + zfs/lib/libzcommon/include/sys/zil_impl.h | 109 + zfs/lib/libzcommon/include/sys/zio.h | 388 + zfs/lib/libzcommon/include/sys/zio_checksum.h | 75 + zfs/lib/libzcommon/include/sys/zio_compress.h | 82 + zfs/lib/libzcommon/include/sys/zio_impl.h | 178 + zfs/lib/libzcommon/include/sys/zvol.h | 70 + zfs/lib/libzcommon/include/zfs_comutil.h | 44 + zfs/lib/libzcommon/include/zfs_deleg.h | 81 + zfs/lib/libzcommon/include/zfs_namecheck.h | 61 + zfs/lib/libzcommon/include/zfs_prop.h | 129 + zfs/lib/libzcommon/list.c | 194 + zfs/lib/libzcommon/zfs_comutil.c | 65 + zfs/lib/libzcommon/zfs_deleg.c | 234 + zfs/lib/libzcommon/zfs_namecheck.c | 363 + zfs/lib/libzcommon/zfs_prop.c | 471 + zfs/lib/libzcommon/zpool_prop.c | 186 + zfs/lib/libzcommon/zprop_common.c | 387 + zfs/lib/libzfs/Makefile.in | 46 + zfs/lib/libzfs/include/Makefile.in | 1 + zfs/lib/libzfs/include/libzfs.h | 556 + zfs/lib/libzfs/include/libzfs_impl.h | 186 + zfs/lib/libzfs/libzfs_changelist.c | 710 + zfs/lib/libzfs/libzfs_config.c | 360 + zfs/lib/libzfs/libzfs_dataset.c | 4150 +++ zfs/lib/libzfs/libzfs_graph.c | 662 + zfs/lib/libzfs/libzfs_import.c | 1250 + zfs/lib/libzfs/libzfs_mount.c | 1401 + zfs/lib/libzfs/libzfs_pool.c | 2770 ++ zfs/lib/libzfs/libzfs_sendrecv.c | 2007 ++ zfs/lib/libzfs/libzfs_status.c | 297 + zfs/lib/libzfs/libzfs_util.c | 1369 + zfs/lib/libzpool/Makefile.in | 102 + zfs/lib/libzpool/arc.c | 4232 +++ zfs/lib/libzpool/bplist.c | 313 + zfs/lib/libzpool/dbuf.c | 2251 ++ zfs/lib/libzpool/dmu.c | 1049 + zfs/lib/libzpool/dmu_object.c | 160 + zfs/lib/libzpool/dmu_objset.c | 1149 + zfs/lib/libzpool/dmu_traverse.c | 917 + zfs/lib/libzpool/dmu_tx.c | 1034 + zfs/lib/libzpool/dmu_zfetch.c | 651 + zfs/lib/libzpool/dnode.c | 1387 + zfs/lib/libzpool/dnode_sync.c | 616 + zfs/lib/libzpool/dsl_dataset.c | 2798 ++ zfs/lib/libzpool/dsl_deleg.c | 744 + zfs/lib/libzpool/dsl_dir.c | 1269 + zfs/lib/libzpool/dsl_pool.c | 339 + zfs/lib/libzpool/dsl_prop.c | 551 + zfs/lib/libzpool/dsl_synctask.c | 225 + zfs/lib/libzpool/fletcher.c | 145 + zfs/lib/libzpool/gzip.c | 69 + zfs/lib/libzpool/kernel.c | 894 + zfs/lib/libzpool/lzjb.c | 128 + zfs/lib/libzpool/metaslab.c | 1053 + zfs/lib/libzpool/refcount.c | 195 + zfs/lib/libzpool/sha256.c | 129 + zfs/lib/libzpool/spa.c | 4501 +++ zfs/lib/libzpool/spa_boot.c | 198 + zfs/lib/libzpool/spa_config.c | 492 + zfs/lib/libzpool/spa_errlog.c | 440 + zfs/lib/libzpool/spa_history.c | 421 + zfs/lib/libzpool/spa_misc.c | 1280 + zfs/lib/libzpool/space_map.c | 506 + zfs/lib/libzpool/taskq.c | 255 + zfs/lib/libzpool/txg.c | 661 + zfs/lib/libzpool/uberblock.c | 63 + zfs/lib/libzpool/unique.c | 116 + zfs/lib/libzpool/util.c | 151 + zfs/lib/libzpool/vdev.c | 2207 ++ zfs/lib/libzpool/vdev_cache.c | 435 + zfs/lib/libzpool/vdev_disk.c | 639 + zfs/lib/libzpool/vdev_file.c | 340 + zfs/lib/libzpool/vdev_label.c | 1045 + zfs/lib/libzpool/vdev_mirror.c | 496 + zfs/lib/libzpool/vdev_missing.c | 96 + zfs/lib/libzpool/vdev_queue.c | 320 + zfs/lib/libzpool/vdev_raidz.c | 1239 + zfs/lib/libzpool/vdev_root.c | 130 + zfs/lib/libzpool/zap.c | 1085 + zfs/lib/libzpool/zap_leaf.c | 853 + zfs/lib/libzpool/zap_micro.c | 1069 + zfs/lib/libzpool/zfs_byteswap.c | 175 + zfs/lib/libzpool/zfs_fm.c | 355 + zfs/lib/libzpool/zfs_znode.c | 1390 + zfs/lib/libzpool/zil.c | 1618 + zfs/lib/libzpool/zio.c | 2082 ++ zfs/lib/libzpool/zio_checksum.c | 172 + zfs/lib/libzpool/zio_compress.c | 148 + zfs/lib/libzpool/zio_inject.c | 315 + zfs/zcmd/Makefile.in | 6 + zfs/zcmd/zdb/Makefile.in | 32 + zfs/zcmd/zdb/zdb.c | 2545 ++ zfs/zcmd/zdb/zdb_il.c | 363 + zfs/zcmd/zdump/Makefile.in | 31 + zfs/zcmd/zdump/zdump.c | 664 + zfs/zcmd/zfs/Makefile.in | 32 + zfs/zcmd/zfs/zfs_iter.c | 408 + zfs/zcmd/zfs/zfs_iter.h | 52 + zfs/zcmd/zfs/zfs_main.c | 4146 +++ zfs/zcmd/zfs/zfs_util.h | 44 + zfs/zcmd/zinject/Makefile.in | 32 + zfs/zcmd/zinject/translate.c | 464 + zfs/zcmd/zinject/zinject.c | 751 + zfs/zcmd/zinject/zinject.h | 66 + zfs/zcmd/zpool/Makefile.in | 34 + zfs/zcmd/zpool/zpool_iter.c | 252 + zfs/zcmd/zpool/zpool_main.c | 3850 ++ zfs/zcmd/zpool/zpool_util.c | 104 + zfs/zcmd/zpool/zpool_util.h | 74 + zfs/zcmd/zpool/zpool_vdev.c | 1403 + zfs/zcmd/ztest/Makefile.in | 32 + zfs/zcmd/ztest/ztest.c | 3562 ++ 444 files changed, 187636 insertions(+) create mode 100644 AUTHORS create mode 100644 ChangeLog create mode 100644 DISCLAIMER create mode 100644 GIT create mode 100644 META create mode 100644 Makefile.am create mode 100644 README create mode 100644 TODO create mode 100644 autoconf/Makefile.am create mode 100644 autoconf/zfs-build.m4 create mode 100755 autogen.sh create mode 100644 build/Makefile create mode 100644 configs/Makefile.am create mode 100644 configs/kernel create mode 100644 configs/lustre create mode 100644 configs/user create mode 100644 configure.ac create mode 100644 doc/LEGAL create mode 100644 doc/Makefile.am create mode 100644 doc/analysis-spl-0.3.1/zfs-report.doc create mode 100644 doc/analysis-spl-0.3.1/zfs-report.odt create mode 100644 doc/analysis-spl-0.3.1/zfs-report.pdf create mode 100644 doc/analysis-spl-0.3.1/zfs-testing.ods create mode 100644 doc/analysis-spl-0.3.1/zfs-testing.xls create mode 100644 lib/libsolcompat/zu.c create mode 100644 lib/libumem/zu.c create mode 100644 lib/libuutil/zu.c create mode 100644 lib/libzfs/zu.c create mode 100644 patches/README create mode 100644 patches/lztest-lzdb.patch create mode 100644 patches/no-debug-userspace.patch create mode 100644 patches/no-events.patch create mode 100644 patches/port-no-zmod.patch create mode 100644 patches/port-pragma-init.patch create mode 100644 patches/pthreads.patch create mode 100644 patches/zap-cursor-move-to-key.patch create mode 100644 scripts/Makefile.am create mode 100755 scripts/check.sh create mode 100755 scripts/create-zpool.sh create mode 100755 scripts/load-zfs.sh create mode 100755 scripts/profile-kpios-disk.sh create mode 100755 scripts/profile-kpios-pids.sh create mode 100755 scripts/profile-kpios-post.sh create mode 100755 scripts/profile-kpios-pre.sh create mode 100755 scripts/profile-kpios.sh create mode 100755 scripts/survey.sh create mode 100755 scripts/unload-zfs.sh create mode 100755 scripts/update-zfs.sh create mode 100755 scripts/zpios-jbod.sh create mode 100755 scripts/zpios.sh create mode 100644 zfs/Makefile.in create mode 100644 zfs/lib/Makefile.in create mode 100644 zfs/lib/libavl/Makefile.in create mode 100644 zfs/lib/libavl/avl.c create mode 100644 zfs/lib/libavl/include/Makefile.in create mode 100644 zfs/lib/libavl/include/sys/Makefile.in create mode 100644 zfs/lib/libavl/include/sys/avl.h create mode 100644 zfs/lib/libavl/include/sys/avl_impl.h create mode 100644 zfs/lib/libdmu-ctl/Makefile.in create mode 100644 zfs/lib/libdmu-ctl/dctl_client.c create mode 100644 zfs/lib/libdmu-ctl/dctl_common.c create mode 100644 zfs/lib/libdmu-ctl/dctl_server.c create mode 100644 zfs/lib/libdmu-ctl/dctl_thrpool.c create mode 100644 zfs/lib/libdmu-ctl/dmu_send.c create mode 100644 zfs/lib/libdmu-ctl/include/Makefile.in create mode 100644 zfs/lib/libdmu-ctl/include/sys/Makefile.in create mode 100644 zfs/lib/libdmu-ctl/include/sys/dmu_ctl.h create mode 100644 zfs/lib/libdmu-ctl/include/sys/dmu_ctl_impl.h create mode 100644 zfs/lib/libdmu-ctl/rrwlock.c create mode 100644 zfs/lib/libdmu-ctl/zfs_acl.c create mode 100644 zfs/lib/libdmu-ctl/zfs_ctldir.c create mode 100644 zfs/lib/libdmu-ctl/zfs_dir.c create mode 100644 zfs/lib/libdmu-ctl/zfs_fuid.c create mode 100644 zfs/lib/libdmu-ctl/zfs_ioctl.c create mode 100644 zfs/lib/libdmu-ctl/zfs_log.c create mode 100644 zfs/lib/libdmu-ctl/zfs_replay.c create mode 100644 zfs/lib/libdmu-ctl/zfs_rlock.c create mode 100644 zfs/lib/libdmu-ctl/zfs_vfsops.c create mode 100644 zfs/lib/libdmu-ctl/zfs_vnops.c create mode 100644 zfs/lib/libdmu-ctl/zvol.c create mode 100644 zfs/lib/libnvpair/Makefile.in create mode 100644 zfs/lib/libnvpair/include/Makefile.in create mode 100644 zfs/lib/libnvpair/include/libnvpair.h create mode 100644 zfs/lib/libnvpair/include/sys/Makefile.in create mode 100644 zfs/lib/libnvpair/include/sys/nvpair.h create mode 100644 zfs/lib/libnvpair/include/sys/nvpair_impl.h create mode 100644 zfs/lib/libnvpair/libnvpair.c create mode 100644 zfs/lib/libnvpair/nvpair.c create mode 100644 zfs/lib/libnvpair/nvpair_alloc_fixed.c create mode 100644 zfs/lib/libnvpair/nvpair_alloc_system.c create mode 100644 zfs/lib/libport/Makefile.in create mode 100644 zfs/lib/libport/include/Makefile.in create mode 100644 zfs/lib/libport/include/fake_ioctl.h create mode 100644 zfs/lib/libport/include/libdiskmgt.h create mode 100644 zfs/lib/libport/include/libshare.h create mode 100644 zfs/lib/libport/include/mntent.h create mode 100644 zfs/lib/libport/include/stdlib.h create mode 100644 zfs/lib/libport/include/string.h create mode 100644 zfs/lib/libport/include/strings.h create mode 100644 zfs/lib/libport/include/stropts.h create mode 100644 zfs/lib/libport/include/sys/Makefile.in create mode 100644 zfs/lib/libport/include/sys/byteorder.h create mode 100644 zfs/lib/libport/include/sys/debug.h create mode 100644 zfs/lib/libport/include/sys/efi_partition.h create mode 100644 zfs/lib/libport/include/sys/ioctl.h create mode 100644 zfs/lib/libport/include/sys/isa_defs.h create mode 100644 zfs/lib/libport/include/sys/policy.h create mode 100644 zfs/lib/libport/include/sys/socket.h create mode 100644 zfs/lib/libport/include/sys/swap.h create mode 100644 zfs/lib/libport/include/sys/systeminfo.h create mode 100644 zfs/lib/libport/include/sys/systm.h create mode 100644 zfs/lib/libport/include/sys/time.h create mode 100644 zfs/lib/libport/include/sys/types.h create mode 100644 zfs/lib/libport/include/sys/u8_textprep.h create mode 100644 zfs/lib/libport/include/sys/u8_textprep_data.h create mode 100644 zfs/lib/libport/include/unistd.h create mode 100644 zfs/lib/libport/port.c create mode 100644 zfs/lib/libport/strlcat.c create mode 100644 zfs/lib/libport/strlcpy.c create mode 100644 zfs/lib/libport/strnlen.c create mode 100644 zfs/lib/libport/u8_textprep.c create mode 100644 zfs/lib/libsolcompat/Makefile.in create mode 100644 zfs/lib/libsolcompat/amd64/Makefile.in create mode 100644 zfs/lib/libsolcompat/amd64/atomic.S create mode 100644 zfs/lib/libsolcompat/atomic_asm_weak.h create mode 100644 zfs/lib/libsolcompat/gen_synonyms.h create mode 100644 zfs/lib/libsolcompat/getmntany.c create mode 100644 zfs/lib/libsolcompat/i386/Makefile.in create mode 100644 zfs/lib/libsolcompat/i386/atomic.S create mode 100644 zfs/lib/libsolcompat/include/Makefile.in create mode 100644 zfs/lib/libsolcompat/include/amd64/Makefile.in create mode 100644 zfs/lib/libsolcompat/include/amd64/sys/Makefile.in create mode 100644 zfs/lib/libsolcompat/include/amd64/sys/asm_linkage.h create mode 100644 zfs/lib/libsolcompat/include/assert.h create mode 100644 zfs/lib/libsolcompat/include/atomic.h create mode 100644 zfs/lib/libsolcompat/include/devid.h create mode 100644 zfs/lib/libsolcompat/include/dirent.h create mode 100644 zfs/lib/libsolcompat/include/i386/Makefile.in create mode 100644 zfs/lib/libsolcompat/include/i386/sys/Makefile.in create mode 100644 zfs/lib/libsolcompat/include/i386/sys/asm_linkage.h create mode 100644 zfs/lib/libsolcompat/include/ia32/Makefile.in create mode 100644 zfs/lib/libsolcompat/include/ia32/sys/Makefile.in create mode 100644 zfs/lib/libsolcompat/include/ia32/sys/asm_linkage.h create mode 100644 zfs/lib/libsolcompat/include/libc.h create mode 100644 zfs/lib/libsolcompat/include/libdevinfo.h create mode 100644 zfs/lib/libsolcompat/include/libgen.h create mode 100644 zfs/lib/libsolcompat/include/mtlib.h create mode 100644 zfs/lib/libsolcompat/include/priv.h create mode 100644 zfs/lib/libsolcompat/include/rpc/Makefile.in create mode 100644 zfs/lib/libsolcompat/include/rpc/xdr.h create mode 100644 zfs/lib/libsolcompat/include/sparc64/Makefile.in create mode 100644 zfs/lib/libsolcompat/include/sparc64/sys/Makefile.in create mode 100644 zfs/lib/libsolcompat/include/sparc64/sys/asm_linkage.h create mode 100644 zfs/lib/libsolcompat/include/stdarg.h create mode 100644 zfs/lib/libsolcompat/include/stdio_ext.h create mode 100644 zfs/lib/libsolcompat/include/strings.h create mode 100644 zfs/lib/libsolcompat/include/sys/Makefile.in create mode 100644 zfs/lib/libsolcompat/include/sys/acl.h create mode 100644 zfs/lib/libsolcompat/include/sys/acl_impl.h create mode 100644 zfs/lib/libsolcompat/include/sys/bitmap.h create mode 100644 zfs/lib/libsolcompat/include/sys/byteorder.h create mode 100644 zfs/lib/libsolcompat/include/sys/callb.h create mode 100644 zfs/lib/libsolcompat/include/sys/cmn_err.h create mode 100644 zfs/lib/libsolcompat/include/sys/cred.h create mode 100644 zfs/lib/libsolcompat/include/sys/dkio.h create mode 100644 zfs/lib/libsolcompat/include/sys/dklabel.h create mode 100644 zfs/lib/libsolcompat/include/sys/feature_tests.h create mode 100644 zfs/lib/libsolcompat/include/sys/file.h create mode 100644 zfs/lib/libsolcompat/include/sys/fm/Makefile.in create mode 100644 zfs/lib/libsolcompat/include/sys/fm/protocol.h create mode 100644 zfs/lib/libsolcompat/include/sys/fm/util.h create mode 100644 zfs/lib/libsolcompat/include/sys/idmap.h create mode 100644 zfs/lib/libsolcompat/include/sys/int_limits.h create mode 100644 zfs/lib/libsolcompat/include/sys/int_types.h create mode 100644 zfs/lib/libsolcompat/include/sys/inttypes.h create mode 100644 zfs/lib/libsolcompat/include/sys/isa_defs.h create mode 100644 zfs/lib/libsolcompat/include/sys/kmem.h create mode 100644 zfs/lib/libsolcompat/include/sys/kstat.h create mode 100644 zfs/lib/libsolcompat/include/sys/mkdev.h create mode 100644 zfs/lib/libsolcompat/include/sys/mntent.h create mode 100644 zfs/lib/libsolcompat/include/sys/mntio.h create mode 100644 zfs/lib/libsolcompat/include/sys/mnttab.h create mode 100644 zfs/lib/libsolcompat/include/sys/modctl.h create mode 100644 zfs/lib/libsolcompat/include/sys/mount.h create mode 100644 zfs/lib/libsolcompat/include/sys/note.h create mode 100644 zfs/lib/libsolcompat/include/sys/param.h create mode 100644 zfs/lib/libsolcompat/include/sys/priv.h create mode 100644 zfs/lib/libsolcompat/include/sys/processor.h create mode 100644 zfs/lib/libsolcompat/include/sys/rctl.h create mode 100644 zfs/lib/libsolcompat/include/sys/sdt.h create mode 100644 zfs/lib/libsolcompat/include/sys/stack.h create mode 100644 zfs/lib/libsolcompat/include/sys/stat.h create mode 100644 zfs/lib/libsolcompat/include/sys/sunddi.h create mode 100644 zfs/lib/libsolcompat/include/sys/sysevent.h create mode 100644 zfs/lib/libsolcompat/include/sys/sysevent/Makefile.in create mode 100644 zfs/lib/libsolcompat/include/sys/sysevent/eventdefs.h create mode 100644 zfs/lib/libsolcompat/include/sys/sysmacros.h create mode 100644 zfs/lib/libsolcompat/include/sys/time.h create mode 100644 zfs/lib/libsolcompat/include/sys/trap.h create mode 100644 zfs/lib/libsolcompat/include/sys/types.h create mode 100644 zfs/lib/libsolcompat/include/sys/types32.h create mode 100644 zfs/lib/libsolcompat/include/sys/uio.h create mode 100644 zfs/lib/libsolcompat/include/sys/utsname.h create mode 100644 zfs/lib/libsolcompat/include/sys/uuid.h create mode 100644 zfs/lib/libsolcompat/include/sys/va_list.h create mode 100644 zfs/lib/libsolcompat/include/sys/varargs.h create mode 100644 zfs/lib/libsolcompat/include/sys/vmem.h create mode 100644 zfs/lib/libsolcompat/include/sys/vtoc.h create mode 100644 zfs/lib/libsolcompat/include/sys/zone.h create mode 100644 zfs/lib/libsolcompat/include/tsol/Makefile.in create mode 100644 zfs/lib/libsolcompat/include/tsol/label.h create mode 100644 zfs/lib/libsolcompat/include/ucred.h create mode 100644 zfs/lib/libsolcompat/include/zone.h create mode 100644 zfs/lib/libsolcompat/mkdirp.c create mode 100644 zfs/lib/libsolcompat/sparc64/Makefile.in create mode 100644 zfs/lib/libsolcompat/sparc64/atomic.S create mode 100644 zfs/lib/libsolcompat/synonyms.h create mode 100644 zfs/lib/libsolcompat/tsd.h create mode 100644 zfs/lib/libsolcompat/zone.c create mode 100644 zfs/lib/libudmu/Makefile.in create mode 100644 zfs/lib/libudmu/include/Makefile.in create mode 100644 zfs/lib/libudmu/include/udmu.h create mode 100644 zfs/lib/libudmu/include/udmu_util.h create mode 100644 zfs/lib/libudmu/udmu.c create mode 100644 zfs/lib/libudmu/udmu_util.c create mode 100644 zfs/lib/libumem/COPYING create mode 100644 zfs/lib/libumem/COPYRIGHT create mode 100644 zfs/lib/libumem/Makefile.in create mode 100644 zfs/lib/libumem/OPENSOLARIS.LICENSE create mode 100644 zfs/lib/libumem/README create mode 100644 zfs/lib/libumem/README-alpha create mode 100644 zfs/lib/libumem/TODO create mode 100644 zfs/lib/libumem/config.h create mode 100644 zfs/lib/libumem/envvar.c create mode 100644 zfs/lib/libumem/getpcstack.c create mode 100644 zfs/lib/libumem/include/Makefile.in create mode 100644 zfs/lib/libumem/include/umem.h create mode 100644 zfs/lib/libumem/init_lib.c create mode 100644 zfs/lib/libumem/misc.c create mode 100644 zfs/lib/libumem/misc.h create mode 100644 zfs/lib/libumem/sol_compat.h create mode 100644 zfs/lib/libumem/sys/Makefile.in create mode 100644 zfs/lib/libumem/sys/vmem.h create mode 100644 zfs/lib/libumem/sys/vmem_impl_user.h create mode 100644 zfs/lib/libumem/umem.c create mode 100644 zfs/lib/libumem/umem_agent_support.c create mode 100644 zfs/lib/libumem/umem_base.h create mode 100644 zfs/lib/libumem/umem_fail.c create mode 100644 zfs/lib/libumem/umem_fork.c create mode 100644 zfs/lib/libumem/umem_impl.h create mode 100644 zfs/lib/libumem/umem_update_thread.c create mode 100644 zfs/lib/libumem/vmem.c create mode 100644 zfs/lib/libumem/vmem_base.c create mode 100644 zfs/lib/libumem/vmem_base.h create mode 100644 zfs/lib/libumem/vmem_mmap.c create mode 100644 zfs/lib/libumem/vmem_sbrk.c create mode 100644 zfs/lib/libumem/vmem_stand.h create mode 100644 zfs/lib/libuutil/Makefile.in create mode 100644 zfs/lib/libuutil/include/Makefile.in create mode 100644 zfs/lib/libuutil/include/libuutil.h create mode 100644 zfs/lib/libuutil/include/libuutil_common.h create mode 100644 zfs/lib/libuutil/include/libuutil_impl.h create mode 100644 zfs/lib/libuutil/uu_alloc.c create mode 100644 zfs/lib/libuutil/uu_avl.c create mode 100644 zfs/lib/libuutil/uu_dprintf.c create mode 100644 zfs/lib/libuutil/uu_ident.c create mode 100644 zfs/lib/libuutil/uu_list.c create mode 100644 zfs/lib/libuutil/uu_misc.c create mode 100644 zfs/lib/libuutil/uu_open.c create mode 100644 zfs/lib/libuutil/uu_pname.c create mode 100644 zfs/lib/libuutil/uu_strtoint.c create mode 100644 zfs/lib/libzcommon/Makefile.in create mode 100644 zfs/lib/libzcommon/compress.c create mode 100644 zfs/lib/libzcommon/include/Makefile.in create mode 100644 zfs/lib/libzcommon/include/sys/Makefile.in create mode 100644 zfs/lib/libzcommon/include/sys/arc.h create mode 100644 zfs/lib/libzcommon/include/sys/bplist.h create mode 100644 zfs/lib/libzcommon/include/sys/compress.h create mode 100644 zfs/lib/libzcommon/include/sys/dbuf.h create mode 100644 zfs/lib/libzcommon/include/sys/dmu.h create mode 100644 zfs/lib/libzcommon/include/sys/dmu_impl.h create mode 100644 zfs/lib/libzcommon/include/sys/dmu_objset.h create mode 100644 zfs/lib/libzcommon/include/sys/dmu_traverse.h create mode 100644 zfs/lib/libzcommon/include/sys/dmu_tx.h create mode 100644 zfs/lib/libzcommon/include/sys/dmu_zfetch.h create mode 100644 zfs/lib/libzcommon/include/sys/dnode.h create mode 100644 zfs/lib/libzcommon/include/sys/dsl_dataset.h create mode 100644 zfs/lib/libzcommon/include/sys/dsl_deleg.h create mode 100644 zfs/lib/libzcommon/include/sys/dsl_dir.h create mode 100644 zfs/lib/libzcommon/include/sys/dsl_pool.h create mode 100644 zfs/lib/libzcommon/include/sys/dsl_prop.h create mode 100644 zfs/lib/libzcommon/include/sys/dsl_synctask.h create mode 100644 zfs/lib/libzcommon/include/sys/fm/Makefile.in create mode 100644 zfs/lib/libzcommon/include/sys/fm/fs/Makefile.in create mode 100644 zfs/lib/libzcommon/include/sys/fm/fs/zfs.h create mode 100644 zfs/lib/libzcommon/include/sys/fs/Makefile.in create mode 100644 zfs/lib/libzcommon/include/sys/fs/zfs.h create mode 100644 zfs/lib/libzcommon/include/sys/list.h create mode 100644 zfs/lib/libzcommon/include/sys/list_impl.h create mode 100644 zfs/lib/libzcommon/include/sys/metaslab.h create mode 100644 zfs/lib/libzcommon/include/sys/metaslab_impl.h create mode 100644 zfs/lib/libzcommon/include/sys/refcount.h create mode 100644 zfs/lib/libzcommon/include/sys/rprwlock.h create mode 100644 zfs/lib/libzcommon/include/sys/rrwlock.h create mode 100644 zfs/lib/libzcommon/include/sys/spa.h create mode 100644 zfs/lib/libzcommon/include/sys/spa_boot.h create mode 100644 zfs/lib/libzcommon/include/sys/spa_impl.h create mode 100644 zfs/lib/libzcommon/include/sys/space_map.h create mode 100644 zfs/lib/libzcommon/include/sys/txg.h create mode 100644 zfs/lib/libzcommon/include/sys/txg_impl.h create mode 100644 zfs/lib/libzcommon/include/sys/uberblock.h create mode 100644 zfs/lib/libzcommon/include/sys/uberblock_impl.h create mode 100644 zfs/lib/libzcommon/include/sys/unique.h create mode 100644 zfs/lib/libzcommon/include/sys/vdev.h create mode 100644 zfs/lib/libzcommon/include/sys/vdev_file.h create mode 100644 zfs/lib/libzcommon/include/sys/vdev_impl.h create mode 100644 zfs/lib/libzcommon/include/sys/zap.h create mode 100644 zfs/lib/libzcommon/include/sys/zap_impl.h create mode 100644 zfs/lib/libzcommon/include/sys/zap_leaf.h create mode 100644 zfs/lib/libzcommon/include/sys/zfs_acl.h create mode 100644 zfs/lib/libzcommon/include/sys/zfs_context.h create mode 100644 zfs/lib/libzcommon/include/sys/zfs_context_user.h create mode 100644 zfs/lib/libzcommon/include/sys/zfs_ctldir.h create mode 100644 zfs/lib/libzcommon/include/sys/zfs_debug.h create mode 100644 zfs/lib/libzcommon/include/sys/zfs_dir.h create mode 100644 zfs/lib/libzcommon/include/sys/zfs_fuid.h create mode 100644 zfs/lib/libzcommon/include/sys/zfs_i18n.h create mode 100644 zfs/lib/libzcommon/include/sys/zfs_ioctl.h create mode 100644 zfs/lib/libzcommon/include/sys/zfs_rlock.h create mode 100644 zfs/lib/libzcommon/include/sys/zfs_vfsops.h create mode 100644 zfs/lib/libzcommon/include/sys/zfs_znode.h create mode 100644 zfs/lib/libzcommon/include/sys/zil.h create mode 100644 zfs/lib/libzcommon/include/sys/zil_impl.h create mode 100644 zfs/lib/libzcommon/include/sys/zio.h create mode 100644 zfs/lib/libzcommon/include/sys/zio_checksum.h create mode 100644 zfs/lib/libzcommon/include/sys/zio_compress.h create mode 100644 zfs/lib/libzcommon/include/sys/zio_impl.h create mode 100644 zfs/lib/libzcommon/include/sys/zvol.h create mode 100644 zfs/lib/libzcommon/include/zfs_comutil.h create mode 100644 zfs/lib/libzcommon/include/zfs_deleg.h create mode 100644 zfs/lib/libzcommon/include/zfs_namecheck.h create mode 100644 zfs/lib/libzcommon/include/zfs_prop.h create mode 100644 zfs/lib/libzcommon/list.c create mode 100644 zfs/lib/libzcommon/zfs_comutil.c create mode 100644 zfs/lib/libzcommon/zfs_deleg.c create mode 100644 zfs/lib/libzcommon/zfs_namecheck.c create mode 100644 zfs/lib/libzcommon/zfs_prop.c create mode 100644 zfs/lib/libzcommon/zpool_prop.c create mode 100644 zfs/lib/libzcommon/zprop_common.c create mode 100644 zfs/lib/libzfs/Makefile.in create mode 100644 zfs/lib/libzfs/include/Makefile.in create mode 100644 zfs/lib/libzfs/include/libzfs.h create mode 100644 zfs/lib/libzfs/include/libzfs_impl.h create mode 100644 zfs/lib/libzfs/libzfs_changelist.c create mode 100644 zfs/lib/libzfs/libzfs_config.c create mode 100644 zfs/lib/libzfs/libzfs_dataset.c create mode 100644 zfs/lib/libzfs/libzfs_graph.c create mode 100644 zfs/lib/libzfs/libzfs_import.c create mode 100644 zfs/lib/libzfs/libzfs_mount.c create mode 100644 zfs/lib/libzfs/libzfs_pool.c create mode 100644 zfs/lib/libzfs/libzfs_sendrecv.c create mode 100644 zfs/lib/libzfs/libzfs_status.c create mode 100644 zfs/lib/libzfs/libzfs_util.c create mode 100644 zfs/lib/libzpool/Makefile.in create mode 100644 zfs/lib/libzpool/arc.c create mode 100644 zfs/lib/libzpool/bplist.c create mode 100644 zfs/lib/libzpool/dbuf.c create mode 100644 zfs/lib/libzpool/dmu.c create mode 100644 zfs/lib/libzpool/dmu_object.c create mode 100644 zfs/lib/libzpool/dmu_objset.c create mode 100644 zfs/lib/libzpool/dmu_traverse.c create mode 100644 zfs/lib/libzpool/dmu_tx.c create mode 100644 zfs/lib/libzpool/dmu_zfetch.c create mode 100644 zfs/lib/libzpool/dnode.c create mode 100644 zfs/lib/libzpool/dnode_sync.c create mode 100644 zfs/lib/libzpool/dsl_dataset.c create mode 100644 zfs/lib/libzpool/dsl_deleg.c create mode 100644 zfs/lib/libzpool/dsl_dir.c create mode 100644 zfs/lib/libzpool/dsl_pool.c create mode 100644 zfs/lib/libzpool/dsl_prop.c create mode 100644 zfs/lib/libzpool/dsl_synctask.c create mode 100644 zfs/lib/libzpool/fletcher.c create mode 100644 zfs/lib/libzpool/gzip.c create mode 100644 zfs/lib/libzpool/kernel.c create mode 100644 zfs/lib/libzpool/lzjb.c create mode 100644 zfs/lib/libzpool/metaslab.c create mode 100644 zfs/lib/libzpool/refcount.c create mode 100644 zfs/lib/libzpool/sha256.c create mode 100644 zfs/lib/libzpool/spa.c create mode 100644 zfs/lib/libzpool/spa_boot.c create mode 100644 zfs/lib/libzpool/spa_config.c create mode 100644 zfs/lib/libzpool/spa_errlog.c create mode 100644 zfs/lib/libzpool/spa_history.c create mode 100644 zfs/lib/libzpool/spa_misc.c create mode 100644 zfs/lib/libzpool/space_map.c create mode 100644 zfs/lib/libzpool/taskq.c create mode 100644 zfs/lib/libzpool/txg.c create mode 100644 zfs/lib/libzpool/uberblock.c create mode 100644 zfs/lib/libzpool/unique.c create mode 100644 zfs/lib/libzpool/util.c create mode 100644 zfs/lib/libzpool/vdev.c create mode 100644 zfs/lib/libzpool/vdev_cache.c create mode 100644 zfs/lib/libzpool/vdev_disk.c create mode 100644 zfs/lib/libzpool/vdev_file.c create mode 100644 zfs/lib/libzpool/vdev_label.c create mode 100644 zfs/lib/libzpool/vdev_mirror.c create mode 100644 zfs/lib/libzpool/vdev_missing.c create mode 100644 zfs/lib/libzpool/vdev_queue.c create mode 100644 zfs/lib/libzpool/vdev_raidz.c create mode 100644 zfs/lib/libzpool/vdev_root.c create mode 100644 zfs/lib/libzpool/zap.c create mode 100644 zfs/lib/libzpool/zap_leaf.c create mode 100644 zfs/lib/libzpool/zap_micro.c create mode 100644 zfs/lib/libzpool/zfs_byteswap.c create mode 100644 zfs/lib/libzpool/zfs_fm.c create mode 100644 zfs/lib/libzpool/zfs_znode.c create mode 100644 zfs/lib/libzpool/zil.c create mode 100644 zfs/lib/libzpool/zio.c create mode 100644 zfs/lib/libzpool/zio_checksum.c create mode 100644 zfs/lib/libzpool/zio_compress.c create mode 100644 zfs/lib/libzpool/zio_inject.c create mode 100644 zfs/zcmd/Makefile.in create mode 100644 zfs/zcmd/zdb/Makefile.in create mode 100644 zfs/zcmd/zdb/zdb.c create mode 100644 zfs/zcmd/zdb/zdb_il.c create mode 100644 zfs/zcmd/zdump/Makefile.in create mode 100644 zfs/zcmd/zdump/zdump.c create mode 100644 zfs/zcmd/zfs/Makefile.in create mode 100644 zfs/zcmd/zfs/zfs_iter.c create mode 100644 zfs/zcmd/zfs/zfs_iter.h create mode 100644 zfs/zcmd/zfs/zfs_main.c create mode 100644 zfs/zcmd/zfs/zfs_util.h create mode 100644 zfs/zcmd/zinject/Makefile.in create mode 100644 zfs/zcmd/zinject/translate.c create mode 100644 zfs/zcmd/zinject/zinject.c create mode 100644 zfs/zcmd/zinject/zinject.h create mode 100644 zfs/zcmd/zpool/Makefile.in create mode 100644 zfs/zcmd/zpool/zpool_iter.c create mode 100644 zfs/zcmd/zpool/zpool_main.c create mode 100644 zfs/zcmd/zpool/zpool_util.c create mode 100644 zfs/zcmd/zpool/zpool_util.h create mode 100644 zfs/zcmd/zpool/zpool_vdev.c create mode 100644 zfs/zcmd/ztest/Makefile.in create mode 100644 zfs/zcmd/ztest/ztest.c diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..04ebc20 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,4 @@ +Brian Behlendorf , +Herb Wartens , +Jim Garlick , +Ricardo M. Correia diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..8a74328 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,114 @@ +2008-11-19 Brian Behlendorf + + * : Tag zfs-0.4.0 + + * : ZFS project migrated from Subversion which leveraged a + quilt based patch stack to Git and a TopGit managed patch + stack. The new method treats all patches as Git branches + which can be more easily shared for distributed development. + Consult the top level GIT file for detailed information on + how to properly develop for this package using Git+TopGit. + +2008-11-12 Brian Behlendorf + + * : Tag zfs-0.3.4 + + * zfs-07-create-dev-zfs.patch: + Ricardo M. Correia + - Make libzfs create /dev/zfs if it doesn't exist. + + * zfs-05-check-zvol-size.patch: + Ricardo M. Correia + - Properly check zvol size under Linux. + + * zfs-04-no-openat-fdopendir.patch: + Ricardo M. Correia + - Do not use openat() and fdopendir() since they are not available + on older systems. + + * zfs-03-fix-bio-sync.patch: + Ricardo M. Correia + - Fix memory corruption in RHEL4 due to synchronous IO becoming + asynchronous. + +2008-11-06 Brian Behlendorf + + * zfs-02-zpios-fix-stuck-thread-memleak.patch: + Ricardo M. Correia + - Fix stuck threads and memory leaks when errors occur while writing. + + * zfs-01-zpios-arg-corruption.patch: + Ricardo M. Correia + - Fix zpios cmd line argument corruption problem. + + * zfs-00-minor-fixes.patch: + Ricardo M. Correia + - Minor build system improvements + - Minor script improvements + - Create a full copy and not a link tree with quilt + - KPIOS_MAJOR changed from 231 to 232 + - BIO_RW_BARRIER flag removed from IO request + +2008-06-30 Brian Behlendorf + + * : Tag zfs-0.3.3 + + * : Minor script updates and tweaks to be compatible with + the latest version of the SPL. + +2008-06-13 Brian Behlendorf + + * vdev_disk.diff: Replace vdev_disk implementation which was + based on the kmalloc'ed logical address space with a version + which works with vmalloc'ed memory in the virtual address space. + This was done to support the new SPL slab implementation which + is based on virtual addresses to avoid the need for contigeously + allocated memory. + +2008-06-05 Brian Behlendorf + + * arc-vm-integration.diff: Reduce maximum default arc memory + usage to 1/4 of total system memory. Because all the bulk data + is still allocated on the slab memory fragmentation is a serious + concern. To address this in the short term we simply need to + leave lots of free memory. + + * fix-stack.diff: First step towards reducing stack usage so + we can run the full ZFS stack using a stock kernel. + +2008-06-04 Brian Behlendorf + + * : Tag zfs-0.3.2 + + * : Extensive improvements to the build system to detect kernel + API changes so we can flexibly build with a wider range of kernel + versions. The code has now been testing with the 2.6.18-32chaos + and 2.6.25.3-18.fc9 kernels, however we should also be compatible + with other kernels in the range of 2.6.18-2.6.25. The only + remaining issue preventing us from running with a stock + kernel is ZFS stack usage. + +2008-05-21 Brian Behlendorf + + * : Tag zfs-0.3.1 + + * : License headers including URCL added for release. + +2008-05-21 Brian Behlendorf + + * : Tag zfs-0.3.0 + + * configure.ac: Improved autotools support and configurable debug. + +2008-05-15 Brian Behlendorf + + * : Updating original ZFS sources to build 89 which + includes the new write throttling changes plus support + for using ZFS as your root device. Neither of which + will work exactly right without some more work but this + gets us much closers to the latest source. + + +2008-02-28 Brian Behlendorf + + * : First attempt based on SPL module and zfs-lustre sources diff --git a/DISCLAIMER b/DISCLAIMER new file mode 100644 index 0000000..86778c5 --- /dev/null +++ b/DISCLAIMER @@ -0,0 +1,22 @@ +This notice is required to be provided under our contract with the +U.S. Department of Energy (DOE). This work was produced at the +Lawrence Livermore National Laboratory under Contract with the DOE. + +Neither the United States Government nor the Lawrence Livermore National +Security, LLC. nor any of their employees, makes any warranty, express +or implied, or assumes any liability or responsibility for the accuracy, +completeness, or usefulness of any information, apparatus, product, +or process disclosed, or represents that its use would not infringe +privately-owned rights. + +Also, reference herein to any specific commercial products, process, +or services by trade name, trademark, manufacturer or otherwise does +not necessarily constitute or imply its endorsement, recommendation, +or favoring by the United States Government or the Lawrence Livermore +National Security, LLC. The views and opinions of authors expressed +herein do not necessarily state or reflect those of the United States +Government or the Lawrence Livermore National Security, LLC., and +shall not be used for advertising or product endorsement purposes. + +The precise terms and conditions for copying, distribution and +modification are specified in the file "COPYING". diff --git a/GIT b/GIT new file mode 100644 index 0000000..578e731 --- /dev/null +++ b/GIT @@ -0,0 +1,162 @@ +=========================== WHY USE GIT+TOPGIT? ========================== + +Three major concerns were on our mind when setting up this project. + + o First we needed to structure the project in such a way that it would be + easy to rebase all of our changes on the latest official ZFS release + from Sun. We absolutely need to be able to benefit from the upstream + improvements and not get locked in to an old version of the code base. + + o Secondly, we wanted to be able to easily manage our changes in terms + of a patch stack. This allows us to easily isolate specific changes + and push them upstream for inclusion. It also allows us to easily + update or drop specific changes based on what occurs upstream. + + o Thirdly we needed our DVCS to be integrated with the management of this + patch stack. We have tried other methods in the past such as SVN+Quilt + but have found managing the patch stack becomes cumbersome. By using + Git+TopGit to more tightly integrate our patch stack in to the repo + we expect several benefits. One of the most important will be the + ability to easily work on the patch stack with a distributed developer + team, additionally the repo can track patch history, and we can utilize + Git to merge patches and resolve conflicts. + +TopGit is designed to specifically address these concerns by providing +tools to simplify the handling of large numbers of interdependent topic +branches. When using a TopGit aware repo every topic branch represents +a 'patch' and that branch references its dependent branches. The union +of all these branches is your final source base. + +========================= SETTING UP GIT+TOPGIT ========================== + +First off you need to install a Git package on your system. For my +purposes I have been working on a RHEL5 system with git version 1.5.4.5 +installed and it has been working well. You will also need to go get +the latest version of TopGit which likely is not packaged nicely so you +will need to build it from source. You can use Git to clone TopGit +from the official site here and your all set: + + > git clone http://repo.or.cz/w/topgit.git + > make + > make install # Default installs to $(HOME) + +========================== TOPGIT AND ZFS ================================ + +One you have Git and TopGit installed you will want to clone a copy of +the Linux ZFS repo. While this project does not yet have a public home +it hopefully will some day. In the meanwhile if you have VPN access to +LLNL you can clone the latest official repo here. Cloning a TopGit +controlled repo is very similar to cloning a normal Git repo, but you +need to remember to use 'tg remote' to populate all topic branches. + + > git clone http://eris.llnl.gov/git/zfs.git zfs + > cd zfs + > tg remote --populate origin + +Now that you have the Linux ZFS repo the first thing you will probably +want to do is have a look at all the topic branches. TopGit provides +a summary command which shows all the branches and a brief summary for +each branch obtained from the .topmsg files. + + > tg summary + 0 t/LAST [PATCH] LAST + t/feature-commit-cb [PATCH] zfs commit callbacks + t/fix-clock-wrap [PATCH] fix clock wrap + t/fix-dnode-cons [PATCH] fix dnode constructor + ... + +By convention all TopGit branches are prefixed with 't/', and the Linux +ZFS repo also introduces the convention that the top most development +branch be called 't/LAST". This provides a consistent label to be used +when you need to reference the branch which contains the union of all +topic branches. + +One thing you may also notice about the 'tg summary' command is it does +not show the branches in dependent order. While this project only expresses +a single dependency per branch TopGit implements dependencies as a DAC just +like Git. To see the dependencies you will need to use the --graphviz +option and pipe the result to dot for display. The following command while +long works fairly well for me. Longer term it would be nice to update this +option to use a preferred config options stored in the repo if they exist. + + > tg summary --graphviz | dot -Txlib -Nfontsize=8 -Eminlen=0.01 \ + -Grankdir=LR -Nheight=0.3 -Nwidth=2 -Nfixedsize=true + +========================= UPDATING A TOPIC BRANCH ======================== + +Updating a topic branch in TopGit is a pretty straight forward but there +are a few rules you need to be aware of. The basic process involves +checking out the relevant topic branch where the changes need to be made, +making the changes, committing the changes to the branch and then merging +those changes in to dependent branches. TopGit provides some tools to make +this pretty easy, although it may be a little sluggish. Here is an example: + + > git checkout t/feature-commit-cb # Checkout the proper branch + > ...update branch... # Update the branch + > git commit -a # Commit your changes + > git checkout t/LAST # Checkout the LAST branch + > tg update # Recursively merge in new branch + +Assuming you change does not introduce any conflicts your done. All branches +were dependent on your change will have had the changed merged in. If your +change introduced a conflict you will need to resolve the conflict and then +continue on with the update. + +========================== ADDING A TOPIC BRANCH ========================= + +Adding a topic branch in TopGit is a little more complicated. When adding +a new branch to the end of the patch graph things are pretty easy and TopGit +does all the work. However, I expect out common case to be adding patches +to the middle of the graph. TopGit will allow you to do this but you must +be careful to manually update the dependency information in the .topdeps +file. + + > git co t/existing-topic-branch # Checkout the branch to add after + > tg create t/new-topic-branch # Create a new topic branch + > ...update .topmsg... # Update the branch message + > ...create patch... # Update with your changes + > git commit -a # Commit your changes + > git co t/dependent-topic-branch # Checkout dependent branch + > ...update .topdeps... # Manually update dependencies + > git commit -a # Commit your changes + > tg update # TopGit update + > git checkout t/LAST # Checkout the LAST branch + > tg update # Recursively merge in new branch + +========================= REMOVING A TOPIC BRANCH ======================== + +Removing a topic branch in TopGit is also currently not very easy. To remove +a dependent branch the basic process is to commit a patch which reverts all +changes on the branch. Then that reversion must be merged in to all dependent +branches, the dependencies manually updated and finally the branch removed. +If the branch is not empty you will not be able to remove it. + + > git co t/del-topic-branch # Checkout the branch to delete + > tg patch | patch -R -p1 # Revert all branch changes + > git commit -a # Commit your changes + > git checkout t/LAST # Checkout the LAST branch + > tg update # Recursively merge revert + > git co t/dependent-topic-branch # Checkout dependent branch + > ...update .topdeps... # Manually update dependencies + > git commit -a # Commit your changes + > tg delete t/del-topic-branch # Delete empty topic branch + +============================ TOPGIT TODO ================================= + +TopGit is still a young package which seems to be under active development +by its author. It provides the minimum set of commands needed but there +are clearly areas which simply have not yet been implemented. My short +list of features includes: + + o 'tg summary --deps', option to display a text version of the topic + branch dependency DAC. + + o 'tg depend list', list all topic branch dependencies. + + o 'tg depend delete', cleanly remove a topic branch dependency. + + o 'tg create', cleanly insert a topic branch in the middle + of the graph and properly take care updating all dependencies. + + o 'tg delete', cleanly delete a topic branch in the middle + of the graph and properly take care updating all dependencies. diff --git a/META b/META new file mode 100644 index 0000000..9b66d31 --- /dev/null +++ b/META @@ -0,0 +1,6 @@ +Meta: 1 +Name: zfs +Branch: 1.0 +Version: 0.3.4 +Release: 1 +Release-Tags: relext diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..39f3fd0 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,25 @@ +AUTOMAKE_OPTIONS = foreign dist-zip + +SUBDIRS = doc scripts $(BUILDDIR) +CONFIG_CLEAN_FILES = aclocal.m4 config.guess config.sub +CONFIG_CLEAN_FILES += depcomp missing mkinstalldirs +EXTRA_DIST = autogen.sh + +.PHONY: quilt +quilt: .quilt-$(BUILDDIR) +autogen: .autogen-$(BUILDDIR) +config: .config-$(BUILDDIR) +.quilt-$(BUILDDIR): + ./scripts/quilt.sh -p $(NAME) -b $(BUILDDIR) -s $(SERIESFILE) -d $(PATCHDIR) + echo $(BUILDDIR) >$@ + +unquilt: + rm -rf $(BUILDDIR) + rm -f .quilt-$(BUILDDIR) + +clean-generic: + +distclean: unquilt + +rpms: dist Makefile + rpmbuild -ta $(distdir).tar.gz diff --git a/README b/README new file mode 100644 index 0000000..b17c1d8 --- /dev/null +++ b/README @@ -0,0 +1,74 @@ +============================ ZFS KERNEL BUILD ============================ + +1) Build the SPL (Solaris Porting Layer) module which is designed to + provide many Solaris APIs in the Linux kernel which are needed + by ZFS. To build the SPL: + + tar -xzf spl-x.y.z.tgz + cd spl-x.y.z + ./configure --with-linux= + make + make check + +2) Build ZFS, this port is based on build 89 of ZFS from OpenSolaris. + You will need to have both the kernel and SPL source available. + To build ZFS for use as a Linux kernel module (default): + + tar -xzf zfs-x.y.z.tgz + cd zfs-x.y.z + ./configure --with-linux= \ + --with-spl= + make + make check + +========================= ZFS USER LIBRARY BUILD ========================= + +1) Build ZFS, this port is based on build 89 of ZFS from OpenSolaris. + To build ZFS as a userspace library: + + tar -xzf zfs-x.y.z.tgz + cd zfs-x.y.z + ./configure --zfsconfig=user + make + make check + +============================ ZFS LUSTRE BUILD ============================ + +1) Build the SPL (Solaris Porting Layer) module which is designed to + provide many Solaris APIs in the Linux kernel which are needed + by ZFS. To build the SPL: + + tar -xzf spl-x.y.z.tgz + cd spl-x.y.z + ./configure --with-linux= + make + make check + +2) Build ZFS, this port is based on build 89 of ZFS from OpenSolaris. + To build ZFS as a userspace library for use by a Lustre filesystem: + + tar -xzf zfs-x.y.z.tgz + cd zfs-x.y.z + ./configure --zfsconfig=lustre \ + --with-linux= \ + --with-spl= + make + make check + +3) Provided is an in-kernel test application called kpios which can be + used to simulate a Lustre IO load. It may be used as a stress test + or as a performance to measure your configuration. To simplify testing + there are scripts provided in the scripts/ directory. A single test + can be run as follows: + + WARNING: You MUST update DEVICES in the create-zpool.sh script + to reference the devices you wish to use. + + cd scripts + ./load-zfs.sh # Load the ZFS/SPL module stack + ./create-zpool.sh # Modify DEVICES to list your zpool devices + ./zpios.sh # Modify for your particular kpios test + ./unload-zfs.sh # Unload the ZFS/SPL module stack + +Enjoy, +Brian Behlendorf diff --git a/TODO b/TODO new file mode 100644 index 0000000..0d9310d --- /dev/null +++ b/TODO @@ -0,0 +1,30 @@ +* We may need a libefi replacement. It appears libefi is used + to determine if the device passed to zpool is a 'whole device' + or just a partition of a device. In the short term I think we + can simply treat everything as a partition and be alright. + +* We also do not have support for getting Solaris style device + ids which is done when a zpool is setup. We may or may not + be able to live without this, the jury is still out. + +----------------------------------------------------------------------- + +* Port zvol (ZFS volume interface). + +* Port zpl (ZFS posix interface). + +* Port lustre fsfilt interface to use DMU. + +* Andreas issue #1: + "the maximum allocation DMU "blocksize" was 128kB and it would be better + to be able to get 1MB contiguous allocations for best performance" + +* Andreas issue #2: + "there would need to be some work done to allow multiple operations to + be atomic. This is needed by Lustre for object creation + LAST_ID + updates, unlink + llog updates, etc. Conceptually this isn't very + much work for a phase tree, but I've never looked at the ZFS code." + +* Design and implement mechanism for viewing and modifying OST content + from user space (by manipulating datasets/objects), possibly + by implementing scaled down file system interface. diff --git a/autoconf/Makefile.am b/autoconf/Makefile.am new file mode 100644 index 0000000..688e8fd --- /dev/null +++ b/autoconf/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = zfs-build.m4 diff --git a/autoconf/zfs-build.m4 b/autoconf/zfs-build.m4 new file mode 100644 index 0000000..773d428 --- /dev/null +++ b/autoconf/zfs-build.m4 @@ -0,0 +1,378 @@ +AC_DEFUN([ZFS_AC_CONFIG], [ + + AC_ARG_WITH([zfs-config], + AS_HELP_STRING([--with-config=CONFIG], + [Config file 'kernel|user|lustre']), + [zfsconfig="$withval"]) + + AC_MSG_CHECKING([zfs config file]) + if test -z "$zfsconfig" || test ! -r configs/$zfsconfig; then + AC_MSG_RESULT([no]) + AC_MSG_ERROR([ + *** Please specify one of the valid config files located + *** in ./configs/ with the '--with-zfs-config=CONFIG' option]) + fi + + AC_MSG_RESULT([$zfsconfig]); + . ./configs/$zfsconfig + + TOPDIR=`/bin/pwd` + ZFSDIR=${TOPDIR}/$BUILDDIR + LIBDIR=$ZFSDIR/lib + CMDDIR=$ZFSDIR/zcmd + + AC_SUBST(TOPDIR) + AC_SUBST(ZFSDIR) + AC_SUBST(LIBDIR) + AC_SUBST(CMDDIR) +]) + +AC_DEFUN([ZFS_AC_KERNEL], [ + ver=`uname -r` + + AC_ARG_WITH([linux], + AS_HELP_STRING([--with-linux=PATH], + [Path to kernel source]), + [kernelsrc="$withval"; kernelbuild="$withval"]) + + AC_ARG_WITH(linux-obj, + AS_HELP_STRING([--with-linux-obj=PATH], + [Path to kernel build objects]), + [kernelbuild="$withval"]) + + AC_MSG_CHECKING([kernel source directory]) + if test -z "$kernelsrc"; then + kernelbuild= + sourcelink=/lib/modules/${ver}/source + buildlink=/lib/modules/${ver}/build + + if test -e $sourcelink; then + kernelsrc=`(cd $sourcelink; /bin/pwd)` + fi + if test -e $buildlink; then + kernelbuild=`(cd $buildlink; /bin/pwd)` + fi + if test -z "$kernelsrc"; then + kernelsrc=$kernelbuild + fi + if test -z "$kernelsrc" -o -z "$kernelbuild"; then + AC_MSG_RESULT([Not found]) + AC_MSG_ERROR([ + *** Please specify the location of the kernel source + *** with the '--with-linux=PATH' option]) + fi + fi + + AC_MSG_RESULT([$kernelsrc]) + AC_MSG_CHECKING([kernel build directory]) + AC_MSG_RESULT([$kernelbuild]) + + AC_MSG_CHECKING([kernel source version]) + if test -r $kernelbuild/include/linux/version.h && + fgrep -q UTS_RELEASE $kernelbuild/include/linux/version.h; then + + kernsrcver=`(echo "#include "; + echo "kernsrcver=UTS_RELEASE") | + cpp -I $kernelbuild/include | + grep "^kernsrcver=" | cut -d \" -f 2` + + elif test -r $kernelbuild/include/linux/utsrelease.h && + fgrep -q UTS_RELEASE $kernelbuild/include/linux/utsrelease.h; then + + kernsrcver=`(echo "#include "; + echo "kernsrcver=UTS_RELEASE") | + cpp -I $kernelbuild/include | + grep "^kernsrcver=" | cut -d \" -f 2` + fi + + if test -z "$kernsrcver"; then + AC_MSG_RESULT([Not found]) + AC_MSG_ERROR([ + *** Cannot determine the version of the linux kernel source. + *** Please prepare the kernel before running this script]) + fi + + AC_MSG_RESULT([$kernsrcver]) + + kmoduledir=${INSTALL_MOD_PATH}/lib/modules/$kernsrcver + LINUX=${kernelsrc} + LINUX_OBJ=${kernelbuild} + + AC_SUBST(LINUX) + AC_SUBST(LINUX_OBJ) + AC_SUBST(kmoduledir) +]) + +AC_DEFUN([ZFS_AC_SPL], [ + + AC_ARG_WITH([spl], + AS_HELP_STRING([--with-spl=PATH], + [Path to spl source]), + [splsrc="$withval"; splbuild="$withval"]) + + AC_ARG_WITH([spl-obj], + AS_HELP_STRING([--with-spl-obj=PATH], + [Path to spl build objects]), + [splbuild="$withval"]) + + + AC_MSG_CHECKING([spl source directory]) + if test -z "$splsrc"; then + splbuild= + sourcelink=/tmp/`whoami`/spl + buildlink=/tmp/`whoami`/spl + + if test -e $sourcelink; then + splsrc=`(cd $sourcelink; /bin/pwd)` + fi + if test -e $buildlink; then + splbuild=`(cd $buildlink; /bin/pwd)` + fi + if test -z "$splsrc"; then + splsrc=$splbuild + fi + fi + + if test -z "$splsrc" -o -z "$splbuild"; then + sourcelink=/lib/modules/${ver}/source + buildlink=/lib/modules/${ver}/build + + if test -e $sourcelink; then + splsrc=`(cd $sourcelink; /bin/pwd)` + fi + if test -e $buildlink; then + splbuild=`(cd $buildlink; /bin/pwd)` + fi + if test -z "$splsrc"; then + splsrc=$splbuild + fi + if test -z "$splsrc" -o -z "$splbuild"; then + AC_MSG_RESULT([Not found]) + AC_MSG_ERROR([ + *** Please specify the location of the spl source + *** with the '--with-spl=PATH' option]) + fi + fi + + AC_MSG_RESULT([$splsrc]) + AC_MSG_CHECKING([spl build directory]) + AC_MSG_RESULT([$splbuild]) + + AC_MSG_CHECKING([spl source version]) + if test -r $splbuild/spl_config.h && + fgrep -q VERSION $splbuild/spl_config.h; then + + splsrcver=`(echo "#include "; + echo "splsrcver=VERSION") | + cpp -I $splbuild | + grep "^splsrcver=" | cut -d \" -f 2` + fi + + if test -z "$splsrcver"; then + AC_MSG_RESULT([Not found]) + AC_MSG_ERROR([ + *** Cannot determine the version of the spl source. + *** Please prepare the spl source before running this script]) + fi + + AC_MSG_RESULT([$splsrcver]) + + AC_MSG_CHECKING([spl Module.symvers]) + if test -r $splbuild/modules/Module.symvers; then + splsymvers=$splbuild/modules/Module.symvers + elif test -r $kernelbuild/Module.symvers; then + splsymvers=$kernelbuild/Module.symvers + fi + + if test -z "$splsymvers"; then + AC_MSG_RESULT([Not found]) + AC_MSG_ERROR([ + *** Cannot find extra Module.symvers in the spl source. + *** Please prepare the spl source before running this script]) + fi + + AC_MSG_RESULT([$splsymvers]) + AC_SUBST(splsrc) + AC_SUBST(splsymvers) +]) + +AC_DEFUN([ZFS_AC_LICENSE], [ + AC_MSG_CHECKING([license]) + AC_MSG_RESULT([CDDL]) +dnl # AC_DEFINE([HAVE_GPL_ONLY_SYMBOLS], [1], +dnl # [Define to 1 if module is licensed under the GPL]) +]) + +AC_DEFUN([ZFS_AC_DEBUG], [ + AC_MSG_CHECKING([whether debugging is enabled]) + AC_ARG_ENABLE( [debug], + AS_HELP_STRING([--enable-debug], + [Enable generic debug support (default off)]), + [ case "$enableval" in + yes) zfs_ac_debug=yes ;; + no) zfs_ac_debug=no ;; + *) AC_MSG_RESULT([Error!]) + AC_MSG_ERROR([Bad value "$enableval" for --enable-debug]) ;; + esac ] + ) + if test "$zfs_ac_debug" = yes; then + AC_MSG_RESULT([yes]) + KERNELCPPFLAGS="${KERNELCPPFLAGS} -DDEBUG " + HOSTCFLAGS="${HOSTCFLAGS} -DDEBUG " + else + AC_MSG_RESULT([no]) + AC_DEFINE([NDEBUG], [1], + [Define to 1 to disable debug tracing]) + KERNELCPPFLAGS="${KERNELCPPFLAGS} -DNDEBUG " + HOSTCFLAGS="${HOSTCFLAGS} -DNDEBUG " + fi +]) + +AC_DEFUN([ZFS_AC_SCRIPT_CONFIG], [ + SCRIPT_CONFIG=.script-config + rm -f ${SCRIPT_CONFIG} + echo "KERNELSRC=${LINUX}" >>${SCRIPT_CONFIG} + echo "KERNELBUILD=${LINUX_OBJ}" >>${SCRIPT_CONFIG} + echo "KERNELSRCVER=$kernsrcver" >>${SCRIPT_CONFIG} + echo >>${SCRIPT_CONFIG} + echo "SPLSRC=$splsrc" >>${SCRIPT_CONFIG} + echo "SPLBUILD=$splbuild" >>${SCRIPT_CONFIG} + echo "SPLSRCVER=$splsrcver" >>${SCRIPT_CONFIG} + echo "SPLSYMVERS=$splsymvers" >>${SCRIPT_CONFIG} + echo >>${SCRIPT_CONFIG} + echo "ZFSSRC=${TOPDIR}/src" >>${SCRIPT_CONFIG} + echo "ZFSBUILD=${ZFSDIR}" >>${SCRIPT_CONFIG} + echo >>${SCRIPT_CONFIG} + echo "TOPDIR=${TOPDIR} >>${SCRIPT_CONFIG} + echo "LIBDIR=${LIBDIR} >>${SCRIPT_CONFIG} + echo "CMDDIR=${CMDDIR} >>${SCRIPT_CONFIG} +]) + +dnl # +dnl # ZFS_LINUX_CONFTEST +dnl # +AC_DEFUN([ZFS_LINUX_CONFTEST], [ +cat >conftest.c <<_ACEOF +$1 +_ACEOF +]) + +dnl # +dnl # ZFS_LANG_PROGRAM(C)([PROLOGUE], [BODY]) +dnl # +m4_define([ZFS_LANG_PROGRAM], [ +$1 +int +main (void) +{ +dnl Do *not* indent the following line: there may be CPP directives. +dnl Don't move the `;' right after for the same reason. +$2 + ; + return 0; +} +]) + +dnl # +dnl # ZFS_LINUX_COMPILE_IFELSE / like AC_COMPILE_IFELSE +dnl # +AC_DEFUN([ZFS_LINUX_COMPILE_IFELSE], [ +m4_ifvaln([$1], [ZFS_LINUX_CONFTEST([$1])])dnl +rm -f build/conftest.o build/conftest.mod.c build/conftest.ko build/Makefile +echo "obj-m := conftest.o" >build/Makefile +dnl AS_IF([AC_TRY_COMMAND(cp conftest.c build && make [$2] CC="$CC" -f $PWD/build/Makefile LINUXINCLUDE="-Iinclude -include include/linux/autoconf.h" -o tmp_include_depends -o scripts -o include/config/MARKER -C $LINUX_OBJ EXTRA_CFLAGS="-Werror-implicit-function-declaration $EXTRA_KCFLAGS" $ARCH_UM SUBDIRS=$PWD/build) >/dev/null && AC_TRY_COMMAND([$3])], +AS_IF([AC_TRY_COMMAND(cp conftest.c build && make [$2] CC="$CC" LINUXINCLUDE="-Iinclude -include include/linux/autoconf.h" -o tmp_include_depends -o scripts -o include/config/MARKER -C $LINUX_OBJ EXTRA_CFLAGS="-Werror-implicit-function-declaration $EXTRA_KCFLAGS" $ARCH_UM M=$PWD/build) >/dev/null && AC_TRY_COMMAND([$3])], + [$4], + [_AC_MSG_LOG_CONFTEST +m4_ifvaln([$5],[$5])dnl])dnl +rm -f build/conftest.o build/conftest.mod.c build/conftest.mod.o build/conftest.ko m4_ifval([$1], [build/conftest.c conftest.c])[]dnl +]) + +dnl # +dnl # ZFS_LINUX_TRY_COMPILE like AC_TRY_COMPILE +dnl # +AC_DEFUN([ZFS_LINUX_TRY_COMPILE], + [ZFS_LINUX_COMPILE_IFELSE( + [AC_LANG_SOURCE([ZFS_LANG_PROGRAM([[$1]], [[$2]])])], + [modules], + [test -s build/conftest.o], + [$3], [$4]) +]) + +dnl # +dnl # ZFS_LINUX_CONFIG +dnl # +AC_DEFUN([ZFS_LINUX_CONFIG], + [AC_MSG_CHECKING([whether Linux was built with CONFIG_$1]) + ZFS_LINUX_TRY_COMPILE([ + #ifndef AUTOCONF_INCLUDED + #include + #endif + ],[ + #ifndef CONFIG_$1 + #error CONFIG_$1 not #defined + #endif + ],[ + AC_MSG_RESULT([yes]) + $2 + ],[ + AC_MSG_RESULT([no]) + $3 + ]) +]) + +dnl # +dnl # ZFS_CHECK_SYMBOL_EXPORT +dnl # check symbol exported or not +dnl # +AC_DEFUN([ZFS_CHECK_SYMBOL_EXPORT], + [AC_MSG_CHECKING([whether symbol $1 is exported]) + grep -q -E '[[[:space:]]]$1[[[:space:]]]' $LINUX/Module.symvers 2>/dev/null + rc=$? + if test $rc -ne 0; then + export=0 + for file in $2; do + grep -q -E "EXPORT_SYMBOL.*($1)" "$LINUX/$file" 2>/dev/null + rc=$? + if test $rc -eq 0; then + export=1 + break; + fi + done + if test $export -eq 0; then + AC_MSG_RESULT([no]) + $4 + else + AC_MSG_RESULT([yes]) + $3 + fi + else + AC_MSG_RESULT([yes]) + $3 + fi +]) + +dnl # +dnl # 2.6.x API change +dnl # bio_end_io_t uses 2 args (size was dropped from prototype) +dnl # +AC_DEFUN([ZFS_AC_2ARGS_BIO_END_IO_T], + [AC_MSG_CHECKING([whether bio_end_io_t wants 2 args]) + tmp_flags="$EXTRA_KCFLAGS" + EXTRA_KCFLAGS="-Werror" + ZFS_LINUX_TRY_COMPILE([ + #include + ],[ + void (*wanted_end_io)(struct bio *, int) = NULL; + bio_end_io_t *local_end_io; + + local_end_io = wanted_end_io; + ],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_2ARGS_BIO_END_IO_T, 1, + [bio_end_io_t wants 2 args]) + ],[ + AC_MSG_RESULT(no) + ]) + EXTRA_KCFLAGS="$tmp_flags" +]) diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..7834426 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +find . -type d -name .deps | xargs rm -rf +rm -rf config.guess config.sub ltmain.sh +libtoolize --automake +aclocal -I autoconf 2>/dev/null && +autoheader && +automake --add-missing --include-deps # 2>/dev/null && +autoconf + diff --git a/build/Makefile b/build/Makefile new file mode 100644 index 0000000..f84b8e9 --- /dev/null +++ b/build/Makefile @@ -0,0 +1 @@ +obj-m := conftest.o diff --git a/configs/Makefile.am b/configs/Makefile.am new file mode 100644 index 0000000..326b3d7 --- /dev/null +++ b/configs/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST=kernel user lustre diff --git a/configs/kernel b/configs/kernel new file mode 100644 index 0000000..fc1dfa3 --- /dev/null +++ b/configs/kernel @@ -0,0 +1,11 @@ +# Default ZFS kernel mode configuration + +UNAME=`uname -r | cut -d- -f1` + +CONFIG=user + +NAME=zfs +BRANCH=`awk '/[Bb]ranch:/ {print $$2}' META` +VERSION=`awk '/[Vv]ersion:/ {print $$2}' META` +RELEASE=`awk '/[Rr]elease:/ {print $$2}' META` +BUILDDIR=$NAME+$CONFIG diff --git a/configs/lustre b/configs/lustre new file mode 100644 index 0000000..65b1603 --- /dev/null +++ b/configs/lustre @@ -0,0 +1,11 @@ +# Default ZFS lustre mode configuration + +UNAME=`uname -r | cut -d- -f1` + +CONFIG=lustre + +NAME=zfs +BRANCH=`awk '/[Bb]ranch:/ {print $$2}' META` +VERSION=`awk '/[Vv]ersion:/ {print $$2}' META` +RELEASE=`awk '/[Rr]elease:/ {print $$2}' META` +BUILDDIR=$NAME+$CONFIG diff --git a/configs/user b/configs/user new file mode 100644 index 0000000..f79ba8f --- /dev/null +++ b/configs/user @@ -0,0 +1,9 @@ +# Default ZFS user mode configuration + +CONFIG=user + +NAME=zfs +BRANCH=`awk '/[Bb]ranch:/ {print $$2}' META` +VERSION=`awk '/[Vv]ersion:/ {print $$2}' META` +RELEASE=`awk '/[Rr]elease:/ {print $$2}' META` +BUILDDIR=$NAME+$CONFIG diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..7d09985 --- /dev/null +++ b/configure.ac @@ -0,0 +1,223 @@ +# +# This file is part of the ZFS Linux port. +# +# Copyright (c) 2008 Lawrence Livermore National Security, LLC. +# Produced at Lawrence Livermore National Laboratory +# Written by: +# Brian Behlendorf , +# Herb Wartens , +# Jim Garlick +# LLNL-CODE-403049 +# +# 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 +# + +AC_INIT +AC_LANG(C) + +AC_CANONICAL_SYSTEM +AM_INIT_AUTOMAKE(zfs, 0.4.0) +AC_CONFIG_HEADERS([zfs_config.h]) + +AC_PROG_INSTALL +AC_PROG_CC +AC_PROG_LIBTOOL + +zfsconfig=kernel +kernelsrc= +kernelbuild= +splsrc= +splbuild= + +ZFS_AC_CONFIG +ZFS_AC_KERNEL +ZFS_AC_SPL +ZFS_AC_SCRIPT_CONFIG +ZFS_AC_LICENSE +ZFS_AC_DEBUG +ZFS_AC_2ARGS_BIO_END_IO_T + +AC_SUBST(UNAME) +AC_SUBST(CONFIG) +AC_SUBST(NAME) +AC_SUBST(SVNURL) +AC_SUBST(BRANCH) +AC_SUBST(VERSION) +AC_SUBST(RELEASE) +AC_SUBST(BRANCHURL) +AC_SUBST(TAGURL) +AC_SUBST(BUILDURL) +AC_SUBST(BUILDDIR) + +# Check for needed userspace bits +AC_CHECK_HEADERS(sys/types.h sys/byteorder.h sys/isa_defs.h \ + sys/systeminfo.h sys/u8_textprep.h libdiskmgt.h) + +AC_CHECK_FUNCS(strlcat strlcpy strnlen issetugid setmntent getexecname) + +AC_CHECK_LIB([diskmgt], [libdiskmgt_error], + [AC_DEFINE([HAVE_LIBDISKMGT], 1, + [Define to 1 if 'libdiskmgt' library available])]) + +AC_CHECK_LIB([efi], [efi_alloc_and_init], + [AC_DEFINE([HAVE_LIBEFI], 1, + [Define to 1 if 'libefi' library available])]) + +AC_CHECK_LIB([share], [sa_init], + [AC_DEFINE([HAVE_LIBSHARE], 1, + [Define to 1 if 'libshare' library available])]) + +AC_EGREP_HEADER(ioctl, unistd.h, + [AC_DEFINE([HAVE_IOCTL_IN_UNISTD_H], 1, + [Define to 1 if ioctl() is defined in header file])]) + +AC_EGREP_HEADER(ioctl, sys/ioctl.h, + [AC_DEFINE([HAVE_IOCTL_IN_SYS_IOCTL_H], 1, + [Define to 1 if ioctl() is defined in header file])]) + +AC_EGREP_HEADER(ioctl, stropts.h, + [AC_DEFINE([HAVE_IOCTL_IN_STROPTS_H], 1, + [Define to 1 if ioctl() is defined in header file])]) + +AC_EGREP_HEADER(strcmp, strings.h, + [AC_DEFINE([HAVE_STRCMP_IN_STRINGS_H], 1, + [Define to 1 if strcmpl() is defined in header file])]) + +AC_EGREP_HEADER(sysinfo, sys/systeminfo.h, + [AC_DEFINE([HAVE_SYSINFO_IN_SYS_SYSTEMINFO_H], 1, + [Define to 1 if sysinfo() is defined in header file])]) + +#AC_DEFINE([HAVE_ZVOL], 1, ["Define to 1 to include ZVOL support"]) +#AC_DEFINE([HAVE_ZPL], 1, ["Define to 1 to include ZPL support"]) +#AC_DEFINE([WANT_FAKE_IOCTL], 1, ["Define to 1 to use fake ioctl() support"]) +#AC_DEFINE([HAVE_DM_INUSE_SWAP], 1, ["None"]) +#AC_DEFINE([HAVE_UNICODE], 1, ["None"]) +#AC_DEFINE([HAVE_INTTYPES], 1, [Define to 1 if unint16 defined in header file]) + +# Add "V=1" to KERNELMAKE_PARAMS to enable verbose module build +KERNELMAKE_PARAMS= +KERNELCPPFLAGS="$KERNELCPPFLAGS -DHAVE_SPL -D_KERNEL -I$splsrc -I$splsrc/include -I$TOPDIR" + +# Minimally required for pread() functionality an other GNU goodness +HOSTCFLAGS="$HOSTCFLAGS -ggdb -O2 -std=c99 -D_GNU_SOURCE -D__EXTENSIONS__ " +# Quiet warnings not covered by the gcc-* patches +HOSTCFLAGS="$HOSTCFLAGS -Wno-switch -Wno-unused -Wno-missing-braces -Wno-parentheses " +HOSTCFLAGS="$HOSTCFLAGS -Wno-uninitialized -fno-strict-aliasing " +# Expected defines not covered by zfs_config.h +HOSTCFLAGS="$HOSTCFLAGS -DHAVE_SPL -D_POSIX_PTHREAD_SEMANTICS " +HOSTCFLAGS="$HOSTCFLAGS -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE -D_REENTRANT " +HOSTCFLAGS="$HOSTCFLAGS -DTEXT_DOMAIN=\\\"zfs-linux-kernel\\\" " +# Expected default include paths additional paths added by Makefiles +HOSTCFLAGS="$HOSTCFLAGS -I$TOPDIR " + +if test "$kernelbuild" != "$kernelsrc"; then + KERNELMAKE_PARAMS="$KERNELMAKE_PARAMS O=$kernelbuild" +fi + +AC_SUBST(KERNELMAKE_PARAMS) +AC_SUBST(KERNELCPPFLAGS) +AC_SUBST(HOSTCFLAGS) + +AC_CONFIG_FILES([ Makefile + autoconf/Makefile + configs/Makefile + doc/Makefile + scripts/Makefile + zfs/Makefile + zfs/lib/libudmu/include/Makefile + zfs/lib/libudmu/Makefile + zfs/lib/Makefile + zfs/lib/libnvpair/include/sys/Makefile + zfs/lib/libnvpair/include/Makefile + zfs/lib/libnvpair/Makefile + zfs/lib/libsolcompat/sparc64/Makefile + zfs/lib/libsolcompat/Makefile + zfs/lib/libsolcompat/include/tsol/Makefile + zfs/lib/libsolcompat/include/sparc64/sys/Makefile + zfs/lib/libsolcompat/include/sparc64/Makefile + zfs/lib/libsolcompat/include/rpc/Makefile + zfs/lib/libsolcompat/include/i386/sys/Makefile + zfs/lib/libsolcompat/include/i386/Makefile + zfs/lib/libsolcompat/include/ia32/sys/Makefile + zfs/lib/libsolcompat/include/ia32/Makefile + zfs/lib/libsolcompat/include/amd64/sys/Makefile + zfs/lib/libsolcompat/include/amd64/Makefile + zfs/lib/libsolcompat/include/sys/sysevent/Makefile + zfs/lib/libsolcompat/include/sys/fm/Makefile + zfs/lib/libsolcompat/include/sys/Makefile + zfs/lib/libsolcompat/include/Makefile + zfs/lib/libsolcompat/i386/Makefile + zfs/lib/libsolcompat/amd64/Makefile + zfs/lib/libavl/include/sys/Makefile + zfs/lib/libavl/include/Makefile + zfs/lib/libavl/Makefile + zfs/lib/libuutil/include/Makefile + zfs/lib/libuutil/Makefile + zfs/lib/libzfs/include/Makefile + zfs/lib/libzfs/Makefile + zfs/lib/libumem/include/Makefile + zfs/lib/libumem/Makefile + zfs/lib/libumem/sys/Makefile + zfs/lib/libzcommon/include/Makefile + zfs/lib/libzcommon/include/sys/fm/fs/Makefile + zfs/lib/libzcommon/include/sys/fm/Makefile + zfs/lib/libzcommon/include/sys/Makefile + zfs/lib/libzcommon/include/sys/fs/Makefile + zfs/lib/libzcommon/Makefile + zfs/lib/libzpool/Makefile + zfs/lib/libport/include/sys/Makefile + zfs/lib/libport/include/Makefile + zfs/lib/libport/Makefile + zfs/lib/libdmu-ctl/include/sys/Makefile + zfs/lib/libdmu-ctl/include/Makefile + zfs/lib/libdmu-ctl/Makefile + zfs/zcmd/ztest/Makefile + zfs/zcmd/Makefile + zfs/zcmd/zfs/Makefile + zfs/zcmd/zdb/Makefile + zfs/zcmd/zinject/Makefile + zfs/zcmd/zdump/Makefile + zfs/zcmd/zpool/Makefile + ]) +AC_OUTPUT + +# HACK: I really, really hate this... but to ensure the kernel build +# system compiles C files shared between a library and a kernel module, +# we need to ensure each file has a unique make target. To do that +# I'm creating symlinks for each shared file at configure time. It +# may be possible something better can be done in the Makefile but it +# will take some serious investigation and I don't have the time now. + +echo +echo "Creating symlinks for additional make targets" +ln -s $LIBDIR/libport/u8_textprep.c $LIBDIR/libport/ku8_textprep.c +ln -s $LIBDIR/libavl/avl.c $LIBDIR/libavl/kavl.c +ln -s $LIBDIR/libavl/avl.c $LIBDIR/libavl/uavl.c +ln -s $LIBDIR/libnvpair/nvpair.c $LIBDIR/libnvpair/knvpair.c +ln -s $LIBDIR/libnvpair/nvpair.c $LIBDIR/libnvpair/unvpair.c +ln -s $LIBDIR/libzcommon/zfs_deleg.c $LIBDIR/libzcommon/kzfs_deleg.c +ln -s $LIBDIR/libzcommon/zfs_prop.c $LIBDIR/libzcommon/kzfs_prop.c +ln -s $LIBDIR/libzcommon/zprop_common.c $LIBDIR/libzcommon/kzprop_common.c +ln -s $LIBDIR/libzcommon/compress.c $LIBDIR/libzcommon/kcompress.c +ln -s $LIBDIR/libzcommon/list.c $LIBDIR/libzcommon/klist.c +ln -s $LIBDIR/libzcommon/zfs_namecheck.c $LIBDIR/libzcommon/kzfs_namecheck.c +ln -s $LIBDIR/libzcommon/zfs_comutil.c $LIBDIR/libzcommon/kzfs_comutil.c +ln -s $LIBDIR/libzcommon/zpool_prop.c $LIBDIR/libzcommon/kzpool_prop.c diff --git a/doc/LEGAL b/doc/LEGAL new file mode 100644 index 0000000..905141b --- /dev/null +++ b/doc/LEGAL @@ -0,0 +1,113 @@ +From: Chris Dunlap +To: tak1@llnl.gov (James Tak) +Cc: rogers11@llnl.gov (Leah Rogers), garlick@llnl.gov (Jim Garlick), + mgary@llnl.gov (Mark Gary), kimcupps@llnl.gov (Kim Cupps) +Date: Mon, 26 Mar 2007 15:37:07 -0700 +Subject: CDDL/GPL licensing issues for ZFS Linux port + +James, + +We want to port Sun's Zettabyte File System (ZFS) to Linux and +ultimately redistribute the source code of our work. We've been +talking with Leah about this and have a meeting scheduled with you +for this coming Thursday at 2pm. I just wanted to give you a summary +before the meeting of what we're proposing. + +ZFS is part of OpenSolaris which is licensed under the Common +Development and Distribution License (CDDL): + + http://www.opensolaris.org/os/licensing/cddllicense.txt + +The Linux kernel is licensed under the GNU General Public License (GPL) +(specifically, under version 2 of the license only): + + http://www.fsf.org/licensing/licenses/gpl.html + +While these are both Open-Source licenses, the Free Software Foundation +(FSF) states they are incompatible with one another: + + http://www.fsf.org/licensing/licenses/index_html + + "[CDDL] is a free software license which is not a strong copyleft; + it has some complex restrictions that make it incompatible with the + GNU GPL. It requires that all attribution notices be maintained, + while the GPL only requires certain types of notices. Also, it + terminates in retaliation for certain aggressive uses of patents. + So, a module covered by the GPL and a module covered by the CDDL + cannot legally be linked together." + +As an aside, Sun is reportedly considering releasing OpenSolaris under +GPL3 (i.e., the upcoming version 3 of the GNU General Public License): + + http://blogs.sun.com/jonathan/entry/hp_and_sun_partnering_around + + http://arstechnica.com/news.ars/post/20060130-6074.html + + http://news.com.com/Sun+considers+GPL+3+license+for+Solaris/2100-1016_3-6032893.html + +Since the GPL3 has not been finalized, it is unclear whether +incompatibilities will exist between GPL2 and GPL3. + +Linus Torvalds (the original creator of Linux) describes his views +on the licensing of Linux kernel modules in the following email thread: + + http://linuxmafia.com/faq/Kernel/proprietary-kernel-modules.html + +Most of this thread is in regards to proprietary closed-source +binary-only modules for Linux. Linus generally considers modules +written for Linux using the kernel infrastructures to be derived +works of Linux, even if they don't copy any existing Linux code. +However, he specifically singles out drivers and filesystems ported +from other operating systems as not being derived works: + + "It would be rather preposterous to call the Andrew FileSystem a + 'derived work' of Linux, for example, so I think it's perfectly + OK to have a AFS module, for example." + + "The original binary-only modules were for things that were + pre-existing works of code, i.e., drivers and filesystems ported + from other operating systems, which thus could clearly be argued + to not be derived works..." + +Based on this, it seems our port of Sun's ZFS filesystem to Linux +would not be considered a derived work of Linux, and therefore not +covered by the GPL. The issue of the CDDL/GPL license incompatibility +becomes moot. As such, we should be able to redistribute our changes +to ZFS in source-code form licensed under the CDDL since this will +be a derived work of the original ZFS code. There seems to be some +dissent as to whether a binary module could be redistributed as well, +but that issue does not concern us. In this instance, we are only +interested in redistribution of our work in source-code form. + +-Chris + +To: Chris Dunlap +From: James Tak +Subject: Re: CDDL/GPL licensing issues for ZFS Linux port +Cc: rogers11@llnl.gov (Leah Rogers), garlick@llnl.gov (Jim Garlick), + mgary@llnl.gov (Mark Gary), kimcupps@llnl.gov (Kim Cupps) +Date: Thu, 29 Mar 2007 14:53:01 -0700 + +Hi Chris, +As per our discussion today, the ZFS port you are proposing releasing under +the CDDL license should be o.k. since it is a derivative work of the +original ZFS module (under CDDL) and is therefore also subject to CDDL +under the distribution terms of that license. While the issue of linking +has been greatly debated in the OS community, I think it is fair to say in +this instance the ZFS port is not a derivative work of Linux and thus not +subject to the GPL. Furthermore, it shouldn't be a problem especially +since even Linus Torvald has expressed that modules such as yours are not +derived works of Linux. + +Let me know if you have any further questions at x27274. Thanks. + +Regards, +James + +James S. Tak +Assistant Laboratory Counsel for Intellectual Property +Office of Laboratory Counsel +Lawrence Livermore National Laboratory +phone: (925) 422-7274 +fax: (925) 423-2231 +tak1@llnl.gov diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 0000000..07f6386 --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = LEGAL diff --git a/doc/analysis-spl-0.3.1/zfs-report.doc b/doc/analysis-spl-0.3.1/zfs-report.doc new file mode 100644 index 0000000000000000000000000000000000000000..34638b2ead5343bb2db9157713922298500fafe9 GIT binary patch literal 54784 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 literal 0 HcmV?d00001 diff --git a/doc/analysis-spl-0.3.1/zfs-report.odt b/doc/analysis-spl-0.3.1/zfs-report.odt new file mode 100644 index 0000000000000000000000000000000000000000..fd5286113646ece913b5fdd78f255fe5b90db16d GIT binary patch literal 33209 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 K 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

*L~vx^d@>0hTF=R+5Mrp0NkZQNlS{o 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

|#Nx_{{H1z?zT@VW#R0!!tx{m(0G~2duJX zeXWqos2V#dDq?SEI%^ta#iH{L!C3d@E9)nDKIT&E?uB=@|CA;EcAc`6?Doe9g;5+s z6C{<}w1nW4Fl`6BNq4`nVE+{@tr7b4zXn|^8h*27Z||IYb2vew8iNwa-~dwgp1-2a zkHjQnI38j~PW99-8Sxf3Z9=H$;3tHR|zy%3&%IHuKS4BRT zI`R1$snZd&L$9{+t)UPg8v<4(;F5?iE6Kr!)~Ye-%_^Fm-=!^HtTi8VaQ9jv+#+8J zv$uG`E}`_)>h~N=4Etcu!SAA)YkANwsccUT#7mRs8BR`5L^Un83iJBLXJ$aoS906) zWdXdX%cFsXqL}I4?IJqMJ%F#u2a18;j5X>W`EK=JW@}+U=g&WywKP&>YQPhBvPkv- z7|?ecI?yuz2MPV$uM8edan?y1c_sH|#yl5=pc*`K(sSloN*vJ;Guh8D-Za zZ>K}&fHWf9OVmHf#Jx!*mZM%ppE1~mýhsv+9!EtXQ=Xl0qaS0W7)mLwZ(6M3$ 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≷82jEtaTgWYk9}qDkducOEu*L6KYEEXhngE`kOl}pBw1hHc2ST+|#XuDl z{6lB<-*uKZvjaO>0C;$QvWF5nnK?i;wlQ)t6E`z~n3@4@>3`YP?Y;g;x7{QUp5J^u z?Ch)9ZOY001L&f$8nA*0tv zMs9+H@46jS@Cv-`2&uvrhNx-IofWzXn|f-%MbRrt7g( zOfHNGDRJJ68C*{_9=FO4?vjO8GAfRS)lRnD=R zUx5#eXdzIUL{Y|}<0PJ+lIDLoWeMk8-O`8cG^nSd;HSqpr1N15IWY>HBnytEuztYS z#brHmf=&5R(tY=x#QS41+WdJ5HxD=liqW-|a{AeZLQpv- z1dbGyazK(p&bUPV1eTP(5og-$kwn;O2_$BwZ2`y%a#(5WRbeAw?i;j%%Gi(VACgYq z%2w7`w$4wMHXV-SzY2;fNaF8%A8Pd>-?>@ilGxdivwJ;vBu|H1YY@&=ebV`6Hf~U< zc1F^xo`-a%ZSS}zHIAU3Q67SllXi1t?8s}t0l7Y4RWhFM`Jb{H$L~@6pUm|mNvJs+ zJKZI-+m4@+Eo$UwcAFah=NzUE(Xg|;i+J|i90AQ7ce&5f!O=;~!pPxQl9D(2dCkVj z`2$7E($vYq5t{986V7jccfEk$A-C=S3n&{W5Wo%RrV*O(9Kb-%<`iCc!-!0F(1Nch`UTBWxW@7_zadF?Bx!E}G z+FT$Ifcu{jxw$z2>^xj|VsmrdVsHR>IoN+t{DbwA2EYwsgDT4P8&BxE0af;=V7J5l z8uC^+sA4~q<^kOf@S7>_6ofM3;NZApczgb7o?l8rd;g*T*}gUD?ePEMb$9iX4?7R< z4<0`-ZYBA}>*u`Ojl#!8vjNc~rH~DUL z{nhi&#NKMc$@LTKhj0DDxpf>UgafJx=Pw<8AL$nF&JXY2?r8tv^Nas~Ise4D?fK!? zf3~1kKQVr?{Y~kA*#8Fm&AdMe*r9U${QF68=b%vS?-1DUDuX+OfB4b1XK|B0nvklR`R5l6ou zcgFk^^fQ#iAkNS*hwi!lbey}u`pf!vQTIcEe`+InOFQcyAW?{eDRk4LdzagOGFLS- zfu;)(bOGbyhG>}2uEbPA``7T_4lwKkxPL>e6J8nPZz71myRhi!*&`SWF ztr2vC1>rL`v#^2gRv`}NAYmIDI~x`-#D)GB@~t|5s>J?Zf*sMR4%FheeQdYG{u&ZWa4Q}=^z9bT4*W+1sQJaATUG#_I3Ki=2gu3B z4gzw5K-|ngZdxFa7HZu;&En=@W{!b+TcbeV|9JtpI60vU$8FEuIJ{gSsQGSR0O<7I zHK1>7(8#%c{RhU*`8SN6jhBb*U$pF;9RGsx0C}OF_d7r60u2N~v*PbCs8UdE{(?dO zpTPDP9+Z|12we>RpoQXb{tL#-%>i9rf5(H;a`Hf(=r0&M@89}@c)7X%mW2(-0WCrQ zpoL1p34|_bC<5Dy5-@^9K(@BAAEmH98*v$JvkYkqHG&^^H)vOr;M zJb&4WosEZ$^Iv$p9Gw5c$=<5tv@?CKvR^N0|x3{F=FWf z{XY^kLZDfVRTTn(R#nhpepNa$cIFW1#^yJGIJB@45Eql=<>cWN1&M*UIM_wm#X#KL xKyhgPU}pn~Ns6&UWA;~a|35(fRi-&Q896xp2wHY_5QrVR_oSwlP?E$z{aII}m~dcXxMpdz-#})8lsc z_kMhjG1j1Jt-0oss=fEw)JaqnpkZ(zo`2?}>-fWLUqrD2<7Ytt#Lmjj%+<@$%-GS< z*2>h_)yl!1&E5VjtAnwNl?$tbqnZ6%2U9mYGkaH77e{9^qY(kLij}FWo3okApD+X;94GXet=K$K0a%}ia@%p7fv zP0gP3abf$9ikyS}K;O^twI;4;CNsr4G?pwBw{No;RQqY7vWO1jdNdRq)-($4*HN>o zn}mUPhqU9CHKCuz-`MCfWp&Mp3g`$vNEAYob{ms?&R24CHR^gZLMU?{kG3jm(mkS6 zbMo;n@#@kHv~E{_XKy~!zi@Wyyd>P_GI*5jvE=7ZIRuEz!9sC(UB>dp~u8$N210TyL)+2{d$ji zMyZ1>KH9LWyZeha=pH9P8zN`lc6YYHRMY2fMQpD1$AI0{(X2v^p=pGn@lx!*ApG~z zRiw`*OnOYVRf~~Mmlcg60rs{bnf|r`q8wIzqt3x8A={xBuO>Rb%U$nZ56~9NZ4#e4 zR}Apk`RE>8%xsvk4B!VaoRS((o4plDY?Mp%}sEvt-zTE>4I@?rmS z$mv`A4I&)e_&xUG56`Fh(oge-=C+A?ck8#lfh)IdElIcO?Sx6XSn0E!QQt(#SIA=F z0q1eF13ce7YkvreLjT#(4!I`AkH-zwZuwF3o21r(r`}{|#o7$^@br6dS{NKi2y!z; z_+yvhecmErMQkDXht|!@d9q%3&lT1{asFy5BX0P$71;o8TZnb2tVvcQFN_j=ld+ti zYLPl@#yjoqcmpQk?Pv&rkOLT@{Gk+34+KZ-hc8T3v&>5lQdy}Hq~_H?-;z}e6=PN0 zBRuuka&Y4HgreeXx=$%n7d(3cDoFZ2c%!s3etR?P6fKp*_-4k*PJVREX(MLC@I?Fy zaonFo?xKIRI(gHaRDgOJb3lPGNat>?ohq?>WE3BbG!`8_*IiVwbGG8kd!>Y8++J)M zO464^m|;4(=w=9UtBb}l;(A4_*!XzrX-?(SY-%%2S|~#M@LF+3W5_CN5?WCY&HXCq z=Sb+b`n%gfP>DkRksHyT({Q$zcFcY=T$9%F+jo8S6SuUkGoT#pO@d655s;Z0ow+}M z>|=T=b(`$^k`!Zdv!YyA>>ydk0aLUWCz~9(vIX-zHW|{zi;Yt(nL}4`6f^Wxinl5` zN`4#Z>}i}{&MrkCdMv7M-VY~b8j|-1=%+u~FlUx;>}ipSeda4~PO7}I>Lu=zN;CM* z=`j8Nt==Mk+#Eb=Hs3FTWT*$|ABoE<^>h35Y^Mkv~+h?`+J=;G+ zI9ovs@nNoyVQ>}`Uy+IvhP+%KB|r6$EIO223n}{Q+P0khDevPJ&R0E5#>Ue-x>S3i z+Se${+t&AtsTL1hLa(Qo8nDk%HC~5ComQzAN0Gx@uHIoXPk7GjYOSWqC67zIkK6Zk z$5sAFonmIuloZo9xKK~3H-N2tkrAsQ+W&^pr-yfc1mXD92kx9No`| zIR@3mc-FaSM6SC6pWWESD1G^cGj+RbE zlGQ1h)zvce*4r<{%;6IAcb^gX1Xm3EW%^{Xj-1kZVs#dP#QQT7X^7my7dpsy(cXmI zuaeM}iEmTxYdFDjc8lWv7!|*TYww0jDX3%U_&^m9KuZjhm`|Bx`a*;#A8I zzv-zz`1&5D&!W84@UxMdPUB!p@Wj>EeF0vYotBWZ`8WRXTj7ec3+<7}p7&@bhh2Nz2ZTlZ^NqK4L636Sdf0MP(`B3)6(zGhX=3 z6YP%JhC(4t2zH&f^0RDvkRJ(%OUjOR5;6K+WOcIZoH{JJMO(J)v>wXZ6^GVCz8Q$# zeeXWP@%8c_5{NFr1zM5H1;o;mU9kgaQd-65Mser~$1 z2I(8gVAzSdvdyW^37u-)Aq(&8exJ=gTFREfK1ze|K%mVKSjN54H=gqKRWC~McxWOT za8DODF3>e(G%irUxaBythrBl})cX()o&usc*3rhef-Sm_5UCb!)ex!lgNNf2-Jm>6 z8Ll>|j=g~(yWGYF&?18V$^<);Y4QX+SfTzg^OS)Oyk>N~FTCR!G9Y#T4Z#}dz(pfG z%0K&7-W=?%NU#$)6t{#e-FWdCOS%z;!%v#Fh1mUMRqFUcd^Ji!B!)d@oe3`X^DkpV z3cd~bR)!_kb!V>V&MUMUHY{Ie20ia_GRdqt6)>!c*Ya&c^Ff#s^TtI$Myzb~PQ ztS`5_dw8I(t<@)SP9xcXQf>aF)Nh#Jsm#iX1Z7`2pyMO|?6`9>#G>Udu7*F-1k?V# z7oR{>qCmHJXcS$0BiUYH((ZW;{%u+9%-NISDK>uYtTTqx&Gbe_kIB*8vMG6V(;%{H zTfQvGWwMw3O1+j{*O5%dy$*4Y8k+;lvu9)d)iNaRb3jAR78mFRXuw>9eQ0V~Sz|Vb zQipyrK5egui7He>t_u@t=*6+ux0{!QSzoPTsrtj&8~q}qF@}@fU+l}hW%GI&z18>g zJmsY0E48VYQ)%d`KA)7qxK}^2cSxXbejulTR?1suHv9e}&ndd&;Ox;@qZUJ>8>reQ zvA9L1`W7}}l#RBteEQZfIex_=SB+F}*eJiX4rwmrdks=@Tj z(^w;^qPhN~!pWkLl!5If;Neer(@OYX50UWI7Bj$Om4nSv;LXl_nZ8`tNgit5QU|GM zHz%>R4Gf3*n(}I%d=f$1&r`Cp{)j{(;ljOWTx}%Z6VUvlh(VcXe7A;}1^*-$0WpHK zgfSJn5IaUpEWt!Q7fu`+tj~|??KiMKHuhX(kElX#C0#SeiudU+OR&>}molJLQqUbC z!X3)+q4v919!kz29>dZC!g|hdKYM12|02~xI3A@8to;cg>7`RXZnX186Z>u;)M$mZ#iC_|dT#-@?ZM$&`S-aVTFnp@n zY#zb#fj{)l$7zq3Ibh?qa7|cnUPVyiNUJvn@1+Ln2bDmbrY&c45}u1 z=Hr6NFYC9{ccp~|50%+db?sTt*PRpbd<~lh`zQ-Sma;Y|IG9_}Y3|(?i|B@mC;6l( z4)52PcC!4M6!+n_jFBV-Zc}jubJic50nBe;3O-j@N`d3ceUVaHf@tEtc(2(`;uQY$ zua&J1qT`0fy$Ec_vy=bf{0e8#0r$*N;pTvn%>FsCtqW)`CE(uGvfKUL_sdsM6J(;( zLp{2us7PEFKKRkGd~UxBR*Wl(`w_+HARq!0uO(pZD}hyH{9AN z9gkho>)QG~Ra~kIqWMQEvhE+uZXTR_<*tHF*n&KkS$*tDk+@ z*R`hY&h9XLj0-?c8_rwZL@;!&h73+rRm#u!86_5y&Z#8$rr@JrU#Qz+Lhsrr*{tZ7$R}tO z1$YFt5n|?86bJ~VP~iRMk9Vd2AFnkW2l~j4xEr`4UJIHAe4!pNP^Yq}sXTQQ4o;<_a)NU3-P;iKKJWAdd0YjQ39 z=5)$;JRV*6JX}nTyzV{fTwE*;{XIOM7F-TrEM-4tzyj)@au~CnxX#vJ7XrNo%f46e>JaW4aE@a z-MkyB&RpZ^{qHjej!lAd<9#G(UVfaBN;gqj0TxBdg~A@!snHmr6~A8Jd&9F_Q#TLm{ z6YvbkhMKdAomGbxIZqNE_~@qi6IWfJt3kXb^Lt@NXZO36tQEWMWhB$BG|Z`rG*{88VJ*ZlKvVSTtw8x%!-@vuHGjw zqc8k2`ZK?RJAXM3StU@?O~Z;$z%w9^i+W}5l(;`SATUEAg$sM4wQo0-ReM%TS3gHz zdrm7$xT?`OlG_)~egN!#axhm*Q{7Z}Sh7f5i6L{Pa(zj^5S!7d(xmul=$g^hX8ZdS zB+kf}e9tPi?BhC*pS}CEV}*O-P9quTLk~&l(spw3+*mGhpx~kIF`Au|*DAIMqB~kk zOa?6ZtHoRXHimM@`L**hM3KGo4%?>dt?WjF4`L~I;z*LhR~s`)c+z$a06}sC8`<0S z*7}~FmXe2hw!*yPt;cnQ|GGBJuQiP&tt8#SFe1F?JhqR84zLW?2?`OtEqXZcN+OlE z!-)VmaJB^}bH&KD?hkMfREFPGJ?&%lqUG}L}v7LG?%Yu^F^O8#g8w00;ixj`&y{(pgkB!(&kJD-kG6BHo zjeG?HELbpD7-7?+RRX7);Y#T10KrBf%au75N1D$eRh)R=5{d?X=*`Wh2qoQ9@NkMr zt0K;_s&7}Zh#d|)ailiVH1WEL5!q zB-v-HnAve@$g0Hj&)_2Hb$?ZS4RRx0+-21-UGy4%G6^OB%wXOBii9)!2G$ejI|rjC zF>JrINh(y$!|TGPuTWvnL~gb*;*dhZ_ zlD^+U(L5WFx;z=B|1{XKrtCcHw^U$fb#y|Z*yys8pmecj&hU7$a(eLinUp_;U*a6o ziRCDJ+b%&rB3%0zA+v*MY_X4DF2gP3gYSY;7$mPC0}yTDF1u$AS(iz{f`I|Btnda& z($z561m!`X&CVvYBUsO=;kK3GffFA2|9Jt0jr{$C@Do?K#!0)ra|eFRXg>df4>2oNeP;Xy3pCh$B-$ zZAqJHv}3ed7swILT4=p~_rSVxQW8|;Kv9@Qn0q*WbS<8QZMbE!Ws>EvkO*_=%%CZ# z_;(56j+#(8eW@41aM=3rdPpCUeMz-onMuXDLDvY!=F4(M!j6k!lCj_g@CKMdBJvG% zX5aug0AdP~4J3Vzacs3r1%AI0fx1O>jdP6tgGM9>6RRv1tZ&8He_dxw7rxC?3w=*D z*PBw$2f1V-2=>mYJ zNIF1iLO!AR!h1q!rPAGOaFh!M_VQcTLGA?+2Zf)~1i->3m4yfLLAAjs!4INg;6fV0 zpuw2qVtEBI1?8-z^F#R(^EV*O!`nk2!qCFD6EBM|>nuYqJ1-ZTFhegIGB51J2aTzj z1#a>$M}sTC-_(vl^;rBsKkv%3q7!^-p)GnLN9SQ4PRrQYh z#0mb$p?Q69RH%OGivQJp!a6E{@0G7$8~)3&!JhiH^ecKLGddM0g1S@Eg&20CKEea@ zY&X&TA)&t39iwcGwK!9Q7Xl1C^Mnw$^=Ibdre3ko=@tt^+gwAH4g%>G699z=)WR?f ze^-50Q{Rj+cPbr!px5L>jK~@DU15mVn^?g(uQ{2XFl19h5=Pt?0Dw>6AAl4uXSsoN zm~iC}!&(L#5S!XpT(|vI`48yq>2<_S8JNNWZVyqE>uU) z!2+O)c)|Y-kt2B4s^PZK;=}ior6BH!1Uo$D^zJvE6oaw9OMg^aFzc0Mcm|@tF9Y;x=4R;v8M`-8Kl3 zFfYJ&4gX;cxosQNEIKK=2(VU60q`n-g!fkJseS>NkN{R1K*9L{z;Q-J{qaXddBe}+ zyIe;^wKR<%sF?K6;{ysKA1mk-&(T|#25!4MxJN|0iXtBqQ)zFzo4(`)+*4|tqkq@> z7BF5I^~k{iOj^+aoCQN(0Ld%N2UDM@ZIDM%#G`65<*kf1Fp*G`e2P%Ud{}en%?sF9 z#Cr(ohN*aTa;V$})lmbrCo`%(o>ET)%eW8u;hN{@b$}t6Y(!M#BI5A`_^cWKCVm?P zFPs<9yb4ql4k+vZE7@tpV?`GgKnhs-yd|H~4>2F8wgH)=x(y!!3~BZwqH*<7PyEYx z52i)B=kWu8;?N0LicXf>ZlStukS1WIcZ+`Hm}j|_aR3yofIPWJK03|Q+;$7H0xRAm z`NY4D^a|1$q{?a*KseWVaLVxfa`jeeR zcHtwq$z&!=0fD_*7W%6Wi%@^Se!DF+s}Ga{u6Qy-t(eQGt~vqNPwn{Bb26b@|3O)t zXIRYek@iDr0bj73)x^;GZot#sZXQc=WfM0Q1PDUJf(a z$J~|P)gbT}OD}>ZA|=fUURJGfEyD%&HeR1iyNip7Sf)ys)QNG{lCNb31~v7yp9;Sl z8?)A}8;1B}&mDHSKm9~g$2?R{2!;@Nk*_Xke=R^-l!KDzmT{db9QlTlxNddz2(P@# zoqVL)Ht!A&d%=-n#7$zvoIoT$|1~5s?0pd;zY*WEk(RF5xQZ+{gREw{XXFIZHQn^E}^Ui)4Y@Jin9<&*6H`s8+#R5jVdCjiWzraN?^f z;=R;Kb3<*-)VffM8;cn0;bZDWKI87A?mj|$oJ)56rln+Rs8xcGJ8=yYXpXdB@;~qH zJJX1ysFJZ%_z&{mDed#gF2}Vd4UhX5Y&sm$Me;mvwk$1RLp~wY06Uv%{Od`&pm-VG z3-Kv(1d+zV+Qu$^F~3faxVDZvh*^>72a%r5awo{kd-lb>Yj|fzqeJHsJ14C-`Ai;f z!Z*DThkHCxA+0;MUY`?TImX;DRQe3|U3V0ql=7Jl$r!Wa-?2zp7J6dPo)7C{cAL_> zuc>cLp)86iw_pfgOdXR&EZ#iZhE5~r-}}~`2AaC6_JKIv3n6OI8}^NhohISq+dBz zW+pFPNX(j)N?w-qk7`A;mv)U9k(gOR5;mst+2dM@!q?cS?C4Np#XrJ)YH z(65g+RM8G*b9(JoTe;ZS41Uzrcex!Woc9iy^lW*I*&Q-?%`Xlk5WKM4I1yK)J5k72 z_u}o{6oV_6_qwlyi?4mc-l_yR-xaOB zo1@RYG;&&y#!^({6|XkqHpi;Nc*=ZxrZN{Kf%>77rSplFfvK8Q%W|07p5l?Ow{T5L zSK5@;3HhW=P99nE_n3EM2CbjV@(=HVb^ejmk#xCv@q34)*oVJB^3Hbfn?&4^x)D!` zl^k>KMzt@K>%*>>sDJwX#Y(1nnViA-ClkM=Ap3tq|G0D!%;)%z!@{~)ogA8d5<|X; zWZJVXom(y@#9;4xD_)qKPR7aY>;x*y0E^;PR2*YgjU9h7X^dx3=O+Q7tN{7`lS07oEi#K6J6uIVoue#w1Jfru<_w1U| zU2lW)e}ZDVK9aFfvt+f>bvt=ytet-g#7RClm%CX-mo`<3$4^RC3J@7`^7fJ`N~2rY zS^2aYf2nZ+G7bfb^9#upfc2ABlbZy+Ye^8pm2SEXyD zOPcSS?j9Tu?xJu|&a?0s(|xPmLa(1jYUij)U!6*scblEE%Zu-bAZ!($bL@>E#ztu8owPyT^&>CBnL!Yi%;?FcK$ zy>;uAw}_4sfi%0kl^i0p;baE3q|Y`R1_Z^_1mr)>1T@l`YW=1|f<+NzXsK9VUe)dw${lkl)56Wx2 zwRunv&+(azpD9O}RPH@2C7^Hi9oAN|{F5#Fg_4heDqchZz~d}uaZ~UoPx0YUHmmY` z*k9^tu+Crn8j2g3m$8y+DUMCPU%KWot7JDaCnz5DnGTpirX^IP23ft>-?7YN#!%G8 zTi$3JbT%@jSjKzCOB){SNNSURsALBBWo2y)b%}#nsO=<-^df}y6C+5!$?D@FsNFd` zxM+iEp7k~=J_4A6T8Yp2nXHY2Kk>PRd_IkwBYL@~?3H4=HYK6p+` zxFLmV%|&VZN1|&Y$3&@W(rf%>b@0wKpXn3T<&p`TG~*rWvHkJ^_@jsjra-@pGjMy8RnYRk--s( z(u2?u*%8)poI_F+`a84%BFY7}@S98Q77<`Jc);d;w9j_MaiW!{=<5Jx0aJLIEFm=$ zUQ?EDLFAEwqk|lvuAr8nvoqo6APnIS-&D?DP%Y>4q7l=3GC87`djwTC6^ISq2|D6C z4w6^gm4LIr$?@GB#}v!R;FrcDTWMg?V+i5&ZH{HNW$9%Z@DijAa^xYjh$j6NKB>4X z-Ek|0^HMWs6>dGACvklHk6hyNnJ(pbat|{t!FMt}7jQV3rSJyG z&G6TXi9IbCBM%VA(90oU!u+@20^HwE*S+Poh1L_*ITyW!>4Z2&T27Y7!>oKD?w0kc zi>>Oy5=NWneKW|$l*->+q;>d2aGn<)sF@l;+6uLZuBRpM%68nMR?$7qnb#Dk3>kn* zLYjEjL`8WuSEP6NW$Zy-m{ziQE!$MY)R}F{_*2B=&9959ubse$HOcq~ zjl3mgCF)z@;{ogq5oJNdT1XN1q-(Eh{>ZAiKnF-Jn?k zX9>=@cfn2i+v8jvuS=)ZE}3)}FZ9+vJiwxzbmzL>m23xc)r2+bXFW=?y}((B+oyg! zm%ut~>gnsk;DCtY5E;^+2vd5107+(h2q%NQvfa2ay$r(cSWiU;lI7q{aU11r{86%Q z_xv!t7B61;uX{}nNUnR~0t`N|W6g3ZF()p|KP61J$CXcCu6s>!-6U>l^I<1$Y1AK4 zIzZDNp5+pl;x)=2q!S2SocaEPL^njHQvDMFl_*il>(1dXq7(3mTlz!mGT9ajuOzcI zS6ZW+8xc=2hP2|pk8ZsOICTHU;eVZY)pBHCYrh~L6gGaMIR<@9e*_YY7B2uLpKiygfem6`(=1l1$7dk28KBfNPrX6{0L0A3UQ>XV{j*mu z;Drq+vjN&fKr-aD49uG{Hd=Inyek3Y5}>?KC~uyiF!sXd{3%M{js);hP%|IZ0@|Z@ zp&JXtK#%}gtwpw={!@Q4V4@66NC7-wc<6>cV7=-9pv-C7i~fM}>1*hQS#65;;sg+? z6=d!O&j7Tm3g9tE?DHuBl9d9I2#b{iu9ju(gUtQ)-!<60(7y@C6#7uZ1@iXjeh7q>GR~o(aJ(OR4f;=g-I4$Z1t^p3=OU8MR#BPb7e5uV0{9|MSo;|(p zDK~eoW&VDhcw(`4C-K1&AIQCAqj5m_cuMlmYw7$~cEUp!IQwwoykd!Z+U!we;_usB zsKuPgvW=sh?(`z{fwrCX?CMLL(2+N-=&8`2Ipt|H(v|-D2^C@4?K&T<;DJo_3Wb;H z@_WteWyBv%*}d^XLyZ{`LS-F(_+Ix4={y;yg+}^?_ne!LubyH5%zJNQXyq|QT?!pK zV1pZFIwq=ZMUBc5c=-;dQH;oN7@oP_J{7EY>A07i2g__ai zoh!SlDa8nLf1Ei=$m5U5sOShBYG0nHZMYcyvizyXFX_df-dpO07Gr6f$%L`HHPBI@ z#{5vHd1_=bF%Dl{JYB{lU^KaXNH$p)p)OH@mq zG#!of(#zUB?fvlrWn|SzhVzIO;S$8AQq(8)q4ZrTEZ|o7 zD7ypP=%_wxuJ}LhEvC>uHl_(z?`@JKG95TmEmu6+Y$f=b2*mZH^0xTeq18ro zaXkv9?0BZO;0GGl!LgAS&>6RSK15TWzk5ldxH`Xsgw_vV%f}#eh~Ib|u}U3Gd10v* zx;a;l9@>d8IMneIzg>i{T?7OcQD@Q4YiEN8ZHLJ5(H`Dat@9;hcdkGD(DS;AC+(kC+f6*el!Zag*+S8;>}ifp?ZM_ zt2?gzsm2=fEHd5zmn9h2H=5U@^v1%O7CuZLxJ*weiQ2F(zv%_wX*np zBSk2;I1Ypnb(5#jbx}5R;gjEU&GEPR4~Rr-iGh<}-)a7eOrqY^rmC--ud?~gW~*mE z>>iEBm1J|9!8!PqZJfN>Ynk>|#vraV9_HA0pE_{2Y}$OYw}A$40D2Rp-rCXXAhMFY z*K;uF%mHg$Z}!!00`W2I4kQ2r8jV7gX^_#f{c7n?#&%G+UNnXegKKAYpamrmJMc6>j* zmGS2JNRCJx4hC^Munm>;!sCV8!`ugXqLe6~3Zz%1999OE@9?vvKUh*l?{N#E$khcM zKcD^1OW%W%ysZ8jH>s~YA2rvK6Q;UcFsa^zgZlLTiK{2wRHb(l}#Z%z6BIGeVodIWC#Tj1xDW+&p40NKjq<=@ew zp?~uv$x#3HE#QIti?g@2=?8Ge{mdh`kEg~=y+o3vz$soQ0i2rKNm|H?D>F_uX_ggs zdG=)>?M<)YvFcY=e`)>B;b-rsuJ5h5kVi9pEbeSWbb!B9>tIvFJM~R>pl%v(wR3>d zwOi|3fwYBB6Y8af0PacIWk`blI}CmJ6ML%b*rCTjF_#dVsTy}ZSK5b3>h%y+-O(Sh zl4hgG$sY^wYn8n}ZOJ_~3ZAwXv+S%$=W*_oGz!Y01Nz~!0={!`5rP^T;d$BDv=5$9 z$>({Oa%`tUJ407ezx6E@Wy$BOfc{cw#|%2*`A{334=Y7(DQlCurBm)!5K6Huxs?a| z#%MhC+>uH(<2>mV-OlF&AMY<)a_DcVKrypPWcJjI$^H(h$>)^-(V5VW!aK^}`b7bN zrRVa;o$OGuKRM{XJZY{(GhvDG1>Mn@u=j)E%ika0eymZ#@yx#4r7Q?j)jfPJ@{kR+ zcEdkZ@G5?`;p*{m{*v=A)05t8>Rm_j#^T+a>((1`mY7**5Riw9p~hnB-Eyz<(ZfRH zCS$>8?X9yf&w1Y|n$9^Jx>nBn1dJVqdc$N5(UMX<2JjLh%QYp~vd?OkSt82SCl(V^ zTw5&1z)&D@dDaDAibA(MF@SHDO>m0%rF2IZr1MyJe6_WdbidaswO&(;LPt}*_xaxF z^wqI?W6_R!*EvTaXKFOcVy9Ce5)&2{#aD~&=BiWI5Cf7@0`fbj^Vhg)Rj?hkHABM4 z(l1vhL8RmAW2H=}L?YPwufM)jj!Hw;B^G}cvlAV3HI~U2Gwzgz{7&ie_1B+Xj+!8* zG16G5Fic|Ddh5Y2d1L=^02j5q7;BG{4l;Unz;gWx2OK;UnV>SHBYU@ySbF$-Vx#R1v z(%iZ6>0_TsZ6xQsM1Wn5U8m> zb+z0#m0S2)M8|_GJ z%*etwswSaehb7<6WI3bIN3J6<*1Cm}icMDJmx@J3d#~KlhSjQgjbt2fx+2x_A~b+f zPoX}|k#7Zr*j71(@Aku86Z4Sno@NEI1A1Xu9qH#P#tL^wekc=yDBXN}z%W0eMqg5S zy8np>jV<*n+UD?b^c9=X2SlF>9Lg+5ffb_;&{b#uwNr;)hcZGq=BDZpiISFby0RzT z3POipZ!wA}U6?QJH(F-e=D70EV`HFRs!+4gtqUxZ*H}oleTYZ9Vf?MHw0dzj#}P{q z=&<7GnzPG49M^>=BZ!jU({+4v*_lUVqxtyKlX!)>Llkq6_SLoo2Q7&Nf7^+76K#KwTO_|f(1Tg$#2B1&;}x+j6E#`$`UzHus(6AUe$t9r^I!;SK_V_=4FWxxtqIZ` zbCu$Fw#Gi0uc`C;>qi|#_XWn9@sp)7mbZkM9h-t>JN*u2*NqoskN0;Hd&WeigDq(C z6S}4H6Q=BdE6WfR=9G738AHq^9RgyK3E@+Yfy)sgjXg2cSLJzNXQiieX%(Ha5fkUQ#4XQ(Z_+_X?58G&yd%fQok~LQ%jZ zIj-QpVTS;X}Gd_{=7jvCPsdp}m%9F95vepMwiOdXaf=6-Jwp;6 zheFNHN1BAj9J{gVvFjn8$xEi63`gAh_I8>UEo0l4cD{m5d=zwkOlz&CWXf!kW(-@&IrMs9w0DBO-@`CbFM$z+k3%(Ft zk)Cn-tZcEGa=q})LC(6!xeN5#_4ghUGFx(GSDvUpO0O$q^S9)*r+IaVFK!`hT98q6 zSaj8|eyIq5_B!@1&I^>>ZaPjwkd_=GN`e6d-~h$^jln~%5Th*7H0Hwh>_^z(?UXNA z$W64xBK!IK6GhhZ9{oN73K5#U99=iQx=ezhgSVP~e@FCzqKFe>O0M6126A)~N#FWg zk%hh#21Tsx(_(0a_@TqvPmSnZe~S^#l*a)1qH9_ft<3pT)g$RY+VGt|ZQ-`q^?SZxBdGW$M|Pi|J4N7OWoCFc&9p;Ftp zXk+)z>P^a5@}|`!e;e4~1vG+yW-+^_;T|Wcrr|%n(SYxX89<_SgdEqC6^=ZIt zfZ4{Sj!rzYNdR+p{w8vGGfqB~x6X#LxbVv2BNyb3ewUgK7Dvo z#8^D@4jX%R-Ww2SKo)7M8$b${E{3VYn}-R*n~(AUq1-bF(e6+H5N|-t0TITUoe!t3 zZcu&(rwnhBB@b`Hy|Q$H&m7)-127Nrd>YWV1)N7id9U07<^zD)*Wla0f+?UJ0+hIb z@&Qn?Wejhgx&sC)APg~}WC4_W096`b807mjkYz2NNzDCeKoOvf1?Ixkp9cI?0k9yD z2QMJXG}R5SbS5`hlZH1Zf(|(G0R)><&KylQXE{P0mW*I$B_hj01BGV2gwR;$>@UXCDtJp4b82=Y^93*60Zc^3VW8 zv<4=$fJ6h4>3|eBffSwFjP1SytJ&veWOrx`)UcIN)9^81a`RD2Jkw@EdD?Dr^YFQT zV1u>Nd>YvB@0rybJs1QrKabDw;` z`;NiJWe7;(v>rS=PXerzCQyw9Ty;YLPcE=Y#WL^8A?*(Fpl9c60Ob@Bu(gN*Apknh zl{>(~ojX8kE0#Go2lUiPERzWZ=(9o~@6`_&OBZw+z}p@QSZr+Ix_)bSxP+BE@IVeZ zJFoma5P+Q3`w_@nR|N2j0sKOMI0Lc+#7P6<_zwX*GQgycaCmdk0^kY-kP3j}h|q$4 zrm~Q_h#n#8kEhOmA3DGT=#QbJvHZSF z*Dk+qp~plHnLPiu$q)G zYijgf@}cMAGB$WgVLXxlKvjg`Z2aLo+Y3m?DEq6^5=Xql4UX)1Jadf$q%fnZ%xX3A zKb|COnO?foyE2Ql4u>1*&yjTh37M;wqurG;pQ*ER;CW_A_GKUXTT!i$ODuu6i9~nr z?z&7#sq-9`1?O@-#LJef^4!Z5 ztexM4*^8eSNSH-vUnrtRXA2#j8jx4U3?5A5l`i|SM`?U7u^JiFL zvpW8tmM&$u_ob6Q6&Km$A|IGLZ(6pS5GnLVBqA!tBzpF;4kB7vCQD+q?c^@sj_>^O z$El9uDy5Ik$=?8< zoXP^8ua8IIqtn^b6)|4Xigg#^2cg96?w1&6AMjX9m2+Ah^&YY8E2Eghd7Cp+udEtk z&=zxSS@SoD1b%@mwdKsw#wvN+1t9cU@S;9X@|X$u5z(FQFy{FV@IMgl>d70KAYlol zCCurnCukK7EXjDk`wDC-;n%<_3csH49KRSwhV|Ztc0GmUXlk_&8hZ&H43gbnsmire z*s1Tf%#fok8iV8=bLP`~ytZ?(2V9-7{eG1zQB8Pzyvw1|9!y|&r7aU( z!FYmy{+lfig3G!!E(8SX;Q#G!w#5JU|Lx!)3)%7g1a7xO5-OH~90D9X+5|Yo@-aoQz|i!r~^~tK9_LNOVgkYUxF0RSEaBEUfK{ z&`UIItR^br)~d(OKOXHb_%t}0I-Kt}aOL#bJ}zYoFL^xr3_7&gy1VrLl2Yu`8KPHn zi(-|s4z6UAI&|x>{+u23agjoP0q&Ct!sV4Ax-Rad{TjvI4k9g8iG5&cZ@I=t9D1cZ z&-QP5_aYcAnD(i=Yb1_qh4ZnGkN!foiQahjufJ$)FGP(u6kncvmK9*(Y!c8SjkEZ7i+E z2omVY*;^ukLeDsdJ1TYc9;^4Q$js5Rza38nxhkvW6Z4`K5wVmqeHgMAa)cU?TO3`t zN$FVm9=s4wR5ZSLChOu#Abg!CD6)1Q!c1cHT_L6Tg9IMKn~7i9^FjEY1lSs6RC~rS zd^X1{Gd!vDd6hMttXKgGj3vExW;~_}`;;ejZ|4Y{qkm0eHObzyFnpn`E4F2+i0)iU zDq-qnVbuKtvYzk!8?r+tWv@D;V}c*qM&l4q{u@5=<1cs(=Wn<+$NS$=FHtFe!+(BW z$4e=taZkL~tt<-t4QcgsS&mNi^;YmlVXd*LO`VqF*ELy`Trdu=Ymb$R%<$^1z<0mA zxV&(!b~Dp-Kd&~^oo)1Ca1zzaG-_MDZ+)Qgvgw_RuG?h9`J(&YD@|nqg^#nLIjh0W zlir$(S~=vv>DX^0>*Uy_%-YH@BshElc1l3jyOq8{qNarBChMe9c#1{|#-IOvVaTs{ zfx6Tw2=glD81>^)%N0UUX?P?-D^!*O#kL6bQfiUlJ}MNbgU2c{pLWb``l6lOO5kfH zax_pgNFie3prE*5s^d<}4`__UQ1*$hDIFbBUK*7~gAcr5CIKF*uU5Z#N+#ff+#ajI zjlopITyei6e-v3HFJFuY-v(2aAIbhEI^LHI5i5!YrwT!3!OAR8Gb_TUphDKkGGZ2g zu*InFj|ZCp>RHpj@=RiY3qoauQr^IvR&j(e$dn&@X(jwazY`teCW5KG zHbk~%;D^HhDEGT$B^_&w`i>{K6I_62rRsMDV!FvMjY^`yZKnvfEc{SZnZGUI%mKI{ zrU#24CLrBLTyE0WyfE{WUnFx|f08?9%DSlHIoj?DNOKshaE{t8fX9 z(|5ULt4$p&>KnGXm)07YYbHEB{MZ`0a6scoe|n(mZPQ1+MwxxT?nZ04x3Wj>eRCu7 z;{&`|@D3rrC*=~UbQf#oIWD{ZgkwIzl-H=_?R~=|O$5pz4SeD!+B_=f5qi$c9@%`h z;1k1m&M2oWI>myaoIS(fo@E1P3_@#40m>ub-%95*A3&~+$q@gnA55kl1){UPeCx>e zA_9d6lQa<&OJTal?1vV!G!Djge3`gl@!vWf<1!Sa33*`byy({f>{gT z?|3eVJ5eOydjKM3h)?g!x5bg$5zPTmgv1s*5=Oq4iP9Y3&k7>XZ(JS}FbL-r&?qGo zC1naeNOpP0aW>+|EOGQ&j<(1-VeFi3-KLRq)~At3zWWdn*TV)3?mhkP{-|-6Wy($=Xm33&+29r@}0CTqJQ0n9<`e>+P5z3B0EC=veiWd z6T|gBw|N{HXjG%wzn+eVqm7x8;GA#sDMz_N{Sy_;Xt6%p1xnfSI8Q2_SGj@%BI`mHg^*BugcdPQjO9Bo2 zJlxx6&XP#}^A&sF8qpsm8XiQ1Bpa{Kvz6UfW4vuq@-bIpoHXYRHvZAiYC9wX-W~;a z=n&*r<+qj?m{j7kie``%)3wxU_RA_~T0 zKZrD7-P+pR-dumV*;TN$X3||z=5VaA1Xel?QV#@)@H97}(q z1g*Kg84f<2a)QYHkaI3#qi(L5{JV_{ALcXGzRH=6Z&x>1qkD|2y@h9(9@wVLCOUf< z(k!ss^osSFLwPx!vl_7*2{pfN^b9mq2l-QV+2QQ;<~?#fJSk=np30YW<1>tSdjF=1 z2S2`^wpXGka616W0_iWFT+ zo(AFJYl^*R+&_?2t)wZYnwoc1$~G@@&s~u;H=LVcXG0u0*Ei_i+|Lo}_wMvL}6Wm4=Riq{56aQjbD9+Uy+Hbh*6j; z2FE7l-`n(dX?f1~4gr=5N(RNdk_d>~h)$@oM%tYB5p8#IcZh^VgkMlKvqWlOF`&s9 z@!RQPEzhxOX-VG<@2~*Uf)Huq9r||^+lyy(qs$O-w$40b)F&-^b3zw+=x(~0t*xE} z6@uL2+3Q8~Fr!10^_B(_74@Y~B6pd}#?80aw>CSD92^}MW=|$f?>g4O7s$7ufql;K zS_dnHvK3CmZJCnfcVBIyouHkgIv2YT*L^)h))MrNo5$T#RAaulf5oRo#1QR@UN9RY z9ZHg~gz9xgtu-FzO{To)ie7XaRUAk$iYOoNVESR>sro2drRyl%#D@2@g>|jCh2OT^ zXxz*^yry>1cY@S+&5gcx^RUwVP+_Y(G2-Wo5*hzMi;qu#k~o8 z?c`YcgX*0}1}X%Qm3~!zAM(BUsn8GhC#HD-?kDKC`DWq0&G-%xujLi1BIAN@qJ^wn z`3t0Z^b>lmX$l9!2g#PX(ra#Z1jHYRk@$4pZpHHA8Y!Etty}GZ)8QpJ3Vpb;THwJQ_lk*~^hhyO+ShNIyqYSAO^^(NotT+-`d>_fWGg`ZCz z2b_>WPUNaaLc>xw-D}r_LX^?_{txDvmv>EYPZbFF8tIhm&o( zq7!tnX+RW{6JIbQMio<|i5@!eNozA@6KoN0!Ig*!tPnYeR3$PC6|; zm1jE-Tn{-sDo6PGpE(^F@1mfhHV@y;Ry|A(+}IeE8P{oepHc20a{eusgGtjJY9_i5D` zIPFnV`>j$zDQwxON-jRCi7UA*-&bklE_F4(83;t>4dz-YEfQ{_jXNE&L&-5D$o3N~kV+IDS(fE0O<4r1QWa{BC4E^$z8J+;Xe!jmB(3zZ>!17m~=GDp*btCo&}; zaBd8t;Zogb>9jhRi(TH7VY7ML^{f=I=vzvC)NX)Y)#|j)+l$!K)@jGONw7w?wz)A8 zt3EzD9$~V(w%r~maagV_Xeqi=EYikZwhS);U&4r!%^Q7r8djq{Pv6<+M(3tU7BrPj z+do#xxskv;Dvg&>Z-))Tw|#lWBPQ=!MtADFp_FKg?%&;P9>axuB0=B?A1MnBj!Uo>7}>!XvS6JC9J*E`O&LUk^wi1%m8-{r4iZwu9ml;lAaU!B+ee5R0gP# z&=_|OiE%*L8uqL5^P6E=?0it72W-VxoCz&!*p$4M9ibBX1=dJuShOb{eO%E2p)qrtxP2$5h;tU6TnCNeij3!7TGVJmR7yq)?!-(pD|Pjq)m!2<5bX zRXtTX?*<>ny>*)3@zn3hgH=p`pay%pXp<7Zy^2xWF1a?Q6p>Kh=Se+Rv&_T^&m-u0Zjg{o8;V4AC~V*SN~WZGSF# z4)U30oBNu2s3;)Nd@m%?Wk}rr#N88-sRV1uO69v86s;&7enHG%db*I z1WN~}G$GXEk*$&pqW0pRY_QD*c34ed>l|5p&V%$&l>O5SEb&OQPJL=zN**fS9L%jy z6AtK$Qqx+hj<8)l5 za2+;zzs!CZB=I-X2?&4-@URr8CzTG2l9917D3St-4=Vs3e(+&smmd!&RnzK0a8lyq ze46fR0#SsIPNR!N>anv3b(=VeP-sN2WN+pgkDK$AhD@v-`)gpftBp~7Jnm23^J@L9 z7j;waVtu`7W05N}D3p+be~8vR#*4U6^k9PeX2nF!+8&Fu#8O zxAOSQR^XCG(0oqWaaXmN!*|jl0M$tzhSIa-ktNa2UfCv z0i!MOTxaLFa~>ToYt81`Rf$)JI9u@4n!~eV3gd_BXO01$J=c-13UmF!_HYKiKY7M> zZ{)l(w}?61yxs{<%3{}84$kL{uf7m(!(*M=9GO~k7kF~|e3u%2K0NanRk+?N>r;hL z$@e+n)LTEFNPIEFSD(r_avu7n_(li#EwQ^n${Z)s_p217-#Vx|oBO+m&s~J;!Ja4; zu#x%4JCxbmmmS4I_XXv5SCPM8wc7rcmXWyA?88^DOFnX*^K9a-WW`)-b@Mh%%_tU3WDmg4qTPj&$v*(^B-LD#`zGO8jbn`` zl@9_l$?|C9DG6<4x(5Um^9EFT4llAsYQSD8C)w8sWIJ{Z!_w%S zk+58qZT|XM&04l({!2|Z!gr-cPP+XDlbYFy|*=ZtdL0=B{?XgO? z5utCVF{V+Z>w5YFL$OlI_Aqt+cC|!rM2g4y1?xTthDH_0Wy@r zvIdOVbQumyc1!okP!6sF42T*7cn7AZWXwzNoaof| zIh43`2!WJhJadVY)d@sy3ZYR>>_bKwwUxY{wY#;u9*#RKrIXxMZr1_#EW4YaI%w|9 z>ah@1EiX&c4oZtks>bmcqRU6MQFSmRied%~%?1B_kB<9QThx2-D7zj87<7VCg7q0matOXAs`ZN}S(>Od`if+ic@R}v{pxf$ zs(QaO3U+q-Y5}!%a)h$7y=E*SHShET{)pQy%XzwWg8fsoVZbnZhtQl;D#HeU;ts+0wzZW zVVUtwQzqdHhmH~oJ$T?ITb1w;XqW>u&**{iM3vb4%ILQl+Qdg7iCHoj3E+|7{NFZE zEeO30EMeX#6MM6h{2m57y@W9`he=`az~}+IYLNS_Nimtw`+NprM%4z!bCNk%Bo-B# z7r4w;s}-lRo)KS=M)dxIE1_2)p%TDE1ab^lqTCY?6JPMH#p8Nr12cBixe*RrW}`*& zz?|7!s(KK5t78ebKSk*H)*lk;_1)(xvyc}?h1H_xFC?)dsU7quzo%CK?C zmva^qmHVUdqp?#lWKEN&r|;gao6x*#0V?uv@~m(>R&()MtZHM_Q~IR$^)UrQ5}2jX z%IbavHRhB0wxK{`Z5F?J_oxbRlZdBNb;U@*g)sK|kntw^<8&t$?$?L7zy<4dR#c0C z#f0Dz#cuvMU`F>PrS_TmCB^H*KHwU+f$}}oH%3&23D?#u0(AB1Eaq~?#sN`?brFp9 zWZ?ePLrhd+IbT*`BABr}gLj^V!$fAgi}hgyi$}#3hN$amJ=DvAdphcs^9>uN zzaQT1Bw8jlUl68su6kK-LKQ}_u*RMeo%YVh>n>B77859CbyHQ(cY*fnrQZX{hj@YO z=(8Z;I{M5|=rFf@D$<%zNI78yOY(W zQ2Py8HvNa42*3{ckScKdI+u(R-!PfQjF<_n^dejP)bHNkRg`vmt$pbIkC9Sl;8y%^ z#WQaj)nfU8>CY;5pTIqtUmJ+^9fry&xwZZhfJheQETp(l(->s76AHLq|22h^1wGt&^zwY3klQPV>DB^`Sr*h6H2h;W9#Sm^<29dS!fh0n8k#6%Zze5&Yro1N z-xoYO7MJc)C>eg*ZB1{f{ZMj))c$ecY{)sznkvZr11Hb9v5=+8+4?=+D3jcgLuGR@ z&R6LLYbU25JnPH%T&If`Ut48Av%hD4x^1Gv@<<*}bGw&3JC~n-X6=Q0eR&Wm5_^$F z;;A(iLxX$6C=>_-4*G4tlU|w6{$2#YMUDA7MhvMVB}NWjn#=eIpSd|g{9}dSra+FI z!?avHe3UWd0X{p^Ic^S^9&+G9Y4Ywf6IIsx_<4Z%_Ypp~U*aqGat_0HU|D=l56|D} z)kF1CWqrjp6lp?<)PUj0!@045J<1pK^AVv2Z-)BW@7j89MLJ$z1Cp<4KJBuYfD18QUKo5d-1@zS%Co zP1BFzuou}wRDd`&M7%D?MfcZud#+f9c70O^maDfJ|9lOD|ADq2@DD2dug|}?HtAXR zKUs9%5%RP(-F!F9Zg)EKsmENP60^`xivqkn(wW_I&vr|`WFK|L0!6v*P5T859AB7f zANv0A7dprHtc%7bM4lNuRiFjL3r7zqus&(a{FF#9l;G!a17@f~^6BHsMMqvz^k=+f zgnA9wdQNykRI^WN;1=i&w|Yqrfmh55!0+`7c*Sf&9W@{?n9(hN|E?B;3MDvACkG8j z0q>>)l%R zhLE#y1D&a}a*O7qa;q_5^PP9tf%+UvO<8m9=lRNKw=MM`%AfyL%kj^hdF|X!3$iuu z;C=hKH$T7Xs6j!|KH)0RZKAHjlW(}2wRtz0!(w1p>}+Amub^-#*DK)a+0Y9?;l}TK zT)AeSETuTJYd<6P#5|^PdnXmEG&!!)6$z}nF4jXuONjD!1y^raGMJ=!~N4KWlMIp#ZnL`hNKXi(`u@Y%dZ%M*&(nm;q*~HHqEM*x0d-bKBh35pH&7ZKs-7z^xzL zciKB!>t^MMw!1F)uuc$Gn>-Gu%)2^$>$^01lfR=^I~$a|o+2R;-d)n>eDNWgd}j z?Sl1Q&ex+KZu462k$&^kq4SjEI|^!Na5e}<$pR(X%1%!0_V}pv*mtaK%QvCoZSET1 zwIFa~SzCE{Pd^tS#!4f-f2`2yaV<{uJ0{jj+tS^X`ie%izIx*>yi2i9T!BQnh9U13 zo4w_C8S|RF_IT+?6TO0_wW0Trd$zlnhs5n));d2Um_O?j!$SHb`xw@ok&qg-7XZE1 z&6at7eB$DqNm0tK(vIblKS3spXszNwL@x#=1u=0890a{NNKfY5iw z-{TTQ88+u#oeh? z{5D5VVB%M23PIFditigmFJmX&NmI8lp9|bOn&sr?Wp`uX42aF`c(3uKW9R!bqfdE}1FZQBw^m8+S#gCvcq+k~zN|F;*pZTX;zk5Ry@e z)OtXVpAVX-Yg|Bo)@QnYuE8Rzz9gzAACrfs&H=MU9w@rNsr&-~?|#GqNp!twvJUtl_UJ$m!FKkMU(%wyvjLD!!n`2Wkf_hiMr8LekVAMYD&XC=O8n z>O8`|?l(=j7QfA71JIZ>L}H=2gzA2y+%J?X3h+g1#TTkVn;H)ghyjPk$Gd+4T}haf z?E!?O*Mdk%bi5$&G~F-AOo@pMvC{i9t-*%kMHspJg|a!Z6o|x_A@fED@`w_niIEVV zgVzSMp9P@hF>XD5)N#I>+VZ_|&y&Zt^%?IVJAB4$*!7?^x1&zyOU-Afdm0%K^a$10 z)BFAK$rCz6*Q%Hm&kHU9uO`-#>7Ib^SUL|R-t~-Hiny{9LaeSUhi~(a6unRKP=>ao zQ3L7xJC?=u>rM9`7}On`AhUmL_m$)-1a~n|a#1!GDIuMU*}WC#q+eZBCSPo358@RX+ps2YGMZvrYD&w4+NI0 zVm4!*KF`Iem@SgoUznTz2q0d;dC|`R)I292;s&5*O%TGHj&8GzqFMip!u+Gc;oGf@ z=HYO>RKpS|(a{9JYC~Yiu<_>c<8(XVh+&V5NgPpV72fg&|A-;QlK95~;ua%~U?HT$ zM@$}4dn@RW<3T@Xgbna*B=FiE@=aGj=|g4PnXE8T-8s$r^k7^I8-X5;UAkD@6=*#nnkh=6j0B+tAPA6cG$YE;e>3f+o2>SkK z4O~duBVLoC{+o#Vdnu&c2TZ6mHGeW={5GRVrIoa&<4;!Vs>G3|pQ{l{+e_&}4idM_ z%`AoAEaaH*Gco_H5bk*vKN2z<3Fi5?Yl>BK2>x`1O2C*8uHZm zfC7ec)al2>($5j?{yXL#vX*%d^&D95;hXG-8b`%8cz-@&Bs?zs%bkKCw)1g zOr`riP9-jCWYxUrd#atNs@XAV)@eEPk#jsD0p??L{b&3y8{{8d(`pX(k_R%MacaIl zKq+0T*9nqif15)fHyLCJC#5yGe+REltY0K*)cV2Tvz)3pf+^|t-gV+pL%%8P_f{i$KcKc7}U?rUahg zWt)K3d3c5LL?hYpOJIB1y&KjI?CCI0wWBiu(EdbF90W%&NV*luKQ=I{${@%NW>XvU zYYY3v+nLy8?M2eEZ4mEUTXgV92!3oWT;tXCa;GR+%@yf5!OZu58~(6oyw+sHRShwi ztifo0G{jfZ*OLqklii^u>0LS|;WWTWJtlQGHUEe9M=Hs-osU$sMX4?GJkgfbE*+_Q zeT-7BM?fCsN2=kgLh@hESPJm0ug9$~q5T_(oDR7PWEk4a=2r=VOBhJhxq>w}#o(rd zk{yPI1`DJg%!_OXJMRB@lNGDX(g`s#6jWXE{|>Sr-87by`{TIeh}++g&=zbvy!93R z=z!HuD}*GAT}w@!KRl6+aP?Yie)2(c;A>XFAfa>{YG-Y z1LU~bUKm)QXJX6jg2s`@tZO!@9y zv715>>`A1L+|#(|27xJf>-X^FanL6$cQcBMlw9#E-^R3(J}!J7cKCXM$@2S>%3D~; zQUw5JD+|EMkhRUa+N1;}+wIF`E4?57xS%{^;>Wtv=w^P*?{>7!`;f=|`dxBs>a-o1 zi(og7V6OoDr14$P!OwSsnsT(1D-L-Tp9jB1Yp8R>b6VgtwO^d9| z@-|a`oK^mDrXhf`3kPi3-RHx1`eW_L62G~C^&h-h5+@3Kz~iWAxNH3aHROIH|5hn( zmhH7R{C(f9Gs{(uk0Mj2!5t^bykDiVFgWix3^eM6KP-EtVHbm}BaRpSK!K%|M}0JN zSu?j*ha?Mmoe|J(4`3U)Sxt`FtvktSU36E{92QOS8#DV;a7v_-#{6 zi&@>6{uu<)pE3PbS^#vH|6^c`by1W4^dwqKEJnB-Vo2+w=vDh3V)@B~naQ?K=EkdY z+}cUZq^u$RqVUM=?H>mBKa|ec;C4*~v}bg;$8K~DObJ#RqIPO9%h+RK_OC7CbB2peq1svn9gk7W&wHZ zFozImy`3kjCoHj0A1$Q!{wsoeiT5~~x-r7qW30fYMYuqrxf!Hovo}qwL@wLm4*p)G z)DZ~wOfd*J_AqQOV2~b3EIh?7Bq`~`7Vhu-a5w)9uXn* zNJ$vS{BVRP35^IJP3K9>L)}dCS!!Qtgo{${g{cGzJ(E~aT~SF>JKxcBn^81#nj{(V z0n{Zp1~wtuFA2|Zgqv>dnPBR^LiOpzcD zvhlgRG9{KDpcob9i5wE*BhTfrbop_xLD&r2b?{z<)4+qToWK&pDJ=jPYv{&Dy#Hlo z$n$k3-#8O<3^IVY-X5+4NY^A;22XSff>Ki}to5B3V=XgwR7|q9PEPL)0p0_+d{e82 zd`LyqT6D+qOG`mPbgXaV#J$^9G!`ti7~!7F^Q~N=-LAva0~N1Asd$~K=~3IQv6_cU z1*H_Ry|Z@ZUy)3$s{P4`@_j>W@Okdw*P>+ z-{_+Rv>Va=^X=As^ffWxXpC+I@)4rP`v7y27?km?B-Qxu8DD5r{?Yo?zct~WJf!z8WuwX*)CIg4m~Nf4`ND;kZ$UJR5C0@UwePhK z*39@9i6?yIuG!iegemW#mGrauN*Pf85evGCo&F_YHlZp^uRxQM_LRgH# zWSUwkyWuFU(WlHKIcX^|>3cZkXqIhPu&=0hRxqO{Z1}#O%JG=9dkl;U!n;?F zS)nAn&dkReR^`sMeaD*7IQv1^SUB zC1x6{&p~(A$I65=&~#ln=%IXiJm(Mr$Ta*DGDzLmH^dd`_xljF9yhE4axp)|AKC6xJ3ZzN*}F z{!9Es-yb60p*)B*v-X`MgG9@UbIPGgz`xzXYlfuyDZAi>6Z>L}P}zQ* z_o2@0qNRR;1`-FQi5+%#QobDQqo-p=??V^hJ$TMJ$HRSb7T=;$M$q`Au^47yb)t}g zSqyv;cuH{@+eS8HwQx3i*NX(7%BCM^ILaXOy8m6oziK&@A7{ zqcNMBb-Z>9VV&0q0V>5&gbyuUh^k0yK?)R%CH9x|^l zY8sy|5k6a+`CC|o>~0`jBHxE@--GM@a`cg?zCWLA=mbxv)A&Ab=9pA1bg5&FyaofF zCucPYezvrzbr`CLDZdaVc(k!bZVH+1;+yM_z0cFU1G0gBDW>?fgLceojW<1(5eS;z zU1$qiY~zG_w2-&{k>qSra$Xnsy;O#RQs)iTCI53NR}`rJ;b;A5!HH6n56ASnP;yGf z6J27E&Ub8eGFl+}=4A;HWQ7&FjWWa)>?jjq2G*)J!d&Rohu}5B7zzd-6}7@x-PtwQ zk={?JVqG}ZZU27XzaeCkIK^CspV!fQu1B~suZWTaGpQ|KC)QgIBdlpaVGC1vUy>}Q zpSSHA_=!+>1o8!x;d-FeEb@P7HFMmcVD42e$H=KS1_td??s7yM52Y`rv$ZjblW~Tq zABBFntMTr6yKy&?tkSfsCSqV2<-_!6nv_ELxQKLRIQ#H9L3A_>Dx-o>`|!e9Wf?+3 zk}Jm0gM#5g#Jk&DGd-bJLwz*6MgtHC%;{OV%L~veb}d)y>^9fDUj zUchNvdi<6DBe*Qdbvl~$Tn6V0zU9Lp{4W9Mq1*GCYTm^iR=S51P)ailzF&c*fIaOO zy8hJhl#;!{?wAb`c4@~4cR>B2B*jYMLPF9@^6zJjb9^Cv>OlwyMvf7NFf!HvG%W?z$byhL_yP1?{Y~~nT6#`^rkztr=aHuL zA$b!0GgTD951gsZv>zhkkHybP6SU)X@NoR~kG?DlfLlomo*_J$ z><1~nkRUHrQG)Olj3BV}dpVMKUZ4<9m{7$Qf_X0XjjlS4OpJIAOlOvB@%!42Fs_86 zKN`kx0B)6kNC#uIWd+0jLP9S~>bI`odwxwmflXoGjJ^GUhVc|-4umkl4s7;Z^Y1aL z+CA@9Wo{vC&C}hCJG*=kUhoZU0t1g=zmQn4PRxSz%6lMy{@en|+f5e6ait%0982OI zW{!E|+l582@Pfd(`a(kqRroB!Z*_LL!V8|fpyN0aZv&F|5OT@=SJY_Um@`*80YYhT z{_=+GB=EGns}Ujg%g20KjG|@l(LL!pgyDD=$e%nXT*ahH23pL0WA7?t{Ya$SNhs0U zzo5xp3EID;+Zp)uwFNC7v~O_GZhgsG7xhN#Fe`aS+jq#*y}>nu%dt<1;e1^g-!h|Q zgyj-Sj)mpuNKRe`@x|4CwQOqkH6TWMDhR^kUndE>F#6E0t~X;eoz-@o@lFsSyT(+D6VoB{Y9*jfJC zS8t#8D@uJ4oguQmx(&2c4}pUQkWt{E(FY+kC<6$8&L$M&Xlk@eNAgacXDDO>fV0N1 z@y1_U(Gmr?EuP1gFV>%)U6$XjhO{0?%rM7`I2F*v3ePRhEDZ%RokeA3WY9?V3N41h zDyr#g1hXY+GQ})zNDsygo4jaLq-SZAmf0R+eeP^CI2{W8V}5S>%zZ$gS)B}hQBWm= zDexnOhsmu0Yc5e-u#V6HBoIkm4bwQFcU!j3{$HK>BWHx`ZnGEw9jCK0m&*XicIq(U zzxw|bfCQ04SEMy}I%~kmEtOKjOWJbn_^O0D$^S6p)*>Jd3D`?DQ;21$9|~_MyEUn; z`xsXW!T+LT1`mDl+WN!4NYv3#`CgXEs$*jm-jmu&3l-P4|H6`@7GYWE!ohtVHl1o8v zQacyLW;`EmxI5wT53yuz*f-LdA1JxKZBy*q=u~mzmwaw^Ek1IZLRr4M4-6M5$?0}M zhAGXiaV&Q&sbT9+rv&dTeRi7ontk+;H8&|UCjZDaTy!ku&3Rkafrr?ea{>ACOvINf zzGdd+$x%oZ!|7eO)Yiy}O>8K8RWYyHL$4;)|lD{-#T5YaOV?RHx?#yZm%N}s%FuIN{^)97I zgO_3gfeV5dW-Y{;#+4h8W5QD}lTe|7G+^Ru6DZfT2YD3+dpxYIP1;WWIqb|wqFhti zowO(V#CJ-&RcdJ183cCweZ#~5T^-Ei@gZmNT2l0(ilZY3(OOWw901U3Kmgz zwT@FpmVG+=EBa4y7_U)|(v1U3%!&d=Js`VFZI8sdniQqaxlNzov%T$dsMLWiVx-i` zY$nKZ73?d9tmqS)H~;A~pLdaYFY@uenVxT>M$uo*^_DB#4=g8KlOd_E4N2QHtLpnw z0QTA>2&sWRwjmzp93JdmQq)JxY#g$(6$Ka_a?wgtab{vtkfjWtVr6Rx+5Q~zcvd1e z)f_M*stmq6Vzh5m^Sou3xyDhtWB+}-kh}raopij?nm_m)3_DJH|Ilq{=?$-P%%Iu) zBJEw17d&ixWh;n0!gNeNx;-ckDA-O2TrIe(&#|n#6T1*6`c|XPoG$1suazh@W>v3Oh#~C4hv(3 z8aCg_NMoSfd%EugGeiJQjf&T2@T*{y%) zWQPBmf3?zXJmq`e;w-!%cMa>Edh$3FfX-pKr8>>w#mfT)*9}Gb`6Ckt3(H)XS^4L7 zjloT|fTNCP7p=`t<#`C-}w#i6l z8+V(UUpc+mx*SQaJ@VXYqWI=1P3I|GhyMB)Da$evT3JuAfL`-F`6q|@Vx@JjBKZ4( zoy=GWKOV^n)4fF%%$$KT(bRn1T^~n!2*fT0Aoc+1v@R3g+=CSvPs-;#3(_A#92{7ppGG5P&@itrcR;!|qmhws0M$m3?eC>g*w zp4o31htc0M-hUx3`$_*MG*|yc2Hf>q4|?oxVl*chyCEUcNv=~_DO;%&AWpX=U|r(1 zw)cDuur{c~C{^5Y8y|7^^t60#&g10#+>(itjUy`V4bii)#}`@^dy{R-uqI1ZCc4s; zh6}-I*VJ(6OXBKF$Ic~h0zjDqE{p}cpZuAf^>XzL-ThIvkJuXCd{A^yxq$jy>=Q=B zTN4lAFc&Iy{319=!_4IkAoNTjMB`$_GjoGp#r)U z8|LxEXc}g`(=q4(uwfqci}H~zx{z=$w8`YSylFj>D%>pMhCcnI@+HhpIcAm08te|> zEo<3-Z1BL;SYO&DJ_H$$&~nknBB&?U3-0@8iRB6qE)9=J4Gw^cH%DfsDfIUzLc{^ z=X`K=Wb-n4hIt|hlhLcE$Ohi6lHPaY#q&2rLY49k?F)VBMqCW z`{UuMbnW$bEx#jXXVOj^_{r#nX+=hECKXtLFoT$zQhB&1*CXlwAD*-UGr*I6C{zyNQ{`J~2TXlEagnC~izxli`UG0cx-h%#pse=3 z$ol*(f7;u2tjWuxp= ze72+~ni(GppEK4FP&zZW`T^5*mU;T~3Tmj)E zYa<`RZ^FKu6MYm$3F&H#P~O*;jhS1lxjTUg;VM&()G?*I!_+S z-}@=Q=SrjO+BAxc0vRWL`iPb8=lDRqxYE?m;k9|uQ9j`1ia$u$hOo-61Hd>OAfBT7 zXqWMqnq;;gl;x{rtI-EFtl2YDcJ? zto(A#zE;Ny?atd|f_^KS_5y06`L;0BcR}OUshJNJ#Q5w|nytdHCjzgK8fA#~A~*JU zysQ>Szu&*|cYy(q?-brSL2O_dy;B!rK0og@9a<$Qx-H4xB#hj&qvG|J!|OvLR6}S; z{otD`5~%rp=(w-A>&JUNz?WzU zYa*`b`{IZuZeyX5=RlijhjvN3z!Kla9(Au~%C(-h>UfqwcRRK^duPZI`)i(lB4jJE zw1&T5Mu|B%%X&zOl+vcV8b%arbUf|!bDx=KU50EFHjUxG{sHAcb&RNKz%Oj!j4IxD zdT)WzyT)?RWEk$UK~h(%cHn#EN!OQxRN0!X!o^MGs?@Kx`^D*rBiaUtU%S#G>y;Q~ za<(=%Uq}Icg%!LrdQQj}%G1>xRhH^LWirfcUe#SFmja#USA`%CKQT?^lTyydI;z@T z9VVQtOINy|$7k`+kQ(k<8`rNam3C|OGC6fIXB&Xqn8VX}Y_pOMjJ_ulC%bl}F`^_qx+m31bfL zAhUKp&FyuH<~4@X#P&AwB@@oay)3EI3C*=f;}rZQ_d_L;oudt*BYUKYTxfz)A8(>G zGBazHn>arW+YsJ=mSUvI{yl@}PBKOb#(bqz~(j;{mv;9q;ylRhtm zcAtPSD;Tt6Dh?BwiezQs=LM?K3#v^TQ?=~U%e02NA3SLhxSR7*qg8&DH8Jg;x+NX( z+m17b0`5{~6jyxxaITjJB^ev7Uf@0!HY0Js#NuA}Q3 z{7Pxc04~MrYx)GsujYvP05xI>EDgtDTj4tfa4+@+X(}xiZpN=hn{J-Hc+h(D*&R$` zEc23?#uVdPk&V#$=Go&6E=^%GfJt$Z8kxv>$f@{ko!$jj9cc`ryEp;sw5{Dq`2efI z#Vg{J(yZ3lkW%o00N;t>QP19qUIp0tVLypx#F*5DR>{evuq^N*acMn@kZ>=6F)`Un z+#u!W*&omj^oqUu-1eRQP`16K*2xHI(u{OlFNxMh;td&C62;L?Y>qmTdQakna_GE^H)P1MRcH+qK^o!C(q20oB8Ysr9=OgqXIL*$a))0+ z#?_?t5P4atttm7>csZ=zAB%WoIcU;g12bTBGN2Qlg1*$iT!mv`;TP7x5WaPBK**O8 zLdTLpWKw7mL0on~y!b%p?f#u+081sa?ITPX!~w>U)u$lrK~?X-4Vg6NJh?^+8zFQMTm0JkF05V!<5BoCn@de3U}GF3;U~a&VblcFj0ZaAd!J5)mF^CLzVDu zBDZH-xvzaQf{j*!deXUzR~)cd4WeXB0?mu~wgV_XoUC7H(+zROap0IpxA2O6B;qR0 za19h}zlTRe9jSd%tzw8HnB0ggMz}iUPbn+qoY~<3h_rpjN^QG`$012wyJwf*LFdT> z#tVAbmw_twHgZoc!Xc5X*e?_d93b0g6)EYsxT+BWagYVaBuR!S=poHwGUY4gq=^fo zKv8$gV9^p}!SSGwrOX4TcamSUrk z5CXFGadA}HxSuolN6PeplnoToBg&|bcHyd%p)D4UNL?s51suZup1-I`;g8j2Y?>Y6 zK-zkIMNYa20u%2Dq_c+KM3dZem#sG=W>VOnx=zuMhqy)8H2Ngj3w<5=cV=lz{hrvF zyr>AL04AuK1&}e1JhA;3Q+db%dw6eY<}mjtJiHC@JPU9M+T>Sv4J*P;Sn>gb)R0QB zMgTX6E$#n{DfQY=pcbN>ITl3DK%FcXT8wN#seSXE<`lzP8VSsXl90-6p}3fI43zk{ zsAOZY6W7Dzk$$D|SJ44!c^Ic+giHV_-A-$S>n@hSX%Hkin2l}dKdO!wF!EN0vD6O* zvI0c8d*6-bz~{^_pnovVr|^p^Lo1B4#W zdj-tIR9}56SUaSy)BUo)pZ{q=h_hY++X@P-gEK`unsS#4Cm35Jx1Nx?qTI@g0uDa7 zJd?d4b@VFj!_;t2@N-;ovvNQ|#ZhWe-)dLVF}Fl{<_b-lZPb`)AzYxvHn@ccY@Bd# zB?pW6+QcS~@G;O_3h0tvDZ+}+&+!QI^h1b2eFyR*m=Jow^^pS|z* zdB5+^dCrgXV|%A(s;YasuBn>t>FyF`eaD;@+N|Zvt6*pQ=D7UO0WH>3utK!joq4(Y zf6w!sJa3v$KXs5n<=+xSKmRM=qq*#GqwEs0gfBY%D&g7cHQmK17C0^H-)H(U_~h*3 zvE;|=D}MY%pKmrD6egqs8KQhi;`ySYA_7fsr;yP>r!;bE)t(|pyrcem8n@Gd-xNm7 zM4W@Dd9*#{a9*(NDS@hRcEjRnmSELObGw@xpP)t+7{TYVBmUn#&>dp*zg{m|1d-z} zqw@J&Xa&_a*Z*-Zx!S|;iVKCX1NaE2je~N5XlnQ<(o68nZ`1mno%*uW|DTKBu!jG&b;L2e|FNkL zSmRrL{Rk}-@ja1>#J$sH!E9CAbiA?R&jAfc`fuNT$+KVS&XtJWiCQ?ro<^AN>cJX1 zefzan;kuv4UY7@zK7xBlycGX62?6D@|Jd|O*CodEmT65q+@jM~8{4_~;OxDTG}`Z#rG}Y+VPV2xYBE5^aItDy@EPUnWr2N- zbjnUODR717e`XV38fA=@Ltl8~694}X@d}>l`~R~|MPSQJ+}Ua*R^Z^n4V{X#l%m~; zhe>+a@?pHtFMVOb;MFK9(+l?Dh5P|@v@c8@YJ5TrX)AMfj#rmgiqmf+U zi_-Zr;+^Ev)xkIfCkHrfM*ImU^Hr@j)8tc{?2te<5TN4~XZygzvC1sJVZ)3#_`I(!WQDJyU7qgB_Y&;Pt;u8) z!8v-m$Y2*<2}_c6dSMp9YZ{jB{kZqorNPnC~1N!x&Xk-Zl?M9QjYPM?_SwMCmAC7_!$u1w8**{x`UD)@hn+fo1TdGZ_Q z|08I_Yq-rgsWQ97T~N7D%K{e=`6*e)RnjoAu_wrIjW~m>>fFeUg*T0-|FYMB<~?R; zevZ|ew7Zg8sv1=WyYc$%_+r1P=$jBKoa*{#bKVeh9W@!3h zTCc9UGzg^Ir+2m)#P+y*#2eRrnf;;;?{0Fi?|n0(4F4dmeF;(003@_BkG0PMU%a|2 zvyiT%toM$TK7sN%|9kL7BD?E> z&f?mTEBlSF^i1bkoa0RTClP0^a>qkv1W6xnr&97)LjKcF4Qvji!`GU;A`V|U>3RR_(BI|v{a<3!!(6#7Z9@66zy<%GL zg#M#2GcK_hOrR{@TWQd%>#O))%N6mlRr#K?+ zyU^rBvMFiRc&ni=A}FS_@stM^W|86T18j|wi)4F<50kz*UZ{oPbwpDP3DjH7JS=!6 zZ(nH+X%x*%p!yP;kN@*fsgiROFq{}$U9VFRC=zVU-Hd|r?xsifl)q8j#D~23jqFQE zJi{BlJ)aaaX0(aday(o~=zH-mbz@3hrlWR(w{ZYn12YiobP%rr5jO!5hd=I#4^6C= zWzk9J6V9|v(s+BEzT&bwS;f=-`XD>+*;#P66BlXp`+A9pV2KaGJe%&Q?AL`++LH=O zrcm{5u>&s%&mi#@o{T$T?_<&BeqxL1&Bmw^v(BLG7qZC|_sdkS$p64uPr1e_7ZT6>XO!q18wb*q*V~3^%yvv$f(l(Y_n^JW4)U52jpu z%TH9fc7$y~xmOE1q`O5_rMzCR3qAY)LbMDT4DRY#PP#YI{s3_NFGONbRG6o|^hyRy zh+a>GN*djaW!Z_UP~-2;G2hIp$uZB0Ji~bvcuTsb(APuM0E5{ymdPGpSdR>{LvKDGO5y41ONZLc~XFX&;G?2tyBv+k6F)v^4F< zkkY{amByQ|H(2zm=~xY}*d-n9ezZ&b`=ijAI71{F{dElln$7QEQW{%67o)&+qp?Vh z|4tLX;$q}!U%$t%^e~v7kitHGWKjY|LjYNfo4pc2j2l|NYs~WsALglYGM#M{)vEZb6|t~Q8{IkqhVK!~=m1K=?CHs6 zBrK@26WN?BO=0QArWU%De4kh zNzM4bTQfv9Pi2BF?C}fEJ`dh8jx$m+kLjY4GSjNH3aA&z{v)5Lo-F2H`Ejs+T~@>2 zZ?P7bR~?^3!1-09Wx_RK|D4I8m>+;&yOlfrUL1o0Z5H!>L;uN@TUTzX+_wG)K41Ns z?-pu*9<~W!41YFf7tAPN#?kL?%M2oi4)0z%T``*DVTE}0t<=q`yDC=h>bxkt!^-O! zcsDpD5VsLM@GBjhXo3}2WjKgb^_D_a2;Ol|h1ne~^G$mvVmO6gpDKZ5j34ieY+4pk zcbhG3*L5$Tr)Um332A?AyMg!Un8$*7{ymUHRL43X`p>g@E_6Q#YLf6eM|vI+ekT@_ zXGlASm^J%*A?Y93y}sK**+PF7GT3smRidkP{0+ao?>l1t8Czz+3luH~Hr^!~=r(hk zM81&;X=Zx-gmu|uMX17-%gc~4y?>Q@WhMEvURQe0u^CliFkwkx1deWyO@^Bdv6SqT z?^uPUc}Yha3od)!=#%Q!7$#%+_mMFqckAbe>nY7U8BeS5sUg7zthnC8U9^S2G}WAt z(}++CIdIPE;_%#|83XU9pn@{!@ z`08z}7dabc^yn?P?&l}Vy5S-3(^{+V_`}r*pi^&~0B9hetg*WFu{ua=@+!u4A1&F| zU{u&z6Dd8b5X+tTXb)<2DE=~fuc=o{8m}pxa8&t~mc$J>+?O#>j^KY{xeX3U6;PnV zz~F}dfAuq|NIdTG$?6UQx9i&VHQ><0uEgEBM4eee+Dp z-0av*kK)dAwOsicm1Y%<7x^_gh3s(P;f#pA1#UEn^)n1>=HFk64H8j0Iy$`}tw<;A zZzt<+JNlNIx#>fFB*@SF23qJ7>IUD)Iv<`6doXwS-%irsPT2SN&BZ3~63>tuGTOZy}u zLo85Cwna-%tEa0goCEA&ZEr_>#*G>-F1G*gv~8t@ognP@mNo8>6Djj z?x3&y4b|K3-(4)j^3s!oA&{mrZ`BoWZ3y3SqKAFrN>cLd+z#zFEr#nF`-Fu6}t%S=<%-e7Cb{l((qXyz@8-~EgE(W3B7 z#iFoM^ar-FGqt0tsyElMiJjo05f;a=#rxVX6;$@|GuB{du}OJrm7Zo=!+qlumbAZJ z#_!YDAKw$)Yc0L$`v^IHT65eRc=t9PVhR>dg~d>UBmi^oNOP?C1v1s5`R{vD@Q@m# z_JN%vW6bRlU47Z6UOr}vf zn5Z1^k;LU&|*nRPd^9hnTu%?I~%fx=(9;gmSXJIu$pGzDyO z@cJe)Rs27ywO9o-_o3^Vd1SSeL`))gS$aP3vv?94^K}I3?tABAj02L@7RDrAy}wLx zlMQMAkry6PL>K}7+4`9s>JX>Rf^$0hCeFc)pG{uvCIB;nJx7#0^#eiF+a-Pc`61-PTIc)@OUDl^L|?wx2qiSDj^So*d5F3DB(%A^7IiU}z=| zUGw7xxvltkI1~|!_Q|HL|&Y9{)?LYh-M?Jtq`B4?&40ekWWpP zS9RIpCdsKz=v5AHQde1nK5y|Fhu#{Jc;;C}TarcfY}&mS@xU-Gzc**m08Y34<7am9 zFqr4?{m#rigqKrup=I6_0v5IGQ`s#n9g3| zmTt~9^Atr@qJ8BNyX<8tc(v8flIp|*}E~bT%0ob(eeTfv!1q8uICiQ z7$v9D4b+>5(e(esQ&V*Q%5K8V6IZEZ7Fx_TOSj|eqsN_)+p<%5qX|E=pLvaLwS1FZ zzrNbAZ#mSAma0Xd%8_?JQ?Jyz;=3-lw115O^06+B(;%>8`K=t8sgjJHo%TUj-}SJ< z^n}0PzlM~ByVONYCWVYio@~?nV ztC^}6@w(v(TkxDoElW12IA}&QvX-fg_&8 zcWV-z_x+zD(#==O&1tKeVxJuIDfU2|!*EExr)8v!;iA}IH?iIMK7GmX?p)?E{zrV%(`hY%y*j8C z=+)2ovh?%5Oz}E@$N2bCkNj>#)59BVk#&$9c2|w%*#G9q#YF$7qBG!nqG3^3bHVV{ zO}3}}=RLPO?2v+Y-`YtgMKjNvZ3Mi6*@EJ{=m)bk+hi&F~zxv|>GT z%ZMIm9UZ!w?p7m$AKA%7zJf*s2MBAYKYCn3KY@}2j@zt>E@ca|8yt5=12HqhW83QMEUQJ0z4>EDPwcU9Grdt9srP7GO;6R_%9M;H$Q7tixZg zrKd(uEd##ie9P-2o@xhvM`~!l6yBRh2r%IH8}6jAxysYAVM^tP$I90}-BKaJ^$mrS z{Tn8d*oEPz&pZIxLjv~$|2;DBc4l~2&&+B>e~4TPUIr&BLg7j&0TY8H z$)S|JEkF`{k?#KI<>shI%&+7Y=7_O)T29>2T^{sdlky=~wfBf)eD_CS?vH11kD8j8 z|EtU08`+D`)nyrdbx*+i>B{3nGRAA&+@~Y?UHhGH-wX~OC}Cc;^vavHJDH;=o^1@w zX)fp;hsnv-=KD^Ep2tb{d7qZ8Dkr*Bsg71Ry~D0!%s=})W}iz~Rs)~@I3&4wK2T*N zjbKDz(I2X#CyvQplWJUb>tu(@{&(?EKM;E@DRN{+?04Ho31& z@V1wM-~J$y*WwqvuRsC>c88FmtD*+K7R?wZiv}xfaGqp=0Ym-tHGa-ux7@ zeOwm`OJ-p`Xv|B?i#uZ*Z?AGeUl8_?&IV(5@OIkU{s+UKY|Y2d08Z>JId zV5_sAmA?;3vYjSd5gAG%P!SUm@#)#bYiQ;h2s7l~%)ND=&YFaJct1Z|JN%}mId#Et zdJyMJ6d89H^U-9!P_ieNZ9@f1fK92mFIM%MG;$Uh^N<>kakH?_+N$vVoM&0!{V|}l zWB&KFgZAGi`P~b>-NI_t6wHy#5exlc6*DjTj_wOt+aBP9R9a7qjZ$<6D_nT7ovv0Z z2>4udFyi0p0l~JjxY$>PGc3qVll_vKtZ4CyZs(jAf3;ZrnTwg(*$Di1lW7>FPxm&u z=397IrOsPAvTVD0;R-A{Q2Qcr)%ptPVCXu0>U@2Ce44fwjP&TinCfVm#{5b4dI!4K zIgYVcEFGw*vc;mlI0a9VXg;`W`FcY`bnFc|ID@+zSre*7OIkGZzztRpm#6#}rrxPo zU7{DK9zKcbx4Fu;gpahZe{#E@?7`dXRYk5G7qzY}f#L!}FO#;fWgk24{LJp~H^TIN z51X&Idh)Dl0;*b5c+D-vhhm<3;I?YJu!fv8mR$$3qdF`c;`qdu280;00$PVh?&W~D z2BPg8uiK4S;eo=>&wbCH;z%5{oWqs}GiS3{Z)e)OhWmwvT^-pso!O$$Amrja$Ftjn zf^HKp!NUBcLi}zHkShnPfabZjLRV>fex9D9la1N7!WI&P|6KFhhvv5@ItuC4rvITK zamWzC@Fk;mduOGR0>aG9&>O7nvLfBrWG%GUME)+?G*9jB9X z7~WsfPOXYeC%23x^L;}E9EZ6?T_yXPLx?V5g#nR6k)E^;^!4DW(SCkC`8B_t?8qOE zQd@2cp9gTJRexr<=*#nQd%2lp5}s_bT?F2D{*9L;8uNmLmXC^U3I{F=9rGIGj$#G!SCjE-Z=A1kQ~PwNY5#rXT;xp z5HSb-7^)E*z9wQ{NUt^o(e;$N^NmGMKB=400VOVON)9ZE*;bxiG)iM1Lsi#lC}j4+ zE2LVgciYn9;H4;s;17!4VeD#t_-+^ycs3_q*z;`+poP&`q*&V^tKuI7r|blSb4f!y zl%x+ARQy&~aVuPN+o7d9qL?vg{AYkMCP_j=7!if906JQ{_r*l@%Xrb6`V zXGaPrd2#T_g%Pu1@|LAKe8w=d+@KC;HTkvpqa;dnDH$pRImSK>q}+_70(FRtwmcv4 zLf@)9$|ouOIqT+pg>_v{Fx*7e7NB$AYLS7K+Bn8vi1zk{Bk8baFLT?9UsRv8`Si#F zz*(e-G%H`YHZrCbbL%h`6?{1UzA}CZ`ipgU&~5)*h7gH9L*3_aL1@oqdQ0WL8cIn# z9veHQqY@L5cf472w<|FE$h*cHPBmCBk`O{k-QwG7Rs7zTm_|X0m{J^B+Pe@hwEhgl zx>n#R&4CJ=V-$fBRmby1a`9m1W@6LLL2;wORjG1Y((k8-E%lU6IN}R|=_G+@78WjT zt?wnYRkBL?IALY-*dpOO{B=7e^9gAMBJtKFWn{?f=fqf>Hp_;ppLo8T#!%8S$|S^X z(%PD0lK?+?RY1Gt#|xiHcY;(uhj2vV9kL(pW?8^)GIj*HK?ebf*wbU~)&5XVqcKgx$8q|e zPO_9CBQX;7XpHd*$y7q^%&#%hO@VJSo~MlWt%?SIn-to>{XUX9PwMWpjj}DOPhz<$ z4Npxz-$10DV^##Q=gITN++`2y7tCU5M-fZ7+Kl-cTe?|8Wp;du;lPx3Lwk;uE((Ij zD_Y9i-5XK56zc}t*Z6cupcGi+ZRFDQEj;>_qW^FGTQNL4q$gf#>Fo%z8u7N!b|5MV z)A^S8Y;4{Lxhz=lFs5FQ`!MX&M9Vim`O!o7ppw95{#x?0Uh;RLow}TT5ysPq%~(bG z?P5_$jcmomwES10G60xgfPH$feYnv>@00n-_xlF65Y-U9OTqk>D73XaojLQk?{uzn zyoz$dBh4It{~f^ka?B?KEAZkBdBIHf!#2fb8KOnbCrPFY8iZ{#Q2))#8WTnhtBtF|Ni{pUtO4qj7QH2dL>?f(43*}5g7)Ip644Ki*C1ZNR+ zyfn&+s93%?jGIH_eyEU*)TMu>U^bT+!7GnSj3a-Pc#|nOoyg~0GBvPV<5B`Mnr-8W z(&94kE@FQ&qjSMwZ(?Y!#Hllg#Gc&4qnYHP?>osu@DPp$E=x=Tu3lT)t5qb8J-L8K zDh-tc^-!YDcV77@$#ORZkv>5U&$uS7fwxyh8=1pY4rWNB%Za2vrYx(cHiuKXLoHT^#vLC44Biu#m$D9fZMm9BjB z!u-e6v3yuTkAsVW&IgWDky6;@+eaG&XN-w&L&CwASx zk8WiztOSA3`7vdc0zaTbCIZ-RtU;VMvIL7-&!U>oU8OrD&G~6Q0R=t*vx^IAyd{9z={cavuRJ z4Jwsr1zfeWorD&?YwzW|Hzc=C!n@zX_37o_)6XXnw-cVIlQQQV45c`BDdz!Iv?kb4Xd@T2Jqy(-$Iip zw8+=w*j=pFC2GjX*9?|l1fc<4sLx@X_Yu;X(QGHyh~bYNSe5G9^eWFA>fu|fHgjoG z30q&5BAM0d4iv;b&rqoY+tfWBcp4Anuqjg~`*2zA+hc`-pAZEhor1cCBv_!>(e^Z;$7-}}33$vNrK zS}Dy?betL1>R+&uzO)I-bYi<}2}yL^UNM6F)mb8NH8OOF<_&XA$gRYYkc`Qoy z6MWU4Q=(!(XrLQ&zDqT`!JHXSn@p#R17+aVM02xCv@WJQ%#r!jy>Q!vv^ zlL0aDOG-Q_@Pds#BK0#Rc>BIRQ7rF~u>q`3vei_?h|c2Yb}!wsikiF1Mn9p;(r|y# z7x)(O@EpP+H-c&_@2){(6-tRdu;=`hfq1x3nfObHB_yC+jnI^eBgFslP&h)nF6}#! zG3)!$CAX$hBRnEO{T*r?067HB_=dDBffJaH@cLa6pE;*rAc`hAzy&#SgvN_&8!kGo zbJ4n=$oT_Y+p&-ek#Y9&kpLC0deyOUE?zBRVr@W-y4G+Hb@#>6dw*v$lhO|28po-C zmXqx_mnE_>aB32ItCNQ{D;V4OonKrFPbM*OV^VJ&zfkttmHSZ`gKlvg}Jak+a$e&UW@ z*6_L54aNwvditGh|CGR(46YuEn_B)jSSd57{kz+O$?2an zc~eD=ROm!Le1~>=hjwZG!p>W|H@wldZ4oR*<<**LHDPx34daA8VVZnOoCTqdtW}4) zaD2yllARpl$SkZ1zkkH*AI5~8w)j`Mw(BcAc@j zWbo<10%AP-_$+LHr(icVZ(ty+SCsZR>`q&2N*t!5RoVy@_ijFGm2UYJupXO$6-=QN zqMm4yKR;L0C&w5BgU84`@uiA+y(o1!xB+KS_Qa>fDvcJ!oN<4r(mq3TeG{t98&@F)gC= z_oRqdwq7oE9jNh+wsN4jz=KSsxP8-C<+~$SYp0s)K#1 zimj-UfHccE9eJjB@`C8_X9@}w-!(gnA+j68Q0fukHQD|HzL>?E2i?S7TfuHEBtfC> z<+~rTyCjBWWXYzasGW6rCe|~om;3e{^nh`sG20j@lA!?9R?Q&;lm}Wx0{f@aM!3Sm zDEL~FwMi$+Rx-@SR-_&j=#xFRR#*`7@oHoV{ifE8aUbmiFFAsL8t?>P1ye(1F(hCm zb6DT!s&Gqea?9$7!F%ugMmZiPGu_|wsPcEwD;7pgIWJ!#9Psdt_&MsDOU}Qe5Kfpd zGoLx7V_Y)07L#)8OVh@_<}coepApwV03bC1(8P057@RKJ#rhY(%Fk+jjaxaeN`qZN zk=HY$(xHt@atapsbgDwSkCN+nQpcA2al9tX0M17j)p5k1*#OLIi?4*5cBQ#T-IrDS zBdQii@=MaZq-FCkdijc12?zY!kuDm|q-EOX+L67tnbqjpydN$3TTe?(EdVuX#Mak& z69(`>a+elSu_}mBrLOrSYU+}l!p)rT=B7CJJe9XReI8v7u|{znG_o_5m4>#shqg|bRKrWWyvv1OOYpkHIl9EH zj>%wt=b#Vg+1)GKCYP?8ql>>-kWf*)GZgfg{BnwvZM$Gaqp)7;QW>pK57fOV#ye{L zdVD@FkfgZ0WVgUU6`CkQL~vDUAD=t&;HtC6nbyLDO{GX55k7%K!6XAd*f0BdGa0kc zD_H{@-RUn9M?p;yIz1$W%xcUdLh~&)9p0s%R%bZ9&q|S!p-G|&)r6F7cLMU6PKye+ z!tXt9Q_MUbstJLCY*jyde*$MhrL{@w(P)U$`W5rutleU-YC3OSP(m|_PMv~6hiN0L z+$TNCk_Qf28)6n>hHRcXX%=K_siHu|d8E#$JUN4s%lD(VZNseuIUlLRdmkI+&BB(f z@>EHC#xE=F4*o(k^*&%Re~URZj*WzoEYYa4`u_9fb3}=Tq+Umdn*)^^R*SDqU0N|P z=0_?E>Bv&os?5Y(k*s{9TM05Xt#H;)ZZy;oq?QgGiw@E$6BZU^q8+c*j&pL@BwDn% zmh}L_iGY^P$~4=D%pnCG4jmCw2OL3D(q93DdOOSR`&Q%qYs{4NdNJRZsB95o1N&ys zS7H%4p+TrOP7?cFB3k^uc(JNAj#fxTANH|+CznKvEA^#2^YG5XB}8gj7X`72H$rEm zKmS1Bh`#(2A`6loqTZE|mJA z|G99jX_Auqsy`#a(BAh{WHcpYDgFBrh5&Mtb}&vb6>3p}%vDgb=uw2z^K1q`)fZaI zP|h=M^&rI(yT^Hk^{%p3&QlKpoAK=$|BcN8jL<6RRsq+$98}|qY?md{I-mmlcj&~N zl%IBphpup#FU^RuuEtL(PXoLUA$!;1jsC@2Ap!S8#2e1Yw2u;kj_2NRw`ALYc9x&S zSKqK4d1g9GZ=f$(Q^T$9V#*8#qD&-?29`8n)R;?KU`$uz0QVSqbGlZhwt3*>^+%HW zofT{D+VZpw%v;f4iX2?D{X@2OBUJvE`GKOvL$!y=av3|+KU2?A5vtv^P8%{dK!WPJ zF`X%|)b?3!0;TMn$!?~Ig0c_CFGy`nR_Y*@`p?N?;4zCuSd2621;t!e-B{+vDmZpy zueT?4k4z1v%LeqlbWk)>dAO1RovHo(-%91BG-<*|-1V~RP7Uf4m@qla%&#t&kT1y1@>8$m9L>Ws3^o3~4D+D~3cGd}wo7ck{ z&ZD>(uo4d{lLs9=&B%pwC>7_ifJBhu0re>?|rohpd&J@mBYd;^C)D)B$V!A zYNPS%b-}ymNL~G6!~SURV2wnOzXR6l>Nz0+fb7i{pD2gTuwY+-cGYP5uJL27g>U*T zkQ&Gc5ceeD(rT`?!eiz@F zouiro6v^locst0Y$jm+K>TM%ilkdKI^v~CxIs-W+&DoIsRQYt`DPVD%Lj>9`^XzP= zSh=myyybpKyt{j<9?cfXWlXo_x&98Q%QKvNZTQu7O7di&#Y3naYEa-jIqp&viD>KR z&ShC?Wr0QB-J9gJIommldRmFd* zExq6eG<&9Ml>hSR87Yx#o2;F5z-R3$wd=|fvYp@7b~I=dZYgbAH{Wen=d0psDSulg znXZfqCd_68r4aawm0+ENW=@_C-M)bX3*Mg6R^qC0f*>7t5JTyf>6ZHmc`^D&!en;Z zo9-y4JAvx14(M9S*Uiezqmxz;Kv0p{W`fR9(&cR}E!!g(V}5`}Wmebn>h=NL_)r`0 z=mY^l6%e(c;DsLSFe+i#y`94jn6SngV@Mk~JBt>=B> z#^>kq72zTwm`2im`jeGx%;^O#J!sqn~cT_N7SIN(bHUeCG3x47I)c7SsDN9UHQm0aK`sVy5!jI#`~4 zrRtMQpB($?ds6vHXV8Qk?#~0d?cGONXBewc{rq%m!EHGiM?fG$z>{HB=h@ zYWaXDrmtgsLFJuH=a?V3(u-fy)GRf<0Ox(-pQJIj++I zY(Y2c3936+Nc=gm?-%FUQ?8>P9XBJWK+IsyDJ}sDz2Yw&hTbLm03o{!wXqH}amt&X zagyUqx29z;S)f-dV7K0WBFa7F%ktDFmt{%Zh%e81*K^F(LGs2QpkfUr*K< z-vo)gWcAnPb)JBs>CT5-_vy@ZOa#@+Q!d*uQ^%}@w#Yv|v(xv>B!}9j=k@68CpkEa zt8*0f5fd$u-SaN7=fA0FgjgwNDyL>dJ57i{&qAHrr~RzLpsm5G2qYw*KjoXgZbEGz zt<{9{#`VwYSdRTtG>0>7*}0TApN~5No)&wbEVWVxTs(Zm&lJ0&pT|4K&_MTN3$}cR zuiNq>d-&HKE87a$hW7WLNUcfuRc+zY$J>srwMA$4!oQzy8rODL-L;~bOeaIH1xx#Q9omZk-Ms;J z^UEKoPyS5YrbU6945F6@J^_-3zUKD#!ouwA>Gx0OFLc#mol0^XG5rFRg~0Xsmexr? zazN(ZLg$eR>!B<~l#lq-E7!Q~>kD~G=Ebj4ZEa1DA+`D~K|b^Xip(x0Yu{*gd!`Gd z>KgL(A*qFTxBi7w2IO7-=F=1Gr#t!9cR$}>cQuQp{4V?wAx@6ksj^xXklS5ma95A; zWqE}CctaU%t81CuIodhZik8!IURH1o z?}BC{LWYXH!+%9AgQ@vmJf97g49AaGPjcNiXY~Y@w z_tI548Mcebjpwf#?!m&NcH(F@a?Vu@63=Q9GTfkVolx{K|07k%8=a;md{Ps(R4{Q` zVO<8?bozVg%butsRoLExOD5p8PoE%vxtNqYXYott(_c_Crbfy1NzSGxnIn--V` z>*MO!j~TUqy#d+Xr>*Tp2t(n6+Of?;47YoObdQA^0E^{;Q5hz&XaE(cY9I#oXQHJb zs5TFkrlYB9yK47X51~?^JwX9#7z7}vSebp}JGZ0lgiiUX^|1R+LjkX$g4VXhM->6X zII_Keg(h`(2h*^jLAD1CNXWETjBLd8AveL3;tG%2(ya2Rk9d@FQbx~ zOoGVG{*}Y7DCXX`aw}(1LloI*GheUxx~Ti|!`0#Ci~Y8$geQBv9aHQuE)X|x+kd!- z^SKu#{2uedH|pLFL-)fYs`uf_bz!Hxq^vF4qm?ll1z@9pgfhx})Y7GAJ67dLdd&C7 zKX;M=!@|o=)kC=_9^jl&A9P?#d;}J%oM(m$*9nCG97qrDI#^kamAzK<0hh8&Y`^wx zKZL76V>p5r-h$$(fJhYeZ?*X=NI0R`LG!N#2`YpEKilP%H)%xOA8uls5WCZ|$hlSG z-FY!^%}W*cRZ~Qb$MgO&O_auRN^+Igg{t0&nm^{*9G}R;$ToC~6ejaUEAs)lwIJMG z{f&YaGK+$iW=%EiOizN$Pi+Q3lu!wSPZlQH9M@}^?H5C`SkSMTe9yWwWx4R!TMkzx zrl%f5t7IU-T+>sb;Tk4kXp%VjX=Igm|0S;8wqTmnbj9fCpTq*1W#k@Nbzk>K6gz5p zYBA}Ui4X2Ef8f*j1^u%i&Bwjme2^(AQ-WjVnfj{i=O4x@ zE_;vOR3$m{_zfZa58pAV?$_xBkeA>rzIi0%3-;H9_#r*mMNtHOZVO2s6UKUwteU)X zpRqlW5R?13XR3D}40#tp0E?CEi|MF{E;aTlb>6C?AYpl{A*SpWlrK131LubXK?hUy zMxsNMLASjAw_%vJ&}e+hU{hZ_zo_$f>T_a$U$VpyWlBq3+79^&@q7$Hhkvfnc3yUg zo+MU==ENWh1%&!UxW`?E8+ds3o({h*a2DZh^4D(3_66>H;-R>aYnZ1Vm354#W#tsTVtkE*Z`uoX~u0X(?bw+TAhf>5HBEyZ<}tA7z^m&uv$CmQWmFdKxnS z5&j=`4ip%Tw42P%ry#Wyl2$l9Za;-XwJY@eiDe5&aMQ=Jp@0yQxW&Jh{|H#c?cSsN z<|p|(@Y#f7wVf7(L_t$B$G3rR)X$3~N z{%l}S2I8s67tpv><)KD5)u!0}x?ozk|EgvfC7sx5(z~zvp&HQ5#{8IRFvl4OlQ;sR zHoiv| z-t$7!<5YhH#A_}OO_Bgds~x4CTlJn8ibz7YksiW;czk2%diQ`Uk2qL zM7Y>f+?rCd??5f){cD|{32+oA;pclg&V&oiy`rC5=M0pe&t;UAPp!^x#E7VR~lW8C$vfmbcX~B&Q zAA1?*!$pD_Ux9OlK19Ks$h6`(P>(P<=Sc<{yL(AAMN|` z+Y0R>5!YOGRCJ_;nCUVybi*|pJ`$HidKB}5dj#a}bpg(sx`@P$)f*L#zvs{ZldtK{ z#+x0$C!ew&PHY!L>8+xrg7%I?)1m%?(2iI!kKGLEK6Ce6=(nfS%pWoM;D($TGhe4I z#d7^3^p9=>TSs=FiDX~;HN8m&wCIO+(!!N6JG({$v02K!y!B9hOoq0S1VijIik?-Z zV~3Q}ka*_;0;?4)3E0$t47z`yaQrV63_+?fBipb0C+{b``Qvy zf9^Ammf)ee06&xM{VTUM5+pnkg`*-8)O~*Db9Vi5_lLk+is~+kfxay()L7tuu>K~Ntp#C9q73v?Li3K#v$SqIr zk(~(j4}3Jud^FYiw5QBehoPP8*@-rbL9-va_zeo~hnLw{;{G4X-a0C-CHnV-gaixj z4uRlKaF+nVAq00PxVt2HaCZpqPH=bE;O^cyjb~1C@BO_uZ`Q0e^G{I+&RN}b)!w!D zXMd|Y3;*w%!$2Q5uLo;yxa~{h7o!6X6(_`r8fJoi2Yew1^Q_b$Uw-Duq%|Z?88w!FL5=4l?jEB|NVhnQ@#MNv&&{*{N9%@(Iym zHw>){1N;Kr!r*|8f{<>bvFyNXK3HMsXXw*g&NMwzb^p+(I!PJWH2e9tlsuK= zghBJ)TVH#iL8}QkG60U|Vax;!BZg3)I0zi4CK<9k(CaNq@EtnpG!PBqpe8r+M>ue* zUq6mi1rx3FcHqz(&@(yOxq+bt6>OmEKjsFZLQ~tU0&Cnl745nWv4v|vCsKbF>sxf- z=`JW@MyNJ&bQd`axks;ii9&+cT<_x8l%t)6juP&PS-c{zu6B59a@j$@|6C=RyO)-j z;})T9I8}6779GQXxe=R2dQ?NA9p91r!*6_;aacr9!~1E6&G_CWtj@B^q^PHK-_-pR z)$zO^q5qHJX4M-9l^ciFYKe`mGT8T7bZG}+QZ3s0->1L1qFNw}FYet=gNN@CM`NM` z&Fh#Xv2+wYmPB?P-)$1J1lKR)5LL}b5dn>)Ngg?N>V>xiJ%@s6gqmfgBhWCj0he*f z@S=-&6Wnc8OLdM*3F4lAA~9!wM3q@-(x!%BixLgk`PPE1J|-k#urp&OGMs3LOEHTw z-gpE>Xm3a6z43-PZU4elVv$t=+%@7TXBi{TcedS7Ff4F+mz*MX9Pjq&x1-%p*=ys_ zmNdleCAy3?-9AYLlaPT4uhp30xR22iJ*+X{ure{L3E=rAVWn0w3E&%|g#;yDF_V!d zoCgfwh`-8BjrNuQlLu>&81-q0OKQOR;wJ{GWRP>?yu-*P2I^pqE+gh>JgXT z$Cu_$sv`2t3PjE!3fW@ZSk(lurDL7vDJxtZ;jibv`?_xU3R&8utS-0$eGFO$265Mg zK-pC{pO6@^1U3;!%xOm+?!tNQ6Apd!LPEWbQQhVFb}9U)^={fmC=1#4bJGx|w;onR$dQDDFFe zvlr}-^ENl*BHM>2H}jT{Y2R=0ovd4Un2&qr+;RVZ0p)M7>8Zd;?GR&)30MASQ|?RN zZycwY>R*wE7Z_x<@WYbxZIE`{HE&Cs$IG6*b&r}wYcf{cPBTxP3T|)&Kq8#HfxiL& z68IYue^x^Li@p`$U%D3o{{l27z$G;sPSe6k96k_|(9A5-%n&M;pYW5xgm`XHB%Yka zIirnECOZH0Jo&W!dpuw7gZM`rh7)fzRN5;w+8Yl+X38o9PskMe90nbok_h3%cPFAJ;-!RFSA4tXRgrE{$YuW=t`Z)$_ zp#BZ~g1*--+$8c5(X2zMs_!cCkc%UYl#5WsnVjLt7k+#p5^AnB2Yg3D7%46)$cs*Z zUmMH$B(D1kIE0zPULo(}O&I#deddpKApw1!f?t*x+J(ho==6fwrv9UK%-PVJh4RY0 zj&j?3i6TTqXuDxm%h@)Q+n~nJ0~Y)a6fI(jeF5tBg5mw)5scX|x6@-j0t#sglfQ2Z zBcUXXNj!(-OB5kI&zKj8T|m_7(Wx64e4a=)`Mxv|TH&@>i$nd9>ULOWgIqR1m)d}B z?pjTE4lY}kG%K%(N^1o})`=Y3TkH+?iY*1hHUADITrOQp#hrt0$1R&(WRz=rATz#;Ij<@;L{Sk>S%tp0$ zO%IIhy^v12Vy?NF-4wu+v#~kPSrHQ~&XPz5f^&{p)8+AX8ooa!gEu=3ep~Sb>A;MG zmC3fc6UHw`c%^Lkw>_ec4O=2bPp_U#BU$*7S`afmFz+9@#6QXxL^(#>fjzf(xkjm+qCcw@Ng4WerfI3 zhPKqQ9p20U2TKsakuf-u*2c_l#AoG*eptTNbJzZ(#nV>oILX*exSGN1_J#aFH|P6F z*AhOkFqB6475ix8O>NP&;&EOM84p~~neI`fH*>l^-Q4v#3|mT?#WxX&bb%Q5EcmAG zN)7>*GjF=U+M^Ql;<+$3d1dyegRSUffcT}VwDunk@*cODPb%(#ILh)`bz0gAehZ>C z77wE3ZtT;SN0ZR1e$qdYZd7IGmX&>vN?p%!?$5ZG_77!cL8F(7)F%7lcIT)PKezCa zE~S2ztJUO$^*Y}zT7K`#EdB5gxolUzWl+McjJFZ_wplxL!%%pRP))mBOu(}SxVD3q zb9TzV1BOCPr)>kCX{V13fvCHBi0>!n0z1~lB3-p*TsTmYsLsxqHl=wI@F=ds@M?g2YKe{&DxKhiiOA1aCdq5 z%a!zjm&Ms;EREMD<}=&fX>v_$OiX+T6y2rXxC@i#X`I8R3lvPOm6xOC)@G}_ODlut z{c90|mWs`OQ;#rp8cE*E7wDw{PVho%olRm7${WQA1$prBsBq*(Pu)nFBjHSWX z-c>EbzEDYcZL{rkv711tYVdomQ?jHux`~V){heES0280JM2e`tlZvMxi8sgJ8F>=qHG;qUf&xz_!ZR-InNLq<({@)(K6qabMxTv8mE4w( z)fn`UAWcmM%kdofar#J^8b4)9yJYr~+oIpw_pEoDagV=U)+ejQ3YE1+Cf2R(hYPf zZs(_#^TDM*s|T5EV2xRl#>HzH-uGwh$~_Z(qt)BbN>oKTj2JCHU1DiYoLRja$3X+%mFU5NkL;J~M%6xi&*S8o;2+zoHVktKC)Z-c**6T#{i` zo*}4pZRv2WT<%`i=>9QMq2chL&i&(S>X)CpFTX9iOo@~Id_VDb*nqaLTa9+V&fof3QxhQXOXE}XBQ_Z^*4k2_h}CK zxcHyC;tRcxKe2y5FQ!)O9HVHQY=CqWP0gcG+T(h0Xpg~pBVoq(PMKFdsYiEjyiVeO zubdtmm22tHWP=2L=wtRlvv_)1dJLzrA0&Pj7)@RMW3O|SRS1T5Zx0h|IS`0T4jPfG zC@(&ywX**y(^aJp^)zry)^x(qhRNco%W2yW!cIXhJVjhNWL`s#=VoeGR_D(&0SDRN zD;VrxP6>F1t_Ip{)kn1;O)NS0b27y1KJj?Tw=NhXnp8&WYJrKv^CY^EWtR18O7A$gYa;!!je)!B^8nI48Ux-fB+x=WQv@DJ!!5c?3?{O^i9K zHJWw2q3-f*(_!xI>a9-Zk}29Qfw{^2h6Bi)sJi=Yt~j1T53!859ZkKPov?2cJTVP^ zHP9rUY|rSQeOG8Z@C`{=%*garXk$(ruDj?WRlG0&_F^s-4d2+IzkaVRNDVC4{vEh5 zQ@tD_AcV|%Sg)luse`kXjR%V^uv&nN^lgu!qMFT&08#otqZ&b}klynz`j@82dR5>shP*Y!S_PsY~i_VI?L01S1+25!S4t z{(nsh5zrH|jzF*&AOU)22ZUI&3a#94UsLRHO<#5Gi(|!G!q30uv#l9@dixW45!| z&t*FZ0V5$~wxhLXs}?Pe4S`&9Uk(c1$k7y%)8YyZx&yIa1R76UbQu$b9*7O;X%9Sg zJ4*yO&4Vh5?!Ny#>7?s$&Xrqc=wxV~d#{4rz`B558$=pHVPD3g^>xb< z_Gh!y7>%T;2iW6?H%GoJ(7cANLtjY9P6cXV5o*b$`M5Y_`|Y>3s>?pPm0CmDHtwm3 zTR|Bs!9lo(4Ejzv8yOg7jRkvZm$`QTVxt|c%EylskLUzr&L1J(KG+g^bvJ-b>I0rbL8KLXH%LhKKGa+{C; zD6Np^gQ7EgUdwV>{FC0wtJlpEP+y8tx@3cBzJQf= zS13q}+r|m^S!TYCPU)#{j9(#)g9LB70}!J#8};?*5MLmert6}xe%={@jfp*4}|nP4pS)iA(RfRt6r9p6iVwO%tNbo(wkfbCU}oy#7%B z4x{X3KzN78HB>BFECk<_v}y5hy7of*Osg(lwE-2C?C&2DhH-1556g5z(Op?GW&$}al_?6&C?#UySS5NE>q zk9pwr+t)X{isTHbGN%R8Fc2f~GNWr~t5sVXQG z87%>5`919Rylb1^Io3tSmk>&9;OVuCM3B=usDo&dhE%yD9l+b5EE(vF%%io%dj4Q2 zW$I#?I)4xh1#1&q)tY4B7AiH`n&=9gc*W>@iuWCf&Wrxbf|f4IYC$K+ONvm?F$o?4 zEtm5VuLGKlZS;v>3C2{V4<=lQ2>9CDx-o(YhHbO0!{7jq1JBZN@fAl4gs=tyT|D$5 z;Ld)ygGqgOxTxw6QW!j&-K-FtVVurMmU^|y5YKNiM0`XWnq~iq1tdO)k$)7DQEutS z9W!aG%U4-0HJys1_Q;50ZiYdqEdDRQK*QK$!prQjlMETNGk0LEQ$2BXy9 zGc>9MjCk!;B#K3L?^ARt7!-z3_xF;BqE)t2>1tpIe}G zDanDrLEIBFkTmYJaHslrICo)MKou@dLSdL%sLP+Xh+%juz1P<`Vxmrl5ySjz{!*_< zVbLK7M?~S32=_5^VsqEtiLh>eu+uH*jEL;r`@1egMqtp#kXK2a)@G)Jew;)XdqjnV z;8M}`p(E;>WkoaBHisLxRfNews!DpV01`SfzVp1%Y}mu&>2f=kO)%h~O?JxH23u9F zfU4F!`O{ZYKyBFA!{W`*iMkdrY0z+~HzxqD$i)H;=BwPx=23JjpAufJc%{J`gF)97G(WuEW?86@zWidp$gY+2E zfu@+VKa8DI#$iOQo^8*0H|(}B@=xqf0b&RvVWKq1BWW4mDgRk0vrJPFy;Yk))LtG>RUnZyUyX{^#N)TU@ zp>E@l6sOqweekd=GwbHzrNJ!8XBbk2@)f6KbpOkj_|OU+5Q~aBNC`pEa3VrFoY?lN^DX=XM{rfhOGI_#i}yRk zN&`0&{I#p)3s#qhAq1Mxnoqd?L)J90R!HO`zS!lDpZMF&(r>fu@wFhtF5;CCMJimQ z&pPO~5_y@ZfjkpR*BSwsVnxj&iST8;f#Fap2HmVc$qSx{Thi2}ao&cMoL(tY)@T`X%A*j4q2q704=kuw8&~WG5-gBf`kB zIKs%_pLP2ey0rYjlc)<2FYJRRh{$-ps&2Nixq;956;3nj>ON)`8mTcZb-UmV1BDKH z>G!_d#%7RsPs~^yp^?rLQmq=OoCtb2mM@hElJ#ZM?dij4%CaK13y=KBzDrPIXHjTT zbi@MCB2Cp}jB9~z>;2|`aM9g|&yymy^J)%w4S9V9T3cVV!__O_yopR&{(QtP{c|Tz zaGIXKS(!$?#rPl@w@6d{(Lv|YVRV!27g97r%Xr#nGep*>t=>a%ANAh!(Mfwg}>v-tO|LNsFoa) z_FN*s5^1qHt5^JRK|@*#+j*8!#g#4OCh*|iFIa)Ue|X3HHJ)7Xs}q^y#!v5IFy-j6 zAW;_jOO$mHjsHGj_{O)j2GxEjn%2U>SvQ#+eqkz&h{l3W>zF%?g@F?^ul_4!A~K%MiArPom@|KZ}P^vC$}=-tKO+c(#JFy zsVE1C*N-Nl+Y$s~v_&Fa|sXBbhTTy|r!)5lVNp`lOl-`N9;QP#UY{FzuB#m};D zt_0Ep<0K+31xfCC|0kD4N|;YWGmb!T3R$laIq1d0kPt(N31(G6`54uMn}WiD#xGR) z-X>me@vEm=IjE|xNf>74vkvF+6h=8HB)`dX5T{s8q^atZM)g+t%LWB>v?n=ZY&d=c z9S1$#W3tJ{0zQjM7A)Sbrb}<2aj)PUy@0+d^&nW`%)fxQqMw?)fcisIJrvX1oG_ip z>LaGDo;RTHk2DBnf%6vR8 z?z$J+(4;NbIhX!0kIM<-+r;lVcA#iz&*zwrIl;v2oJYi zNz5ng5M&lWx#yk&lV!j9BShm{sXAgP@e4TonlQUCV5q9@$_6h?bvI{ee-ys!c%|-+ zqdskf<#iP4roy@Uv7~&zprwWRP=4ad3(W%eJ92?beG$XS8^iF=b=%KPGI?LaqezM7 z`q9qf-N*qn2Y3BZ7#eO{`t*|s&WXaKw#Q>dR?-B=*2pmBg41H$j2F3)`JHGeR=G&~ z=?aq7sirBN|7p3bKrG1u+?Q8HuU?PFpMFIqdc$Kah6^w3OrAY1rM5HSp3=h-m7FHx zP2bJ&`i@%i^$im7=V?@Zs56q$+^hhQB>&7t=@_oFOoDRH4_b?S`ZYk-l=* zYHyWnUX(E452JDakSbXkCa!Ph`g490?C$cuo;O1;f5qHfMC8@`8DjdZROppGI~{3U zGL-$k9a(2t&E=I2Yc)CGXt=@DszlH;^*;=8;Xj7R>nF8tyizz^qM`BQH!7n_mkiv3 zjOetIe-E_ewsYT3;;RO&6seHUQ~oNhEG^gb+5okdHxU~H!Q zo40-&G`4Y-EOY$=THV)ClzTD6gZ*PAq}ByzIxZ zQ=enMzA4i|uQb z-paHDT`F9dV^pUEsj|rUC5))7m~=d~B~o?yTy{p}am&*HsXZ+2`rzs7+Vw zkmrbSY|m<>h>;Ol3>JilbxAimeO_s}7*g*p_cpoA9Q2H{TVouuze&gzi$%G_6AjR$ z3~$-kArV~m8de>dT@c~rn@YM6Yffwcx}Dmz*wTkZXH=}Kn(1R(b-FE4=cVh z`CFYG*!G2mPW*9_lqkHov>aviktRL<+J}+X^FxMj?AeAM+`~1e^Cm0u*2{)>yKbFe zHP-Q)2T~?+JddYvRbAbawe1`e84tB682G;|(uAgWGYwhOIH)-=s|DT|Bz`CrAr(Qu zc6aTfp->;wZgHcpakAcSywstco*dbfByov;v<&6dPD5Z$;Oh=}+l4iPZ5-*(oqV*6 z6Jc#SRzn6doqxKZfLcOEgKGZU@Ls4H_RofQS|xv4$cA@z0-Zq56dhyf%iFk7$^+(9 zlTTk#N1RWCT#^o6pCQVOOQcwMR@A?q?_+EtHrGi46&EZWCN~}~>>l2Ae;wygmES2g zO)ZgdxIcpYWikwX>*bDS$Q_N&|Dez1-mmiYGnb=~`y)-(*;M)aiuaNq!-A5ua}I9~ zm)vbMs)@?}MWKV${~HR$wgQhX&br2pj}A%oGUXf_cz+7j@HvrHXS#tcL*6*~KS^lk z-~T%a4MQaTf2d5=d7f_)xGkobVm(thTZ?vD_C*yRg_Je~ox@^0hV< zSC_;Q2{$=d;tC0VbAqym4P_#FJ0H0wa={^F^Z6D#`_^jMZ>rf0hNq*AQ=|mr2e0yO z%VXER=k&2Oo5}Hc6Uqnc{kYA@pjS2+DkZuFr$wqpR-KqP=b=Zd$2G@2lfracN2}Y0 z%kYUsRjUj%P?g@hZP(b&%64r5V5)ClfwL0mtx}A>RfJew4j!kjx>vT?+U+%~?3+G#kwHj!Io) z#PmvcPTfgymVWx&GX?wg4VH>Y{+IZ#mz#PvT5>h5p_Y7E?CPMInJ6sF3SyPJ(ju_Q zSGVLZN1n#L2JO(-Nly*A#*@$NPtmt`xh)zu(>}U!IsE60%?QK&%&SYyl1E|bL2FCP z-5KpMCZT+#L-@t(g*6@5cZDTsDd&1>W}qBvz5bF2hmv-e_yyZ#eUogzgNcZMU7D8g z<9Th9KMxWZqh&vC8nxx7O2*3B4VM?k4omSJleO7TJ~pRDNOK-GIp)-U=KW7_YIXI$ z2~LmkM4cPH-->i{Xl-Mk$Ttlz9AOWC+r~cjlZ;sIGUj!2!iqzBJH=1e#%SgF6c|LR zVEt;lx~v8Lmr*<)#{1^*%oAl_bi#Ym=+o&9`*EvHL!sRDK_Ko(sfTslxy6CRTwu&a zKRAl1R#TBxD>Eu@deLTUqtxF7#9>0DTMUU-!p$hQ6o$Nj9M6bL<%b4@Z^FO+z+3^a zEzs1Ha)JQ`kFCpk+tWJn54DVXKx)z8f@#Mt^NKr1;gWLm`kY_!y*uK9ijG=%!_ZEf zq53O53PZeDKq(;7pEUHQQwB(WrHOp=0(gT?k=N>p*FBA`ZDRI5&@Ulf!8Df=K(`*G75$o{<^&eO-{F!0ur zTb4pEaX+y(Scrd|SadtGbb;C&|PxWm?Z1rQt!Aw@W_xeRp1%pc`Y)g!m!F4afGJfMb112SB z7juyk$)m;l3|23B_Uk;104^0dAF`7wkzw&>?~x031fM)ztv`n(-x$7hw&%WN$G%iz9#Kd#&eNXLFbp(c}zK(NeK*Gt}gro~@3x}DzdqN|-&?;QU% zdsS)B*dx}(*ec^EKWsHKv^_f_Fh7OSNH0FA09d7g_kHPW7g*4{k@K!8zSXBZx1>TB z1gf5d#{%jlZA*z^n`NxeX{2-Fe}-o>BCnKfNE6IeuKvuFnY zH2^FpgTQiB2rMUqz;ZGOEEj>ma`K)gEADy*0G3BLRZ(oOtR*1E-?EO|zJ;(y;a|)q zl{a}#M|)V*iIE76d#MT(DhruffEi`0nW)%R(K)UXA3J%f1%Z%P@KT1uK(iCtA21Lh;`Y)?QU7HZ3Tf;K{saCd3=-OncR=Fh=;HYDYKljFeA~2S-dxi13g`2)PjzX=g@u( z;b#EEY)(IyRo`kfkEVh4Sea2Xt)59{$sl+o0#QIxWfFk0s;K{%QgSMEA%QKiM>ux8tvXXG$ zM>`dM8`WQ)>{N4xR< zzLF*1wT{8h7?fmZJ8YZ~L9gTwOina=)s7ba$|Hbi&J$HSc!wS5Yt_M?0Gl~Ra4^wo(iwC>7H&w-m3G_!p>&djG(DCtd2qz{*iWPt-oJu=-64I`*$zLXF_Ul8o~lX;Z?*_F+Y>m zKb*`u+pwMIrsUrS!o1r7PFAH0VczW!=G_kAWZn=?cC1=YzURChc1tZLMa>GJq6k7| zzs|*P?om)q{PAkdGn}2IN}Su-Z8igFaIX%`LLh^L9t0%~8y2vP#btwX#q;*XEC>1# z43!6(Q9x1x6O{1I8`NbV%z)dt?W)$8`{c&nN;Pox6a6H$YC$iD2kO@#>PkqMSA(G? zneqtd2F}uMQy3z?4EoO9N87N3kNY9oJ|+m58LWh#5IgnAao%xU#OG1G7&GFFF>gyi zef~wZoh|{660;YtD?ipVBWChvD2xsWNnXWkf(}R0)lwV#VS3Q(93W*d_*dUWqzxc~-VKGSkf?uG z-`9v<4dc%}8uV&{`A?ZGCPU~SJysL&NnU8^YQuwd(Fx1kjhd$B_Jh>^qP3+**KhvG zKiFLgS>=e07y9-cKhbZRWsbZ*&3yS4^Y{WdQAiQWZzjD`A? zt3L)4EyKmpPOR4Dp__0 zKqU*f1S(lQkV=+Z=~9*huOlG5?I(@t9=2UrL?%*3?%ijUtjJXSmC?r1QJ}VZ^9a|& z`Zr?O`9f;Hz`1u*LU7XJAopq>PjDZ2fk+)|YWW+z%} zc=x-7#`cph0|$LHOMf@h1c8+?LRdtCn3W)bN)0-BwMsuLQl@gqC8TtJzcKTU$qN| z4V6@y6`5vAJ-KhLKxpVwAB(f@*Y`|=FXQ~HLOdv7eD(B#c556*T`c(qi}!qvbn&HkoGk(gqXh@-FVd`=-?7=+$Vx^TS=|wsfKxQ zq2DdUy(@+h_IBR$2g>kA$S|K4#>@)MDa{#{A63x(I~TK)^Y%h{N3O6-U2O1*~d zr2jSW{^PeqceisjoQFx@ngb7jU>cCdnjWkAY+%1#nYp*3e&S|&YBV{>i5kno3f_QE zLH!@_Z5Za=UfP~J6BRAKMo(iND_|>Vim8ZS>;!?{j++TuCWWl7tNSR+?f*jW8KLJ9 zk8`~$XO47yrXSlL#Zl)~1B&Xd5(j0l7${v&q^m*SUEH_R+x{N2Q08sW%rrfRW?yT2 zl|lEJt5hXKVRw)4rpgzXI~_eyaLM7H@KiR>OkBJ105?RJo7NX#?U4Ux#&bp_ujU%X;T45zj)&x4cO z@6c##j-lEQXP+w#`jm<5>cc>H8t5ePI}xXwR@&nJ2OyU{J)7pWNtz;FZXU26Ht4}% zsv(HHiPMQT&MBPZfriWG{?cMICVKeVK4vY17qIk1!G&dEKgz% zOQtph_4}hfZ(wvwWH-iDoxKK!(7DUWd3&G%XfAFQ*gk)LbF&lzNKhZWF-|Z3+MG&x zkPk*5>C%kAb_c2|$0QGmIx{g>!uEBPz0Rq+@zeiU{B@>yF`>BD>58R&^XKA68CH@=S2WEB8@UAMe8z;-$;Vyi-;W=O%Ic3s=bz7w93S>J6HmY+ zp1J&Y^cn}mlMZW@Do!JM#fQwLL!BQDd4}E@FIi1uReDU26bVt14bzY;GrvWFkPhy9ne7=KCyQrYRvZPYCmKXJDEf;e|Wn0a**YW#(aN1G$^p| zDCOeP^hy1F-VP()n1?H>i*!;QDJ1=^r>G&Au*a&hx9#zyd?sQ+9Ri{TBquaE$ zBx}UfPTTRe5vPZ(`ULRLU-)fF{e_J{#OclqO)NQixN@+o(mKY#3S4{s$QG^2dV!f` z?IMSEC?-A5^#xNBzAHW5=S*q(e6M%R-QiK+un{eztS>agSi}X2wduDdH<31@jdeu< zGfw~Wh~e4gseR)ubNiuVn|lSxNzJYlsw@_sq|>R^2JN><6&AeRNoN$Em0lmy@}A^P z+AsbjD6XjWcCuWm<^3fkHV)n6ShqgVnc;1W^*QH5Ec5Xz69J+9gV@hr_2+bVk7OhM zF=n=iMBEMGsIImJ8nK^JvIABk0`ePRp9n=B9|!DiZX>&o&#&FNn^vrkseD+IZFYwP zw4)b%Y#$M@b*Ti2B|xef_WajJk?r|^RekOb`v1y2M?Rk_8k;(m1l4V9o&$-nD)`8<|yRi}L{_vm<&S7;YNm^i6-GtP~_ zj{=X5^3wk(Aha)(9JFPKmH#v=_t90sl=_K2>V${mVvy7qRDUB} z94-<^qcrC%O6={t?1!`Rrui~6wP@whJHT)WrR01j{KM73cI1M!3%1Ym-PQ1kV^xOF zBDfh$HF`E+0Q<4KowvJN**oODWy4Pd%Oat1a+XvVI$Ll$9M@hek=UwOY z6(W72%mgHovo$&$UHw7CJw);pMwTsk&%|~NOmaLqdtRSdJ}^B8?)?_ylX(`7 zL?Cp7nR*YIcw>0DUQIk=yT({eu?;`5#TJFRMJSC53(>|7;@M+mZcZ}YRe%TK6a9~!gCA58L#c|{ujj3Z#KK7u%WvirQ<_akMdlSsJ?8YBre*P!~ zJ^>%CoH#<$?&nyuAd5OKmgo7nG)f#;u?Cfry{agSyQz zDg?&$A1jvDHqv@LDm7alx(jv#0+wk0%3dSpaHeD5R)y%rqI8@lpVwlhhIMl8>N1&b z?KE+>v?LSi{B`ElU1Wj(%g@8rpc6r5L&>>xH}zxb+~lTj*qy#9%?gKvOk$eA+cIm1 zVuBmW`ne}@_{89{4=cZF{PFf?T#W8-^(DyzqQ#|q)A`@7oomOI`PIe0VDl#Tb}iAI znv+piJE*@1x9>R0@=H!OM>j0p*!VpK)3ui>wl8d3n_&8p=MqcqPE5R9n>D-d zIJ|Pk(GV$(f*N#Id$XHfE$?h@&c@qtjQwW0h@`^Z+UiMDH(Yzn-Ezc7&;7XnQ?A*j z`qR9wq4Sk|Q$WA7`APTnZE=0teCnO7O=ijPjBw^?nYFtW$YFd{_WlMr#{PPKd8f3$ zZ&hM7LGY;cdLg>5@tJ3e<3>{A##Mg4e`WuR<}ZX~l&4r_vdbcCCuH~F5Ifgg3 zH%@XC+tS~|B-Xk@SBlz*(b!IGZ-Z+iY7d(hx$Hsf!|=)Pmj8>ejgCcr*To$2-X=I9 z;8QCaqzS;(4S6Z19Kzpe3LwC8;HAmDwY2ZKOY+$HC;>DNp%rlRC3d* z_SPTV(oSvn2<}1WXP)g)YfPJ-L9BhUHDBf$+_*e*=t?j@d_Rek@TI7v`2GR@*b5ys zrX&AABBq1V-10N}ymC$^MJ4zX-!tMQZ0&WSRO;q|#mk3NIc6U)4-qZkc$RTE zPf;#oUL`c7H)#J!81Ykk6B#rP$^yXxpK(9860TcX5kseXQhoPUbz|v_a{$KjUugOk ziEn-%7l;%4LAkykfYHTqhAk9zcHtsv21mCPv(V<=$Sm;q4ifk!^Uu+CeJn+8Q$@xe zGr%`RzHB%ah<4WUS6;Vs8<9IyHE=d?R{WgbrGI#~4nAR!(wfjF!A|3$=%;w$FTHceB*9fr*>Xoo8w7PF3e&=?S6m`EW-!sQdX@>SE6FN%CS2rY!oAebSZU&O+%_@;PO{6}Vhs z4d1SCB#{8yeeN@x`PG0KdAZVmjV`8{L%K+5_zOuQ`3?&37}UxleVx@6>A&jC3;Ayv z&IAg+`@Iw|pHZ2LUOui*y~)CVkhw^HgE7B#YsP)!QvIZvI4q*)JpR>U_M;4p z@~+;7jEZ^g%kwM@8aD1)#Lcny@$>2P=>&f4X~-xUD|A|9jH-kTI5PgtFhQ5Kd02>T zYQ97HRVtjN2wR_s04Jh6JV}!V8Kc3M7kyKagsj>KJh~Vl{)f>%udMhT;@l@Fhu=nP zh)P>fjH!das?Ea<5{%E`1&s`okVTV;7$a9cLrVkxXH0ffrU49$@3D?U4+y^P4s2WA z!|M`MbF%zlwTY?$3YR0+&NxWH^`KnoU6C+25kr2kAL%aP zG2XFS!{u=@^$UKd!Z)bU`_0_=mR91|a0`MzRlkvLIxi8=9^a2S7;9Tinpb*61t|WE zY0JM|MERT~Ii(uY7IPJ!TS4J9IvYvhB|CxtSr;Q*BBqV8v?li-+M* zog-SJQJ6|v2$5IVKtbe&WLyd<-K`gnT_KlNdWjyvfPUAeEiy2^f2g^pI;&69gpe`B zzqkQTzp&VVwc+@tfysWA|H_u|WW_VG3`E4eyC(Gv)T*&9_FDer@Sso>@NO}~o-48KT?8uslx&hND+efS|&a`NclfPKh6x>mz$ebfAmT$~o1ZubChH z91q&uXJj1QXTg^U<>I?-Zouzf{IjsB`z}V$J*|MjN>^qg&d*qvud()G-xJez!7Do@ zCBSfPt8L!Rry&uhvIo=yQa~B=@BK^&telr`wUPFU>&LiFW!0W$HY|^c6Kb6*Eh#Om zNc`E2pb}Qdi;EQY6M<2K7~fLJQe&V{#(Ei7TIAA0bo*gjF#j@zTiQX6RWjeB$!q%5 zJ`G+4*`(gijp_%nIi#N?;0C^LRM7N_yo$}Jfkn3QM`!ExC-LIP8KHKe&fXv|w zOWeln#zw41XoUtFWzrn)rxmCCbqeho1=Vf61(~;<)!(RGRj`QpN@|7AUraOC<7Ii( zs$t$7+uEq{2%u}V0Z{X?>t&E^|sdtt5hI(&ZTkg-FHPtsV+mflgeZ`oGwFr|?R@ zFW)zI#dgKEZM$OIw(X>1R9vxb+qRvGZB&x8^80t6-Tmx7=j^^X&%W8YNV3-RVy!vm z7~?a(@5Gogg>c!hPyAQBCG>CHPA7i8Vv_^WV$P3)Xo~4VxiG(}t$*g2+cWd)PJ3qB|en_+fTwQP66Sy1rBr+2Rw|P<8sQscv?oQ7~9!klP9W$mBI2X^?ew z6yMb>R^KM-sLm_#M4$PF5r0_@bGj2svyX3Vke0_%OWE)$tVEMQmcmk75J4(SV_{hT zKx|=%(jb<;BC;RPQY+a>ENh+j1bFE@&N4SJw~4zsNs7f=WGagoe&o>9&KXlshGk?I zXIV6slGG#wzQHr)%3d7w1Te~zLL31@N8rO(+qSnboKNg`jd=K#q_WhCT#8(3Q=1Ag zhW4*=G&lF)#Qc~tOPr+8J{;WD-nLQ3lT~zJndu3I{Sga5+R1N9y&hlLlq?01O?4I$ zA{g4ncYEu-77bTrnxZkV(O7RderO5pZjivIPMXwNYN-F}-LHEq_!Fp-OSQCn`zI0BPJLlSq~6dpyw+-*K_`$v-wVe0Co<1DADSc^XLbLbj_? zUD+u3BTW!D5ZGEAe;5dYzDf25*{SY*NW&M^utK34f_H9Df5%=+hMvhE>#VDqa@J4n z@uctn;P7SD+RKbkETqGTP)LLvOXolT7*>f0PO?@aU43rg->eHrrv(v!bPBNUPw}|{ zKssfq1*B68!>@FTBAx2w%=wj0VMe)lr(A79>qZWv+j~t6nbG|%;w&NTvzy@H%UMxX zU?o8ux-@mXyEG|T%d*ZK^bR`s7o^kGiQI_XdbJ9u?>TT0k= zDnFU3Ju|OdtLXnk_l>1RH#_ZqPaaOQJ+f358|baG%OjMogx;$Er-8ADg7|J)=v}u6 zE=hI%X=Fs~WX2l+Ol|>|)X=TR9^3+&T2zfyPDq0HI)`A{;t+{FQ4{ z-*ek@3oop6HMC-$v-PWOY{}Ig!=fMvqv{2kYVaC{WlS{2V>1WrdSDi5HOMx&9GP5a zzy-B>|7F&H?#R6m`i)w_gXejZyKFL%ydByEcA3AU^!c`lVwZ0obP(pRb<1&iwxD= z&^k)9*ZD!qDdimi9Bqrn(>-$M7wnb%tq1hnyAON%6T^tjh)vN#4GPm6z&0e0#TmU1 z2aYD4{;v+)jEu5P6kCy!sDrRA)^v#qyX49d9NO+V4Ve*yuM_)#KNfBN7~ zBy_pNOO7-&;kmbmzM-}udLrk8+ zZBgf-0qptjesi4?5`1>E)0(xc&YE~a0`hobX|`QecVf4m#T?k=4o*?ACLofaK_=G>F`d(j6sLbFGfQbG_aAnsTJ5wLi8cYojSM+{Si6LoBtHR2 z$Rew(dJKekG_k9aNYWE>6LeG&Q^~yee9ilLp`5Lo=jR);MOp0@ihCx2vlwT(PuUJ! zdjRo%!5#YeU!s)gv4Iw}r@F@Ai9tX1rpkeflK79YT?nn`D5?#nqs*o(2wy+ zXoWUX5z#lg!=3d`(?{)_cL|JhQ|p3jC_`uBj3Y^cNx#!T8%RP)3#|Y&2cZ>!=77is z&>X&712hMy41nf<$Oh0Hf@_><#g`o=hbZh6+&hP>cQoN47Y^G}_5(vG9eAf)z(>-= zA}oPZeV`Xl(@%E7SH9YYjkUZ#@kiL(-qE{(UP9kjyTa;|3|tYJHxBeHc@ zO~#&wdrKNs1Itk;8v#xl0Yc;z%poMDX8(>+97{!IxsE%S{LT za`EoMX+yV=)y{iia}G*q&uzfB16$!y4g^3^`xg{hs+S6UD|gStPO0aDi@iordj0gL z0Hng0hLx-izO%Q@*$ou4OUZ^-?FDtT4CTgWe=2~F{$yE>(C@ct>hHw#K7`|oVp)Mg zuWJqJoY)%3Q@?)anRBCX_FrPU6r5X~Nu~Ig4IQ}IgcBS%U`LY*BV=!02oekdMEh@? zD~a;ulPF&icHqk=BLF@LKqEke9r*G|vJ|?bV@H5bR$8J4pE`7nx01b8)K>;S0S1Q~ z5Kf6*4s7xyG*vojiPexDb3u-Gg;?4U*rU1;=gDvDY2}ni8Mr!IVq=@2TK5-gq%X^a_7Cg= z0w>r%fwM+&nH%Q`Z2xEw9I#N1rI>5=gd7Y^Vx>^5#V>etWcZXxU;PP>t)byFu`hVCxD*{iJ;iHkF!d`?7Db@KX> zREmTE;@M@?=G8|HlxW4=@9;$;DPRlK@?^N~jz__ROdXd@$OQr1^gHa)hiJiD8JbKO`L|2K z>;fqhO2Z!$zrUZL0lsjgy3#-bQ3`4;pe9AF1=OUX)_|Hcas^P660relQc-I_O-gBa z;uu4Bln||Gi%CUfX3$mMs(W8UyVQS}?%H8U#Uty&v5ZI9^$Vc6YLzV6n+C?80qRns za_p>YKsRQ+Lbw6kq9m&XFVCFX`E9(u)}Y$O)Lz+UNao&<9Hnz4Iikyys^b_8kO@~8 zli-Pf|M4Ak38XUz0C-0J1D=Yk5`sq#e6Cm2Wlf%{0xA!p4bRk^7NrQEHux%>3NR>~?o7I+?y%Qp_`QDzqa0S|cb;SZbyH zv5&goazj}{nmR#f3;7ipC>XKtBZhq(0OcwA_mD-1f|{b3#6E1(xaV13iIY!ICH0!G z;xsj}FR+pPW(ivc#D~(LcWr)U7RaUYX3>^kcy}TVAm`I52imw&^}{PbdtQDOFZh;m z&A8yKy+OOJe!KsK+K9-~YqzV6&MuB7PW1me0SXA}e>wr}Kb>G?XX|WY z>rCfvV|}3|?X=&5)ODm*^J{hCP#|I_Bl1t>sylmX+JZ8V$HJCAazYImG&+!ms;>{W zcVeEk&8PA2QlkHYR)HByom0I~s*Q50ZMyiDV092QnwVB2b6)LG-D#ujf z!8~M%#TXNts|h?~rHN>Ec641CRjvA$Q!$);GFcVwy``HcIBYJr`4Z9JK&y*6Q=Ih& zjfCpo8oLsi^k`GV`VAavFrw{JDd74|1a07Au)Dmc69cJZQb@i_rK#tEYc6$A#=DpK zL^fL~HOXH=<(y;F+(;@9_PaWY@mNo{kgXZ;ak9f4`nq4Zd%9n=aI+6Ox{;Cf-ZCRb z4Ijzd&|N#1(G9LUsSH@~d8CoLx$zlQFsjH>z&0=}Gs&81bh))4$k!2YBnP{#0u*SgUSMAf}L;~jAjgH-fr zWKwt+Al~7bSf?9kK>VpDPTQ%H{$FX3&cW?LJH3iQ+Z$g zq;x;xl(vV~BLz}H$DQX+!My#I$hRv_qNF2n+yYG<;$Alr##8(kxK^IUd(dU2aj1oR*tt18`Ek#7CG1E) z>@2y1(deJ0{*OEAYmG~!+szSBt>t|V^gA!y&rw1HFAQ2?GS>8HKhzZZniQ%WQ>hwG zr&Vi42rrN~4xJrFesG#?0Hbdq7^8jkXYN~TWqJaAn!{Cy3%}birNL<8ej9JyZq9&B znZXR^u`d5!vZbDdFhOzgZMnTo!+VS36%w|c0U@Nwjyr&z{#Abd@%ETM?5{d+wai&)fD?GHZM5Jm$$a#%n+E`$<>+v-u%- z0;5yW?uwEehhaY1_ayO$bc$JbXepE%4IdkQ2<@`kB3iO1x{l^%LTD6Xh|5_Mv2%N( zX#b9uWlegr{`WGK%1Z{jtBbWEZJ%02t9WH9@&Ey-D$rt3Won@9U$6(QmFHb>u*Rso zcE0Z+_75(puUt~?o5lV{;upj8-v+|@^=PT5%1y*=Y=nh=pBh%a84CE!)%^)!?_@)K zfyDvGgS`m3%3+>S-KBu`=p$j-28(6ln(4sUt`Y|m(sVwCh%pKYvFg;E-s>MmSd!w6 zFJ(1iA6%g)h+7qIySeX`ZWkCDQ=EGX6%j#QjAz0oPr*jtDi1p{zNp4Xydf0)yZ9ffJ$TS!a=RM>oD=KK2 zY)p;lV7^tsMzidCYM8_TDbz=77BHX#gkXHkO6|P?GG-s2BPQc2)=NNi^vbL}`cG1T#M-`q*G<6aa zui}F8tlN-9Q%~bnVYZ}3irXv<7+4I#GB^12FE^6T90c*C9gK3dCTma)_?-1R*=_eC zJ#7PlT-~!t!6&FHyX5n&@re;5_QM=%w`}c0vz}dJPA%c-t@xX*yg-#v=@SKvjrRJ3 zHjW0qeg{FrSXk2PJ!CElM2rp5_Kk46nLv)SL5nU87`rm+pO0m{h|57fwEEUQC!KjG zT5@OH`FhW)jGthA#WM@Af0VCQ(vsAV&ndFoKJ=V?}dV zQo-YH$Zoxj(~Hg?;f{akQX@CBY?51=&H55&iqh~;{ek8{)J(Zbivd+Q;L=fplc+Zl zHs@|KO6v;qrgrvPogwd3Wm%9XTAa>^x0pNvpJr9uNzXO=J`4x>yg)jsiXzd96?n-p z^6gc07H}voL+nzoYZuf?E)K+ZnuVv=WCp@X)hOE?O(>5Pqu!I+W*r2HYP0|XevqqW zABf;B@LuVl`XhG=hFtNwICg;}Itw~jp=IV=&L3z;oIVoRak|8a?Cfh86T47i$QMm5 zB;dC^RmaYh@@}MeR_=!r3i#jJgeRfv+j7gRA0Vp#nCQN<{r#j`{sjHvoPxsa0B0y5 zplc?ef8(703R}MD=YMdHle34liPIOg{7mX~3?f7nxO#^PYe%+1_y(B$tT*EeB*-EY z?*cUV;e&<)xz5tB=svCEsv&n&nm9Vri1b@3$qX{de(*PX8SU)wo7QXO)Z#pJx)_6; zrM~cRvh2_IgbkAV3OEgRX%=%)e{6&BJUsRRCJ7wV2t|I=`K(=@U0rr!?lWI_!y- z>hu{M?SamxNR$B2vrw1-sL7GO&-2D-HU}XLqr(@n!7H4bmS!1v3V$zJs!0X7%?rc$ z(2)m)k|6al|GL0oc&tLllS~UX_%!`K*JB^&UN2AB z5uEG4Q|g+qY+|&ax=Kj{^PBbP+$In4X?m$vQS1d*ODXGjA)iF5wph2Ql08VB%puZE zy_ma-T06peBO9fW+Js?L4qK@Xkhu-bIMCD=fETBq<8Y$@pfikdb1PBxRRMYZk}w zv5KNxfQQ%?LaKwE-uf#``fiS#`BjQe=n zLmKn(n&&RV%w^dv`hTO6@J%5M_boSAWm3NA*^BIrXB1?}83lpJ!}NgwCZ#o4hiUlk z0e_PEW8`Vuc0nA=_}F(eZpTD;VT?sMh`G<6DhZIN3cBd)ye7mN^jA4#N^xK;pnlfW zzPgp<{f*`C34jV!WPF1(HGjlP)+Sz))pb9orDG(H!~HA4aiyRvQlP*GAC9!@RH2GT z{u!VH7EphEV1@)K`AZ}jGXWtPD8SD*4}`~&wyRe!2@_RGUYJ2HgQka*k5`l6iEX7ce;5C`|P#%l3oI=3EdXalZ-Nbt*6SM&b2i zMzS>+u97Hh^_yX;@ghdsV#0LjzoC;v@t2#&INwM=S*@y3Nv>i$(k-zx54M7!tR2ql zBRCqJB)b-lZ8^c|%vt#}{7F?Xy#*Xni@dTDAF`^c5~bxYuETmnOOGzKvrv3=5U#l>mp?`R(n+-ck{KVUzkaO9=fM?R!YSV?1;u@m$;opTtCi?)rr z3MHsTTpdTD%}r(Z%G?ybCu7Rb??Vc$Dydi{IV7NS^|XFXv+^8@?0l1NZ3cOl$oB6g zf^qL@xNn^e$Nm|Yxov4rm9DWpH_1M`b2-DKhI^NcQtM!w`Gft=Ji(CWWC@kBJ5Z()#lYQ%odR3^khYSM z{NY&n)#UwpWx5a(=_^cgp1_mF?#`dW;0S!_W=_mrvg4B0`FUDcUxKWTZQ({44@}sY zG1!b5W|>(kDwf7pB>Pqvm>u&7TTDzTVnvC|GGQ=}7_mt*`&|S&L*yB8E7M&qv3MgwBdf=*hv%hOJx$}qdK?r#p&aO}Bvz&GKYo1Z3{?>H>xn<=8Q2kvw5zS-bL9vk{JdGkluoVQ;vQf_R)qw;u*nwC zW3v?f_De#G!_8YbReS5?xq45=uk2kEcKGh+ZXtwB0WDOPy@lnxfAh6D$0GjzfCk+J zuVs(OT8i{A!Z#Dkcl3|wBa{n!U~VZHKg%ALwLQ*gikpp%6p#+dDaaqmGw#q%zp!#0 zdEV3yy2Hv`jxQ^#;MHE5gT{*R%TxMY zy;S4j(`<0w5BQ5N2#eLHPa%PT(i#7qF8;sBj*% z1=)ont0ctizrW%qB*=_9ie*PL%oAbtU}bSnXE|PWdC?u8Lpuqodt3$U?o8OH)n+EO zdmUZ*z0K-yZu9;u%n#eGd{$`4|{5h z<=$BmN5V^;O-V^`mDQQFxb_q~obMLRJLKm3tEjZPwz5}4S`Ck5QQ70rC^DI8H%#}K zy#YMMyuv4PiLI0e6=y=c9YWF>N#&*iCksiwow{oDF+6tao9xCnyUbVwyUg_t@Ps#N zMvWH&pi+Lm4*nw$`k_;$20S&BOy&GoixL~(A$6-VLk94&$)ADxmlu#;(@DS2T<)jId2;Tp>sj#L2= zM(Z;E8_occP4yFAMdt4fBvkkTCR)9pevB!BA|+fN??Kw|<}R`m$l&)Fk#KlXQDcaU zeQ1X~)0p`(lmj&mQB*I*GT%IRERx>51mW9U6vFFid6__QxM7F;N&Z1p(siYnC-@>`2`se^-*Z7V}`l6tDQ2P4xvq%)vyrXL{4&)rXCidV-!6-gfxqG_(nFgGWib5BIB%IGS4uhKSxitgJ0^ z91%`&NPZHm#-S&*l`w#j+Aw-m#N7 z8?@JCpI5S&`E;oS-ye*gh7Lx~iq_dgRc@{ByP?>*!??x>pxJ%5zCx)DQWj|<9CKB; zbkr-cUH+qLlPp-Kq<2HZN7@+&Ql34g2#v%SkPS5nWUjnX#eAVlwDL<0P_A}ho zSxSVbRL?^`gy6%GXc8t$~1?$tbT4~Rv&seP4o4Rpmk4AH%bqyQ#;vr6OV``d7=l->QXuE?Ew$YZO|ES(s!{*Mmn?8Z30IDP>Z7nw_ICrJ*UU6=6BQv8 zsvcVG*JDuIHY&YJGbKO(cOIKRf9ghltQXn4;pZ=Jj-Ge1sJ`bLz7t>QR+$ zJ8$~&zCXM=*a|o1^)%mZe$n|h-`+5P47&Dn7 zFp8zu$7V9fM;rA?jtW|@_s35Zr=?XAmh(k4LiGnIJDzw&_TQ4LM(a^@T}Lcdz8CM- z(}dy8ebd}4lw|3{As9jUAm^#sC%IB>?ZVdM%=L)pEpc7Mu3~O5EJ(UYP&BZ2_PwH^ zZ53OB8a+89AwVkZzo6p4xJh9e3M&P&K5{~hjjC#ObiN`CR8WjwRwUU5I}5QK)ZoYQn{D6oA=pD= zY?IULv-Bg=aA|s%p7^cWz6mMsJ<14S&}>H7$eV3w?t0NIY(s4BL|-4jK$;$^LRS&A z2p~O@hFjZU+vnePhYRbezOIVP1cqQtR1z=wmp0ILy3fuQ0qgbW-kms-$$D{u`bXem z7wNYB(t>C8uil+mROSR7*R>{?1p=%~;|M=nmsgzA9m?H5nyv}Mo+cLvAivotV!JMF zjag>E@w4uARp_24R41ksKt(tKcO~4w{Ih@=jAl$4upv?2E1Y={GN_#-_vrC>yrAxr zp(f~*I18714^cl4Z6vN{J3uY6U;Vm1ul2rYtuH`jr3*l7pY;E$w8r#*XzhQS4#)i8 zr8VaNL~B}FcKa=8zE{-@8-lZZ5=)y28OF75HBkT|&d|(DP!GL9WPL*-PhhULv%4#- zfaFJHW->VvkNG}u=I-wI>0P&~q=a_67*Rg2zGxK9t!bV0v}yaax#*3CQWlguM{ugC-i2mYNB@%;r&=j%Qc;0SiVj?CVz8oto=J=lP`@QpCS~ zAa=RMTBg}L8?W0re7ZB(e<{s2c$VDUOaG_Qn1A zG03u6CqQwb?Kz*aRhJBtr;377h}75UOD4wBV7S{^^!S(Ppk90%C$c%P5^k3rWY!9T zw}~VHPtzHsTA^il=Gu%5SSVn3X)^jXX!)@l*}~6%Q1{+raUK+5uYq;4`HBz^2A33 zR=STE=`za2_i3w&FF_6w_pboQl|4X^i{r~iB(6K0&~nK93sG!uv@U-@?zlhW_E!q- z0wE0un2%opIGks@D~%tPk)t3p)F6{3&EE-NPQQ&}5@FmJKEnDxZ8VK0SaDt$?rv_KH30IC@cSeOtZB9wKUQdWs=Z6M_s>^|-=YB$W3` zI7v=3a(DKE^WtX$15=)PFS?Ou6JI)99pi#}+PmUb#xz5fC&}?BgRyPzBdw&Vi|Q=y zNkz)#a-F_%LcPj?a)pI$#1C+)29?4zp9Dzh97(mj3f^IbkIYc03oq!Gc}?P>w_+6{ z+A*fp9uHooenf1q%DiRU;a&qlsA4a&YT!~4Nl*W5S_Z00@}#S`9)lr@dYFNx0+yut zgG~J2F@FjO_f|K$%IKyFp~{F&ipd%?oNA~-AV6$tYom8*Go>`E#Fn(lwSG{GTV-m= z%6l1TZVR=A@=sgNv)-w-xNw{=$zmlTFZj{sJGsy9GlLX~kXNcLgBM;&JMlO^f1q6x z?T}VnUHFi-VI+)hP!wy0+t*SXW1m%}yd(scA0FePoGKM!6D5b zo^N3_z+qvh#{hAli)1?Mh2iOgak?5GxDmTaD3yZZmYZK7I}^f}3NR<_XY(9DiPQR0 z;vDnO8Wz}kxtIUE+wR|HKX0Ra9z^heB7|pw8SraQdonMR%gF(5ubj( zhGunyjUD9Pwpk{TbP%=VCCEgyF zK_8n{_{-xhVaOsD<}Dv2H{Cijm+KDL5R%S1LZ|75wk*+lY+x7tfa{Kwp_92Y$&r@o zJHwN&uXV#raW!WAV_i%?W@vvl^19qHwiH>@(QE2CVixb9|Fua@$ys)E&v?Rig(al{Pp)}({4H5B^kL$0M*cn_3`xPwqMn%fhaV|S> zQnor&*3d<^P34hi(&Kx#wbL0NvLzv9v)Si+>fg$6jqRvy)2o~vYvLwd9`46D9|$Du zpjAxSO|=|6Z=47ZGl^cHcHcEx1$&e{FrG zA%|=OY_@a`yQN@XM27&pPsC(P)|%Pq>_+xF^Ei~msmN|i%d$_zJv19HgI83x1v>A? zAJ$_q;pQKQfKMmFJFLq^mY05=4yp9onkZ>WptE%4n~ovYnd>XN5dG@c@cb{ToT?l1 zF$Fx`eE^JF`+pvk_8*__GXEc{{Qr$A|113!2Jpo9f2gjT+UqgiOO*rl9U>*|aLE4X5w6jb|k*E@7n_0u&)4q$oqX`Rx2`-H53F{YWKq%of=utaiK!yfYW*FZO5({1JpmY#7y; zxAEFC&s163TF}JQFdTc-AVec-eR{K)3Es11G@Wx=g~Xa^Cyun*y0kk{3_2CY)KcYIbQA4?~aQ?32ju~;YJj0#B%H4o*p$JrtmNIk!rF=mdqm`iOvNESy2Boom(r; z6Gl7p%-s|1BMhr{k_C2nFBf>b1{JOYOAdQv###FL`cfQ3Uo+xNPpTC#W=6rrXG*1` zD^E!)qXIhzw0Ib!<91pwO?u)}{G9zASVMB-3iS#25q7|00W3Y~4wurP-|SD(5{2~l zK&6&0jpCW;<5P~eP2*&#N)Px&WD;GG3KR$ed?70+ciN*R-s~+GALgqH*HLic` zX?`&Io<@WcD~mdAaAH8{uy+tylQ4K3~NH_pQFfzbup8 zDThBt)D{9o6!1X@d0+@-MS)}72>E?75%oxJb@Kui;0Odbr-hhEsSh$Az`Uco+q79$qmx)Z4-5ciYP?{J{mLT9qnxjfsqpb3RSs;s}h z7ONBn6W2^O8Pp)BU^TCr z&HcW2c;sHB{@Ow{&?wJB;r=t5Vyd##F?l@YdCi*+j$3KgvC9+UiqC@&hbeu4%erBg zRG`d^J!HTVmgFLi%aYHgx$1BJQ9~X3IaRKUQ~amfsn*RAZVRKUeL@QyY4qK z)a`26BVdPUW}b^krw0=whc!X^5W%#u*|do=632=2)a{DN&yQC-?&fzOoDH9DE^kPO z%aC}d=3b&M0v$)?~w%_w4Pe20&lf8cwJW@K?#eZ7Jf0+41%}|qEt~%^&=`&?N9N72< zbxdxz`gSu7-ese1>s`HHduN0}O;Q&xyk&Rj+;o0;XV@JsdLl+*yi?%ER;T)ms}p=S z%gHDDxP%R?y+>h9Cs#p7n=J%1 z=tUTv4*VMiKkYWwBmSN&68UjyAIL*S{yiBT!Jr776iD$kO8!d7a*tICLcR)ZP2R1Db&uH}`K|{ZOEDVx=WG|Z7}> zbJInV;-Ntcm|;Dv5X;MmIsu8y1s#t0G|Txq$>!Y`i5bE@&)&N=k}5 z7hWfWK596pKBnCei~2H#hle4L+!=5pVi`F0^c2&!x5LL@FB)Azi)K*U{S?JV@sk!U zfSYe_-=&OLQ5;p!Cb(toEg7$Q{r{YK5nAHfY%;(#yMGg$?`c4o$;@Meo}Z@xM~0t z^bq`a6#TCSn;`?<{P%%=|0^2Te?-UsBynkL#jgKL;_3+Ml3bd+Eo81Av7PB65uLSe z_9-M?GZh0ok8B!#f8Zq{)hM9f9}Rx^9S*vZ-{0>va-}Va)B5?+N&}%a!I8Vz&u|~K z3#5iMDcR_W`LW;3?=`E-E~7ysDfnU@+eHPER6R`L%Y;94?GUttLdH&RD4rKh86AHyaa4>HXJo zx(b%%d=EpK3;aDbhy^QDcrXe)Ju$07N{;epZJY1hYUNigQKwAnIfMuze=6=Ip|$77zJj{I0Y zlGm$arJD^%>zFu?kAzj0G)_-4J(wM4v&j4Tdk3E6E-CSD*b5I|Hp%={0MegjqyM_v zv8k8EJX=aBKr=v%fu5Y2NdIlu{@$IR>-*h}E-bHVTxGw3uzFqss+T0l6+zPvRk%ef zb%)6wV;RH=NTz@Q3b7mk0a5CeCL&r%MS*8DfW%c?=vfN>;H;-9;q3AKw6q&eb_%*b zO+g(aP||0+;nIpN4}83DHnpt)F;oF+UF8#LG3oOJgCSfWGdXCRQ8W`hh`A zb03;1SYXf<)DeWZtO&%3Q50r8tJzBF{sDhY6hqY@Wd!0I6ku#OntyL?kLzE;rs40T z&2UEt<0M);M`63gvSN+{243V1&e}&0&NNc<)E%TBY|R&|uRfa2r%S&}NqxgcEos+g zVQxvNKG-4T^~|mgl2-+nud7SM&<=m2t9S%_$4P+WDj=oesE`FHTlM62;sa~zbB4r= zSo?;P457HN$v+OAmDTq-hZ;o#+_#RLlk#X1F8k5 zlye7xm%rOk3rP0Nt$!?=4KsnUM%8t;D)K_qGf|wGhj`6n>M~A{-jV|)e9Kq+4Nlk5 zG|$6aF%40)3!lFRa`8;P;pn|ayS_)lTEgF{%iDci%em_k@aU<7Al%CaDGs)seCWP+ z{iR*$JD7TvPl~V$F+G5zIfLI&E)`BF99pNrP`2m|OQ(EkS7pgu z+yYa?oHducFRLC*yCQfs_Z!WNwsNouL2(yl-v#dv>&D^h<_Mcvc@(PM(rQ*#>N|$a zX9I@#0aMig4m(#?Jwn$OP)?TVTd9^sqVPDv00D->iP*pf6`&O`-(R>Q8AL>gxvz7t zH?XCugkW5k#lRwRRo&>AP)d7miv3Rfrzcj)7=R6U2<{1Zpjh*8l-#kKsH2GZGY2{g z2Yqp6rz&YvLT5l-1i|lzFtdzhemARb-=U_rE=sLey3P00^rq&f$JBCMg)RVmp zNt*6e$qRU04#0jMU8s4q-r#NK!+0G%7&%hnhaw_uBFr~)|`a;3z+NJpa3 z@Va_wm>kM#C_)}x;kL^RG=+)`!+qlS=j0PHLh}g&*U{bhxZis$#z=LI(qc@=$Ssl! zGjl%kYE2{}&yp~iOiz$l)Uk z2B<*oLZEo_$(=+4em%wJQ%oI!H8vfERGbHb$Me;1zw3-Nm+sgDzp~(uyko%gKY5KK z624_Z*GZ}FgDCbZe1H&@%ShDS6*=VVsZfxka}p`c5A!yLx|_V?M=ro@<{$b&!-SX& zX0}%JLBrS}*=IvIi6@l47FRSy_!HF6TA&Q7IpF?6@(Q_7AYn&po#V0C$K>{f z&7Ter1bYDex8ONL;*=qE;uM{g6U50FRzZA@9GnDcFIK@14i3yXDZn*#xPw?~qkinw za15eQk5T~MA%zF!m#gfF-E35dFd7ebT}04evS}kd&8!Usln?&np`xpiD@e7WOU!ZA;Zq?h{^i? zxwa`ag#uvRvJhlO<#LK=Qz5@|P*4`2zO^NOLJ&9HD z>H14@er2qnl$ii8_PvK^LmKcwP2l@|tqDYjFJtnqN{Ii^(LWTa?i8oKAJWFj{hc|c zL)~SIgGr@L!h1$>8>D3I=8T(e26o=B8I?yaAD7=iD_-Gxqyk)Lvn|-!Quvha{JU%h zZz;(J#9Y>R`O!!qi@jSZq?rfvB`M%SeB6M8{CJsM+#IF6`+dK@>?K}Gk%eJ$QE9tO z`U=MM=ox*s4Gg>FX0>4Yc5@?tWIg_a{O%h0+h>mZU%=E%NGN%$|BJo1jEZYn-+c*z zU?E6wcXxN!;O<;|K^FH;yb1q2$3bhISg+l*d%FN&QcK%ls`@bfN{U0dw-}1%%jY9v~7N;#4 zyTFFlGEyPWSiV^QWpA3POqO#hsbN;Bh+_ejh!EB~>KE8(ydFOSm}E2xX_d^Ch%AMg zq)Zd)iqFfXlCIF;fCY$x z@pl?js}2z3Md}!Pr|yL`FTxE$m0a6J~Td!pFInxOFcAOUMa8X-tW#2C%swPKHtzlfF=i6FMy;A^|Cb<>7L z>W?>tXxyq2+=T`zD$V3N&Mm*IffNDCDNdTgh)f~D%k8(HBEmg-+jp{ z-ZO(cCa1zaXe+|;F0Uu~L-~PM{E7~lAxcR=PO;MvV2UH8X?jEhN#CD&Je%T3bMzR+ za}X#{1Lb8;NsM|qx>E)0f_ag$d!msrh3;kvWhax3;2`y9#m_3^}7 z-2$e#w(S*ZaH8!!xZ6I70O2`Gj7E)TQ(Wh`dmX%_KZJC1w@IJVoO$24;T5DCs;BN} zwaPU%BYux1{S{6Z_}3oNYvD9Il;_GKPy8_Yo^FDJY)^p!8|!uRR=`0BHua~y-VdLM+K<- zWxzu0M8nn-jk2O_UP)NdTZO15&Ud$*gU_Xs?D=vl$Uwl}!i}Gn^U{XqNo$Py%*RbV znfHi^7^C2Z>q$wS9#SF^x~OoZLpJ$9G(n4`vRBpgb=4{|s-P;M2?zAcWH&17Xx0e7 zF%uu7EVyX@u$kKY8Si>=H19+>HHT%I9Dc`x1bjNDjBh}i?DNigV)S9%IGmUy#0O_P zK8PvoBHjjcvYJ)H?is0YmmHm^qS2xG1eA>TDUO}4XNj|Se%6+qU6)C)H=^4Vh z519_!+lh1X6m=>Tg0?>VDE)`1{r92EhUrIMjq_K{GYw*C`!fyXN8Qw$soxC8zI%?g z@bGPj`&^XQsobzx@Yv-GIOy0z0B}i7kLwih2?!!EURaKpkFR5JQGQE5AN({?{Fefy^ zD}R0zi+Hmx*PJ}r=0cy)-^9oKf#3JRBdqEH$4F;{Muh(8no{ z%cunjP1gSLQ?DmuNIczv5Ve}MX2c_U2m=m*S@e#NpT5!iUzY@qrl^#tpvArLN|W zEAWwjs7#@ZH?h`$jqUy)D%1Y~t^Vs``~P=0>E8stf6K)7H?;Z}51S;AuLKpyS5lZ* zUbNAsgUwo7z_t5*r8V-Kqy1Ue;cU z9v%}Hw;>($fDP@+Nh#*AQG66nbIs$${>nkk#lRB2^{lnHVUwh~=TBR@!^BPG$6*03 zbnGg33tZP00|~_MeF}lyVm;WZznWs^s~gzoZW!34c8kBCi*x7-mMqyiz{sG zP0g>?Z&AMk*owsYFSepyek;W?&ELJ?Ov!JoH7E|Sl{<=xowT@V7h^M+Op~~$qxHuv zYu8R=PuI>|+7!ZuR>atvJ4m#PnS>D;y5E+iwA~B7R9m?i%`%-!@up!@FEv6YFNzOAZ*b?ZP<*0{>(ye3j(sKiCRt}{kdB{gq;nbE%atl#@#RthB`z-E8Iv0ZOl8M`$#Ltx$;Du$Od%sVrEA_=S zNy7}CY^!UIFuoGv_{YugXMO&X@dW-V8Yjo{(q?e%fVw|GqmX^>=PczgBe^`znu!07 zEs99bC|^YId#5e+8cxASs2{(#hv${>`A=0>d8js`ymug=f8TJ&e)?&2G39QQ+nsfw z{C2&x=#BJAXW7LKVk`vA?Cqza?+`Yi?uD?2aSf5%^`QiVZ|XAG zcXrdde3!4CGZ61?-g|^_-n3?cZ>oPnr;wuv#QR{FSmGQEYdiHDx^KGh zZUa$-O3t5|zr=+76t@Vn&EVoBGC7U+i znKeUKoy`Visg1RS41DVL{kT12YW!4mKFyBLd>Q30^Byu06qK}Zy}I~~AhH**I)e1{ zdHoDlS5-A5UGEebYfu&9g&O+3@*7puG;2fwuu_PfxP_Dgz$!r3b!FlOtazqua7Ldb zz)MZF9?F=LptJP`!Vb=4J=-ZH1^O6iCFPsHBo8%n>_?Tv>fv)PV;??Jn-(ps&4+&8 z?8uN?c<9yexH;n2zk&hP-h0JiuIDZ&DJd&+9(b_U(1{$IUCi;)EW@2Q3y{-~P9cPw zqC;g|B^m3J-8^c^>c~+=*<7kzJveROT~vEl6x6B68PsdH%uLwSre~u!V_R9r_5f66 zMrR6763J^vudAE@rxl;N4|O=OB;yvH*>Vx3DwEstUc{TV;gC`CN^uB|fttdQQZyQI zvz$$zHQpo_H)B*cExhSBXV?ydLvMO#fKez~R>s4syr(DTUx^`@qqaZXtH17f&N^Ul%_R3oR>IPwS0@-(qM7&3s9l0?U+G)hCqeL6Fq$FZl}N=i zBg$SvU*AzhBpWX)^jzDTdC+z{4GS0TsqY;#m5XJgN|tnjjpEe0(5kCF+ggymu} zNK_vasVFfI-%!7va*2zZ=H$LW`!voBhpEF_AznPB8L9a6w#cN;p5rsK?}pg~q{`KV zSH;w>u9(AwVnwktzLh%#m)~`mpk-KrUhH9&3qnJ9?v#da{I**Jx=46l3`15;cay0V zqYu$NJ%kz0D%rN6P3)OIl||P2U1%+8@ub4ZQxfR&AMC$G*VreXR@T zc-)Gg;ur4EDOEch_LHUB;mB5TH)Rd zlrh7VxOy*yRpuTgHp6~HBjLxeCj{$1e z#)~_^ho3N4kYCD=vdVGws?yQUt0nQRA{SwEj{#f6ehf$cB z=1v&skp_4M`UfjTFAHT|VKtYTW^o529o~zlh5n_dmpz}fj+(bI0DfKlZ?k#)P1*Xd z-Rpl0zgYgm_$tdk#aFc?9TwP7TCUVS&Po^XMxlrv)-2kQThZGg$kb7b2%#803tQ8A zD^Q<)Py~dO%^#^lae^%e9@DyhB4g)2Fq&Tg*h>m6e*3%(PZk_4hD1}{=l-#Zx5m5v zVK1;um89(~3Ni(QeEgj{(W(tX{d-3C5;%*_SWaNGr`u;knk}ZpDim_gnWhiCGmV2` zQBdS7k%WmmE+}$GOoLHXD9&PbHGXQazxQ z#6z3b>~}nB{IN7Vck^yNdVk1-S*-%?4HTqKJ(*$L=P1Rf8h4Ce6=vV168fD5?kG&XWyVF0cwO{8KZe>%&)6j)9HV{jSK$X8MhRgKXor$k zLCW_lPtflG^s2ui-zvO9BJH3-NuhB&fF@6TFY)d>vCt%iBGGFKe66l8Tn?o|X#`Jg zbWK^zhb58h`*QcIG;vm^S;&B_g--S=+UVrQy(m{G>wE)Zsk#VU`o!D7YZih;1iW@R zyDF2btcJjB%tKK7Mi1>*T%l`H3qE&F(&KrMkq<+%_RQ5%HE;8=*17JlyVU^J?$rAX z`4D7ahv!#+6k&MMSLZ6!f9~*X_iHA$x3a-pqHwBlYS_XM1_hp}ra=U=O$&!j^q`!Z zss|PWFUJstg^H&gi1U z-OH~OYP>E7OMZp23X9!~)GI`|0v=_~Y!wopIPd1;ZaB9{u@^zD77lfK0oP$ai5Gtp zozDWGR~+3h%dS9?Tol|e9XP2|8O&hBAN{xp;4S>GbRl?#Q^w{=46%!Xi$7C@66kDl zsZJFSm-Q3*eak5fGxKU!WHhDELgQmGHoHgMrucFm{(27|#`j#mq*avUz2JGx;BUU8 zF7&V%M1RX1OV~HzKY1yN4T=b*r)1Smx;^ssaDZrNHexapF9A#bd`v| zZZRHQgw7Em-6d?AK74bP8m)t9cKY;;qkD~GmIq>L2D<+r)&x*g@za6Tf2El<{=@! z(kMB=t+;|27UZ^F+e4x$HAO#2mYzIVPk~IdFy;Bh^`=p=c609w*9_4YWgwLLyX^Sw zgF-pjw_Wb#kIMb!0~nv)E}iDrEoWY(2L~^0tu~BDAaw1g(z|J+$^9z%v7XVK6i2AD+{f`P!?l9TCL64< z$`ji3-PQ1qA1;sDULNb|SS>T18T#raW?PKCy2J+k*bMZ9HKC!O6{Dx53dg~5wyy3C zO&^NI5pH|KB^l}Hh=z@M%p1K9r0*G}w^Bqs6qk!C$tweDMh+Q%$a4A;Z|NT|e8)x| zuZ$HAcruHkfI~#_s5x4M-x^M1pM;Uea$K$#{$Us7yxtRFheU3`JAW~C>~DRxGfic$ z7(7`njVWnthn$bW0If2u0)cISD98wzk zH{vmqGDf9)l%m_ZQo+v-z9b47Fe;Fi(=7$ji<+vDqCdn_tk3?Cr3`m_ZEtp#ge{59 zrc^VxhEmOn#KOySOj+!D^6*$25l_iKGrDdvs8)v!@7H7buTBx90zW#m$WS#BI6Mep z*Ky)&T}hMrsle+JHT-a0c(G$Q{V-iOmSJ;-;dHp3iz)roNqwFS%Sc~4bw9gn9)=h- zyzuv?%2In{XKmNJQ=QjD?ma>57#ZMVxBV9D)zj><86`GhD_H5hovwA=%{EirPe1lI zoqhJuGu+eF)XD=-9e2Xd%3ft(SvJKyq?~JYsX~|1P zO9@Fl#$jc|V-uGc$NMJ{?6^v5|BPB#Qn(F&z&BQtTc>gptXKaE zA;wlJ4TD*z(f+X}^Ib&vL8(b5p;D)@$CH6YB3oPm_0HH<>>w6yR79%_YFn&*@m&Qv z0zw5kJ4lJ74&4xWHbejW_3imdDjW~4xztH*W(Y?gl#P-D`zEGQP00rV^pC4YlXfPx z-qyDGb4Dfrl0m(iPl_JgiHj8;Q0!ec(WvjLIt&Z-lvE)zWtn z`P1-zO#Xh~PH*ukOxH4=K7W#kP~f_OcVM{*W6DC`lSu~+LYOALr+8;aAXLp^Pel9? z-{YB`!sb&6eA?(1lTiKl)_=4wIy7lt?gq(z0?T+}f(hnP)ho*gXW{so7GjXdI`xT? zuRJXBM{t)!+q8$}w01(-G!3tFMzq@U45=$Ib<5D~-Evicm4sUgto9&a8dw27Bl zX$j~B1-{{`5qsr27j+b=DTOgPMDCRTup8uDHwkeSsthEQ$v*%yMJA<;j+}OO^R}ZE zvK?piHn&l(@KuKB1p8ns9JcNc!~>6CNrWeniCRbxne#P&D_x_@WRIro#potlCJp7V6_k`frol9G&K$jc4)(>e*a#STHBoNHk zlEwUDt(rU)E#oL>lr)D%*LFot)AB5IK1WB?YrTM~x(u;1T*c5LzmymY- zSpnHE#!E3K)7^+Qgp_fO#fWxep|cOcjoz2mi}0IfzqG!|5hedt` zgT7y%4AadW9|RuuBZXxBUXwg)_eUmm-wPnmIc2Kmv045-UUk2F9+Qv``$hLBWh{e_ z(c}P!-%ay1JW~haw4nsM0#1o@Jrkejx8-IZ_OtluA3m)qBU&;tD_eFIgv@ zAM!K5#>2m#ul^L}CidF)(7ge5#{P4rJ6#T*dZ_+*SPkxG(*+VRzTV{BE&Ibp#zITyu$PG=y8XvGRlGJM{rC9U8DaBQ&PQJ-tZf{hD`qkA#XcHk+;x1>k)#4#dh26+52cx>sDuBQh7o=67TH zci9u6BZysruD6`Jt*%!s{c*S>6E9cEW;zfB8aUG% zT#3$KY*9yI{?!u_1;m#MKJ%|opaGv2KVD|~M3SU0Lw-%B#i3Xfp4ejU%=7#7n$i6* z3xRt0+}`IEo|)M3+UTlYdvDvGW+c*z`JJ;6>YWr_TJ6_^5W*u}pqUa|g{!~Ul@jO{<Q%w+xMjAk|jE^$s`Gw_)K;&&+^qGK|6TnQ0p=R1bS4qdts8Q6ZMpR97~th zNi$^R&S~dJ@7IXSM7WO1xJ~YK)((v>!x&QP{TZL9&U7t){>NS4zj0Uuz+o;B<4Nk~ zd;Rk#mVJORD=7;lG%qZkrdNZshNU4PfVLS(SN0B#K2gT(OQe0_w>7K2>nSG#f`=`Iw>}yCUSDcCM1kNTSCtGCp+f$1@USG3PgUmq;dAgX zT8E^;)pK-HLG_C+?(Q>&X|z%R7*_bt81`b!!UK$1nt(Cuk?=$(L}s~OfljMT!~H-vTCKf~v_2ZuhWRp!SqBB*2KmkU?m_K$e3QdXqZOm8kXgC% z1IpWtC-_Z@PVgoH4Qa-6dE|EO8bRexCCTCvXuNY!M0J|2NE_*&%GKZ`JhNN^6>{x zt3)Vbs$*EjJoXezRER!?01R6fhb#vg>YmB+tqC~e2eGkt+QL&vg*$4w zo7McZCB!ls6pv=uN^Mn`*zcp29LK%OX!=}ah=h)cZFyj>AK`x!A=)huP1C(!knA?5 z%BJzPk6Tc7DygkD4}Cixkxf<*icS;faEaxdRcZ$jVk@Lg^aFkN9u*TL~zHjPP7+Sq^L zFoBjiwWX_>w2GOD`{wjzRbEIz3?O*8G(E8vBfF-a94*P<6)b7A&r$U zT9#0-)i3ESK@ixt6C5A&9qWAzzX-#PA$(zO950eJvvc||!>iW4NR*rr3pJj4(oR1M z^%7~Nw>4;ygHYJTF*}0j>81VjD=zuWofTmA3{4wqnc0~zoFHT|>o9jra)wHDALlRz zk;=pYE0o^i*X5;=_-Xk3_Z%?sd#11pe%q0^8V`vhK2Yu@BfD^T9-;o$Zkg8bo4Tz$ z6geP-fMHsK;CAT8lDd7)bq1E&eQw?NSGN~kNkQK1m_nv(Dlw>FI}*w4Wv6SFAY5uW z7GzFx+ux}NM*Qg$np$q@h1vCQaX?6)i3P^e5Z3d~+A~lb4Wz+GA&ljoe#0#cjN*cs zS9UHV1h+;GrI@Yb>Q${a(61*x{N_T$JHqa$iwfwy3;YdB&F=U692h;LacvMO?xRp~ z#=lgm!3oD-%2%bjZLVN#ypq9Dm3f14$^G@XVPtd$ZA;PXSVi}{+SMrlP}q6^KqaL1 z@&oA|0D{_i%2NRdYU1Bjve^DhQ2$c|#r_`>6#Ksrl*4-CbG4|##3E(;Uvvt&hIdmW zO7sayI85}<_A8hXlJ;k3d&Zl#rLWTALzS_W?@qssVYldx`kNnlbAfgbK4Frxw6bdt zFq#0>z=ci^n@_IvyxD};3xZY_$A$vCT!H+>8LQffj6;z3X}*rbYVH@yvsIp=NaUVz zId9M6g4L?q+(>K89V))k%B-%XMxdz;TABIE8J2$AIQ$8u4jTw*piN_N#G$5fdo@{m z9T zu0#`V_o8DBeG@Qxh;6b;IQ6RSv1DP9$Rt|8S{f&OM)>N2B>Y+L-P$|S@griQI|~PIJrL)lJ?neNG$pOi-sJ0%kV6 z?e#m+`h1;D;<2>ps4Sw5-MeHe?zY3zEUeuPZA$@ui_-U9*@`wtoPp~jMZpX$+oSwL ztcG3_IH$-EK!SeG@m~7`rc`&v$og5xdfN#$zZQ?tsTJ5D;tfW?X_8E-xjtD6Fx0?pcs6#uiHdlpW-B2r zdZ(ENuG#qK+k&}d@NyGNA=l{as>}UlM9UQtqK)#my~IJsdavlgkxU;f51GfBqol1w zDVKyALUhP}00yjDQCPDC0HUVg;s-e$;G2JcFOXUd!VtbuZMGA5B8K^vE^|{iY~1n1 zopz#mFfx$?Ps0L&2@r@t)Bf?e6Z&^&;F~;Km1pb&;Bd7N7{mNa!%9M z1+}HfnY8VF%KO5CD;2pGd1P>P9guw=_p8Q7PF;d8dCDOi^e#1;#M6uAEJh7Y+2y6s zL?jZKh=%*>p`!Ab90R5<-z^*!A35%@#lAeq-C%bgpvSDv9l!2P zvcs9+S@o%}k~-r40ct!Y?v{7$D}B%x`hv`R(Gjb_iK%gBJ7zy#2R`#-BbQz{8$IMJ zY+@@TmT_w5eYv+^7^A-m{4UuC1s7EZG(f8>7|AGJdE>jD^xj5KFXKQGH&Rl_W8 z%*f^y1#eZL|p~yiDfH5MCpSqG5o- zGX0@rK|oU)x(NTA;G)9j6f~V$Xgc&|`OUbGTzy}|%)-W^hxjy^-&dIiAkE!i#r9Ar zEdObv6Mi#Nrl*jApEo%?*Y{xdb@k#f?!!kjHKI3!)gcz<3rw-_N7L5NgXP^3=>(T zl=-XS_xQ2oO^%ZKX{$Y_EU6J_w=BOoxHTZ*zA5KdjWMjIkKEc0f{R-boLulg!hJCF z6tCVurVPj$mp>J(5N1HZ>Y0~Hs?fP|GvdF6kJ;%jSxbBy5o~1tI{9Pd^{-iqV415oC$?8_>{8f8 z?R`4Qv+gj_LMuixlGT6h%Ikzof1+9=!Ech0S=s{Z?|=s@Ei<4&X(Xk=eKDJFiIm@t zh|wlm+@+6LBnQhb{?hl2V+$M#8(%R)*=xO5gw_nwI?dYeL$C99o;)AVN$1y5Si3g% zyV&~tMUsT_Y0SiMy-Gu#^X;z+5dDC*Ke^7F$QeZDa6{`b*L|*k&hZhglmlqEfIpRwo zQTh-%FACUN*z)A9<_YVk*?5e})NCWcXr7W)VANqp0#{n}N8wFlV$E+{9P^4UZ^xe% ztUdnB)Gz!Gm^jZ0)`nhUP9LCPHOt#nu6}=?Y9LF%+4Tk5Kps%A2xKCf0=G)Ri9x@5YoM@#J$go(@BmgBLzw<{y*8cOGVPjLMTfspFH zm5GLhStOrZ%!3NSlvu-~XxuC1M2-%LbD;8u)e}4-7?n*pY^EeXeKh6m;W(BS^a&I- zm5p;vXAW`vG9g-A{VT>Avu2aDcdJ9eimY*5J-7}5yz^vRC3dvMRG$Bkc1>prd<_`` zg4=d#T--dNKh2%Bo1!W*7_Sk#Hxm#xJXB3NZ)mReup18Y%3;8rDK$p`0*TN-AWZ}F44>Z4NLV84a;u*)2+B6LG$dM4eqmsrJ3)h=e!2&EPN{zA;}3j zhh4I~7^-O0$aT}ac9y34lvg!=S+><~$ccTkfuDbPQQ{N1Y??c_Ru^Xy_P#0~5U>mZ z0c(y6sd;;|z)ii0e8=N+M0)0cjroW*8{aXnq@3L8g_dlZT=B@Gbw%SbAYg4PU8l>f zsPRFz8=j=gwa$BLHt#KbJWFO#Zaoe^m6R(W|qi|&2EZXFzV>WMpA);dzEkYF~Kyb}w`+2ki z1&l3yeWX*YmeL58DI}iV=e!^`QiMn5F+$Uzn&HpXA(j*_4TH-%nD#h;e~DoC%`$1~e?!%AFSt3*7oe z!{WPVfS=xz`<7UEBF_5=7Z%=_kMDTEi`>}t>JDxEM+*%FDQL+518tjGJ4vC-c1^Q8 zrS~uBPIGNNvD8h^Wv7P@n1X(tDd({ymaF2^Buk?-YVPwEAc~mSFuU!2zEx9wdM;0C zi%kYZ5h3-;xbgEXw*C02V=@_cTI>?M*XbU5XwBD=Weu6vLibnTmu^?f)HW*7H0qg5 zNn$U|5{=pjRFh_qO9S8$aa-iiVHD=6U8h8@Bba*blol;u%HO|Ito;o-(YaM|sOB-l zRKCQk#3HKmEN7WH&s99v}Bn#K_KfVg>1ZYMPT{2Vl*Mk+(K>jPZG%zIFh#S{Fz4?l&EM2~OgBmC@oo)rcvV`%;^dB|xV=mZ^|nhC zu3C3^9`T=5FjV;p-b4Vx*8g1N|lL+p>BH;id(E!RGfssJ=e--ZD;y6@Cu(v(pV8{8~KO~31 ziF?S1?vF}M{@c^kKh!@j_owPGf6L=q&#+vbPrX43yyR~tP#;}yfa7~riED1~BOw}i z=gkAx@5aC;L>l>sZbdRF)wS#`&G|{GvH<~qp7G@apv+PHwcsT{U9`H@M->|H7ZxeS zgIWed&{EB-Y}EC(pahqIdg;;P~*wIh0iI@`JZ@z&=!)M?O+!mZxQ zVl~?L7Uh44Wz1Kne81iX*TZTRLAqy=kzFH0<$Qy6wp=cV%o|cXYuXG15_N24Dn*-I ztUss>3xt0g@*q3wJPnAmm#)pOBB zpYF~Iei(;3I1-VqkU2{dl2Cm$Esmew%c*mk5aI44z?E3rBSEiY&j+fdGmb`6f}z&O zow0W>5n-v6q@rZ1uD!ihj6s4~KuA3~9I6-IIt)j;RTr#;Plf#^$eAS$`T4-bJU%SP z9u6%5N2rIi-7psmVU1=(NG9a{2J~+$R zYWSH~qwtre%Kh-qm1slbK4(!*U}YtVE2p+6yKmX3A$m0GaXPwzdpIemvPB*W3b*68 zqQ^ge*R0m%?G9>XD7ZZ?6oEw3hon*r$>NWJFv*9{2A5`K5vtr+=0$99x(Yk`+K8rW z&Dk^Rv`V9*gEz+P8Di)5=JOm}D08CuN|P~4(j^E>9@NgL$Gq2X^kpHqd0zTdA_wjG z2qG|NsKS>a8GRIciFIxJx%@c;bnUviw%jD03I{w}Z=vl~@X;`P1W;;x2x&yZfRahS z|DEl!e9ZeB=sN0w(0Yh=%AV`*%?f_6%OT#me>DgVUk@|N;#pZGU=?aaDPwfnpF0De z=b^2B?b4A9J$X$%&a@{xa9gj4bj`Dqre2GAQb zf+k$qEU%6Wp2LSh%hdgrmu$4U{Is{amJObazc*&37QmD?jGEOprtY6gyqIO@O`QV! z8`S3M~4U-@J9qvGF{ejXCMA!$vh68aJvMtzBGJW>?!iWm$`9abF@2xeI3??xfW$glQNJ>O8l}Tz@5aT zD z-YC2q$tmrZy~$d}cPA(?E!-=1+T@uOOAL=sEljINSUL)6oFKSFQ!vKlyfQi2h#4>~ zV7U$n@Z>t0^34s|tGuI>LQExuJ#G#HYhqV%n!lW&<8c1*Yu4(qX#tW-nt1?H34u!o z0o{`fzh})1-#9h*&~OT&9R2cRc{cY{PxvkuwUij_TcJaQj)iX<+9qGVlOgTcCVo_EqT|P=Z5E(e8 zcLD{Aj=$C6W%owbE}>?~B3ztk;7j@3ZCandf+@AO7aO^Jy}=nH3s+^I-EcfqR%8~^;eC~ zeZV?=;GB0RM8u>#Lk-g`Sl}3%99g=}j1;6RE&fWhr?`qK{zD+Al)4)ZMcq~wdy0l` zIhZiWegpD*+E4QbO+HorpGy4Y@b`W4cLC+U#1H9K#es#$3ELp4N0)+CZ0dBys-(hi zs;y{AA4Gfvdus zZ>)EZ*LLTScCJlrj*bO3=U}{pwXc29+BuJc1UWLUry1-5F+O;3oS976B0+^RgrU{m zvTborRAVMv$)7E%=!i_!shLVSn5t9^Kh<>}{vof0b&tXy0%S+`zb$F`x9yFb|0TQs z>tx4yEA@#Hs9^s50FtVKS%Dl4o@>MN@B>c-&LDdSGaGAOB1Sp}A_9=Lk&UsLwJ9%= zin16j2N6FXERT(eiJ1|I%h<-q$qHocNc%jV02pcwxHDf4@e(=NTXWeMIG8zbSsPe^ z9Jm~fxNK}e)_)D*`g?D#mlOTb;%aGTZNW?Q(b3VCi=N)a#f8p=na;-El%A23lav1C zDAAv%GB)~0KU*hz%a>lpM)V*{(DR}k=o#r4>Hq5cyuyEXQO|w`5OMX z#Fx`CF)*;u|M8xP;Lp4APq&ZnuY2`8DId?v)TSV7kiCJUjXj?nFq_=V4f|s@ItCJH zGixVTf`2}sXKQa`4l;72XX9Y7VkQxCGP5+M<=|xF;GzGQ3I8~OnYEdtnSmv(kv-_; zG(z@f2G#^ZppTZ%AAr5dpTqvO+aH5p1_2*Gt+9b4h!40x4q66QS_V#K1`aM3RxU=C zKS%#_KOSQvuK)Gv{&w)W<3G*J#H`H7z{SkK#R|;)*TLsrmIl_QP6nnRK9Du7iqgxm ze>DBERFE-noz|wbMs7xyAO}81R`x&F_Adwixwo+saPxr+}icbCT7+kV}h5C&);fZB1vl#8v;fm`aeI}|7|y>f3F+!zt@fB zAKm`?FGcI%2wazgqnXhkD|9q4v;@%_**IA{@)B_n{n2OyvIM>tf3&eNv#|czY-9NR z{rc+wL=pJc8F*wuzX6`-+7gMuz+lECMFf>yQxA0(;ubjG#*HY5 zYqjMkrzKZTNyE-2H+A;4`5I^=qTpw9)Z<5YL4mEok_>)N@>;096RUq}@cQLEF<=qB zq3QlwWtRU*CPF5H%yfd8Q=59bYol$!0lDE`F-g(;7fkDyxQHp7QqE-}EF{rGTB1B{ z$4#apyegR}G;T#IsL$T9=16MZV-qUF zUTZBrzqjS_@v-^qlB42G#y;vP(w~eLRvfuFxe0Yw^DZLWN?oUdWfBxn;VKS96*K)L zg{@GN(+iJKO>HJMTc-9Rc038=gC2`F`pss|O?jmP1fGEK>5VYQH%_DpTPLyoMi*(E zewmN9N1F=DTK=O_rD}l;_s3sqq+dUoFzxz{txrO)C{%BiFMAx`6bnebFP`yK&0+6- z6z%Wjcw#XAftxYsZlVQsAED~GDNOG8~bt~+%8u`+Q{wdL%C z;nn&S-8;X?cZ4s**S~Yq4XxUHrbGum*QMV8= z+Xb@Jqknt0DNJv1^?RWA;c??9y##o4RaBKqr7zyHEb$hSBNy0Q$Hj-IslCb` zKgZQl432ZkH35=7zD0&mlCla==v2AI2DsPwur6Dses-hdl0vn6MWOU`7E|s@@xboU zraX)606VRdMoSE+HI^K7RGY6#O=Yi;(+sMtxN>9JaNlbvXvuTlHX*kNlydrE`4fA4 zTGyhn*4S*Kwx---m@8CYBzrpFL}T=p$j4&hiL&p#rg)jD)Fs76c1+!bNjtL6^zGrQ zk?Sc5)bPH-`7pR7+S0%+o;zt#LxP3t=|=Q=4TyzCtL!#M>AN`DsfHybU5Ky-V;o`D zvZZr=?$6iMcKtF5tc^QKM|slWDV-X2bY{OvBs6`j5Gjx--R3j}i3i-HwW6uLs0%hb zH5fxQ_n{frOO&DTbGNtn~X(N~UD9 zoYHp`{W!OLd}V7+A!hfkb5(FF+Bn%Qr?U7r)c9lIl^>-Pw63D`=UN=$sdOg9U(NYL zTQ(nQeZdkRi_d07{fd<^k;LrzM#6)&bux--Mvd>agm>;cm(vYe;R%0M0pr*1d~nkk zWw+C}ytVapC>CL90g1={TN;cBF1g2pqxt-tsG4)9NIKg2{%jAeBBkuqwf^Q#CDE2% zT)&M6@)~2_(!QFo$M@E&$xCG3`6$N2Y;Pn8V_HV{HMfsze9ls8Ead3_>Fi9vq3ZrP zK1!nOB8+t=Qnswwm$HQHOV$t*BTL4=nk<~B}AqpgWTyn(Cq~JKbk`^Ur zpVZ>F<@8~ubS4yWQr&-`$HQiXEqNsNv7qdQSVYV5J5&z!yprv+7)|K*iyDf! z`(+sBg=F1qib?E5GKK%vr&SpSCHod*Q9ZFLJtkbSneHRn6u6k(gUgxjJ$VWx4nI0b zPY|IEwA0Fq6@&XJm+QL*)rV2@=a3XvHnMy}BV;$PSh(N*mgSV?nmurSKDPN&eTK%= zT(s z$9+g81<%%}CpfCb%6Q9oxfgjjd$`!!S@|{a%f^~4k#!&^P}+4_2cz`awWW_)LX9Vy z+vs>u?I!*C?rJD8d6~$T3Z|}{nJxp~k#zkDH>b6FQhx%R3Z1_GT_^JG;n0E0R>hy-P*Io>Clbc63t2kDxP?z(B^eEHR3ZRu7 zhZ7IOV4k|fMbWTGizABDkiOc>bDw*}GfIr0R3SZz91m$-<7Je%1-oZ4TVWiY)S|+A zOf+xQxEA?_e4qmNQbT(b2akQwdd6@rfLfXnWx7&=gRybKJgJWbceypnCfC#bb1e=} zT?4w@tPvJjk!pxaV&v_#qL99Y@iTnN1*=3-r$|23KD=dqg|1zi5+lQp2F@03Mz>2b z>(5ErGZ&ePvrJ7&+N<-mhEJT#kQ5#kvcE$=_CpUt4LYAwTzF``EzEeP@H>VkEqYYH z=v|~w(krUvvm%$^ys3x((cNK{nsKlyR5y)}329`(4!*M&1F2hZj8!QOGMqNjAGj(!|S zL2JMY-d=QdaB)gKo=LIX=NNC6hJr2^i)cF;O-~5EwbH8NQtck@9?#@LNcbUYj3>g#GGrHrM7ND zxQ0h3GxLl>bG~Qmzyrf#;fv3twcXkECyRj#1W&;#6^nS11FdDA4oN<#ZRLUU1}HBT z)*G*2=-F}YqP&#mP{rwo>~*QoTbF#j*uqK%__l9d)}3536!vsTiWNhDkd5@xQDxh@XjtWiS3EG@RD}R4 zRxFzmt4Wczs%>5HK_I)d$MmeNkCcrw7h#yvA_{7KDUUXO=aINi^rhXVDZBHF+C|?f zp4$U4#%x*en|)LHUr*XZ4kLBRxjbu4KbgHXtiS3|Yl^LEWcMxZINO>}_O!l3U%bPE zxu};aR}AiUyyEusA>Hk+_upc(A{=LQ62tW23`|is?i@t>l=h0MmU5pyeJAy)m}QCz z-$vJUKGhXdizqgiKq{G%)jRxY+V|0_$6#fwPZ;#=v{@GmZJ)|NPJ{Wx+iOagCa$M3 zX31uWFL$M{lulwdSU&NFkPI{XE|n}ZE2%AIZ@od+8Mm@z}yy{Bhg0H zOm|O1E^m>RmCWeU*WxVtJI9UAm=$Kf@|0TL63YdOMZSvXF@NbxWDSb>r|HL2ZA;^@ zjftLu+Ihw4>0DA`WLi@#^qs1+5=gbbD~cg=HvmEg_A@A z!-m#|IR_g(O;<+7C)ImiB4TNhE{?KtzH&&XCVq{5upISNE!{ZB;81S=j81gCQ1Udd zgX~5YkNO0a;+L>uVP5OU4^tSrw#VF{T$cQrPUV!J-WFOo66Xcp=OFrg)d_WPRHwN4 z)e)2XoQzw>#ajhFg=6G2s@&nsp%1hxwo3H*WIjekHgw;Le$&cPMZ85HTjv_b9>4;qApyXSw4%_?9DAKhdk>?MW_-OK~LJ9nnn)c3II( zdRFqpmnEGg@}7W~i|&*G>E!HicWXJuOSH1Zc3~MarFf_OjQ>WvPv)(*c3YYFg@*0> zs+WTFGiYkOEUFnMbQ(Uk^Bjv-5+-S69JUc|2x;zjCq2e(uO6(>A@ur&g7CgZ(qo8y zOky$aJ@7ALHdL!R-?R{jWlX!O3VT(O97+WGKGot>Af=^D-qV-t((w5CT7-LiyPC@J zx7dV{yYl-lU%wGgnNW)e#XX1*Yhy~ZHIH{~bQz>oN;@!!t4h6I!|~)>j^vuEO#)6& zg@-BKHf(W|l&iqc(C+D6ec?%+OX}w0fYl(P%refzBH|9-Fj0h8dKK z5s*#Y&f)^~4jTyfDL!Ujx{4I)zN_}shq5&+g=T~p@8l}aYpM$o0}bx}ch)E&c*D4Q zAlq?;Q`?X&0s6Ng&O%4{$JB`=m7H|Wn-DYO+9J^oWtordMvknmkmh!fm_ME^SHdcq zg)_ymsz!cM;5pXKLR3*xwZ0F&FXs9cy>}Fh9%MPBLZtH>1_#Zn$5pV_CRYY22MpBM zG(#xM`sUW4*bPa}{mNgwNTMR%Kr4|MOhZMJSmH{v^-NVeiozC}(+ssJMlO3YI%?fF z-d6gY<$jQeOWUwWZigx^kN=ZK6+?OGhdRqK_2_a zQKaushneO`W7{Z&o@G#k2x7S>r)Q?%O7V)amt=*Q#_9fZCGxV9sX3`eB>7EmoChQZ z7k;d&W%9SskB`F6C>R*@eUu=2hK=o^<>3For=NZ=u0B3nHS9CqZk4smZsJ z8cH~k5ohyvP>0{?)<-5L=#?izy7tgNO(#rK<612EJ z6JNgi;z!UD6lv9TlO`;$riVMjnf+zIS->QOleu1)gQ}vDLJS&YOwG>8gRSmD)Sf4c zHSLmXrS_BM+gkqM$T{mb)H|?KuMZ`Qg{K~XKrk}9)$8;?y{@TjpvbSLr6TBL?ToyP zaC6_O;6L28aOpjDBz$g@SJ-k|z_rHi9NBG>83QD%{llZ>$$hm%+EEF0T8Fm-C|R!~ z$>}HI&0h5>uf~em$HpaUrVLXhwV%`G4bm)DXbkJH9VPo3O)C4&jhdUmN|0`dq%=60bjPi3u&yzX+qt ztlj=#@aTp2cc%20L)MDKbezW}>CP<|0w97$BQpnhy1GRbm4xQf4d ztAu~)N}@%xRwyDO-6rvKfrY1^=f{~(h|^D+XB9MX?h4yV7HSu0bGzoAR639#a6#fk z8(WVbk)?f_W&Ku;lOI?&6Co{opDh7@4Q2k}t@ap*VI{raG-|C{$^R;>5(%rk2;;Yc z@n41UqQ2qC<>wvaH67z; zapTZO?cM6|pez~%94R@D^mzwkbqAvYj*;<^P+PfEOSh%dg{JkV@7X1-ld4hD%j%VF z`~}}kHM4B}7Y?~cdcguoGXBcBnc?=)Ui%=BO3s~}OSJC@WGAjN1kx8>DskOW5-k9H z-}&r}pW`PdBrrDSa}_~o1wM-!d;l>3j?TI`Uq;&fGdAf4?4dz$O|StO_y7_D9O)o! z+&xeTw_j#IV|eSeL-}HWev?)h1j2%6mIEAV|26KXhq&r!ZS&8l`QQKI5IXhKfnQ4B zzXTOhK_F}Kgi1jayDAA0Dq+yYGaQr<$hmMrB_GyZm4peE#7NzS9e~<{;yt0#SKeKf zL1zd|>GI&-X}-P)IuD z)czfhx|0b#dKSE!kT{_bGRd7R=RfZeW6W+srwN4?tnM~s146MFLXQsJ-Azb>P^h-< z%ma3yywL*X4L=`kKHN=6l2FL(VLc5h3Ia*>ISc`A6)4#hLLfFS&hEe@*G?Px({SgE zoyrMxCS+$`^>?C_B>J^ec|1sMh9zHzIag=HE#kPvAd%_$hHG zZoWb^cLQu*Dd9W9Rs=QwUc(426BYUKxjv;10Im6GBEM$b&y45UvCJ-MX<=6G3)Xcz`>YKO0i;SpCj2kKf6omgtX* zK7KZ$9n|95nlGdMuc!tXOOTtbfHEW&Rd{@>A~ zN4;zZ@Z%CBkly^f0T6h}EhF1D8)*;{+k6PpP^4iblaMAOO+h*V=|rTHkWNNA1?g0zDAH7<(~zbiosKjeX$I0vq*+L_k>()H zMT#NiBIP0FBNZSOA{8MOBh5pakF)@(1gR8hAyOIABBaGgOOVb$T8dPTv<#^NsS>FQ zX*tpgq?Jgkkj_MU3({(&vyiHh)*!7#szF+Zv>vGzX#>(mq_dIELE4108EFgBR-|o6 z+mUu4?L;~ksSaruQaw@wQXHufsR^kWsRijgq}@oZNNq^%NC~74q&-NTNJ*qFq;90W zNc)h^N7|2c0n&v?{}1V{NEabpjC3i|dT>hM$|!2yj{lu<1y1zyGZ8%|*vx8fzB8P=uW?QGv18g>l9)634i0 z!1}>}9f_ z^^ow+_8*A_&U<_ep;%tdl{XIf%AXGS$_0Xvp!+gQOG)eYm6v(t+XsB*#|M06_JHe2 z%`>oi9vJYI|2yC--!|YYKQiDeXAKzJ4-fdB9~=YUIFHQ>_z zaKKl7f54^v%Yd&e8*pjwAMll*9dK#CIp8Z_9PpJL1FGjK*(04BII~%~e9iLp%g#C_ zdz|xW&St8stG7j~PK!lP+lAZfVs8BUf8@(K#3 zlkv?4rHm_}JW_v7I_S8+HJrqAwy9_xWVrw95lE8Rg?lk&il){mCQL0=O( zXV>!a=x+H}x#aBkj`8}BqqHBgpTR^WRva)eU{GdPy9hIcll%wKSk^N8Ld}U4|LAf`AOx!S@SCu z-yY5`pR{%f@ek+s_^0SD&gJe6vag(MFK3AE@k!+m^BLu~d#F6w9{saguTc3@kN|kQaIKi`U~U z$ovNJ!J~5BgSh_a(L>H593MRdUf_H5!9z}Q<58#h!3RM{Zl*&>@S!v|9&$ePp%1|~ z)aVo+0s%gvVx&WoKr6z#mps6|ISPMypzqyDx)sh`)p6Opq_b$wS}cQI@l1MeSwqd0lW4#rp&v zMlWbd?Ch#b?%uPrtGP2?*O0u7jhQ@R4yEpN&7>J`3MG&w^1g45G?H1qb7M2>zaR z-cL1kvexwH9nNjun(UV7+!K;-6Ir@or;JwI^=gE`zAdUZdE&mrgBaV#4|9e3h z>RgfmkK)k78RhsccTV{;ybts#4jqXEpW)EwAxK_yqS1H4;aCc`;JU24t2xm*(;1rS zRCKn~wMQ%B&8>hY(b*VXyesv)u)MXky)~~XvG$_U^ z#^KL9vwd$o+11ii*VU3}k0u(UY?a;d&h~g~v?I~k72VrXx2vVKrE7n*E*afbSHBxK zi#8@Yqi1y|yE^01WV~~4yfc}Hny!y0yIZ@Gm2gZ(mqlA!+Plv;Sh?I=lF_bseRF$D zeO+toe%vLQY}wTskJf>4Lp<5iS%;&%W#qE_{Q7t+s@LW5sjA)>?TB~c{%v*b_3lpf0KaG$^KUXKNO8u-cll&>_wSz-*p^yyCoq(|KWKqP?-Dsk<}Y5Z%|()f@$qz;0J}Q?#Ka zN&FgcF`5$nadpj^Yir^0J+nR6ZCAFJ+uF6=?K6|nZZu-DqYij7FIp$r6|L*&XhjRL zTf`vtaD!Iq8$6ke?u!G`&hB=-dslNDU81!$u@56uFK#hEjNjbc&Uh0_EtGlH<9teeqPx9oN#TOx(s@%!Rgt6AqWKGAp@cz~ zo118COvbyG%&W$@!L3^3b?x09@*9QUl}N@hW^;3!yU`tS-?|23KOElh&P07YnM`yh zk$10*&z&s|s13(2`g2SDvg#@fnc4&fR96zWs_L$5&DBF(U!7>c09;eo7N6yws!RYi zTohf~fzFv|pVieiJDR#pw5X_X0d7@UvoV_Nh{qeiyJ%iPL3Q&5;8t0Oz8NJT=;(#j zC~|eYEz!9@THC!VDdL@(675}`XvBEuTzJ*bZfR(#LpSS0+o^1*uB@3qKk8oSXO5z& zHYB?0TG8PO7pz2!Pzm^)(PUm{6+T##SikJdssfCK&Xx}Lm%92+h=6EeQM4B0tf3Qv zDH&~PkJh2Y?M)C27qG=wV9BBO%;;H(rk46>ezbOf+b;AE_ZSxz7Az=T9<8Z7Gk5v< z9q4Jv=!$Ok^4#j04Z?UHPV%9BcTwvH~{VzRng% z3iPi;cT;n8Uw(AO#$`T-djDsG|1<9YZ1jIN`9GWepDq5+^ZcK?{hzJ=&o-kbC4ri> z`j|1;_T?DBth`#<;kKlk}R&-Z`s$Do9{kdorAKt{`KfOG+>=Lmk) zEfx7m^vv}bS5Y}YXVOz5pC{kD<(q@4wxJDoTedeIoy8l>=0Q`FdjO2|Obbu(NKVO-T-z?+3Hk1j?>+L} zDc@ScF8SRp-+SeIpM0M$-}|BG)~wyIytZruL|k;43ksDt48jJE(3WJPRfPj}w+o^S z>0~s1eg}*U5Bq8zy{3PuSoNdqD zzdd>$q&?&WMJhPLBmu{Umc~YCA=n1>Dan-wJFN<)Xk91t6$EIlch@S6%_4178PA?* z@h|PMJ1i)GtcoLJLvnVAP00Y+z_MHrEX>?!;IJgx096pERKH4gmVgOSRT8{1(cR@W z3&5lxi|*PVy=Z>Hble02Dj|xuqj`Ul##l7jn&_f2(2%#5>?#tlEGUzGl+Y?v{P8{A zEwnwrAX(Sd4Gp0h-EUt&4x5lgEfIr|hD}`; z%qAFD0$JV^3=J3_)ph%$g{9Gg(z3#mvf{#;>S#^X229_{Ud^*;4ei%u?RBlFUJ^aS zYOHbWH0-Tw?}8wt&c4sD& z-HJxCzO#E*86bzP96DhUk&o5oDbHO}sO3@5C(-3d0!_*~O z>YC`u7-kEtb(jIza-}T!WFCN7v#feK@U7hdg8+x?moKZr;W_Ki+(2^W>e`Lfr^3q` zZ-=p~CUi|e^|?qx;wBr~u@{1^tW}I+O54)b-6j=0bFG+f(MFgou#`|vAc&a?>`_#_ zusVi;%sOlTY(QIS9||EXw@KhO(P*W@#|c=VIN;HgNQiJ}U#5ywSa-$i>mV6KUc_7D zd+WL&){&d`F3ZAJNygiFbmm&QO$T;&M5DVp6PPhZyE`a&*i@WCviIW}0Nezpf!gnR z(ddTeIPM@+ml#bpC%Ri3WXzF2rWB9|EZM%g{c@K*+nk{pxKBf3A9&(~PO-qfr40iM z+EX<=d!*DRSlfNgEhz8qcwFqzu&W`OY{T91IBl0(b=I+YFtJVS1MYjFUNCVHo|K0& z`ds`IyJS`jfz{o~(aCaQU}EUOwsi~Cfqo}bpG0@3s8nr2xl?irq4WAYDpH5(I`{<2 z4!Bk&_MsGLg|28jOg__N1Iz<6_Y9Jo8 zR~lM9U(?dwzhYsQDcyS~Y#oZhvVt@EQ7n-%bxlohQiw`)i&t1&SWr@g8A`B`tYr*~ zsj}kv{Q*%gLq~HRsb%x}1QEVj8ftBE&pBH*ub{62hW)u^rI?ga?pHwE!J2_I%5d`X z^3kqy>znHm$$9zpZ4LRY7tq_2FW=Fi#|H;+mo>Zo9vlU|JvcOcJ*ne>rw6n?{5;5! z^77!f>*K*M*TaKfrhf+nv_1S(OY)l&ZSj21?V6wLtY^jYVKlXLbS3klfEuA;a(AcwFjnv*c>(^{H{(A}C@gxSJ)tMs6M8AcG6;>WbxlcFJzba% z(|qg9b$@lXdB67gw1xnUiUkaCw!T^a@7f0roeiRLK*29Lga5nsq0IBz@KxA`yYrf4 zF68Tid7MTsn711jVmeG`BFudDwkjQx(aPp{{cd#OQ2%*af(Ho z>-Nf26!XP)@qB;_JsE1h!8VR^0tVL;=8<5fli#E`l+>jZZ|BO5_z0Lu{2^sJN5qWJ zcL-qyh84IL7?zy3(w#@ovR4HZJlfIHA$F5Y58(^!-6~HrdJAJkv3ar5SmA2HcOI_Ih06*)F#k?L|A5y+-MZ9crhs*GS*&j_=Y9RlyqeJs(q0d@4XkPUMLK*8 z=zc-`vdMo_^Vx?SfHGF=-tui3i0ru zE?nI420{b+lTO*_eTVy^6K*%zEzsb*vs(AZDs+fpwSXhPgIfj!l-BMBbr`lM5*;x( zOx!_-m1iEov)H%?K46!366QUhI_rI?!5t9pS7rVJF;36NL+U zI#cWo#I$P`%yY*~+8LChr&B0gPp9S=7GZ6Jaw44}3;{TitFcNV>mCcEg@t9sSo$cA z>cWS}aBOO*3HWl|RaMAv--p7*%<8)HqqA1FRLn-p=Ss`Fem%ECdd;uv6KEa`gZ)_k zl9ARJ-xtNTuFzc}127>jR5^_?vgNg-3`25s` z-d4Apm*{#7vl!J|SeH%=0{HXQL#+;BhzU3n$&UjBnXrr$^&}Fn;R2G5 zsz4c|`xD*bE`@+<0${;lzxc_LRU{8dq}B zek2gVjv}UC&hdbExAEf0_cIqLQ#7YG?8dME?97Ma%qjG7PMa-SAgM`m;GRDt-O#E z*y@#Z_h6P4<`yn!!Vbrj7;{f7DYOxbY{X(4ah{Dh-$qNs3Wn7v;mt`xkq=5Y$(ma zHqsYw{__v3chxN2Df9!f*%S=$na(}V&R-COAk+DJ_8hrlu6$oAdBI z3IOFWqq2s5aYoHS$@Vt+o&lEF9E{KJmtyN_>h~=CZj{fFrLzOSKjEB)0yZG=?@}k0 z5zFwd_x@+#>F`Y4@vTyE$NBy!?A^nM$Gqz15&kjv>vj(JkGYrk&w>1?LWXzV$;1{k ze4v`}A*e$$fi6Co&P(#&$%qIoLuh|7XdH2w08Bn^zAOukJB`EnFpCGA=Cv?+(`azT zrqO_W8o%L|dyKHqSjTW#Mp^O^mvFv_B_GiW=Nn_mH`YSqqdQq43?_hEm^RTun`EI) zw$S*TbC}-=7TSpx+DR7L$rc)nEu)QIk|C*)9vt)|4$uq?MD`&b@jr(p;t6E(C`p*h*RCSi;@&OHzrgK=(B z;BT;SW8TRe#gbgmiHgDNIHn^KAA?8ioe_BkokzSrA~7@cD43H7`#IS&Bk~Ll zbLPl7IQ-@A?4c2fw!w4kn|NTsJ;z=d0TP;XJZc$1E%k`KGa^wrcpdv?MB;Psh&?hQ zkve$9z8I019Xw($j7anj9}QiD-UnOt zRFuL z+rn=S@`dk#1@AOk%tFJma$3GT3oRcAhVM~ep%q$aMHX7Ig*MMZn{S~lu+T~@G)7H9`W34&@wUy;~d8f>Fl8qhkZkjn2WuFN4#gmVSmsgo?}nAN6vAa zodiV4kNx*#$;DnfS)$@_M9n~n%n`+FXGrc-a4q?=yb+0^qer|;fqR#klJitICwqEC zqUu540Jw5)>Pn*I*CHlmgRfO`#ZtM5UPR&nk$4fbLB}c_C7tL z{KE4j?ok$wushK`%Er->)X^XuVfG!8GA@a*dt$cp@(1AX`eCR-*Uq1At z&o4j4kK;Syl)HB=F!&W1{0aC>M8T^V;{3w$m5~GRyiVc3n2ESs1U$McjIK_|hEJDwlwBbC1-#mlg zJcHjngWtRqKgz-gA}A@p`3ArF2EX|Rzxf8g`6+&sqY-C?%Wr|fZ-K#Yfx&Nq!EZr| zA7yYvVpa2)U?Urh>!DX?*WwF6!vB8B}E+P@h$a9IoWr@LMiNR$_iVL;F z5~L_}nQ8}W$%wPoE!i0c&od03XBa%sFnFF}@T8WFNW?UjbE&~|sljup!E+UT z71_fwN1XL8*2@fBmKpq(8T^(R{FbHovAz+BM#u7182l;>eia743WHxoiXX9xNMt+m zt2Fpk8vH5^ew8VHypHHb;QdhfSY>djGPqP3T&fH%Y>$XU*CWs62AAaqm*obRrXF#`z~f}VAR))3~bM`-_KIKz%`9v1#&I>Y5UUW46R z&Ka3woi9nI@<7&;$&XWO_Xf_8Az#k;h-BcT`TO7b!~TDK<)Ja}x)dQpR^L1#6K6Py z<0Nkv!izN{Y*-wj#TpQ%uMChV#+i5~i`+hG`rtVH$lVK^jLW zKa(U9Cg%a{{^|Z3)@!mv%H$QqezL^O)T2xsZ9xt_%EHms)KNB$@N|xQZV-;J1K&Lw zjH4Ym8jKkUQ92EYpQA^4#FBz5rYVPqz}YZLz2B+bx_VItF1l^yuPzw>lS|427Zo{U z4{L@@Wcc2L_)I=%W|)8sQ*qlYO!VHpcJ1J?r~hiHazM}s_`(iX!KHL~ux83ermE|H z^zhtYJQd88$xNBdGy|Ll$+S>Pjw60@Tux*+c)}?ePPXP8Y|jaH=*Fp>CsQCnEa}Gp zWxjwinc|1*8QvzTN^ess>rS8DbmzYRnYHb`Ke{wncLZ4!aI#R!=}!06le5nrZ-BGJ zQE)hhW;mt4{m!&cOb*l=j04~>+~W+V{O66&f8(b{rXV<30B5Fi;G~?hkG|tp7eRm{ zoJ?@CoWY#;{_VG&cRpKvZwf|~H4F~Nixr%50cR53aY1mr9c09Y9?a>^m;UaV!>Nxx5kaR3_%of*!Dp5O3+hI4-( z%oN0i(K2T`(ffb;*Uxre3tHdWu6Ro^`MCoVgnh_Z&Uq1>=y1M?cAX$swu z#3p?!9nl#i=xk|N^u*dxmjx?d>!LGA&{;D4(NA?OGFtJ#D%`?>AJ4;ePcxJaB&$dxCn4dFAoB@!^LH=;PTg7-}8k} zj1N>9j03m~7F^ao^4;?P{NLaD;DLDnm%)OI^Y=N!J~$>YqJ)!)3pLv;r{j_nPWbE( zzU$*6>`ZY(En>x`PH-7TWx-yf8#{>zJ^9m})g|v-cJ2cPA}uZ=Lj;i{H{HLwWTVlQ zT3kei2qIT~rS+r#Ga`Tp7zYsHTHp-lOIwEj<0E5#<0As*0YrufB8Q8fIeNwDTl{(o zCliq&g2>_hw_V-#kFN!Dru7u+Pd#-68d+_h?F*B&}9SUIPI6LRxbnCm~0oeh@0bqs#m^se9pKQ7Bp=qjqqhS9$z-rwVk};bm`(JgUhHINWM_Dho7P}q`CvvhzVAgu z<}uD;`F#`@4Rr`rrgJX}L>4&`@d-aLjz^%@gPk8_o#H%<<1t4_-40Lq1lWYs2V>49 zCHT?FN7UE`fYz5O(Lq5{*XX(C&JV3dSU~u7V8|6GFMjc*i=J5O{YR;CAaqloB9!w_ zPBBPDIlLl6ehxVTaoqX2iK(a~m4spuR z>+!cPXB-^Bym*AOi1P>bOC@KTWae3ygAqw?_zcEx_rAh>XAbjw#p8(ZipQvG{P4ob zDBeiP$wDqq{pwe7Gi}9Xp;?Q56_<5WEP5#}g0S(!oKt!zZdDGkbxue5VL#?Xad&1< z|GZD!W_rsRQd?puypfq0tN=B`S(d|T$7hgE28_?3jkh7O(N2~#Va_}g1t(UBf4kgV zEQME+=bTe=PC@xHJc7X_LrTUnVQEN87Hm5PX=CYvbsGiUcqZ0^KMFoSLAoSVw-M4m zyxWhGj+&jZ0X~5wX7<{Nv-<4BETq?oXXemY;D==)66sYT5~t>1hSbjwT#%^k4X+Rz zB9@WX+rx;hMVZFH(&EJMF%A`J&k5xT_ncuy z&*A-TWj17rrOdpJQi-3JP$ibNmtpR5V2|=@O%6=KvY!p@!*={%$l$qb!%(juX_S4G z^u%!)HR$#9fzcC2?^Zirn-O=SaCdg&m=wOg!d6S0U}uejV}fN)FR2g%Usr`nYNE`s zMYVGDXt>4!7V@({d@=^WNA-y}q`mTJy;vq_cQ`-K7gM1FzdhAYGvd`%{TbNY5ryji zO;(Xr^=BSu)juBt_`jw9Y7aREsy|DVW=QqZ+0k!ZDt)7`LH25?{%l)u>00P_ao-@S z|8A)Lt*<-PUkCxe+Ex9JqT$0rFs#T>0q6Cq`ag5rRR1loU8wrULSOc!`cFr`99L~n zRqzc*byzf^(nvs&M6oheHDHBtJPm>mj)Jm6e|Z#I>U~HygCLwI+@DNUMP*=9m9LFK z@J+Pp8no7EsG%6@7cP6)AXtICQ~PNUMB$}U)4}WmN%%=@2)t=5uy1(Af~;!NHg(GR z<$h#UlMZIijHogO4Wec7@3n52wvIPT&><+^I^LkrPDONWfc6*3=j~Xf90}7nK?~Dp zScYl5SD41e3ezAu(rC2!!ujZa3)9%JVcG}_jru8^4`M5g29=jagAXN*hEAA9gIhF> zhR00OXq;$;%fcn~Fb&HUX*4dshx2hKb(qHe)M45Q78+u5r{%)~#%Z*ZEwocCv{OSg zf30;V&fFur%l)-hEcam71N+@%cXgC&tmop`Ut_IH9dV6ySL%psto7~@duc@8QTR3N z0p~;=JMtzC#rKdXzwf~G))>_nV{W0>>8lWh6fyCcxanxE@v z5od(tf8i8ce)jx`yeE9+hFg%ND$(DLYW2oFnnp0?u85gNgySo7As~#%%Q0pN7vQFY8)X$bG>GwkwVoCjMEUA1x z9LmO$G8;=u2^oXaLyN$tL;8Vfj?)iJOTgPJ=`(w`xb>c@E#zTJLF62GTi&uk@(XW6 zT8149ywt_{$Dhv+UFzX%yOcGee2>9Nr#0deF8#os&jt30eF=EGH7d`2?CSIy5mZ~T zSfepgBb4ggZ+$9ssn;I1ODTD&omBE-Q6M0B`M%1@n2l&ljzige$vY0W^Cd5JSp+i| z9clVx&l4h9p0$pA>bZ!#36_#&0`3YKCz9e>t0bikjL0@J=AS6}lW_Ur2_-9k>c@z@ zVV3zPN&b6f7pT z7icB`kp@Qr3vQ&esCd`iK3r{$O7M-c~K()rg?>RHJE9 zBb3e50$z=Jx|B8In29(C@XaHRmI!I|&qN1UH!jgfaBzt(H~{|R&QJFnQ$&u8c|02LR!j5U5vWw@A+fhKnA z3C`?E&LZq1r0t47{A7>d&m88`HS}OEaP@NWCvqL@9RR&nhD-gn%5WLptqgw-xH3ed z&xOO+ZhrvXjwhleKZO}h5fU5x*zXS*-OK)f*J4e+r}Ff?CV%W#!N+6HSc7@^TJ)c> zXd#=^JDex%IL5;|T)k!w#OL+p^!_R2Gkf3T@Rj7EgyFL17-b)oQ--`JX4Jwd(GJH4 zp9_h?A=85C(r*0H*U~p&kHbFv=F94Nz6-zF@ays(oK(@qw;%zaRld{5)?QI=z0XUejt^1PPHat@T`@8INK z4Mj4t2W1(~6IM8{XJrX5t85v&3(9g5>KHEjI78(OOj%ZA?c?Xv( zS9t5!7^CC^SC%$D$4XiD@~kVTvoyG7@^LH9H2=+3g`H;taoZ@)SBB!c1>?L?@deLQ zcSHhDi^4)oTV*Z>q&l~MbCp?RK0y(sc{3pn(`c_`m6=<~!yeOr&sFAwKq7sWnSL$G zzsb(q-1F@FUL+!2g@#%Jg*h|j+_^Z`qu^QyAD#8rLNYNT{Iw7We;CgV_|8JqLtRzA z^&~tPclgqBr7$AH6KmgnO*<%GgR#vK2LE`Qc+jwMOjoC~g-_h&qpQvGz^cn98zn?s zhA*FT*|R@YK720Ipf))Kl;1Y<{21k&gmmo%yR>S5N+lmWk9Xk ztAXL)o*T&{BYu$N(?^$;Gmb@?#fpjV#9Q7bKvu9Snh z)Js>&N9UqME}s!;^%@c2la)f8b46WoXL)o5{gcb_EU?Rmu>>N{j<1|KGV^DO51%IT z%H#8)G}D!M_6|A~BhKA7qaTCoDB*#Jhrbzhd5j8R^Vd{aDE)MGo>y_TJ>vZMe6;r0 zwXM0Zj#WW4{z;E}6qY5}`lF53*H!Tj@by>4cVu+l{-3BF$F;2AXYp2nl^X_#j5#}# zQ(K1rIFv28a>EE2G3TpyGZ9Vosmfk9F8f`oO+KWO)paL>zti^ylAU7R$rlRxH+Ntn?hH zr<|*9f17!!*B-V@StIKBNsg!Uxccrzu~6y3)%MBGW%#B} zpHz3tGe!7JdoTHX>U|!c6Tm_D5U@PDW?ymcZ@zz;%SWS&dK8xD1mSb%mAr0isyw=u zf4*BDUCR&3IgLvy__*~tQTQwpK6}w4v=w!Yf4|E|*Z8UY*SY13fD2tdx+g%wBDj3G125u0 zn^#`x){A@Wup>|Sj7Z_bUaflx6(lrX2?v-KxjDojole+zrTR&V|@Gc!MQR$!8 ziaeQ{k|$Gz&qeqqpQ*y<@qb)&-d9~d+?Ww@#9DRx`BdTaf{fogQ{(P5;d8Oe2aaA` zb>Vmf0&@9q+eF0qP$f9Iqd0Lz;z>(-%OQ)m8u*5LA z$UZG#26-j*?3>daYV4l@;KiqgvIVWB)1{=if#baBg=mSBH|i|Gfy`QeRj5nwHOCGE{xnl7c^uyNym{Hn-n z4);dA66`T?_qhZ@T?A@GP;JGcwS;IHC>B0}FdBgx^>itF z6J@h+EzLrWOlyg9db0C&eA8N*jq|3pM47GD(i}N=E{@4}4l3|npV#4PS!skmVKGY<-ubq5yg^$P&mrt(nK}4i?U*nc1H^9f=MM>@B`vo*Y zv3LPo$&@F2XhJ>imM1U3=eAUNsIh#%fJP<`S~?n?SS*5XYrXOVd{{fhhgwee$Fp7< zky!i`Zh15+F)}&M!^%e^6DvM`y{H*=_a^yhBx122T|R|UuYLDie?q>?M8J1ey zr{&Q|!_sS;0tFOE5k$F`^_pvjmmsL2@BSVW@Z zEWXApLQs=2+;L1_7IW}}W;So0$&%9A@^@@9`f)!nWmx1sF)aaa7t=p1zjC`<1PE_d z$iuWJxzFIrYFK=8i}!qld#Q)B?NZi=wxX}e7;(8D*fSJ!pV*gxx2MUvW_>8VMm?z` zYBC0NmS!s-_cm|maG*v#UCJ8K()D+2(#C}!7vDTb>o(%N!#!FmNAJY9w|{do+|=dn z{!RK}2SeF%|0XJe|NV?Ai)#i6=g|I5&{2d}asMXxp}mGVN3+H{58-!sOUm(%(Nqq( z_4F&=k}^^}u)0IR*uR;9UKfhd#5efRM;eOJq-4T#?00DPTHH{KCM_5DWEz1QdMhsL z*5@9X*KQByS`?0ZFt?Ay^7*LawXI~r>ug&|Pvvz^O9PKy@P`_~5CmLlRm9qwUr)Lzxz+~_UZ|~)L?69oEvSdEO z9s7GPH@JH*8Gv8`OymMwlSIyXJT}RQ-p4-xf!PuR-)dsxbF8+SSfdra%1UsU^Eaqw zP8B<$1n)r`hn3**-j{hk`r=csSP71j<-;D7U?^G<)dv^iLeYwp3`CBD6703Op=d=~ zF7B5}Q-Tqe)>7OzfD$|bV)nMr|e`2=xv#c`b z_h`qkqRVo|oC~t?IhGN57o)%Z`V}j>UZziUBvVD`(|U-=n*kp%ePC&`J(Br(O=o-b zbX$sRneFvl+&6%tI|-t69TZ*hahvT;ew``0p33uDD!NH%jUiA1Z$s-`-h-kG=Lstd zQ}n*R6kQ`mH)nfKVYXLnQFK#e>_alhXH-rN{?Eu*fMuHn&YSZ+4;NLZ$8)}y{VJYzM(t{ezKF(_JLiD*;?)2w4cG`+$Fn6&xcTsY48IreI>*W#wl_gF5= ze6Q!?z5&!Aou0QJ*ZE#gPDTpq*W$tRp33uDs=`=D1@)eXYR);QuHAdTB*98%JW*Px+qi~-!$^pUQ}H;PgvEB7E{ri9lX9~eBZ?A=9(SX_vS(Hgv%aw zZN9PB_so{?l+MeKyJF*WJm-7dF!O3|a^d>kIK(;O`rZuZelQO!!NJE_337e!H?P?3 z*~c3*o9|7Mczee~3HDmtwD}&(Wts2wT--N^61?S2C5QnSelgl>sRS=` zl^}zRy#oW&w!Rn66ILXq$b5Y%!56T(fr^dmd;M{HzW&$uOtjw^icVvuj6pk;BY7Wr zlSa85W#9yVaccPQ^(GDHV`FsF##?3AW`=XsF#nhnp@T#HW2&$@f%DW6!Sf6qzbue{ zh%>}_CUDHRcm6GK{V=SC-ViuH91YMOI6nff6b$m?RPOVE^VDjq1IMFbW(4n-gJ;k; z5An-C2F~aE2l>Ziow3IK$6>x+6e!PlJcE3BApZnBWgINuM8xb~7s!w2h0|ij@X233 znd85iBod0?46%oD|ApCby~b66I0SYk4WcBC#`lMZ^KtKTX6V|%poQ-Nk(74rPz#N3 z$qnCw110^ZKOY1aDO1X63{`*r$yzXlE zNXNhjQb#=Z!PF6J@F5%#1@?Ed{FC9!W4$?MBF;7LRYRl(co51x$C4a!k2sPL)gyJp zbJw~@98nSHI`@bz6_MA_@(vnJC}xI?9)bb93q_$b~h~S_Hqf zMtJ%k+l@c#UdP@Xac)T+1-US^N$`68tz7tS?uc`%dmUw1#Q9w6D9D9jM|$EiM!4LD z>+V5ZgfVEnDb~L+(nBc~@h%A2aqe*MGPaMqaMVVe&%1Zw zyEpv1@ZB3d-DRBIn)5ATY28lTL;@gWl5!5H7O~tt|?KBH*nuT_{ zg*M$nn_;2Nw9w{&HZi30=UQkn3oX||%d^n(Ewlm)t<*!B_U8}1QX9h*AbBer+Mp$nMT zv85xz^sJD+|4)#6u@_9nTL>r-3$WkN zf6hIu(bJEW1N+Qm=fCjH^6~le$Lo!PF6-hW}pbh)=^mlsX!OqlZ&R)Uw}C z9nrq{LF#BIj((Ur8iu1sQb)sa^rO_#2ps)5bu>p!qKCtBgPu~Y3e9~qsLN5 zIXL=R>Szp(9#0)H{?N};N8@nxi`3D09Q`tNGyz9X;E3FQj}4&9g` z7j!LE$|z9wNcf&#E#*_=xv=A4WkPM>8^%n;<(h+EPlKzLbPSGuow^6#u#tbESBr5* z&hbXh2{@|#VU{35iQ?5_u>KAql4l^2XCRU%`sGPnM?~^^K_m~@`iSHSBG=X{B6(?u zE?~f=GdZNP!^o-EE3UK^h_jf{4Vcny4eEKoGfBM$Swa&Du{23`7c2 zh_Lkw1QBihOUgO23Jg>VQmANd_LV=bV@w6@E7YqVGHSVIhDK1KflQ%+Ore2HVK2xO z8psq1G7oH2WD3(pP@y362qOwD{HFAkLO~`WePuPKHi}H4flQ%+Orej=v`Yn6QOGvr$i!7OFaFfbHtO)GKsE+$k)U>W z1>#2VrlSv!4rGfU+cz2K;|JdB3K6A<*&>G{lI#5X@m(3O1iqXa$1H$O@+EPV5$1Vh3%KT0qpeDi~Esr1bc zhRgKL55|l1&5sfc3g7%F!JzQXj}kPfZ+_4`_Cy8yE`EnZ1>=MG=0~X^DoTNlZ+?{a zA}UIOhp%i)MO3`7Tt!7`ny4rhQ4yC2L%$SJQ7WS1S$H7uirtTTqN3Cg6{RUrK@+D` zM8)bmRjrg7Vxlx9CNw7n=yU&6wZh4Phlt($V9XI8k%a~#3k^gT_JYVl1CfP-NMeH` zvQQ8SD%ypDhy*M8?Y zXB8Drk33ZD<_F`J_^6Z_sFWF~l=Xs2nSn}~pfdM7MWrmQpOgtIZHVXm=O3!FEfZ93 z5= ziv$q~KzgV4lSKw9iwsm22`b-xOi|(V*h9r`dMq|jS!|%P*g$1*FQ_awP+2UfT>lnD zWpUc*SuCjh9TAhR{icqd#e&Kt89fI-;i0nFKxJ_X750Hj@GU~-h==0THV2`XqYIhZYIax6iuW6r&! zuPgrK7oYG=4rU9Q97_O2%sGLP#tOX@%HA~iXd0Kb?cA!x@3Ob%Y;nH==0l>^wI*M+oY7Fvac zR%xMCS!l~Gv=tWGN(*h3g|^y4JIg|=w$RpCXlpIB8VhZmg|^;8tF_QJSZEt9w6iU= zb1bw?7TRVDZHtAr)k525p>4O&c35aTL7NothEfHT3+;FKrYT!K08^H|aI!q9Ob*Ki zV9K)3`lf7!Vaiq*rfh{_%2q(Q`lf6JgsX4LRzSG=rfda-t8dCy7^ZB6Vaiqj6W^4r zfQa%<*$PM^-;}L@!0}Dl3dj}Tl&ygH@J-nYND1GRt$?8QP1y>_4BwQkfN1bd*$RjV z-;}L@pzuvuj%iQzWa{sbDO+i%o=QXYR2r(MvKQ4;2|RpLwo+72evzu4$~1*sDXIt2 z7ZDBeVNuAHqIzz@LJE9tZ}n79rJ;H%Q>upwxl&XQL;7CurAuTUTWP4DN<;lriuzgm zLbiBGDAhbv?B;Bhfl8HuN|k|1RWGPi8K_hVDiJ)W!}3?9p;9HNNEFB~|3px!5>&>0 zg{6J^LJyTH1C=TRl`26+e3V6sN|k|1m4Ql?pmOJkL4pe9x`&G0oLz39vfMyrxq-^^ zUQk(Xpt4+0DJfM{mZzbzTu}KMBN?Bq)1Kvm%ENcFv}^yLhstsTmE|c^Xr?R|R9>i6 z<72sj$Z`Xb<$_4%5JiMq%|pa)&aN;JSz#cu!a!t2FNmx#5LqFJ)FOZf`^kzlL{snN5-Kx9P<5#>s+tMrq3+D}#(sH`whS)rrnB^^D~>mDk0 zb9SYH%1Q&3l?Ey+dqHKTfyzoj;PcnvgJily8^&1z=*% z+xar1dM~snj9YRG8n>%}muqtVsn#r8(70U%7=7b*6@ZI5cv;Sm&Tk9&)5tq$+^zze zzHz$>_-O#qYwvicc_+`fr4d3yagy@~eA9$jJpdzwX60n(S(n3E1295pg!)EEwPA!* z8%9XAVT4p;NccubHHL(5gj8cl_(n)IhJ7=OgwIQ-X$z?kIv)7GR3j5Q)KL|$C;l&IS^A~5ED zgr$8(yd~^)YYZ{5CM72H9u%X`jM7awYYY*w#t;!}L`2knMm++YqIihd4Un}4B5Msq z)*6Vc?FEsw1|n+(kt-26hXQe}fe04{*9s!fOWB_NAWL{{Y5`)cfx=n?g|#9q>*p#8 zYYi0E8Yrw46c*jDC~#8dp0b%6l!`wp~gU=Mo^gMnqf5t3dE&GP*CGk z7d307UyS<@OFQD@{ARzXF%YRS5UCMFisvXIH3lLz1|l_r$ivSlBAk+Xh}aE|bp|5q zJVai?>y(%dg2CI6E<<`d(&b3+Kzb+A6-ZYiy$k8xNbf;GKtk)w z{>5wX|HDX!kT5P}&*63W|52omAzhF3aU^()WY^!P@c#xR^c&|!q??dFg9M+F>~XsV z|8GV59MWw_wDn? z>FY?}K)MgGq{onchV(en&yjwC#JvkoAU%onE2O88o<{mL(r=J{i}VcA?~s0v^amuW9tx=c zM*0)db4W*!{*3e&q`xBl4e5W7{uhaZ@$X3gK>8=r3rH^_y@d3CNJo*NgPaVcOr$KN zY@|U*gOP?H4MiG;G#qIJ(nzFHNTZP=NI6Jjkj5g7LmH1X0cj!<-_Sc5X$sN_NGBql zgmf~}DM+UxMUkc=orW|G>2##&NHdUTBF#dYjWh>oE>a8$W}K6Ul#f(^REUJ>qCC~Y z$2;dEF`@yZ*kNkmEJP|p`akq>rX}w0rS5-_{;fYEk=7X!aa}JGah)L%*NH?NSE+ho zU7AE(ClXO!rFhf*qAAyjM0^_3A>z!%m{y6n&X9=f42if-B;ti9t3+I9NW^u9L|ms5 z@kW)1>(ReGi8xk&XDbDzm0GViB;tBcBD&{-YjNv)L1evw$a+C!HF_5{!g@g@xbn1K z5Sbv$#%epP7euCs?Jy0}PZ3#fAhO;-WW69VX|y7;-aur%fyjD6h0=MBsjoL!RJqFHW|~JO~$ll6H4Gu zYc`<^`_r0D=&Am+W)nK3Kdsq>e&HiY%&UPRMo;NkmLH;ZVhg+irh+H8m> z8u6P&G|^6qIOo9Zg6HsJPaJJF#L;F$9BmeH^!yzvWHuY(XtN=XHj6k~^t5_|wggZZ ztiMA7dW(U=76XMX1`1nxL1Bx5!WKc{K6fg!#Xy0$Y!MWKiQ+HSRn||$Kekm6c^>Lpub_WS-Wz=8hH{Sktp+w*4Q#dwHWz+U zvDs>1v(><6t6=l=e=9cI0@#eUVzbS_W}AV{HUpb&yvTUu|~ zCfI!UM)s3aF+tMavdutbn}NtSLFAl!6_ITQBHIi^w(0meTM^kFK*Vm~Y&Q_uZXmMV zKxBI_h-^0y*)E7&?HV}S(-7G%hxSL1Bmzr&W>IUoE@kKUM0=df!hN9)6EVlc-Y^MxC0fmYz|T9NC`v{lMk`UKBKNb^uM^z}W%(T&rm5<-Wzmi+lrTp0g9c z+D)0Bs1xP!opW(ra6y=x3hFGhT^3rsh1Ou9#Vxc(3$4jQYqro@EVT11wA~h3D`??O z6m1q-yM>mp&^j!%Jr-K0g_g9?xcKSpy?zA20hM>r=!(f{v4heeL z>t2Q-It7T41N(qyiWcGM<(Q%#5B3?~$6jw3qxC40zh|}HFh=VkD1BqJ9)i+0M(ZIc zePgs9g3>oe>kVVH-Y`b%Q77LRt%tbrjnR5Y72g=Ghw$)?(R#=T-x#fjDDaKZdW?AA z7_EoU^o`MajDO!4t;aC;jnR4tP2U);$DmhZl#gK71OF+qjLXs0yII;`D3k_6p)?o@ zrJ)yv(g0-CEajsx4Wdw%R`FrQAt=E}F)A8>NyPa8mTpJqenUJ54WdTIiO=A~L!M%7 zFw{tcp+*`+jjX*%)kuS(Mj8w?(jaPN=7el{D7_(t2xW_hh}}eu8;HaWMB)Y_@m>&# zrx4-VKwJ>Hy+#ePc$$ih3pU)7`2ExF7Hr~zjqbm@QVdm&sJMYm+`uL-*sON<-^C4V z;s!Qx!Da}IS@xEA3LDB;4;#C&+Gt?YXkgQ5VAI$OHjM^0je^aUh+#w1vr(}5C*Jbh zb@lo32u-74GxXMzFw?(XuxS)*o)c8Axzg)5jRrQ21~!d?&BI^Te$!}R(`aDRDA=5H zf%com6gJc~9yWGkwaLJy$-t(`z^17eY?=&gngpAx-3<{^B5^OI1gksZVVAEt^(_)@B2nW&@jM z1DobvuxU21X%=kmf|OyuX->nYS+G&F^^1Z{vtaX#$eV{h>|xVvVAE`1(=6CL?oMr* z4Q!eXY?@WxOj2x`1sjf7+8(|E+tRB6+kzU!oLRIb?+AG48c?~Q0o#JA#+((?yE8vB z^g`b&ATgYi72f0owvlVoo8)S5?4(CAXjf+kzI3=}Xd23^?+b zEoi{D08HP2Z2^2SXEirlKM*irc~Q`SZ2_*n0owv3W6qJA?q6N9G2qtYMVFJwD?S_feZkR~zhKbaULFt=F?HH84iPVlk>6=LH7?i$=)NYta?S_fe z4n%wtsU02KH<8-WZ+#P~9o^G6k=oG+c6R8~o)HjjZ(ZhTbsU023 zH<8*gKz$Rb9o@?J*U>chM3&uOmoP+D!Vp;rLu4g-5m^b~foBhKH<|$n5m`@Q!H^;= zAtEcdl#mdSrB5n;UX))#M3y}1_>1-BdKG;$^6L|2(W|*uouqK-AD;T zfF%q8mJk6pE_<*Tj+~}=_}I;)4g;SK1D_59pN?Me=`ir=5PaVIE5)ZH4WACdhYu-4 z@A!g<^bWyi_XRBNB zV_>t#z-EtN^V|f*W{-i*9s`>_f=zK$vEgLZ!^UnLbsE@o8rXCi*mU-SO{alPr(pB^ zQ;JPz8aADRP1TiLebUj?DZNF;&c(GdnmPp=p**aksnfux)4-=w@X4C0_;echbQ<_{ z3O-N#MfZ+!;_l&NH;|GBK1l^Yb?ZC98V1s^0$lbGkZ~e_pjVWia9~8XMwrG9JtFXk4X@3z&=`&`oR2LO zrcqrF3-KFbp$)ar*ofi4gCi`okrvu03ysPk%r9c05v_2(F%}wyXE@(D3k{t&E#CwS zjcpa?H_1Yq0$Mmg)d?2bi5A*P7TU=c+9?*=sUaE_I6taP^oU-zzcC*sMXXIQ_#(HC zz0%-|+77b3EdBvITwQT^aOo zD)Q)Ev*fPwgvsv$Z1J3?+%*expXtsObkzORIIVZhlDqyLyvSJtNV3nez}doCc1~R% zxD=b@g>$xW9>+Z}JH3r5s4lj{OMSA1^FP3g_2IaW$RfIM&unRyz3yj zD}85Ad@pcU?46Oj4wAdB=YvMidt3Bz*IM_kgK)2W*=)%Buw~%Eb<4n3*YJC+>tMNS z1xGNU44(n;6_&Yw8eqW%jSh|wBQd-$UVe&phre2@?w!o!O-NKpihNnB+ zOEv%2x0O?bbLdq;NlOjbn_j!Xo0QH238+a4K9CTaYVzNNtJzC9>iw{cy#>vrCKyPV zOlpgP1jeLB8AvEhHQ8@>>(~F_mefoG30Fz&HIM+5)Nm2!KiqOoFxsKFkdxGcZw8t4 z0%T7E8ev_3x`j5~LYrZs&9uq0VbbbuYE-WOa@1f2AGt=lli#F6AdtV_h(t!nV<3cN>2eMSx*fx zneRpe>nXUT=48~kPu-=xiWY_M@GXLcMb~EdPoGrLz>?y7JrIcU^ojyYt~>Li2WEUD zu#uD8f)3vzarg>coq=_5dtga%n!h`gE$HyI1(q~j8E!Pnpu@Ka(%E-AuUpuhm!R;y{G_d63|B&5QvfiwLC9y;A3m<*% z2@w$*STZRhV#J-Ei0CP>B$b2)mXvrR_p9LSDY&HOPDST^LRAVU2A=S=n;yq5z+_gr zBBKE&gDOP>OlqVRRVf-^^77M*;F7^uRvKLL>04Ra3-9szNl(Eg*-tdMq(tnw zRQpNa0VcCnDSmVe_@+mH0!)hGI6Gi^FtlQDtyu$1{&LS1@A>s#F7_9ZnJu{1tN|vk z+j#vgpZn>fX11W|p#dgkfqO#0rXaWATC)b2{7@w}G%N~b3z{Cb0F&l=2CNA34w@de z0F&k_hkKD{dT^Dp9KeRXNy{v>3Ja~$LaVaSmRo2mEVPvt8rQ+Yf(+Uym>Q=XF~ndy@C`$eTeT9q~f_YX^y3v_@5k28k?_usc_XS*k%I`Q*qK zKB{J^28o=cX6X%{3h6sYr1vx(`)J=mB9jlRVx?sA6zgF99Wua;gkR zutNJrPk|wm67J&{Vuos9$kkZJj5zzQ@lfeIFr;|rB?4e>eMUP!z z$dxUMN>70yW#?Uqju;IL`QFd5w0GX^q0&=0NRAi{2l?C~H7@%O1$prX%}Hy-H$(dq z3KG#PuW271FhlL3Ak&@%v4?`3DbIKWVzn?^&|;47g&_MC}@V-LP45aV%&>7GnC?uCfy{7ennGb;GrNr57?WbAc^X~ zS}4e;&`UUcH5BBjYgI&OD9G#NMM3WqQ$s^RP7zx}1*e9BY?9gPouBkXMBkwxPyM(K z{GLKVQt@agNO`{FmV>gO!U=^ZIPIp!s~HZ`Lq)?uK76jCVupicIW-*Qegu0OdaK5O zSPN+pajp~tZ0VOgRQe7LxfCxjC1pCk=t%I* zjsAp&!;=Mv|LgYwbAv(Af?h8T4R`D3e!6q-+KYU57PAGtUK$#%{=x05zdhs^X11WY zp`qdAk@uiGi`;_dhK7a{lkJ+o3*VS6Xl~d-!9`3=VtW(dBW0eVjR0LZKt|3Rt?kUA;B7Hn6~fmJs|l%7WJWF z+HR3uJ&BKbsPrAC?VPWu2<$sd+w*@`^+E5ahlt(8ICf#$_PHiTPhr~D@O_|PRHIA7 zv`rKBv0A=4cKQy^mc%PzHD_=Ki!C_YwYMlD^!<8>*iDRM7o2UbyFA@faJEZ6$Tbj+ zlcK@dW(pQrsl~OvgR|{+5$QWP+cR%fMCkeU5V4yW$1XVAiS8c3o`SRKOJjq<*{->b zrM>tQ{ASDd6r7DCPlL0G_iKa7lD-49J>b5*jlO^1%;-;mHc_yjKhJ$=0t%L)$%1Bv z259p{Lndw@&wDak(9F;PZ4=)0mOo_Ad(1a8m@Q~#Xn;1E;cW_dHJB}EW@vym_Ny0$ z1-u%}7Bn+#0oo+u+*iDZMBEAm%?w+BHuK>Z_ae{C@Pev^7hiS(i7>6+LTj+l;ucz? zh1O)DHCt#c7Fw%?)@Gr#TWARjt;0gwW1)3gXh{pLD@5bKr~_@1#7w96NQ0jFOHsZ5 z(uFFvif_)nyfA0Vfov(|NFlH`^`Swhw;6L#6NVW*53KiTe(3w)KD_LK){F zVmIxMT~M=Umnk9|)XdXpH2XEE*#lCxNwQG&aB2rx-+|2j>Tas-JCNDd95wkV)jbsK z=G?IhU-tA)MM1-tc_^@_YxuHYWM&Otwom%S&XiZN@9=N1)@ES2tSJ0f(aAmu~FBD+5pgE`E%IEni!TDc?ZooTevdw8DsY% z&zvK$G>s<7YguS!Xwb0UjSK&*8VNL{d^6+CKw(7nUoBABQ)pwF85$_;_DU5K8Ys-$ z4@32#fx;d}ZoF|!W_ov~w%hg{4(xF}r9$-j5e{sgst+3Cp0Ko=6|ZJEFb@R{2X_4q zMWLr~VDINc_&fAzFbxNGy9moiQ)Wfq;lM`RposJx4(!g0)U4n%#6!exR=k?wz&u1W z9M}-N@rdHPr*L3@;40loUlAYL9#J2E74!4xds$|-eBVL9=6+NW={pG6qI0y5a1!Pr zVmC2f%^+YNA{qp2rHhCL0SlU*8U##sD`e{nQZ)$JHG;_XDPL>fVZTniUJ>a#?AOKX z6%kHBJw)s##;Y0j%R@xNekI%m?w-Pa=~JaYl0Kqgzs3n7wV(3(NZ(<b@x3oH@@F@1KGoVwK$1OvcDDBifs@3_2{QM zmJGisU^m#qew~Py?hgKQAQ~#O1dGSZQ)ehb4 z$YFqct{e6%6TdSivnjKVV0wZ7*+*uP<_tTMga6?V!sQ$pN+PZqgn!IWq5uAoN+&PK z4Fs7Paby90UW_-(WoO*$Fb>Kk8EX*n>R?7SzVFS*!Q;n=<@eDHo@J9{I#}YyZ@gdb z2nPZ~tjq$tRS-@$X3dnh@Qpb_`gZ38JV-efWyo;GoJ&gZqw&R}&fTLP96~x((Ric@ zNC$BjzVDFVpOEl3tS$fG7H|#>x#Hx-FTQlq6HC4SC{<1niBeqHkWma$5#~(1mcNJO zjKptZa`Y%`FdWz5<#Iy93ZOF#`()-JUl#HqoHl~S48b?vT?g8_oCvFq&k&~!ae?t~ zUCubfOCo86vnU%U8D>VwnI@U>bk^kzm6!N2|6u%f?<>rA<}g2tcjyp{!*i3RczVnV zkW%ezM@}?*4uQhU8w>6O7*@~B4co+?w7!*+2_N9{75={Zr9jhCc<-Ul}Pu<2$97Q|opgGz)} zfSTbf%V8J(5zUNU!E${ZDa%LdUn2ZZB z{Y&7@AYE{SCJ`QakNj?SIuRqj4sNNWJ2bMSwq6-*LxaWlhQ>?;P(?BDVJV4GdL@a` zsX22bSAV`D&Klrw!z)CMSYlMVLtMLEI+klup!*?dF2ny{qv)4pvY%{8^^-7`w0#Ca z0Jb<4cwT+$ZGmlJ_6qCHvq(FEi;eH5e% z#OW;D@EX)FE8|qDTd+Tc^Mw1;2%|so-nR032vdmK%FF90=lFREB7o)eMT1s=qCv&* zp$G93ANHmgYHZ3mo}nV+JF_Vo$lFW7nmvhgY~hap1-^JRT(gsnnvKd?kGwN8YA_-? z;lxYeyA5Nb9j@xQ6Lo5xZXA=+>FmexET``=HQS0+e?}E+uC)r*NQi;2tAGKKAexjR zW?OM|Qr#UrN_1W+pNs+Up`!HVtXCetwK%)#B?{?WscMEZ7k^@+{ChnNq+$fdOD!Zb>QFl`uW8K$w9gd=%EHK);N&V}<~ z=%>*{iie`866f%>7&K|LF%}wahVZrHEHqka;e0f2!!)*En1)?xX|ySzg`=6BV4P6tx$fcjbYe3#PCcxH=Tsn)Mt_6VzoV9L!7`ZeeuR7;lIZ^CAaw%sle&o_s zzq(-jZQlyi)r?%q)tSq7dM_tI+j)^o2g_aga&_L7@~rnbrd+b-T{VvB9i4bw>xY56 znsH3!^~fXN?jwvhr5x7#u&#WaJC7(a)1sP!=tng@htJ_W62PS*i~ z@7;lcsHU*Hz?rX%XCI*y#%P8BUPPfpeTWIt`gzr1kLYo6x zcpDsz|1d3Pq2*d=c@|o}g;rpp6Au5GhC$Pz(Vg5(p1_6A~yj0fNO49#%pE_y~khRIJ#lrL7NawWZ(n z-p}@vmS5%TRWILvt+n@=S?A0?J4x?pZ~b!kC1lT;z4z?@o>}vsH8cO^NFzsTVwndz z(k^qPUG7LDnV`h$4RWMi=}5aOA&qKGPo%Y8J@q8tn$w$(c`xBNU6bB%%zN2CqPH9K z_W4KjUbDRz&Z9Z{WVCu}-w`r<6|J7y`!tyyCW{jEPr^MD7bm7qJ@w#p zK_pB)Rpj0dF(RU#n)@u3b|N|oK_pB)mFAqk#(J{6IC1*CA%tI;f~w>uaW`IB{@J6v zy_M{0fQ<0GL}dD6^bzwuKt8&9UyK-H-f!ba#5w6ZVgY@@IfH8%79e${uc|A3xvm_W zD#(PXs8W!Gk>3-25m8Ysc!5ege_Mb|n2IVrYhOl&7Xp1%kcn1O<)v3I%wuGzcL|<` zybt&rqXSAosTqrY3YmTinSRX117!N8K&GEUrXM3SxImBzQ&RmBkM0B+QBobnb>_pp z0Wx7qs?<{ZF)|153!cK>gW^(A-P=!E5%sMBBIG|wMkIYIsx@;3kuVk2nY2ym*Rtb6 zR8;q|UD?+ZAQG*jdhT#RBw9r^_I1fg%)-o{H%CR4bb)_%-P9q}K6o32TSc|+)O8s< zPnc%r&ncU|Jg+~jX^vMhbJY*eef~Y2&0d}-DyodlFYeMewU_7h$GgnIOc*vcJ$JXx zX3wAZS7Ec@Kfy`bt%9j=Dycnx?ov_J&KCcqVE&xU4AHY}FUzMMm!Qd+<#$nk$?`lR zb7py7n~Ex-`o&gJ9i1;GLR3`kRlK62N|B3>>mwblsHo=Ek#X1$_A4E2w2CTsCm+w_ zjy6n1l}zkqEI)nZJTaU!I0y`ec?B}Vp?c!ZYw1R5! zcf=ecBD^1^{C&uhAF7BUvI}=CS;HsEVU4Kkv zChHQdpgQ&rK_yy2b>?MKPiSZyAmXMR=~GZG^brYDP+d=9Z>LF^cTrHC&MoIi9Fb@R zRT1+NdlIdnx(}^~kS6DVp&iXpP-Wq!cE2suWGtN%j3aa)0d;0cMKRsY*2W7Kq zhbX9GPe9GflP%go*=*V&3aTI-uRMI?SVKD~n@u}h3aWZu1q^~tvT27)K~>MT_$LLl zqfJ3I#@@z}s9cF&tD78YH#^d9aiooMq zcPFG#qZ@*>Y^=%>Z`hDlP||PU3Wkz?16K%QF-HF{q;@D@0j?4@14H==`{PH);d-yd zkJ{nrr|}~@wER$vR8464p%|%}(DFkuQZ=FFhhmzjoA>khRZhgwf%uUfOK2#@ttPbm zP!&sPD8?!#me5elqL^4hUu*@{Q4@rGiGpfiU8(aI1y$a4KH|sRsfvQ?VD@c%TpM(% zVG625HHPsxe(y}_6vEU~6tetCdsfi9|Vd zEqog1wco?_;~L&M@)p;Ok(&ZkqE%EGm2QvmkR?n-RqjZ2WG^l7e^u(pwH7jNI+{Ku z)nVV2IufR&%5(Fx#RDcvsvj}=8WwLk(Mqa}%%xIaqLoxnJCVGcXF(y;v>Z1jO`no# zfgk1|Oi4AHH%q?6^+l9a2QVIe;`Jq3NwxRmVo#!#R7d)nE!P7PLr0sVq{>p#w@fjV zkAz3E_C`2o#_V~3>jhieK+*Bid%Bg*ZONSb!oND_4MLG2VcO3&N0z|@;Q>kvIMxYgSz3S8R=6`J>a*NF!j_t(%^hjlpjPrwQn7jwxlXRC0ac-^D(I> zVd|-b-_7g`Wb$=V5fk)VEL7YyBYg_0Oa0BNqM#}@;}KS476nxqw#XK5QBW=5)^l`i zfXXc@1nMm+1S&K?fnaQfpc1X1+WmKulNtGsY;I@x@k9 zU3!CbCt(Vz>)3piFts>X9+-tN1yv#pqM$m;rxwxbse`bGm&Rk{`wO}cwZt?# zPF2)X?bS}Ao+^5<(tU_}YBqNt9i9jfiB?Z7^t+E}_0-L$3L@nB3=nbCjP$9e215R# zo@yg<2ii)^<1zOGf{3W6cHn8G4l4pgqSaG-+$kNx9awj4qG!9*Q|m7hM9A+OAmXMN z=~GV~b-lEdF!fYf&U?G2Z%(gr|$RbNVIzD@UIFY8PZhlsp%+jEMLESETC+SrBuqV(?1-qRj$I^mBw9IjIy7}?uuVRHLob@6oQm1M z`<6|x=mn{h+Vnz{Q*XHGvu@MhvexoYHk)3Ea_WJ+u^oPP`fm-rplmk15am>+0B>6K zg0k84LX=aLl1(pM%Bgzujek-=FWQt-Wm4?{RIbFy zs0STs(;R8j9ceQhX)_&ZwADKCzWI){S&p;Q6-GeKbNws@gF{zfzS9le_sYGpRufL8XaSD+AP5kIo9KDaDf3(_b_BW;b&}y&b z&k?oPVJKtFo4kX5uZ+`*Fojp@GDP9E50ogW21Kj7)?6r^=mfqKd8cETM5W(}ON7&> z?%LPaG8J{#z|Ti0h`Q@wF3+bteb%Fd%0|x?t?1fezMv4T=z8ofLE&Bt1vlMHpQ7uz z{~~okE4q?LMHF4T@Zyq2o)Ed0sYD(lvgHQ>B4O&Tat-oS@CvOH#j6#q@S538P?>0< z;-;MGQ+VAsOi&3^c$H})iKZnAuOBfgdzS^ML@T`J`t>AQ;kDrgLF7IQ5jW*bpTcW9 zKWciI!Yl1BzV5do(RUx$lOAk8_7(?-gekmIJ%Ju6>IrWiD4REg+F6*&D_I*+d9A7t z+Szi2*FswR9wbuT`&`!!`(@s%%<*U75(N4-{ z)6PjiAjkWFLL?5gXeVW}X{Sr!RqwL~gQSye+UZhw)w{3#lLFdFuV4zQTef$qPc7OM zUZ(;RGkG>ud9|i0uhvxM)taijT3>91*J8g@6@^zj4528z%Blkirk2NMV&(=i5J%(P zNwnJQ``e|1jaGY&ohF8p1`okl-Ayynr}jF$zjP&{_8Qm}GI|fNKW880AinV(@#l!z zYlj*tZSSRl;fz*$?a(NQM610Xnj?rjXd&XJ9O+Yg&AUVp2~&HuquGku>!(i>7P*T8 zM55JRhhnV>*|2D}*WoJ!5gIrKb;M0K(x>)%M_)li)L!j6B5JR8z&%lW6-D5s^8-Yt zsp+C=YPx6|cdp0QNgat+d(CYUL}=I?AmXMQ=~H_x?`fDG00D2ycmzP!izy(9TT%ApRW)s z4bJ!MrNQ~wzG{{R=VKFVH}7XSn+#6AS{j_MmImjmrNQ~0y)-!AvzG?vE2@#Ns7Ah` z8u^N9FFGjv+dol7c&u+XJLZLvRP>=$J0);{Wqp)|7pirPtAbL^2DA=(`3Yc)^GQAiXp9&~Yh!iM93K)@( z8pKc*C`1YrA_a`d$CnBsETNGK4SjWR=M>Ir;q_!f^sXDe)GD{N*nHU&QvY-THLW-DxF zGd73*DA!UY9Z_skY2v zZ1%6G()K8&-(=tBC`9HcMCLFeqy6a5a}*+T6e4q^{d`*xAzycZh?{84Rfx=0h|E=p z%uRvFT!qM7Mx=bOATl=zk-3b>{wJ?N7cRRy=Q1MKVRb#?VaE}fs}Py15Shz}yk9DH zWUfMFu0mukBT{**5DoJ58=_H&z9`4>k7VG^g>rc&zT=pE&saobmWKyy%4`~%ae|+X z76d0vrBODUXcXe9bG-il_Drwe&;O0_X;3zsXcPj99Pa{(M{}7)G$@-*Gzx)Kj>l_z z&$5UHWwVJ!At0m&?BbV{w#Bo?#v6gA8#n`Qg|<*u9_O6E)iyy^9_grM|BbTpC*k$dZooI&osTL4RqMm}g;~Xal&t)f z=;!bJn`PxmR~>ARHGGwxpUTe$di@vFlM+W zcn?ENo`kU$_Bz%!H7PR>?O z);a4Y~XXiiF$6fxxgLrM8$$@upekkjz{ zizXSLC?k(<7Xp4I`Q0?BMpWuDPI?)CCa(e*e)@xt0V0c zM;cMz#B=HWC8lLN(oRQOq73Vo9ckSiX~gFf&pp$Tc9tWpM?xBnpXnoGT{$8V;A~h_ zmt=f5r2nmw@u%Y&bf>?UWIXx5NIqL6<1=Z8=8`!>!~j7b8#|xh{!-Gh`r3R`C@mS^9d{+)kVwW;8=xzT zWIRdSzxJMWWi1&GvBi~BKeJ@iwPgGm{`1Jdu?$zHH$x+y7zT`TR~oFx;5CrDiex-X z^Zm%WtCox>QLf_mLM7wR^zSO3Nx1#^uI>4*Slq?WN~5S4);Y>u+heHJ%R4}FMJ3i< zwPZZ?Z9`XmPiPTWc+SYA*{3RswLxx ze&>I^an706UA1I<5C5(m`K}fIeLC`esD;vfC_-5;kC*qpWZh?mUo&ZF*2{Z|mL@-I zy~!D#)J&0#?}__R9TCa+bNnldWIWI3?z66}CF2j*AOOIv)|CtWE0bh=%sbnE0`Z!z z#+3;-YQHgWP5g+&!mHv(?Qpa-UsZz9s&XlhS@K zaU^}$s!dD$^3m1$dgu7%qkctdEtM}bUYp4Oo{82U`17dc%iqUyi88rF7a)~Nl*uLH zNwOEM60Pt{l*uJxeb*N(#8&tv%EX;x-no8>s8@@5>+qXO)Jc`7Q;HH%keL`nU!_Ey zxI{2Z!0)R0(NtGx}nxE>?NnD~! z{SuwTB_hA*FMep1Xop{-lek1bc^IOZ#l^StDo_V^_N?Lp0ce9DKPZd_ntw8t+|j7!wpFA=esn77$4 zQI;xER!E7c#$_?m=lfS7{uA?lyytyfsIQ56FZf5q_hKH+6H-oMdMybqfa%A!-~#@LqA##Z+$GV^ zk#>BU!h$v^rEAHD1!_2#2}{t@+|*&Zf4+ky))a&5r{sQwY(3J)&uzApkE7F?hg6QAGn zYJUkXF#0XJ>U2;Bng5LrF3|PEbYdikBkix@kw8pfw$%fK4D=7WKpkdz?Coq@BfNQdvH@wYr?_|5GoR8 zp#I+lm9P*4l9L41mTnL&80udFM1+>Ut6 zz2YES7YQ+N1_5|3JEd5OBZe(r*CSnrRWc&ay*C&{`fEVSxYYH zruJHj{%S1+{B1Z%yW#u?ve#0$LJVlp%>zjh^clJH){75n?#QLJUmgWk0`R6Coi6hVlN8Ptm=K z;S38gKpn(oyxi)@4r%z&;RO!;L<}bl4+6vKCLiewFHkc@5D5z}V8`&0@B$w`L#1ul z6(ABFUf^JvAQBy3U?L*8(Dq~+90iED=|=j(3vBWC5J`A}U|}zHELU(h0&h1$z=(3G zgclgdh~&bDE_EaRb`#(OpU6;+K7_M*vdd>}Ofv#;+3|BY-Jrn1j6i|$|Z~|@N1cDU{ccX#XzB)47 zLy(s3<@oe-2t4Icajnev5)9xQxPsX;)D}*FQ2kcw)#yoLULU3zqvC`sIw?NP7AkHUn!eBi z&-#01!a@t!@th>Iz-Ua{#k^sQg1QnNT7W&ELxhHgg%+T?at+s&;olcjuC-8c)6n#V z6`1VHR)&QY!14^t*Z-Uen1mH*U;?%hOBtjEMTZq&72h8UD$!vDCLfg{%XJniZd#hY zumZ=vCbTpxtiYQD%6DbEvV;{lie&*Y@1uBKi4H4}{i2Y~=&%9}r84(B>E@1_V*?jfojrcMA#x5dD&0qPXo1a@(tX@yq2i_*=?g7z#Mg+C&;m9sy@`E0cFcDPE$}{fsz(z{soxy3@oO*7ILR$!*@8I`aC!9+FL5D6&mXMA{J3Ln_5UH0TPLM+V*%+Hk(>VD1m&+w{wi4J(SI+7OqeNdQ~=9 z0-a=23s)!sy;j>l$)* zcOMsW9N7chsout&>a*N^yuLUv5z%1<4*K0kbXbAx#nOG;ZW&59%}8HZfxOFvW=L3p zz)%t*5?0_!ruY&zQo;&!=P9I-O9Di~!V1VWXqtd^LdHq=5gl6K(;`9T4ht1G%}8Hp zfr(h0Oyv&?EpYWxS|PoM>xqOGSjn_&@2miou+Rc>RYHZySDv5}9bRB*_6a=Qc&CMm zn`)#lyugur1(mSy0vz_>zTT;ROi4JK4v>`2AF(ncroh z;-(tu3o)?e8bKv2#DM6cyvV3PJ|Ay>BDbJn*yf;D(Nu`k(L8{m z9nA?b@ChxLskS^Cq{wg64hb=k|tZ;RYuAvUED! z03jmb24w0{CfO!1B8BV=+&eEoBs%oKI-h1lhaTwurXX^!g@~JurZ4osTek|u2n#*H zdwmZ+&lE#K4|Lr`SX6OMp*k8CdVst(_p*=YfFEHwEbIW`e=pY&rt{Sjw|k<6ikpt6 zFZ4jYAE7!d^Z@t&mv3WKB=o>R_Ix$OJ)~iw2k2QR8qe2!A?ab^2P8KwxMyT03o`dv z$hawK`a%#i_>*j5AqedHav!&#?uAslG|DjX55(!$OOal3Kq_Nm~?t;JG1>{chh68Vn_+tJ#!P!Vi4D@lQV= zagRkwDVt46C*e(FB^mS@zO>S!q?FC3q^|G-de1)CC7onbQdjr^y_4TRDWIg(xTgTv zY*rg;^!nkbI}rcQrX6kJ2l5o{$Ya_u(l-$jen6&| z7Qs|@^CbL0cVLO#)od^e<7Oc` zq-B4cJ@}Gm7!e6MkPS(KnD^BvZFz{|^Inymf+!P~yVF!-*+fyX$ zKrj(Y_5(Wms1IU4K4$yz@!9}|uuuc^#vzW6^LVOpo)E6+a04TMCz#Cu5{6ndC)~iJ zwBY6yi&~I6zfCP9+(6fHdk^%PkZ(8xWwX6q5^msy{lln@uew+yL{L>nv(P zSF@>wgd5;V?5nLwHp*sG3s<-SEz<**KquMM!WC{ni}&~^+0c)3Cv}5z=7d@ zR~j8~paVh^5V-(zi6i1B7wHQ)uylYRA^`{NHBAz5pyShLU=!aKMnnP*e9Ty^TpAz} z9cJKle}zVLn1Ru=q#K=MA>t+$=?gPZ?h^|MGhibkVFotQSTySuMr01Rn%>-{PJTQ< zBs$DM;gixyMu!=woGysWwGeR=i}ZyVcox=!=zxS7uo1Dt42V}p!VG-KcI5T=#)IfE z0|)(36wzS@dS{F8qUB)*c>8&+MKDN}-zFG^yl?H=UJo5t(XqfV2g+s>3<)!E^q~({ zb*bK^v)Keg!VF*y&l$I0Z4nH*noTey%)s|4piy^=U{E%jV7S5zXth3Y1v<$l7_Kk_ zTEovjDIgd$o%R^2nmvs|Y4aUv@OCHVTi{4r=twJeq%Cr!VUb7DJ(f7qzUN5$S4SGi zf710z9cjxPX=RQyY$8dz9;s9&;zwGQiD_sZNonYLlhUvsHYu&rk+#Z_hK&nJ=dN+2 zkshO6KJxWYJ;?U3x|65Q9&3dUi)xMWVa;!a4~y!UiECA)d{{*wJ>9(5kb_EFqTH3UI$NNo&K9Vt zvjr)p&K3Xx8YoS79p+aP#LS$hIL>4MU z7BV7z{7v5rlMq?Rh}hnng^WmSE0y-s`T&uI3Xz2hk%f#%eX-Dqg$j{{3Xz43NOrzZ zPO`rNB5ulAtPm+yh!iVCic=s`tPm+?L}vP80L6@mO(%*O5mvA{Ue+uXGa_TzbCU@} zD0QS*AyTXmDP~0W`f<>T6(Yq7kzz*V^^>HIQ125U;-;L76e5chB8wCvi&7x6NFlO_ z5h?U{>n%#EBa0Xj+vmB65y=xo(8UQNixeV@6e5clkz5~5&4J_sbXI+^#_X; zB8%gQ5RqNXi12E)Uy5IAu|j3BLS-?d@}Vy^LF~i$wU(eqN}MjEW0^8rf@jI`@=ivy z>T%ZM@dYS=O*@z1A#=PHpR^zQgKs`$_#|bsY3C9=d5(8F?H#DIXeVW}Y3CBak>hcU z(MK%WN!e`LxdiVv$AehHS1!8#HnVt~ve~qA31FlLlIT`YTgKl!Zhk6|ss)3jlWf|# z1ZdJp65R?K%=nvQe8r`i{gVRPNiU2jT(*bdDASGawSsOC;WKpOUt2*ph#nfcQKINZ ziJ}`Nif)vkA($z%5;O!eWmbZQVCY5(8iJu4C5mp8D7sMsObp#9K~*wzqXad^C^D6x zVi>wng73Erw%sZ)Y?Yv@J^UZGj3Q-W7%=tc=%s9C8)^f@pS zZn{yb%tWa&6Q#;bl%_BfrNG0`jZ!ufg-}qS23^W#g0;41l(W=fDVqr~oIhf^QOagQ z=*B(_9>s8$Dl<_UHxo3gUdm=7pc|#iM3gEMQOYJ_?C-^J(%>OL#7#GrDMXejM3yN; zmZd;snL=b4BhuYp`nrq}u{+mgjL5kRl8n`tF(PlVKW8vT3WCTog~&36$TCJ`^z%YD zmMKJ*DMXerA}jwYbc2RY0U~a?QKk?nQ;3u)M9NYiQl=0oV?>I54_6r@V*C8c7!lrI zF#K7rBV~+8)g~(K`(OluNSQ*UERG1(yfQ|FyOKT9m6Rz|$`mSPj7sJ*sV6je3{Y`X zj&g-cxk9B}p;Dd#m2!njIis@9Ct>ADsFX7**}R$YB}S#3QJKyiY@av@D_5wL$5Ek{ zQ!e$yryS)9k#dDdIV19+ug*k6=Kv8m!j?!6>mM%adBp zazxosx`6--IASvwkGtTXWYZ1uzGQnB`*h<6t)Ls^t1)!riB`}J^6VJ8QK9HYg`yi3 zif&Y(5g58rfkt5HMg13OVdzE$ z-oK$66?n~tZdBlH8oE(|24Uz%1>UWp8x?qkhHg}#K^VGGffs7%26?^$6XB*CmC8g^ zDicwuOhjc06H%#5L?xSu!f|3ED%nH?+u*4isALl%(?^@xL{zegkgnulTqCwpnTX1` ziI8i=cV_urNu@FqmC8(1vYCiYmJXJDe*r3P%CSnJvPz+{N};kU1uCl)DytZk`m+U< zRg8+=!LDLdc!TQGPctg37?u4zN;~ujm6;6ZDuv1_g~}>6oV<+lFVexTQmCv_sH|dC zYWm2k9rAt#sJLmzYK6*bh01D$%IXxTtX8P3W>h-(Q;n;WP+85W*xTY)Gb$xKX)^Yy z0F~7WmDLKB)r<1+)>kni zBA;*s%s>#SQixP3M5-8(4*vevDuqauLZpfj>DuRbjtjCDh}iQ1GzYZKk%r2el&{*6 zw%(Bj-jsAM3`0^HcxO@?Dn(Knnom;NGmf+^jE~M1{v1yvRjdW#l+C7}YtbZfyn)p7-el2F%4XBgwP-6j9?6fNKgF74 zrEE6+T#Ke;=qKW4;Yf$m0+XbZZ2Gwtt;*0(MAO2N4zlH+WYbS-OxYgxzO%P$T`TA( zH9bQ=pK1mDq=stfXSJfA)rx*rEBaXtV`%7SHH@L5pVcsihJIGV7#jLnt>|a9qMy~M zY=(YTqbeEtS&bTF=w~%5hM}L;c>jieR^v4r`dN*)Y3OG)45Oi+)p)mtepcfZ8v0oc z!)WMdHD0KppJd2_uEI?}*Q>5#z3M8~tFB^wimqb4>MGWASF!R&={(mbbrtKmtFWaG z*K=2q3tNW}1?b=u+uC*Q>5#z3M8~b5}9wk3uKNj0cFgsb`Hs zq(&i9qY$Y{fk=%)q=pfx8ZL;`Fe0|MtA-H~^C5)1h7sw?UW}zb3=pYNh}6UpAwpim zh%oj1qmZl`g-VS=rG`s1Vu6h)neT#2b_9$wo%RCgU3!5%KMeeKbI1qe5h391*G~ z8yOK^7+)dvWTQf5qe5jPqf+u4L4}A+fQp-XZc?ahQmAZFsBB7s$|i-%CPw9;FR8I9 z36)KZioJw>6Qh#H^<-ikl}!qjO>tDHo@`=Nj`>?HHYr3lDMU6gBIg1Tq8voy4CUCI zl5%XubL4pID_`tcdx1qc*5l?j<=Bh|%<=ZU@Z9&>AAZ=lvM8HPIX2^|!6i0-ee@k$ zck666<=6};e4b-d4!W96IX2_H=6FwGOWIrQERPpuvnj`BKxilj0%PJx$IAqJpp$IM zu^DI@%7Jj1IMPux{gZ6UL0mZ7yVR!~&$NPa5OX$^V@oS22k~q}IcgQx zGy+38YS9P`<)}p?FqESfjlfWjT17c(73HW!Wiync7FEemj#|_hLpf?uF%0FX#rrpu zqZY5(P>x!>O+z_q(I5=vsKvWAl%p1}&`^$AGzddEYVkr1<)Cp$U?SXJ%b%3XwX6NL>m<>J%b%jL1MJr_%G+ zB^k{+Mx=|(6R@|dju8=M<@e_Vh}0=W>f(sVRmlfvD=XJ2RO%Eeb&SgBqk;;JLjzRY zl%rmuQm;^{SE$sdK&4)xQqQQ&_t$;ZGb*+Zu%1!b^XiibxhuQ&>KTJ=jOj7X2m1Q8mA2Z*>S$2Nt?HigJGg~+xPh-_1c zY-2>W_>u|Rk`UR(hrWscE@Tj%^B+ zZ3>lbj7sL81r_o`1gN+v$99Fvc7@7zh0696sBBlLY-dyk`C_5llTg{ts7N$EnPl6} zs2t+ywNXm~RJJQrw#QMSda|8S>F!TYZ&!$HSBPw9MD|11j3@_rVGQNik&<%kz;ony z;?urw2-#x~CGWKrH@7Lr4m_a0QfAf+!zC!2O*wYp+#K)N*`I&%&<8tpHk)$n02IO1 zEH4LL&88eX@Lqi{$9uQxve}el2Ou<*1Cg(Bqyu964`fr09YE7i4#dO8kq(pXpJY=G za%E+ESlz~6j^|rJImq#4D8~z}pd92TGnAu2QH};hIT{q@Xh0({l%oNSz)+3`Gy+38 z8qf#~z}qyGqX7-V zP>u$?TSGY-@Cps(Xh4H7l%oMJ)KCs`BL*hIO*wWd6R}g7h@Hwr>`Y-Ib}AFGlTF0t zZ%YTelTCz8Id%e*m}f7T-N`1R9V=y&^T?i>^G;izWhQpU z&4lD84G~^!_`Ya}kTW$v#!WqTDP(pjWOgZJcBMdOmqKP2BeT@+Qg$&iwzp#!BO?=% zGOpjn$ked$Jos2(Ja;K%cEyn)<4L;(W8Tacq?6sHP}vnnMRF1;j)#(l^OFU7X)=?RTWT=1BXgBkgC7wAUSJZ#dH4bfo>< zk#@k5_6tYaFCA&Wa-_ZGNPF9n_KqX%T}Rr%gf!aW+Jv;AHMl8flWGl3sx>sJ*3guq zH8iQ#(8R4F)8Eh8q*?>D? z_u#2>yo*1+?3(MQJ#Q!{WwR;g9zc=fJwczpTH*94n@u_Q0I3|0q<8;xoaN=BY&PZG z0|*V}+yl^ZJl^N=nYE0ZPO>TI9-wI`=N{mf<2_EdTCmWNTRJJAoQ+<8k4(wwE%I7PT7IhOXKZ`n&mY+pkP0P=s&Zy;QQFqnyv#3LB`B~KEwfrpV6kC24 zb(<|ei#pbpo%ILr73y{mH{r}jy^e=F;(r>>W%T3@?l5L^@cr?J&&U56-ie2^@PFdx zIPXh*p4Zbur!4s&!2e~PBs~M^=ykE=JM-|R__NSEKI8a|-53OS^yX#U=y@IIXNNV_cW7IbL<#B!IyB(G5Ql9_&YG{ zAK_h$eC?309a2uhKT4-_Z_bLzivX3mqCJv3AgQaDK_x_AKd3#*N^&ide4I{hkB7TC ztE1PBu7*#0{O#YDFFrPl@>B7ic!G*U-*w~S$*~l#8;W-qa^kgT5l|lIrZYz%H&$kP zo%lNR2-n2_YSzPabUh5caC*jOJq*1YLCSiV0rw(}9_Dm;7(SMVIX(Gd@GV_q1agNx z%+*=+UcB@0bjNwUvU;LyKBPF&{w%{~=tHmWD~tu<&(}vPA#^QLGrVtPQ5D{fpR)ku zK-7A%jasWjfb8n@bi{ zOM(hZ`M7lUvt5jq@gN4I=c0sf;9Bz%p6xuYTfyx+XMGuYGBajDXLh zM88=}RZGTiBnor~Fr>nuKS1rB0HZev!D;&fLop#O$BbI0V0H=6 ziMq-=?@-4iwWtT)FkyX)-_H39>ldNjjqA>-$nkh**bbP`Iy~DV{ItvH!Ov#bp~O6i zb?6*bhv?p}@&*W#;dGUk&Li7KpU=_kshq|f$ODi$5Yzj_Yxol%s!cg~Vr|>#8|u79 zAUBxssVJJL4v(U{6Dh$ir`H8B@_lCU`x) zQQkv1rj%)3K90|a8kegbebtFib6OHTtvreS6r5=ND#dft(-VSZEA;!J7D?UOv15eD z-+G;lR$PZVL}O2b@|ZUScHw>fxRZIB#`A{YDEOR-Zn9l>@CthI-SMwW7s~uU=|ivR zzd#J=hhU}`0sDczU3d?rw|&022xQ>xw?n0*C(P<7X1odS9-nKwYv=SmFNE#?5daccSSl#rWJ73A*zt8?Zn?Kt6N(P-vHe~3uN#myW96I5?DHEoS&znYl zJYP4}cPIjX=o&5g+dr-Czb`lfw}2Mpc{9A8h=V>A{}UBx$>%iG{J=U={YtbUu4`eF zl{bVM2tK2kmQ42U;c`rt#1{NM*`wi+v@I$}b55oFGwzoHWO&1H=lh}OJrZU3ZgWmo zdFcMs<^v4WmnjZz@W0EzeW!qqwf6HR+&{Q?OP*f_)nvCc4ZH!hz)-~*7#QRsCH!-K fEACHtjR}lV_@$fuO-rAjI8C!J-i(a@%=iC)8|QEu literal 0 HcmV?d00001 diff --git a/lib/libsolcompat/zu.c b/lib/libsolcompat/zu.c new file mode 100644 index 0000000..31dbf45 --- /dev/null +++ b/lib/libsolcompat/zu.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/lib/libumem/zu.c b/lib/libumem/zu.c new file mode 100644 index 0000000..31dbf45 --- /dev/null +++ b/lib/libumem/zu.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/lib/libuutil/zu.c b/lib/libuutil/zu.c new file mode 100644 index 0000000..31dbf45 --- /dev/null +++ b/lib/libuutil/zu.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/lib/libzfs/zu.c b/lib/libzfs/zu.c new file mode 100644 index 0000000..31dbf45 --- /dev/null +++ b/lib/libzfs/zu.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/patches/README b/patches/README new file mode 100644 index 0000000..31af597 --- /dev/null +++ b/patches/README @@ -0,0 +1,17 @@ +# +# Mostly patches for a userspace build. For now I'm leaving them all +# out until we go through the code base and sort out the userspace +# portion of the build system. We may find we do not want or need +# many of these patches anymore. -Brian +# +zap-cursor-move-to-key.patch # Add a ZAP API to move a ZAP cursor to a +given key. +spa-force-readonly.patch # Add API to discard all writes +no-debug-userspace.patch # Disable debug code on userspace +no-events.patch # Define away spa_event_notify() in userspace +pthreads.patch # Use POSIX threads in userspace. +port-no-zmod.patch # Do not use zmod.h in userspace. +port-pragma-init.patch # Use constructor attribute on non-Solaris +platforms. +lztest-lzdb.patch # Make lztest call lzdb from PATH. +zpool-force.patch # Change -f to -F in zpool command diff --git a/patches/lztest-lzdb.patch b/patches/lztest-lzdb.patch new file mode 100644 index 0000000..37870b7 --- /dev/null +++ b/patches/lztest-lzdb.patch @@ -0,0 +1,40 @@ +Make lztest call lzdb from PATH. + +Index: zfs+chaos4/cmd/lztest/ztest.c +=================================================================== +--- zfs+chaos4.orig/cmd/lztest/ztest.c ++++ zfs+chaos4/cmd/lztest/ztest.c +@@ -3043,30 +3043,17 @@ ztest_verify_blocks(char *pool) + char zbuf[1024]; + char *bin; + char *ztest; +- char *isa; +- int isalen; + FILE *fp; + +- (void) realpath(getexecname(), zdb); +- +- /* zdb lives in /usr/sbin, while ztest lives in /usr/bin */ +- bin = strstr(zdb, "/usr/bin/"); +- ztest = strstr(bin, "/ztest"); +- isa = bin + 8; +- isalen = ztest - isa; +- isa = strdup(isa); + /* LINTED */ +- (void) sprintf(bin, +- "/usr/sbin%.*s/zdb -bc%s%s -U /tmp/zpool.cache -O %s %s", +- isalen, +- isa, ++ (void) sprintf(zdb, ++ "lzdb -bc%s%s -U /tmp/zpool.cache -O %s %s", + 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 ")); ++ (void) printf("Executing %s\n", strstr(zdb, "lzdb ")); + + fp = popen(zdb, "r"); + diff --git a/patches/no-debug-userspace.patch b/patches/no-debug-userspace.patch new file mode 100644 index 0000000..616a691 --- /dev/null +++ b/patches/no-debug-userspace.patch @@ -0,0 +1,184 @@ +Disable debug code on userspace + +Index: zfs+chaos4/lib/libzfscommon/include/sys/arc.h +=================================================================== +--- zfs+chaos4.orig/lib/libzfscommon/include/sys/arc.h ++++ zfs+chaos4/lib/libzfscommon/include/sys/arc.h +@@ -82,7 +82,7 @@ int arc_released(arc_buf_t *buf); + int arc_has_callback(arc_buf_t *buf); + void arc_buf_freeze(arc_buf_t *buf); + void arc_buf_thaw(arc_buf_t *buf); +-#ifdef ZFS_DEBUG ++#if defined(ZFS_DEBUG) || (!defined(_KERNEL) && !defined(NDEBUG)) + int arc_referenced(arc_buf_t *buf); + #endif + +Index: zfs+chaos4/lib/libzfscommon/include/sys/refcount.h +=================================================================== +--- zfs+chaos4.orig/lib/libzfscommon/include/sys/refcount.h ++++ zfs+chaos4/lib/libzfscommon/include/sys/refcount.h +@@ -43,7 +43,7 @@ extern "C" { + */ + #define FTAG ((char *)__func__) + +-#if defined(DEBUG) || !defined(_KERNEL) ++#if defined(DEBUG) + typedef struct reference { + list_node_t ref_link; + void *ref_holder; +Index: zfs+chaos4/lib/libzfscommon/include/sys/zfs_context_user.h +=================================================================== +--- zfs+chaos4.orig/lib/libzfscommon/include/sys/zfs_context_user.h ++++ zfs+chaos4/lib/libzfscommon/include/sys/zfs_context_user.h +@@ -96,6 +96,8 @@ extern "C" { + + #ifdef ZFS_DEBUG + extern void dprintf_setup(int *argc, char **argv); ++#else ++#define dprintf_setup(ac,av) ((void) 0) + #endif /* ZFS_DEBUG */ + + extern void cmn_err(int, const char *, ...); +@@ -105,21 +107,26 @@ extern void vpanic(const char *, __va_li + + #define fm_panic panic + ++#ifndef zp_verify + /* This definition is copied from assert.h. */ + #if defined(__STDC__) + #if __STDC_VERSION__ - 0 >= 199901L +-#define verify(EX) (void)((EX) || \ ++#define zp_verify(EX) (void)((EX) || \ + (__assert_c99(#EX, __FILE__, __LINE__, __func__), 0)) + #else +-#define verify(EX) (void)((EX) || (__assert(#EX, __FILE__, __LINE__), 0)) ++#define zp_verify(EX) (void)((EX) || (__assert(#EX, __FILE__, __LINE__), 0)) + #endif /* __STDC_VERSION__ - 0 >= 199901L */ + #else +-#define verify(EX) (void)((EX) || (_assert("EX", __FILE__, __LINE__), 0)) ++#define zp_verify(EX) (void)((EX) || (_assert("EX", __FILE__, __LINE__), 0)) + #endif /* __STDC__ */ ++#endif + +- +-#define VERIFY verify ++#ifndef VERIFY ++#define VERIFY zp_verify ++#endif ++#ifndef ASSERT + #define ASSERT assert ++#endif + + extern void __assert(const char *, const char *, int); + +@@ -332,6 +339,7 @@ extern int taskq_member(taskq_t *, void + typedef struct vnode { + uint64_t v_size; + int v_fd; ++ mode_t v_mode; + char *v_path; + } vnode_t; + +Index: zfs+chaos4/lib/libzfscommon/include/sys/zfs_debug.h +=================================================================== +--- zfs+chaos4.orig/lib/libzfscommon/include/sys/zfs_debug.h ++++ zfs+chaos4/lib/libzfscommon/include/sys/zfs_debug.h +@@ -44,7 +44,7 @@ extern "C" { + * ZFS debugging + */ + +-#if defined(DEBUG) || !defined(_KERNEL) ++#if defined(DEBUG) + #define ZFS_DEBUG + #endif + +Index: zfs+chaos4/lib/libzpool/arc.c +=================================================================== +--- zfs+chaos4.orig/lib/libzpool/arc.c ++++ zfs+chaos4/lib/libzpool/arc.c +@@ -1802,7 +1802,7 @@ arc_reclaim_needed(void) + return (1); + #endif + +-#else ++#elif defined(ZFS_DEBUG) + if (spa_get_random(100) == 0) + return (1); + #endif +@@ -2881,7 +2881,7 @@ arc_has_callback(arc_buf_t *buf) + return (buf->b_efunc != NULL); + } + +-#ifdef ZFS_DEBUG ++#if defined(ZFS_DEBUG) || (!defined(_KERNEL) && !defined(NDEBUG)) + int + arc_referenced(arc_buf_t *buf) + { +Index: zfs+chaos4/lib/libzpool/kernel.c +=================================================================== +--- zfs+chaos4.orig/lib/libzpool/kernel.c ++++ zfs+chaos4/lib/libzpool/kernel.c +@@ -384,6 +384,7 @@ vn_open(char *path, int x1, int flags, i + + vp->v_fd = fd; + vp->v_size = st.st_size; ++ vp->v_mode = st.st_mode; + vp->v_path = spa_strdup(path); + + return (0); +@@ -422,10 +423,17 @@ vn_rdwr(int uio, vnode_t *vp, void *addr + * To simulate partial disk writes, we split writes into two + * system calls so that the process can be killed in between. + */ +- split = (len > 0 ? rand() % len : 0); +- iolen = pwrite64(vp->v_fd, addr, split, offset); +- iolen += pwrite64(vp->v_fd, (char *)addr + split, +- len - split, offset + split); ++#ifdef ZFS_DEBUG ++ if (!S_ISBLK(vp->v_mode) && !S_ISCHR(vp->v_mode)) { ++ split = (len > 0 ? rand() % len : 0); ++ iolen = pwrite64(vp->v_fd, addr, split, offset); ++ iolen += pwrite64(vp->v_fd, (char *)addr + split, ++ len - split, offset + split); ++ } else ++ iolen = pwrite64(vp->v_fd, addr, len, offset); ++#else ++ iolen = pwrite64(vp->v_fd, addr, len, offset); ++#endif + } + + if (iolen < 0) +Index: zfs+chaos4/lib/libzpool/refcount.c +=================================================================== +--- zfs+chaos4.orig/lib/libzpool/refcount.c ++++ zfs+chaos4/lib/libzpool/refcount.c +@@ -28,7 +28,7 @@ + #include + #include + +-#if defined(DEBUG) || !defined(_KERNEL) ++#if defined(DEBUG) + + #ifdef _KERNEL + int reference_tracking_enable = FALSE; /* runs out of memory too easily */ +Index: zfs+chaos4/lib/libzpool/spa_misc.c +=================================================================== +--- zfs+chaos4.orig/lib/libzpool/spa_misc.c ++++ zfs+chaos4/lib/libzpool/spa_misc.c +@@ -178,11 +178,15 @@ kmem_cache_t *spa_buffer_pool; + int spa_mode; + + #ifdef ZFS_DEBUG ++#ifdef _KERNEL + /* Everything except dprintf is on by default in debug builds */ + int zfs_flags = ~ZFS_DEBUG_DPRINTF; + #else ++int zfs_flags = ~0; ++#endif /* _KERNEL */ ++#else + int zfs_flags = 0; +-#endif ++#endif /* ZFS_DEBUG */ + + /* + * zfs_recover can be set to nonzero to attempt to recover from diff --git a/patches/no-events.patch b/patches/no-events.patch new file mode 100644 index 0000000..054b7ae --- /dev/null +++ b/patches/no-events.patch @@ -0,0 +1,44 @@ +Define away spa_event_notify() in userspace - not necessary and breaks compilation in older Solaris builds. + +Index: zfs+chaos4/lib/libzfscommon/include/sys/spa.h +=================================================================== +--- zfs+chaos4.orig/lib/libzfscommon/include/sys/spa.h ++++ zfs+chaos4/lib/libzfscommon/include/sys/spa.h +@@ -516,7 +516,11 @@ extern int spa_prop_get(spa_t *spa, nvli + extern void spa_prop_clear_bootfs(spa_t *spa, uint64_t obj, dmu_tx_t *tx); + + /* asynchronous event notification */ ++#ifdef _KERNEL + extern void spa_event_notify(spa_t *spa, vdev_t *vdev, const char *name); ++#else ++#define spa_event_notify(s,v,n) ((void) 0) ++#endif + + #ifdef ZFS_DEBUG + #define dprintf_bp(bp, fmt, ...) do { \ +Index: zfs+chaos4/lib/libzpool/spa.c +=================================================================== +--- zfs+chaos4.orig/lib/libzpool/spa.c ++++ zfs+chaos4/lib/libzpool/spa.c +@@ -4449,10 +4449,10 @@ spa_has_spare(spa_t *spa, uint64_t guid) + * in the userland libzpool, as we don't want consumers to misinterpret ztest + * or zdb as real changes. + */ ++#ifdef _KERNEL + void + spa_event_notify(spa_t *spa, vdev_t *vd, const char *name) + { +-#ifdef _KERNEL + sysevent_t *ev; + sysevent_attr_list_t *attr = NULL; + sysevent_value_t value; +@@ -4497,8 +4497,8 @@ done: + if (attr) + sysevent_free_attr(attr); + sysevent_free(ev); +-#endif + } ++#endif + + void + spa_discard_io(spa_t *spa) diff --git a/patches/port-no-zmod.patch b/patches/port-no-zmod.patch new file mode 100644 index 0000000..34cabd1 --- /dev/null +++ b/patches/port-no-zmod.patch @@ -0,0 +1,112 @@ +Do not use zmod.h in userspace. + +Index: zfs+chaos4/lib/libzpool/gzip.c +=================================================================== +--- zfs+chaos4.orig/lib/libzpool/gzip.c ++++ zfs+chaos4/lib/libzpool/gzip.c +@@ -28,22 +28,35 @@ + + #include + #include +-#include + + #ifdef _KERNEL ++ + #include +-#else ++#include ++ ++typedef size_t zlen_t; ++#define compress_func z_compress_level ++#define uncompress_func z_uncompress ++ ++#else /* _KERNEL */ ++ + #include ++#include ++ ++typedef uLongf zlen_t; ++#define compress_func compress2 ++#define uncompress_func uncompress ++ + #endif + + size_t + gzip_compress(void *s_start, void *d_start, size_t s_len, size_t d_len, int n) + { +- size_t dstlen = d_len; ++ zlen_t dstlen = d_len; + + ASSERT(d_len <= s_len); + +- if (z_compress_level(d_start, &dstlen, s_start, s_len, n) != Z_OK) { ++ if (compress_func(d_start, &dstlen, s_start, s_len, n) != Z_OK) { + if (d_len != s_len) + return (s_len); + +@@ -51,18 +64,18 @@ gzip_compress(void *s_start, void *d_sta + return (s_len); + } + +- return (dstlen); ++ return ((size_t) dstlen); + } + + /*ARGSUSED*/ + int + gzip_decompress(void *s_start, void *d_start, size_t s_len, size_t d_len, int n) + { +- size_t dstlen = d_len; ++ zlen_t dstlen = d_len; + + ASSERT(d_len >= s_len); + +- if (z_uncompress(d_start, &dstlen, s_start, s_len) != Z_OK) ++ if (uncompress_func(d_start, &dstlen, s_start, s_len) != Z_OK) + return (-1); + + return (0); +Index: zfs+chaos4/lib/libzpool/kernel.c +=================================================================== +--- zfs+chaos4.orig/lib/libzpool/kernel.c ++++ zfs+chaos4/lib/libzpool/kernel.c +@@ -36,7 +36,6 @@ + #include + #include + #include +-#include + #include + #include + +@@ -876,31 +875,6 @@ kernel_fini(void) + urandom_fd = -1; + } + +-int +-z_uncompress(void *dst, size_t *dstlen, const void *src, size_t srclen) +-{ +- int ret; +- uLongf len = *dstlen; +- +- if ((ret = uncompress(dst, &len, src, srclen)) == Z_OK) +- *dstlen = (size_t)len; +- +- return (ret); +-} +- +-int +-z_compress_level(void *dst, size_t *dstlen, const void *src, size_t srclen, +- int level) +-{ +- int ret; +- uLongf len = *dstlen; +- +- if ((ret = compress2(dst, &len, src, srclen, level)) == Z_OK) +- *dstlen = (size_t)len; +- +- return (ret); +-} +- + /*ARGSUSED*/ + size_t u8_textprep_str(char *i, size_t *il, char *o, size_t *ol, int nf, + size_t vers, int *err) diff --git a/patches/port-pragma-init.patch b/patches/port-pragma-init.patch new file mode 100644 index 0000000..665aa9b --- /dev/null +++ b/patches/port-pragma-init.patch @@ -0,0 +1,52 @@ +Use constructor attribute on non-Solaris platforms. + +Index: zfs+chaos4/lib/libuutil/uu_misc.c +=================================================================== +--- zfs+chaos4.orig/lib/libuutil/uu_misc.c ++++ zfs+chaos4/lib/libuutil/uu_misc.c +@@ -251,7 +251,13 @@ uu_release_child(void) + uu_release(); + } + ++#ifdef __GNUC__ ++static void ++uu_init(void) __attribute__((constructor)); ++#else + #pragma init(uu_init) ++#endif ++ + static void + uu_init(void) + { +Index: zfs+chaos4/lib/libzfs/libzfs_mount.c +=================================================================== +--- zfs+chaos4.orig/lib/libzfs/libzfs_mount.c ++++ zfs+chaos4/lib/libzfs/libzfs_mount.c +@@ -128,7 +128,13 @@ zfs_share_proto_t share_all_proto[] = { + PROTO_END + }; + ++#ifdef __GNUC__ ++static void ++zfs_iscsi_init(void) __attribute__((constructor)); ++#else + #pragma init(zfs_iscsi_init) ++#endif ++ + static void + zfs_iscsi_init(void) + { +@@ -548,8 +554,12 @@ static void (*_sa_update_sharetab_ts)(sa + * values to be used later. This is triggered by the runtime loader. + * Make sure the correct ISA version is loaded. + */ +- ++#ifdef __GNUC__ ++static void ++_zfs_init_libshare(void) __attribute__((constructor)); ++#else + #pragma init(_zfs_init_libshare) ++#endif + static void + _zfs_init_libshare(void) + { diff --git a/patches/pthreads.patch b/patches/pthreads.patch new file mode 100644 index 0000000..ec29eb9 --- /dev/null +++ b/patches/pthreads.patch @@ -0,0 +1,924 @@ +Use POSIX threads in userspace. + +Index: zfs+chaos4/cmd/lztest/ztest.c +=================================================================== +--- zfs+chaos4.orig/cmd/lztest/ztest.c ++++ zfs+chaos4/cmd/lztest/ztest.c +@@ -141,7 +141,7 @@ typedef struct ztest_args { + spa_t *za_spa; + objset_t *za_os; + zilog_t *za_zilog; +- thread_t za_thread; ++ pthread_t za_thread; + uint64_t za_instance; + uint64_t za_random; + uint64_t za_diroff; +@@ -224,17 +224,17 @@ ztest_info_t ztest_info[] = { + * Stuff we need to share writably between parent and child. + */ + typedef struct ztest_shared { +- mutex_t zs_vdev_lock; +- rwlock_t zs_name_lock; +- uint64_t zs_vdev_primaries; +- uint64_t zs_enospc_count; +- hrtime_t zs_start_time; +- hrtime_t zs_stop_time; +- uint64_t zs_alloc; +- uint64_t zs_space; +- ztest_info_t zs_info[ZTEST_FUNCS]; +- mutex_t zs_sync_lock[ZTEST_SYNC_LOCKS]; +- uint64_t zs_seq[ZTEST_SYNC_LOCKS]; ++ pthread_mutex_t zs_vdev_lock; ++ pthread_rwlock_t zs_name_lock; ++ uint64_t zs_vdev_primaries; ++ uint64_t zs_enospc_count; ++ hrtime_t zs_start_time; ++ hrtime_t zs_stop_time; ++ uint64_t zs_alloc; ++ uint64_t zs_space; ++ ztest_info_t zs_info[ZTEST_FUNCS]; ++ pthread_mutex_t zs_sync_lock[ZTEST_SYNC_LOCKS]; ++ uint64_t zs_seq[ZTEST_SYNC_LOCKS]; + } ztest_shared_t; + + static char ztest_dev_template[] = "%s/%s.%llua"; +@@ -818,7 +818,7 @@ ztest_spa_create_destroy(ztest_args_t *z + * Attempt to create an existing pool. It shouldn't matter + * what's in the nvroot; we should fail with EEXIST. + */ +- (void) rw_rdlock(&ztest_shared->zs_name_lock); ++ (void) pthread_rwlock_rdlock(&ztest_shared->zs_name_lock); + nvroot = make_vdev_root(0, 0, 0, 0, 1); + error = spa_create(za->za_pool, nvroot, NULL, NULL); + nvlist_free(nvroot); +@@ -834,7 +834,7 @@ ztest_spa_create_destroy(ztest_args_t *z + fatal(0, "spa_destroy() = %d", error); + + spa_close(spa, FTAG); +- (void) rw_unlock(&ztest_shared->zs_name_lock); ++ (void) pthread_rwlock_unlock(&ztest_shared->zs_name_lock); + } + + /* +@@ -851,7 +851,7 @@ ztest_vdev_add_remove(ztest_args_t *za) + if (zopt_verbose >= 6) + (void) printf("adding vdev\n"); + +- (void) mutex_lock(&ztest_shared->zs_vdev_lock); ++ (void) pthread_mutex_lock(&ztest_shared->zs_vdev_lock); + + spa_config_enter(spa, RW_READER, FTAG); + +@@ -869,7 +869,7 @@ ztest_vdev_add_remove(ztest_args_t *za) + error = spa_vdev_add(spa, nvroot); + nvlist_free(nvroot); + +- (void) mutex_unlock(&ztest_shared->zs_vdev_lock); ++ (void) pthread_mutex_unlock(&ztest_shared->zs_vdev_lock); + + if (error == ENOSPC) + ztest_record_enospc("spa_vdev_add"); +@@ -927,7 +927,7 @@ ztest_vdev_attach_detach(ztest_args_t *z + int error, expected_error; + int fd; + +- (void) mutex_lock(&ztest_shared->zs_vdev_lock); ++ (void) pthread_mutex_lock(&ztest_shared->zs_vdev_lock); + + spa_config_enter(spa, RW_READER, FTAG); + +@@ -1054,7 +1054,7 @@ ztest_vdev_attach_detach(ztest_args_t *z + oldpath, newpath, replacing, error, expected_error); + } + +- (void) mutex_unlock(&ztest_shared->zs_vdev_lock); ++ (void) pthread_mutex_unlock(&ztest_shared->zs_vdev_lock); + } + + /* +@@ -1071,7 +1071,7 @@ ztest_vdev_LUN_growth(ztest_args_t *za) + size_t fsize; + int fd; + +- (void) mutex_lock(&ztest_shared->zs_vdev_lock); ++ (void) pthread_mutex_lock(&ztest_shared->zs_vdev_lock); + + /* + * Pick a random leaf vdev. +@@ -1102,7 +1102,7 @@ ztest_vdev_LUN_growth(ztest_args_t *za) + (void) close(fd); + } + +- (void) mutex_unlock(&ztest_shared->zs_vdev_lock); ++ (void) pthread_mutex_unlock(&ztest_shared->zs_vdev_lock); + } + + /* ARGSUSED */ +@@ -1198,7 +1198,7 @@ ztest_dmu_objset_create_destroy(ztest_ar + uint64_t objects; + ztest_replay_t zr; + +- (void) rw_rdlock(&ztest_shared->zs_name_lock); ++ (void) pthread_rwlock_rdlock(&ztest_shared->zs_name_lock); + (void) snprintf(name, 100, "%s/%s_temp_%llu", za->za_pool, za->za_pool, + (u_longlong_t)za->za_instance); + +@@ -1242,7 +1242,7 @@ ztest_dmu_objset_create_destroy(ztest_ar + if (error) { + if (error == ENOSPC) { + ztest_record_enospc("dmu_objset_create"); +- (void) rw_unlock(&ztest_shared->zs_name_lock); ++ (void) pthread_rwlock_unlock(&ztest_shared->zs_name_lock); + return; + } + fatal(0, "dmu_objset_create(%s) = %d", name, error); +@@ -1321,7 +1321,7 @@ ztest_dmu_objset_create_destroy(ztest_ar + if (error) + fatal(0, "dmu_objset_destroy(%s) = %d", name, error); + +- (void) rw_unlock(&ztest_shared->zs_name_lock); ++ (void) pthread_rwlock_unlock(&ztest_shared->zs_name_lock); + } + + /* +@@ -1335,7 +1335,7 @@ ztest_dmu_snapshot_create_destroy(ztest_ + char snapname[100]; + char osname[MAXNAMELEN]; + +- (void) rw_rdlock(&ztest_shared->zs_name_lock); ++ (void) pthread_rwlock_rdlock(&ztest_shared->zs_name_lock); + dmu_objset_name(os, osname); + (void) snprintf(snapname, 100, "%s@%llu", osname, + (u_longlong_t)za->za_instance); +@@ -1348,7 +1348,7 @@ ztest_dmu_snapshot_create_destroy(ztest_ + ztest_record_enospc("dmu_take_snapshot"); + else if (error != 0 && error != EEXIST) + fatal(0, "dmu_take_snapshot() = %d", error); +- (void) rw_unlock(&ztest_shared->zs_name_lock); ++ (void) pthread_rwlock_unlock(&ztest_shared->zs_name_lock); + } + + #define ZTEST_TRAVERSE_BLOCKS 1000 +@@ -1992,7 +1992,7 @@ ztest_dmu_write_parallel(ztest_args_t *z + int bs = ZTEST_DIROBJ_BLOCKSIZE; + int do_free = 0; + uint64_t off, txg_how; +- mutex_t *lp; ++ pthread_mutex_t *lp; + char osname[MAXNAMELEN]; + char iobuf[SPA_MAXBLOCKSIZE]; + blkptr_t blk = { 0 }; +@@ -2041,7 +2041,7 @@ ztest_dmu_write_parallel(ztest_args_t *z + } + + lp = &ztest_shared->zs_sync_lock[b]; +- (void) mutex_lock(lp); ++ (void) pthread_mutex_lock(lp); + + wbt->bt_objset = dmu_objset_id(os); + wbt->bt_object = ZTEST_DIROBJ; +@@ -2087,7 +2087,7 @@ ztest_dmu_write_parallel(ztest_args_t *z + dmu_write(os, ZTEST_DIROBJ, off, btsize, wbt, tx); + } + +- (void) mutex_unlock(lp); ++ (void) pthread_mutex_unlock(lp); + + if (ztest_random(1000) == 0) + (void) poll(NULL, 0, 1); /* open dn_notxholds window */ +@@ -2106,7 +2106,7 @@ ztest_dmu_write_parallel(ztest_args_t *z + /* + * dmu_sync() the block we just wrote. + */ +- (void) mutex_lock(lp); ++ (void) pthread_mutex_lock(lp); + + blkoff = P2ALIGN_TYPED(off, bs, uint64_t); + error = dmu_buf_hold(os, ZTEST_DIROBJ, blkoff, FTAG, &db); +@@ -2114,7 +2114,7 @@ ztest_dmu_write_parallel(ztest_args_t *z + if (error) { + dprintf("dmu_buf_hold(%s, %d, %llx) = %d\n", + osname, ZTEST_DIROBJ, blkoff, error); +- (void) mutex_unlock(lp); ++ (void) pthread_mutex_unlock(lp); + return; + } + blkoff = off - blkoff; +@@ -2122,7 +2122,7 @@ ztest_dmu_write_parallel(ztest_args_t *z + dmu_buf_rele(db, FTAG); + za->za_dbuf = NULL; + +- (void) mutex_unlock(lp); ++ (void) pthread_mutex_unlock(lp); + + if (error) { + dprintf("dmu_sync(%s, %d, %llx) = %d\n", +@@ -2502,7 +2502,7 @@ ztest_dsl_prop_get_set(ztest_args_t *za) + char osname[MAXNAMELEN]; + int error; + +- (void) rw_rdlock(&ztest_shared->zs_name_lock); ++ (void) pthread_rwlock_rdlock(&ztest_shared->zs_name_lock); + + dmu_objset_name(os, osname); + +@@ -2541,7 +2541,7 @@ ztest_dsl_prop_get_set(ztest_args_t *za) + } + } + +- (void) rw_unlock(&ztest_shared->zs_name_lock); ++ (void) pthread_rwlock_unlock(&ztest_shared->zs_name_lock); + } + + static void +@@ -2693,7 +2693,7 @@ ztest_spa_rename(ztest_args_t *za) + int error; + spa_t *spa; + +- (void) rw_wrlock(&ztest_shared->zs_name_lock); ++ (void) pthread_rwlock_wrlock(&ztest_shared->zs_name_lock); + + oldname = za->za_pool; + newname = umem_alloc(strlen(oldname) + 5, UMEM_NOFAIL); +@@ -2745,7 +2745,7 @@ ztest_spa_rename(ztest_args_t *za) + + umem_free(newname, strlen(newname) + 1); + +- (void) rw_unlock(&ztest_shared->zs_name_lock); ++ (void) pthread_rwlock_unlock(&ztest_shared->zs_name_lock); + } + + +@@ -3090,13 +3090,13 @@ ztest_run(char *pool) + ztest_args_t *za; + spa_t *spa; + char name[100]; +- thread_t tid; ++ pthread_t tid; + +- (void) _mutex_init(&zs->zs_vdev_lock, USYNC_THREAD, NULL); +- (void) rwlock_init(&zs->zs_name_lock, USYNC_THREAD, NULL); ++ (void) pthread_mutex_init(&zs->zs_vdev_lock, NULL); ++ (void) pthread_rwlock_init(&zs->zs_name_lock, NULL); + + for (t = 0; t < ZTEST_SYNC_LOCKS; t++) +- (void) _mutex_init(&zs->zs_sync_lock[t], USYNC_THREAD, NULL); ++ (void) pthread_mutex_init(&zs->zs_sync_lock[t], NULL); + + /* + * Destroy one disk before we even start. +@@ -3153,7 +3153,7 @@ ztest_run(char *pool) + * start the thread before setting the zio_io_fail_shift, which + * will indicate our failure rate. + */ +- error = thr_create(0, 0, ztest_suspend_monitor, NULL, THR_BOUND, &tid); ++ error = pthread_create(&tid, NULL, ztest_suspend_monitor, NULL); + if (error) { + fatal(0, "can't create suspend monitor thread: error %d", + t, error); +@@ -3217,7 +3217,7 @@ ztest_run(char *pool) + if (t < zopt_datasets) { + ztest_replay_t zr; + int test_future = FALSE; +- (void) rw_rdlock(&ztest_shared->zs_name_lock); ++ (void) pthread_rwlock_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, 0, + ztest_create_cb, NULL); +@@ -3225,7 +3225,7 @@ ztest_run(char *pool) + test_future = TRUE; + } else if (error == ENOSPC) { + zs->zs_enospc_count++; +- (void) rw_unlock(&ztest_shared->zs_name_lock); ++ (void) pthread_rwlock_unlock(&ztest_shared->zs_name_lock); + break; + } else if (error != 0) { + fatal(0, "dmu_objset_create(%s) = %d", +@@ -3236,7 +3236,7 @@ ztest_run(char *pool) + if (error) + fatal(0, "dmu_objset_open('%s') = %d", + name, error); +- (void) rw_unlock(&ztest_shared->zs_name_lock); ++ (void) pthread_rwlock_unlock(&ztest_shared->zs_name_lock); + if (test_future) + ztest_dmu_check_future_leak(&za[t]); + zr.zr_os = za[d].za_os; +@@ -3245,15 +3245,15 @@ ztest_run(char *pool) + za[d].za_zilog = zil_open(za[d].za_os, NULL); + } + +- error = thr_create(0, 0, ztest_thread, &za[t], THR_BOUND, +- &za[t].za_thread); ++ error = pthread_create(&za[t].za_thread, NULL, ztest_thread, ++ &za[t]); + if (error) + fatal(0, "can't create thread %d: error %d", + t, error); + } + + while (--t >= 0) { +- error = thr_join(za[t].za_thread, NULL, NULL); ++ error = pthread_join(za[t].za_thread, NULL); + if (error) + fatal(0, "thr_join(%d) = %d", t, error); + if (za[t].za_th) +@@ -3276,14 +3276,14 @@ ztest_run(char *pool) + * 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) pthread_rwlock_rdlock(&ztest_shared->zs_name_lock); + 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, &za[d], + DS_FIND_SNAPSHOTS | DS_FIND_CHILDREN); +- (void) rw_unlock(&ztest_shared->zs_name_lock); ++ (void) pthread_rwlock_unlock(&ztest_shared->zs_name_lock); + } + + txg_wait_synced(spa_get_dsl(spa), 0); +@@ -3301,7 +3301,7 @@ ztest_run(char *pool) + mutex_enter(&spa->spa_zio_lock); + cv_broadcast(&spa->spa_zio_cv); + mutex_exit(&spa->spa_zio_lock); +- error = thr_join(tid, NULL, NULL); ++ error = pthread_join(tid, NULL); + if (error) + fatal(0, "thr_join(%d) = %d", tid, error); + +Index: zfs+chaos4/lib/libuutil/uu_misc.c +=================================================================== +--- zfs+chaos4.orig/lib/libuutil/uu_misc.c ++++ zfs+chaos4/lib/libuutil/uu_misc.c +@@ -37,7 +37,6 @@ + #include + #include + #include +-#include + #include + + #if !defined(TEXT_DOMAIN) +@@ -70,11 +69,12 @@ static va_list uu_panic_args; + static pthread_t uu_panic_thread; + + static uint32_t _uu_main_error; ++static __thread int _uu_main_thread = 0; + + void + uu_set_error(uint_t code) + { +- if (thr_main() != 0) { ++ if (_uu_main_thread) { + _uu_main_error = code; + return; + } +@@ -103,7 +103,7 @@ uu_set_error(uint_t code) + uint32_t + uu_error(void) + { +- if (thr_main() != 0) ++ if (_uu_main_thread) + return (_uu_main_error); + + if (uu_error_key_setup < 0) /* can't happen? */ +@@ -255,5 +255,6 @@ uu_release_child(void) + static void + uu_init(void) + { ++ _uu_main_thread = 1; + (void) pthread_atfork(uu_lockup, uu_release, uu_release_child); + } +Index: zfs+chaos4/lib/libzfscommon/include/sys/zfs_context_user.h +=================================================================== +--- zfs+chaos4.orig/lib/libzfscommon/include/sys/zfs_context_user.h ++++ zfs+chaos4/lib/libzfscommon/include/sys/zfs_context_user.h +@@ -52,8 +52,7 @@ extern "C" { + #include + #include + #include +-#include +-#include ++#include + #include + #include + #include +@@ -191,13 +190,15 @@ _NOTE(CONSTCOND) } while (0) + /* + * Threads + */ +-#define curthread ((void *)(uintptr_t)thr_self()) ++ ++/* XXX: not portable */ ++#define curthread ((void *)(uintptr_t)pthread_self()) + + typedef struct kthread kthread_t; + + #define thread_create(stk, stksize, func, arg, len, pp, state, pri) \ + zk_thread_create(func, arg) +-#define thread_exit() thr_exit(NULL) ++#define thread_exit() pthread_exit(NULL) + + extern kthread_t *zk_thread_create(void (*func)(), void *arg); + +@@ -207,28 +208,18 @@ extern kthread_t *zk_thread_create(void + /* + * Mutexes + */ ++#define MTX_MAGIC 0x9522f51362a6e326ull + typedef struct kmutex { + void *m_owner; +- boolean_t initialized; +- mutex_t m_lock; ++ uint64_t m_magic; ++ pthread_mutex_t m_lock; + } kmutex_t; + +-#define MUTEX_DEFAULT USYNC_THREAD +-#undef MUTEX_HELD +-#define MUTEX_HELD(m) _mutex_held(&(m)->m_lock) +- +-/* +- * Argh -- we have to get cheesy here because the kernel and userland +- * have different signatures for the same routine. +- */ +-extern int _mutex_init(mutex_t *mp, int type, void *arg); +-extern int _mutex_destroy(mutex_t *mp); +- +-#define mutex_init(mp, b, c, d) zmutex_init((kmutex_t *)(mp)) +-#define mutex_destroy(mp) zmutex_destroy((kmutex_t *)(mp)) ++#define MUTEX_DEFAULT 0 ++#define MUTEX_HELD(m) ((m)->m_owner == curthread) + +-extern void zmutex_init(kmutex_t *mp); +-extern void zmutex_destroy(kmutex_t *mp); ++extern void mutex_init(kmutex_t *mp, char *name, int type, void *cookie); ++extern void mutex_destroy(kmutex_t *mp); + extern void mutex_enter(kmutex_t *mp); + extern void mutex_exit(kmutex_t *mp); + extern int mutex_tryenter(kmutex_t *mp); +@@ -237,23 +228,24 @@ extern void *mutex_owner(kmutex_t *mp); + /* + * RW locks + */ ++#define RW_MAGIC 0x4d31fb123648e78aull + typedef struct krwlock { +- void *rw_owner; +- boolean_t initialized; +- rwlock_t rw_lock; ++ void *rw_owner; ++ void *rw_wr_owner; ++ uint64_t rw_magic; ++ pthread_rwlock_t rw_lock; ++ uint_t rw_readers; + } krwlock_t; + + typedef int krw_t; + + #define RW_READER 0 + #define RW_WRITER 1 +-#define RW_DEFAULT USYNC_THREAD +- +-#undef RW_READ_HELD +-#define RW_READ_HELD(x) _rw_read_held(&(x)->rw_lock) ++#define RW_DEFAULT 0 + +-#undef RW_WRITE_HELD +-#define RW_WRITE_HELD(x) _rw_write_held(&(x)->rw_lock) ++#define RW_READ_HELD(x) ((x)->rw_readers > 0) ++#define RW_WRITE_HELD(x) ((x)->rw_wr_owner == curthread) ++#define RW_LOCK_HELD(x) (RW_READ_HELD(x) || RW_WRITE_HELD(x)) + + extern void rw_init(krwlock_t *rwlp, char *name, int type, void *arg); + extern void rw_destroy(krwlock_t *rwlp); +@@ -271,9 +263,13 @@ extern gid_t *crgetgroups(cred_t *cr); + /* + * Condition variables + */ +-typedef cond_t kcondvar_t; ++#define CV_MAGIC 0xd31ea9a83b1b30c4ull ++typedef struct kcondvar { ++ uint64_t cv_magic; ++ pthread_cond_t cv; ++} kcondvar_t; + +-#define CV_DEFAULT USYNC_THREAD ++#define CV_DEFAULT 0 + + extern void cv_init(kcondvar_t *cv, char *name, int type, void *arg); + extern void cv_destroy(kcondvar_t *cv); +@@ -444,7 +440,8 @@ extern void delay(clock_t ticks); + #define minclsyspri 60 + #define maxclsyspri 99 + +-#define CPU_SEQID (thr_self() & (max_ncpus - 1)) ++/* XXX: not portable */ ++#define CPU_SEQID (pthread_self() & (max_ncpus - 1)) + + #define kcred NULL + #define CRED() NULL +Index: zfs+chaos4/lib/libzpool/kernel.c +=================================================================== +--- zfs+chaos4.orig/lib/libzpool/kernel.c ++++ zfs+chaos4/lib/libzpool/kernel.c +@@ -38,6 +38,7 @@ + #include + #include + #include ++#include + + /* + * Emulation of kernel services in userland. +@@ -60,11 +61,15 @@ struct utsname utsname = { + kthread_t * + zk_thread_create(void (*func)(), void *arg) + { +- thread_t tid; ++ pthread_t tid; + +- VERIFY(thr_create(0, 0, (void *(*)(void *))func, arg, THR_DETACHED, +- &tid) == 0); ++ pthread_attr_t attr; ++ VERIFY(pthread_attr_init(&attr) == 0); ++ VERIFY(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0); + ++ VERIFY(pthread_create(&tid, &attr, (void *(*)(void *))func, arg) == 0); ++ ++ /* XXX: not portable */ + return ((void *)(uintptr_t)tid); + } + +@@ -97,30 +102,37 @@ kstat_delete(kstat_t *ksp) + * ========================================================================= + */ + void +-zmutex_init(kmutex_t *mp) ++mutex_init(kmutex_t *mp, char *name, int type, void *cookie) + { ++ ASSERT(type == MUTEX_DEFAULT); ++ ASSERT(cookie == NULL); ++ ++#ifdef IM_FEELING_LUCKY ++ ASSERT(mp->m_magic != MTX_MAGIC); ++#endif ++ + mp->m_owner = NULL; +- mp->initialized = B_TRUE; +- (void) _mutex_init(&mp->m_lock, USYNC_THREAD, NULL); ++ mp->m_magic = MTX_MAGIC; ++ VERIFY3S(pthread_mutex_init(&mp->m_lock, NULL), ==, 0); + } + + void +-zmutex_destroy(kmutex_t *mp) ++mutex_destroy(kmutex_t *mp) + { +- ASSERT(mp->initialized == B_TRUE); ++ ASSERT(mp->m_magic == MTX_MAGIC); + ASSERT(mp->m_owner == NULL); +- (void) _mutex_destroy(&(mp)->m_lock); ++ VERIFY3S(pthread_mutex_destroy(&(mp)->m_lock), ==, 0); + mp->m_owner = (void *)-1UL; +- mp->initialized = B_FALSE; ++ mp->m_magic = 0; + } + + void + mutex_enter(kmutex_t *mp) + { +- ASSERT(mp->initialized == B_TRUE); ++ ASSERT(mp->m_magic == MTX_MAGIC); + ASSERT(mp->m_owner != (void *)-1UL); + ASSERT(mp->m_owner != curthread); +- VERIFY(mutex_lock(&mp->m_lock) == 0); ++ VERIFY3S(pthread_mutex_lock(&mp->m_lock), ==, 0); + ASSERT(mp->m_owner == NULL); + mp->m_owner = curthread; + } +@@ -128,9 +140,9 @@ mutex_enter(kmutex_t *mp) + int + mutex_tryenter(kmutex_t *mp) + { +- ASSERT(mp->initialized == B_TRUE); ++ ASSERT(mp->m_magic == MTX_MAGIC); + ASSERT(mp->m_owner != (void *)-1UL); +- if (0 == mutex_trylock(&mp->m_lock)) { ++ if (0 == pthread_mutex_trylock(&mp->m_lock)) { + ASSERT(mp->m_owner == NULL); + mp->m_owner = curthread; + return (1); +@@ -142,16 +154,16 @@ mutex_tryenter(kmutex_t *mp) + void + mutex_exit(kmutex_t *mp) + { +- ASSERT(mp->initialized == B_TRUE); ++ ASSERT(mp->m_magic == MTX_MAGIC); + ASSERT(mutex_owner(mp) == curthread); + mp->m_owner = NULL; +- VERIFY(mutex_unlock(&mp->m_lock) == 0); ++ VERIFY3S(pthread_mutex_unlock(&mp->m_lock), ==, 0); + } + + void * + mutex_owner(kmutex_t *mp) + { +- ASSERT(mp->initialized == B_TRUE); ++ ASSERT(mp->m_magic == MTX_MAGIC); + return (mp->m_owner); + } + +@@ -164,31 +176,48 @@ mutex_owner(kmutex_t *mp) + void + rw_init(krwlock_t *rwlp, char *name, int type, void *arg) + { +- rwlock_init(&rwlp->rw_lock, USYNC_THREAD, NULL); ++ ASSERT(type == RW_DEFAULT); ++ ASSERT(arg == NULL); ++ ++#ifdef IM_FEELING_LUCKY ++ ASSERT(rwlp->rw_magic != RW_MAGIC); ++#endif ++ ++ VERIFY3S(pthread_rwlock_init(&rwlp->rw_lock, NULL), ==, 0); + rwlp->rw_owner = NULL; +- rwlp->initialized = B_TRUE; ++ rwlp->rw_wr_owner = NULL; ++ rwlp->rw_readers = 0; ++ rwlp->rw_magic = RW_MAGIC; + } + + void + rw_destroy(krwlock_t *rwlp) + { +- rwlock_destroy(&rwlp->rw_lock); +- rwlp->rw_owner = (void *)-1UL; +- rwlp->initialized = B_FALSE; ++ ASSERT(rwlp->rw_magic == RW_MAGIC); ++ ++ VERIFY3S(pthread_rwlock_destroy(&rwlp->rw_lock), ==, 0); ++ rwlp->rw_magic = 0; + } + + 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_magic == RW_MAGIC); + ASSERT(rwlp->rw_owner != curthread); ++ ASSERT(rwlp->rw_wr_owner != curthread); + +- if (rw == RW_READER) +- (void) rw_rdlock(&rwlp->rw_lock); +- else +- (void) rw_wrlock(&rwlp->rw_lock); ++ if (rw == RW_READER) { ++ VERIFY3S(pthread_rwlock_rdlock(&rwlp->rw_lock), ==, 0); ++ ASSERT(rwlp->rw_wr_owner == NULL); ++ ++ atomic_inc_uint(&rwlp->rw_readers); ++ } else { ++ VERIFY3S(pthread_rwlock_wrlock(&rwlp->rw_lock), ==, 0); ++ ASSERT(rwlp->rw_wr_owner == NULL); ++ ASSERT3U(rwlp->rw_readers, ==, 0); ++ ++ rwlp->rw_wr_owner = curthread; ++ } + + rwlp->rw_owner = curthread; + } +@@ -196,11 +225,16 @@ 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); ++ ASSERT(rwlp->rw_magic == RW_MAGIC); ++ ASSERT(RW_LOCK_HELD(rwlp)); ++ ++ if (RW_READ_HELD(rwlp)) ++ atomic_dec_uint(&rwlp->rw_readers); ++ else ++ rwlp->rw_wr_owner = NULL; + + rwlp->rw_owner = NULL; +- (void) rw_unlock(&rwlp->rw_lock); ++ VERIFY3S(pthread_rwlock_unlock(&rwlp->rw_lock), ==, 0); + } + + int +@@ -208,19 +242,29 @@ rw_tryenter(krwlock_t *rwlp, krw_t rw) + { + int rv; + +- ASSERT(rwlp->initialized == B_TRUE); +- ASSERT(rwlp->rw_owner != (void *)-1UL); ++ ASSERT(rwlp->rw_magic == RW_MAGIC); + + if (rw == RW_READER) +- rv = rw_tryrdlock(&rwlp->rw_lock); ++ rv = pthread_rwlock_tryrdlock(&rwlp->rw_lock); + else +- rv = rw_trywrlock(&rwlp->rw_lock); ++ rv = pthread_rwlock_trywrlock(&rwlp->rw_lock); + + if (rv == 0) { ++ ASSERT(rwlp->rw_wr_owner == NULL); ++ ++ if (rw == RW_READER) ++ atomic_inc_uint(&rwlp->rw_readers); ++ else { ++ ASSERT3U(rwlp->rw_readers, ==, 0); ++ rwlp->rw_wr_owner = curthread; ++ } ++ + rwlp->rw_owner = curthread; + return (1); + } + ++ VERIFY3S(rv, ==, EBUSY); ++ + return (0); + } + +@@ -228,8 +272,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); ++ ASSERT(rwlp->rw_magic == RW_MAGIC); + + return (0); + } +@@ -243,22 +286,34 @@ rw_tryupgrade(krwlock_t *rwlp) + void + cv_init(kcondvar_t *cv, char *name, int type, void *arg) + { +- VERIFY(cond_init(cv, type, NULL) == 0); ++ ASSERT(type == CV_DEFAULT); ++ ++#ifdef IM_FEELING_LUCKY ++ ASSERT(cv->cv_magic != CV_MAGIC); ++#endif ++ ++ cv->cv_magic = CV_MAGIC; ++ ++ VERIFY3S(pthread_cond_init(&cv->cv, NULL), ==, 0); + } + + void + cv_destroy(kcondvar_t *cv) + { +- VERIFY(cond_destroy(cv) == 0); ++ ASSERT(cv->cv_magic == CV_MAGIC); ++ VERIFY3S(pthread_cond_destroy(&cv->cv), ==, 0); ++ cv->cv_magic = 0; + } + + void + cv_wait(kcondvar_t *cv, kmutex_t *mp) + { ++ ASSERT(cv->cv_magic == CV_MAGIC); + ASSERT(mutex_owner(mp) == curthread); + mp->m_owner = NULL; +- int ret = cond_wait(cv, &mp->m_lock); +- VERIFY(ret == 0 || ret == EINTR); ++ int ret = pthread_cond_wait(&cv->cv, &mp->m_lock); ++ if (ret != 0) ++ VERIFY3S(ret, ==, EINTR); + mp->m_owner = curthread; + } + +@@ -266,29 +321,38 @@ clock_t + cv_timedwait(kcondvar_t *cv, kmutex_t *mp, clock_t abstime) + { + int error; ++ struct timeval tv; + timestruc_t ts; + clock_t delta; + ++ ASSERT(cv->cv_magic == CV_MAGIC); ++ + top: + delta = abstime - lbolt; + if (delta <= 0) + return (-1); + +- ts.tv_sec = delta / hz; +- ts.tv_nsec = (delta % hz) * (NANOSEC / hz); ++ VERIFY(gettimeofday(&tv, NULL) == 0); ++ ++ ts.tv_sec = tv.tv_sec + delta / hz; ++ ts.tv_nsec = tv.tv_usec * 1000 + (delta % hz) * (NANOSEC / hz); ++ if (ts.tv_nsec >= NANOSEC) { ++ ts.tv_sec++; ++ ts.tv_nsec -= NANOSEC; ++ } + + ASSERT(mutex_owner(mp) == curthread); + mp->m_owner = NULL; +- error = cond_reltimedwait(cv, &mp->m_lock, &ts); ++ error = pthread_cond_timedwait(&cv->cv, &mp->m_lock, &ts); + mp->m_owner = curthread; + +- if (error == ETIME) ++ if (error == ETIMEDOUT) + return (-1); + + if (error == EINTR) + goto top; + +- ASSERT(error == 0); ++ VERIFY3S(error, ==, 0); + + return (1); + } +@@ -296,13 +360,15 @@ top: + void + cv_signal(kcondvar_t *cv) + { +- VERIFY(cond_signal(cv) == 0); ++ ASSERT(cv->cv_magic == CV_MAGIC); ++ VERIFY3S(pthread_cond_signal(&cv->cv), ==, 0); + } + + void + cv_broadcast(kcondvar_t *cv) + { +- VERIFY(cond_broadcast(cv) == 0); ++ ASSERT(cv->cv_magic == CV_MAGIC); ++ VERIFY3S(pthread_cond_broadcast(&cv->cv), ==, 0); + } + + /* +@@ -549,11 +615,11 @@ __dprintf(const char *file, const char * + dprintf_find_string(func)) { + /* Print out just the function name if requested */ + flockfile(stdout); +- /* XXX: the following printf may not be portable */ ++ /* XXX: the following 2 printfs may not be portable */ + if (dprintf_find_string("pid")) + (void) printf("%llu ", (u_longlong_t) getpid()); + if (dprintf_find_string("tid")) +- (void) printf("%u ", (uint_t) thr_self()); ++ (void) printf("%u ", (uint_t) pthread_self()); + if (dprintf_find_string("cpu")) + (void) printf("%u ", getcpuid()); + if (dprintf_find_string("time")) +Index: zfs+chaos4/lib/libzpool/taskq.c +=================================================================== +--- zfs+chaos4.orig/lib/libzpool/taskq.c ++++ zfs+chaos4/lib/libzpool/taskq.c +@@ -43,7 +43,7 @@ struct taskq { + krwlock_t tq_threadlock; + kcondvar_t tq_dispatch_cv; + kcondvar_t tq_wait_cv; +- thread_t *tq_threadlist; ++ pthread_t *tq_threadlist; + int tq_flags; + int tq_active; + int tq_nthreads; +@@ -186,7 +186,7 @@ taskq_create(const char *name, int nthre + tq->tq_maxalloc = maxalloc; + tq->tq_task.task_next = &tq->tq_task; + tq->tq_task.task_prev = &tq->tq_task; +- tq->tq_threadlist = kmem_alloc(nthreads * sizeof (thread_t), KM_SLEEP); ++ tq->tq_threadlist = kmem_alloc(nthreads * sizeof (pthread_t), KM_SLEEP); + + if (flags & TASKQ_PREPOPULATE) { + mutex_enter(&tq->tq_lock); +@@ -196,8 +196,8 @@ taskq_create(const char *name, int nthre + } + + for (t = 0; t < nthreads; t++) +- VERIFY(thr_create(0, 0, taskq_thread, +- tq, THR_BOUND, &tq->tq_threadlist[t]) == 0); ++ VERIFY(pthread_create(&tq->tq_threadlist[t], ++ NULL, taskq_thread, tq) == 0); + + return (tq); + } +@@ -227,9 +227,9 @@ taskq_destroy(taskq_t *tq) + mutex_exit(&tq->tq_lock); + + for (t = 0; t < nthreads; t++) +- VERIFY(thr_join(tq->tq_threadlist[t], NULL, NULL) == 0); ++ VERIFY(pthread_join(tq->tq_threadlist[t], NULL) == 0); + +- kmem_free(tq->tq_threadlist, nthreads * sizeof (thread_t)); ++ kmem_free(tq->tq_threadlist, nthreads * sizeof (pthread_t)); + + rw_destroy(&tq->tq_threadlock); + mutex_destroy(&tq->tq_lock); +@@ -248,7 +248,7 @@ taskq_member(taskq_t *tq, void *t) + return (1); + + for (i = 0; i < tq->tq_nthreads; i++) +- if (tq->tq_threadlist[i] == (thread_t)(uintptr_t)t) ++ if (tq->tq_threadlist[i] == (pthread_t)(uintptr_t)t) + return (1); + + return (0); diff --git a/patches/zap-cursor-move-to-key.patch b/patches/zap-cursor-move-to-key.patch new file mode 100644 index 0000000..ad5bbb9 --- /dev/null +++ b/patches/zap-cursor-move-to-key.patch @@ -0,0 +1,115 @@ +Add a ZAP API to move a ZAP cursor to a given key. + +Index: zfs+chaos4/lib/libzfscommon/include/sys/zap.h +=================================================================== +--- zfs+chaos4.orig/lib/libzfscommon/include/sys/zap.h ++++ zfs+chaos4/lib/libzfscommon/include/sys/zap.h +@@ -302,6 +302,11 @@ void zap_cursor_advance(zap_cursor_t *zc + uint64_t zap_cursor_serialize(zap_cursor_t *zc); + + /* ++ * Advance the cursor to the attribute having the key. ++ */ ++int zap_cursor_move_to_key(zap_cursor_t *zc, const char *name, matchtype_t mt); ++ ++/* + * Initialize a zap cursor pointing to the position recorded by + * zap_cursor_serialize (in the "serialized" argument). You can also + * use a "serialized" argument of 0 to start at the beginning of the +Index: zfs+chaos4/lib/libzfscommon/include/sys/zap_impl.h +=================================================================== +--- zfs+chaos4.orig/lib/libzfscommon/include/sys/zap_impl.h ++++ zfs+chaos4/lib/libzfscommon/include/sys/zap_impl.h +@@ -210,6 +210,7 @@ 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); ++int fzap_cursor_move_to_key(zap_cursor_t *zc, zap_name_t *zn); + + #ifdef __cplusplus + } +Index: zfs+chaos4/lib/libzpool/zap.c +=================================================================== +--- zfs+chaos4.orig/lib/libzpool/zap.c ++++ zfs+chaos4/lib/libzpool/zap.c +@@ -1029,6 +1029,30 @@ zap_stats_ptrtbl(zap_t *zap, uint64_t *t + } + } + ++int fzap_cursor_move_to_key(zap_cursor_t *zc, zap_name_t *zn) ++{ ++ int err; ++ zap_leaf_t *l; ++ zap_entry_handle_t zeh; ++ uint64_t hash; ++ ++ if (zn->zn_name_orij && strlen(zn->zn_name_orij) > ZAP_MAXNAMELEN) ++ return (E2BIG); ++ ++ err = zap_deref_leaf(zc->zc_zap, zn->zn_hash, NULL, RW_READER, &l); ++ if (err != 0) ++ return (err); ++ ++ err = zap_leaf_lookup(l, zn, &zeh); ++ if (err != 0) ++ return (err); ++ ++ zc->zc_leaf = l; ++ zc->zc_hash = zeh.zeh_hash; ++ zc->zc_cd = zeh.zeh_cd; ++ return 0; ++} ++ + void + fzap_get_stats(zap_t *zap, zap_stats_t *zs) + { +Index: zfs+chaos4/lib/libzpool/zap_micro.c +=================================================================== +--- zfs+chaos4.orig/lib/libzpool/zap_micro.c ++++ zfs+chaos4/lib/libzpool/zap_micro.c +@@ -1045,6 +1045,45 @@ zap_cursor_advance(zap_cursor_t *zc) + } + } + ++int zap_cursor_move_to_key(zap_cursor_t *zc, const char *name, matchtype_t mt) ++{ ++ int err = 0; ++ mzap_ent_t *mze; ++ zap_name_t *zn; ++ ++ if (zc->zc_zap == NULL) { ++ err = zap_lockdir(zc->zc_objset, zc->zc_zapobj, NULL, ++ RW_READER, TRUE, FALSE, &zc->zc_zap); ++ if (err) ++ return (err); ++ } else { ++ rw_enter(&zc->zc_zap->zap_rwlock, RW_READER); ++ } ++ ++ zn = zap_name_alloc(zc->zc_zap, name, mt); ++ if (zn == NULL) { ++ rw_exit(&zc->zc_zap->zap_rwlock); ++ return (ENOTSUP); ++ } ++ ++ if (!zc->zc_zap->zap_ismicro) { ++ err = fzap_cursor_move_to_key(zc, zn); ++ } else { ++ mze = mze_find(zn); ++ if (mze == NULL) { ++ err = (ENOENT); ++ goto out; ++ } ++ zc->zc_hash = mze->mze_hash; ++ zc->zc_cd = mze->mze_phys.mze_cd; ++ } ++ ++out: ++ zap_name_free(zn); ++ rw_exit(&zc->zc_zap->zap_rwlock); ++ return (err); ++} ++ + int + zap_get_stats(objset_t *os, uint64_t zapobj, zap_stats_t *zs) + { diff --git a/scripts/Makefile.am b/scripts/Makefile.am new file mode 100644 index 0000000..e708530 --- /dev/null +++ b/scripts/Makefile.am @@ -0,0 +1,8 @@ +EXTRA_DIST = check.sh create-zpool.sh load-zfs.sh unload-zfs.sh +EXTRA_DIST += profile-kpios-disk.sh profile-kpios-pids.sh +EXTRA_DIST += profile-kpios-post.sh profile-kpios-pre.sh profile-kpios.sh +EXTRA_DIST += survey.sh update-zfs.sh zpios-jbod.sh zpios.sh + +check: + ./check.sh + diff --git a/scripts/check.sh b/scripts/check.sh new file mode 100755 index 0000000..bf93ee5 --- /dev/null +++ b/scripts/check.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +prog=check.sh + +die() { + echo "${prog}: $1" >&2 + exit 1 +} + +if [ $(id -u) != 0 ]; then + die "Must run as root" +fi + +./load-zfs.sh || die "" +./unload-zfs.sh || die "" + +exit 0 diff --git a/scripts/create-zpool.sh b/scripts/create-zpool.sh new file mode 100755 index 0000000..6be37e7 --- /dev/null +++ b/scripts/create-zpool.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +prog=create-zpool.sh +. ../.script-config + +# Single disk ilc dev nodes +DEVICES="/dev/sda" + +# All disks in a Thumper config +#DEVICES="/dev/sda /dev/sdb /dev/sdc /dev/sdd /dev/sde /dev/sdf \ +# /dev/sdg /dev/sdh /dev/sdi /dev/sdj /dev/sdk /dev/sdl \ +# /dev/sdm /dev/sdn /dev/sdo /dev/sdp /dev/sdq /dev/sdr \ +# /dev/sds /dev/sdt /dev/sdu /dev/sdv /dev/sdw /dev/sdx \ +# /dev/sdy /dev/sdz /dev/sdaa /dev/sdab /dev/sdac /dev/sdad \ +# /dev/sdae /dev/sdaf /dev/sdag /dev/sdah /dev/sdai /dev/sdaj \ +# /dev/sdak /dev/sdal /dev/sdam /dev/sdan /dev/sdao /dev/sdap \ +# /dev/sdaq /dev/sdar /dev/sdas /dev/sdat /dev/sdau /dev/sdav" + +# Sun style disk in Thumper config +#DEVICES="/dev/sda /dev/sdb /dev/sdc \ +# /dev/sdi /dev/sdj /dev/sdk \ +# /dev/sdr /dev/sds /dev/sdt \ +# /dev/sdz /dev/sdaa /dev/sdab" + +# Promise JBOD config (ilc23) +#DEVICES="/dev/sdb /dev/sdc /dev/sdd \ +# /dev/sde /dev/sdf /dev/sdg \ +# /dev/sdh /dev/sdi /dev/sdj \ +# /dev/sdk /dev/sdl /dev/sdm" + +echo +echo "zpool create lustre " +${CMDDIR}/zpool/zpool create -F lustre ${DEVICES} + +echo +echo "zpool list" +${CMDDIR}/zpool/zpool list + +echo +echo "zpool status lustre" +${CMDDIR}/zpool/zpool status lustre + diff --git a/scripts/load-zfs.sh b/scripts/load-zfs.sh new file mode 100755 index 0000000..6ba111b --- /dev/null +++ b/scripts/load-zfs.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +prog=load-zfs.sh +. ../.script-config + +spl_options=$1 +zpool_options=$2 + +spl_module=${SPLBUILD}/modules/spl/spl.ko +zlib_module=/lib/modules/${KERNELSRCVER}/kernel/lib/zlib_deflate/zlib_deflate.ko +zavl_module=${ZFSBUILD}/lib/libavl/zavl.ko +znvpair_module=${ZFSBUILD}/lib/libnvpair/znvpair.ko +zport_module=${ZFSBUILD}/lib/libport/zport.ko +zcommon_module=${ZFSBUILD}/lib/libzcommon/zcommon.ko +zpool_module=${ZFSBUILD}/lib/libzpool/zpool.ko +zctl_module=${ZFSBUILD}/lib/libdmu-ctl/zctl.ko +zpios_module=${ZFSBUILD}/lib/libzpios/zpios.ko + +die() { + echo "${prog}: $1" >&2 + exit 1 +} + +load_module() { + echo "Loading $1" + /sbin/insmod $* || die "Failed to load $1" +} + +if [ $(id -u) != 0 ]; then + die "Must run as root" +fi + +if /sbin/lsmod | egrep -q "^spl|^zavl|^znvpair|^zport|^zcommon|^zlib_deflate|^zpool"; then + die "Must start with modules unloaded" +fi + +if [ ! -f ${zavl_module} ] || + [ ! -f ${znvpair_module} ] || + [ ! -f ${zport_module} ] || + [ ! -f ${zcommon_module} ] || + [ ! -f ${zpool_module} ]; then + die "Source tree must be built, run 'make'" +fi + +load_module ${spl_module} ${spl_options} +load_module ${zlib_module} +load_module ${zavl_module} +load_module ${znvpair_module} +load_module ${zport_module} +load_module ${zcommon_module} +load_module ${zpool_module} ${zpool_options} +load_module ${zctl_module} +load_module ${zpios_module} + +sleep 1 +echo "Successfully loaded ZFS module stack" + +exit 0 diff --git a/scripts/profile-kpios-disk.sh b/scripts/profile-kpios-disk.sh new file mode 100755 index 0000000..5d691b8 --- /dev/null +++ b/scripts/profile-kpios-disk.sh @@ -0,0 +1,128 @@ +#!/bin/bash +# profile-kpios-disk.sh +# +# /proc/diskinfo +# Field 1 -- device name +# Field 2 -- # of reads issued +# Field 3 -- # of reads merged +# Field 4 -- # of sectors read +# Field 5 -- # of milliseconds spent reading +# Field 6 -- # of writes completed +# Field 7 -- # of writes merged +# Field 8 -- # of sectors written +# Field 9 -- # of milliseconds spent writing +# Field 10 -- # of I/Os currently in progress +# Field 11 -- # of milliseconds spent doing I/Os +# Field 12 -- weighted # of milliseconds spent doing I/Os + +RUN_PIDS=${0} +RUN_LOG_DIR=${1} +RUN_ID=${2} + +create_table() { + local FIELD=$1 + local ROW_M=() + local ROW_N=() + local HEADER=1 + local STEP=1 + + for DISK_FILE in `ls -r --sort=time --time=ctime ${RUN_LOG_DIR}/${RUN_ID}/disk-[0-9]*`; do + ROW_M=( ${ROW_N[@]} ) + ROW_N=( `cat ${DISK_FILE} | grep sd | cut -c11- | cut -f${FIELD} -d' ' | tr "\n" "\t"` ) + + if [ $HEADER -eq 1 ]; then + echo -n "step, " + cat ${DISK_FILE} | grep sd | cut -c11- | cut -f1 -d' ' | tr "\n" ", " + echo "total" + HEADER=0 + fi + + if [ ${#ROW_M[@]} -eq 0 ]; then + continue + fi + + if [ ${#ROW_M[@]} -ne ${#ROW_N[@]} ]; then + echo "Badly formatted profile data in ${DISK_FILE}" + break + fi + + TOTAL=0 + echo -n "${STEP}, " + for (( i=0; i<${#ROW_N[@]}; i++ )); do + DELTA=`echo "${ROW_N[${i}]}-${ROW_M[${i}]}" | bc` + let TOTAL=${TOTAL}+${DELTA} + echo -n "${DELTA}, " + done + echo "${TOTAL}, " + + let STEP=${STEP}+1 + done +} + +create_table_mbs() { + local FIELD=$1 + local TIME=$2 + local ROW_M=() + local ROW_N=() + local HEADER=1 + local STEP=1 + + for DISK_FILE in `ls -r --sort=time --time=ctime ${RUN_LOG_DIR}/${RUN_ID}/disk-[0-9]*`; do + ROW_M=( ${ROW_N[@]} ) + ROW_N=( `cat ${DISK_FILE} | grep sd | cut -c11- | cut -f${FIELD} -d' ' | tr "\n" "\t"` ) + + if [ $HEADER -eq 1 ]; then + echo -n "step, " + cat ${DISK_FILE} | grep sd | cut -c11- | cut -f1 -d' ' | tr "\n" ", " + echo "total" + HEADER=0 + fi + + if [ ${#ROW_M[@]} -eq 0 ]; then + continue + fi + + if [ ${#ROW_M[@]} -ne ${#ROW_N[@]} ]; then + echo "Badly formatted profile data in ${DISK_FILE}" + break + fi + + TOTAL=0 + echo -n "${STEP}, " + for (( i=0; i<${#ROW_N[@]}; i++ )); do + DELTA=`echo "${ROW_N[${i}]}-${ROW_M[${i}]}" | bc` + MBS=`echo "scale=2; ((${DELTA}*512)/${TIME})/(1024*1024)" | bc` + TOTAL=`echo "scale=2; ${TOTAL}+${MBS}" | bc` + echo -n "${MBS}, " + done + echo "${TOTAL}, " + + let STEP=${STEP}+1 + done +} + +echo +echo "Reads issued per device" +create_table 2 +echo +echo "Reads merged per device" +create_table 3 +echo +echo "Sectors read per device" +create_table 4 +echo "MB/s per device" +create_table_mbs 4 3 + +echo +echo "Writes issued per device" +create_table 6 +echo +echo "Writes merged per device" +create_table 7 +echo +echo "Sectors written per device" +create_table 8 +echo "MB/s per device" +create_table_mbs 8 3 + +exit 0 diff --git a/scripts/profile-kpios-pids.sh b/scripts/profile-kpios-pids.sh new file mode 100755 index 0000000..d3fa10c --- /dev/null +++ b/scripts/profile-kpios-pids.sh @@ -0,0 +1,130 @@ +#!/bin/bash +# profile-kpios-pids.sh + +RUN_PIDS=${0} +RUN_LOG_DIR=${1} +RUN_ID=${2} + +ROW_M=() +ROW_N=() +ROW_N_SCHED=() +ROW_N_WAIT=() + +HEADER=1 +STEP=1 + +for PID_FILE in `ls -r --sort=time --time=ctime ${RUN_LOG_DIR}/${RUN_ID}/pids-[0-9]*`; do + ROW_M=( ${ROW_N[@]} ) + ROW_N=( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) + ROW_N_SCHED=( `cat ${PID_FILE} | cut -f15 -d' ' | tr "\n" "\t"` ) + ROW_N_WAIT=( `cat ${PID_FILE} | cut -f17 -d' ' | tr "\n" "\t"` ) + ROW_N_NAMES=( `cat ${PID_FILE} | cut -f2 -d' ' | cut -f2 -d'(' | + cut -f1 -d')' | cut -f1 -d'/' | tr "\n" "\t"` ) + + for (( i=0; i<${#ROW_N_SCHED[@]}; i++ )); do + SUM=`echo "${ROW_N_WAIT[${i}]}+${ROW_N_SCHED[${i}]}" | bc` + + case ${ROW_N_NAMES[${i}]} in + zio_taskq) IDX=0;; + zio_req_nul) IDX=1;; + zio_irq_nul) IDX=2;; + zio_req_rd) IDX=3;; + zio_irq_rd) IDX=4;; + zio_req_wr) IDX=5;; + zio_irq_wr) IDX=6;; + zio_req_fr) IDX=7;; + zio_irq_fr) IDX=8;; + zio_req_cm) IDX=9;; + zio_irq_cm) IDX=10;; + zio_req_ctl) IDX=11;; + zio_irq_ctl) IDX=12;; + txg_quiesce) IDX=13;; + txg_sync) IDX=14;; + txg_timelimit) IDX=15;; + arc_reclaim) IDX=16;; + l2arc_feed) IDX=17;; + kpios_io) IDX=18;; + *) continue;; + esac + + let ROW_N[${IDX}]=${ROW_N[${IDX}]}+${SUM} + done + + if [ $HEADER -eq 1 ]; then + echo "step, zio_taskq, zio_req_nul, zio_irq_nul, " \ + "zio_req_rd, zio_irq_rd, zio_req_wr, zio_irq_wr, " \ + "zio_req_fr, zio_irq_fr, zio_req_cm, zio_irq_cm, " \ + "zio_req_ctl, zio_irq_ctl, txg_quiesce, txg_sync, " \ + "txg_timelimit, arc_reclaim, l2arc_feed, kpios_io, " \ + "idle" + HEADER=0 + fi + + if [ ${#ROW_M[@]} -eq 0 ]; then + continue + fi + + if [ ${#ROW_M[@]} -ne ${#ROW_N[@]} ]; then + echo "Badly formatted profile data in ${PID_FILE}" + break + fi + + # Original values are in jiffies and we expect HZ to be 1000 + # on most 2.6 systems thus we divide by 10 to get a percentage. + IDLE=1000 + echo -n "${STEP}, " + for (( i=0; i<${#ROW_N[@]}; i++ )); do + DELTA=`echo "${ROW_N[${i}]}-${ROW_M[${i}]}" | bc` + DELTA_PERCENT=`echo "scale=1; ${DELTA}/10" | bc` + let IDLE=${IDLE}-${DELTA} + echo -n "${DELTA_PERCENT}, " + done + ILDE_PERCENT=`echo "scale=1; ${IDLE}/10" | bc` + echo "${ILDE_PERCENT}" + + let STEP=${STEP}+1 +done + +exit + +echo +echo "Percent of total system time per pid" +for PID_FILE in `ls -r --sort=time --time=ctime ${RUN_LOG_DIR}/${RUN_ID}/pids-[0-9]*`; do + ROW_M=( ${ROW_N[@]} ) + ROW_N_SCHED=( `cat ${PID_FILE} | cut -f15 -d' ' | tr "\n" "\t"` ) + ROW_N_WAIT=( `cat ${PID_FILE} | cut -f17 -d' ' | tr "\n" "\t"` ) + + for (( i=0; i<${#ROW_N_SCHED[@]}; i++ )); do + ROW_N[${i}]=`echo "${ROW_N_WAIT[${i}]}+${ROW_N_SCHED[${i}]}" | bc` + done + + if [ $HEADER -eq 1 ]; then + echo -n "step, " + cat ${PID_FILE} | cut -f2 -d' ' | tr "\n" ", " + echo + HEADER=0 + fi + + if [ ${#ROW_M[@]} -eq 0 ]; then + continue + fi + + if [ ${#ROW_M[@]} -ne ${#ROW_N[@]} ]; then + echo "Badly formatted profile data in ${PID_FILE}" + break + fi + + # Original values are in jiffies and we expect HZ to be 1000 + # on most 2.6 systems thus we divide by 10 to get a percentage. + echo -n "${STEP}, " + for (( i=0; i<${#ROW_N[@]}; i++ )); do + DELTA=`echo "scale=1; (${ROW_N[${i}]}-${ROW_M[${i}]})/10" | bc` + echo -n "${DELTA}, " + done + + echo + let STEP=${STEP}+1 +done + + +exit 0 diff --git a/scripts/profile-kpios-post.sh b/scripts/profile-kpios-post.sh new file mode 100755 index 0000000..74c0890 --- /dev/null +++ b/scripts/profile-kpios-post.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +prog=profile-kpios-post.sh +. ../.script-config + +RUN_POST=${0} +RUN_PHASE=${1} +RUN_LOG_DIR=${2} +RUN_ID=${3} +RUN_POOL=${4} +RUN_CHUNK_SIZE=${5} +RUN_REGION_SIZE=${6} +RUN_THREAD_COUNT=${7} +RUN_REGION_COUNT=${8} +RUN_OFFSET=${9} +RUN_REGION_NOISE=${10} +RUN_CHUNK_NOISE=${11} +RUN_THREAD_DELAY=${12} +RUN_FLAGS=${13} +RUN_RESULT=${14} + +PROFILE_KPIOS_PIDS_BIN=/home/behlendo/src/zfs/scripts/profile-kpios-pids.sh +PROFILE_KPIOS_PIDS_LOG=${RUN_LOG_DIR}/${RUN_ID}/pids-summary.csv + +PROFILE_KPIOS_DISK_BIN=/home/behlendo/src/zfs/scripts/profile-kpios-disk.sh +PROFILE_KPIOS_DISK_LOG=${RUN_LOG_DIR}/${RUN_ID}/disk-summary.csv + +PROFILE_KPIOS_ARC_LOG=${RUN_LOG_DIR}/${RUN_ID}/arcstats +PROFILE_KPIOS_VDEV_LOG=${RUN_LOG_DIR}/${RUN_ID}/vdev_cache_stats + +KERNEL_BIN="/lib/modules/`uname -r`/kernel/" +SPL_BIN="${SPLBUILD}/modules/spl/" +ZFS_BIN="${ZFSBUILD}/lib/" + +OPROFILE_SHORT_ARGS="-a -g -l -p ${KERNEL_BIN},${SPL_BIN},${ZFS_BIN}" +OPROFILE_LONG_ARGS="-d -a -g -l -p ${KERNEL_BIN},${SPL_BIN},${ZFS_BIN}" + +OPROFILE_LOG=${RUN_LOG_DIR}/${RUN_ID}/oprofile.txt +OPROFILE_SHORT_LOG=${RUN_LOG_DIR}/${RUN_ID}/oprofile-short.txt +OPROFILE_LONG_LOG=${RUN_LOG_DIR}/${RUN_ID}/oprofile-long.txt +PROFILE_PID=${RUN_LOG_DIR}/${RUN_ID}/pid + +if [ "${RUN_PHASE}" != "post" ]; then + exit 1 +fi + +# opcontrol --stop >>${OPROFILE_LOG} 2>&1 +# opcontrol --dump >>${OPROFILE_LOG} 2>&1 + +kill -s SIGHUP `cat ${PROFILE_PID}` +rm -f ${PROFILE_PID} + +# opreport ${OPROFILE_SHORT_ARGS} >${OPROFILE_SHORT_LOG} 2>&1 +# opreport ${OPROFILE_LONG_ARGS} >${OPROFILE_LONG_LOG} 2>&1 + +# opcontrol --deinit >>${OPROFILE_LOG} 2>&1 + +cat /proc/spl/kstat/zfs/arcstats >${PROFILE_KPIOS_ARC_LOG} +cat /proc/spl/kstat/zfs/vdev_cache_stats >${PROFILE_KPIOS_VDEV_LOG} + +# Summarize system time per pid +${PROFILE_KPIOS_PIDS_BIN} ${RUN_LOG_DIR} ${RUN_ID} >${PROFILE_KPIOS_PIDS_LOG} + +# Summarize per device performance +${PROFILE_KPIOS_DISK_BIN} ${RUN_LOG_DIR} ${RUN_ID} >${PROFILE_KPIOS_DISK_LOG} + +exit 0 diff --git a/scripts/profile-kpios-pre.sh b/scripts/profile-kpios-pre.sh new file mode 100755 index 0000000..1a2dcf0 --- /dev/null +++ b/scripts/profile-kpios-pre.sh @@ -0,0 +1,69 @@ +#!/bin/bash +# profile-kpios-pre.sh + +trap "PROFILE_KPIOS_READY=1" SIGHUP + +RUN_PRE=${0} +RUN_PHASE=${1} +RUN_LOG_DIR=${2} +RUN_ID=${3} +RUN_POOL=${4} +RUN_CHUNK_SIZE=${5} +RUN_REGION_SIZE=${6} +RUN_THREAD_COUNT=${7} +RUN_REGION_COUNT=${8} +RUN_OFFSET=${9} +RUN_REGION_NOISE=${10} +RUN_CHUNK_NOISE=${11} +RUN_THREAD_DELAY=${12} +RUN_FLAGS=${13} +RUN_RESULT=${14} + +PROFILE_KPIOS_BIN=/home/behlendo/src/zfs/scripts/profile-kpios.sh +PROFILE_KPIOS_READY=0 + +OPROFILE_LOG=${RUN_LOG_DIR}/${RUN_ID}/oprofile.txt +PROFILE_PID=${RUN_LOG_DIR}/${RUN_ID}/pid +RUN_ARGS=${RUN_LOG_DIR}/${RUN_ID}/args + +if [ "${RUN_PHASE}" != "pre" ]; then + exit 1 +fi + +rm -Rf ${RUN_LOG_DIR}/${RUN_ID}/ +mkdir -p ${RUN_LOG_DIR}/${RUN_ID}/ + +echo "PHASE=${RUN_PHASE}" >>${RUN_ARGS} +echo "LOG_DIR=${RUN_LOG_DIR}" >>${RUN_ARGS} +echo "ID=${RUN_ID}" >>${RUN_ARGS} +echo "POOL=${RUN_POOL}" >>${RUN_ARGS} +echo "CHUNK_SIZE=${RUN_CHUNK_SIZE}" >>${RUN_ARGS} +echo "REGION_SIZE=${RUN_REGION_SIZE}" >>${RUN_ARGS} +echo "THREAD_COUNT=${RUN_THREAD_COUNT}" >>${RUN_ARGS} +echo "REGION_COUNT=${RUN_REGION_COUNT}" >>${RUN_ARGS} +echo "OFFSET=${RUN_OFFSET}" >>${RUN_ARGS} +echo "REGION_NOISE=${RUN_REGION_NOISE}" >>${RUN_ARGS} +echo "CHUNK_NOISE=${RUN_CHUNK_NOISE}" >>${RUN_ARGS} +echo "THREAD_DELAY=${RUN_THREAD_DELAY}" >>${RUN_ARGS} +echo "FLAGS=${RUN_FLAGS}" >>${RUN_ARGS} +echo "RESULT=${RUN_RESULT}" >>${RUN_ARGS} + +# XXX: Oprofile support seems to be broken when I try and start +# it via a user mode helper script, I suspect the setup is failing. +# opcontrol --init >>${OPROFILE_LOG} 2>&1 +# opcontrol --setup --vmlinux=/boot/vmlinux >>${OPROFILE_LOG} 2>&1 + +# Start the profile script +${PROFILE_KPIOS_BIN} ${RUN_PHASE} ${RUN_LOG_DIR} ${RUN_ID} & +echo "$!" >${PROFILE_PID} + +# Sleep waiting for profile script to be ready, it will +# signal us via SIGHUP when it is ready to start profiling. +while [ ${PROFILE_KPIOS_READY} -eq 0 ]; do + sleep 0.1 +done + +# opcontrol --start-daemon >>${OPROFILE_LOG} 2>&1 +# opcontrol --start >>${OPROFILE_LOG} 2>&1 + +exit 0 diff --git a/scripts/profile-kpios.sh b/scripts/profile-kpios.sh new file mode 100755 index 0000000..88b9343 --- /dev/null +++ b/scripts/profile-kpios.sh @@ -0,0 +1,222 @@ +#!/bin/bash +# profile-kpios.sh + +trap "RUN_DONE=1" SIGHUP + +RUN_PHASE=${1} +RUN_LOG_DIR=${2} +RUN_ID=${3} +RUN_DONE=0 + +POLL_INTERVAL=2.99 + +# Log these pids, the exact pid numbers will vary from system to system +# so I harvest pid for all the following type of processes from /proc// +# +# zio_taskq/# +# spa_zio_issue/# +# spa_zio_intr/# +# txg_quiesce_thr +# txg_sync_thread +# txg_timelimit_t +# arc_reclaim_thr +# l2arc_feed_thre +# kpios_io/# + +ZIO_TASKQ_PIDS=() +ZIO_REQ_NUL_PIDS=() +ZIO_IRQ_NUL_PIDS=() +ZIO_REQ_RD_PIDS=() +ZIO_IRQ_RD_PIDS=() +ZIO_REQ_WR_PIDS=() +ZIO_IRQ_WR_PIDS=() +ZIO_REQ_FR_PIDS=() +ZIO_IRQ_FR_PIDS=() +ZIO_REQ_CM_PIDS=() +ZIO_IRQ_CM_PIDS=() +ZIO_REQ_CTL_PIDS=() +ZIO_IRQ_CTL_PIDS=() + +TXG_QUIESCE_PIDS=() +TXG_SYNC_PIDS=() +TXG_TIMELIMIT_PIDS=() + +ARC_RECLAIM_PIDS=() +L2ARC_FEED_PIDS=() + +KPIOS_IO_PIDS=() + +show_pids() { + echo "* zio_taskq: { ${ZIO_TASKQ_PIDS[@]} } = ${#ZIO_TASKQ_PIDS[@]}" + echo "* zio_req_nul: { ${ZIO_REQ_NUL_PIDS[@]} } = ${#ZIO_REQ_NUL_PIDS[@]}" + echo "* zio_irq_nul: { ${ZIO_IRQ_NUL_PIDS[@]} } = ${#ZIO_IRQ_NUL_PIDS[@]}" + echo "* zio_req_rd: { ${ZIO_REQ_RD_PIDS[@]} } = ${#ZIO_REQ_RD_PIDS[@]}" + echo "* zio_irq_rd: { ${ZIO_IRQ_RD_PIDS[@]} } = ${#ZIO_IRQ_RD_PIDS[@]}" + echo "* zio_req_wr: { ${ZIO_REQ_WR_PIDS[@]} } = ${#ZIO_REQ_WR_PIDS[@]}" + echo "* zio_irq_wr: { ${ZIO_IRQ_WR_PIDS[@]} } = ${#ZIO_IRQ_WR_PIDS[@]}" + echo "* zio_req_fr: { ${ZIO_REQ_FR_PIDS[@]} } = ${#ZIO_REQ_FR_PIDS[@]}" + echo "* zio_irq_fr: { ${ZIO_IRQ_FR_PIDS[@]} } = ${#ZIO_IRQ_FR_PIDS[@]}" + echo "* zio_req_cm: { ${ZIO_REQ_CM_PIDS[@]} } = ${#ZIO_REQ_CM_PIDS[@]}" + echo "* zio_irq_cm: { ${ZIO_IRQ_CM_PIDS[@]} } = ${#ZIO_IRQ_CM_PIDS[@]}" + echo "* zio_req_ctl: { ${ZIO_REQ_CTL_PIDS[@]} } = ${#ZIO_REQ_CTL_PIDS[@]}" + echo "* zio_irq_ctl: { ${ZIO_IRQ_CTL_PIDS[@]} } = ${#ZIO_IRQ_CTL_PIDS[@]}" + echo "* txg_quiesce: { ${TXG_QUIESCE_PIDS[@]} } = ${#TXG_QUIESCE_PIDS[@]}" + echo "* txg_sync: { ${TXG_SYNC_PIDS[@]} } = ${#TXG_SYNC_PIDS[@]}" + echo "* txg_timelimit: { ${TXG_TIMELIMIT_PIDS[@]} } = ${#TXG_TIMELIMIT_PIDS[@]}" + echo "* arc_reclaim: { ${ARC_RECLAIM_PIDS[@]} } = ${#ARC_RECLAIM_PIDS[@]}" + echo "* l2arc_feed: { ${L2ARC_FEED_PIDS[@]} } = ${#L2ARC_FEED_PIDS[@]}" + echo "* kpios_io: { ${KPIOS_IO_PIDS[@]} } = ${#KPIOS_IO_PIDS[@]}" +} + +check_pid() { + local PID=$1 + local NAME=$2 + local TYPE=$3 + local PIDS=( "$4" ) + local NAME_STRING=`echo ${NAME} | cut -f1 -d'/'` + local NAME_NUMBER=`echo ${NAME} | cut -f2 -d'/'` + + if [ "${NAME_STRING}" == "${TYPE}" ]; then + if [ -n "${NAME_NUMBER}" ]; then + PIDS[${NAME_NUMBER}]=${PID} + else + PIDS[${#PIDS[@]}]=${PID} + + fi + fi + + echo "${PIDS[@]}" +} + +# NOTE: This whole process is crazy slow but it will do for now +aquire_pids() { + echo "--- Aquiring ZFS pids ---" + + for PID in `ls /proc/ | grep [0-9] | sort -n -u`; do + if [ ! -e /proc/${PID}/status ]; then + continue + fi + + NAME=`cat /proc/${PID}/status | head -n1 | cut -f2` + + ZIO_TASKQ_PIDS=( `check_pid ${PID} ${NAME} "zio_taskq" \ + "$(echo "${ZIO_TASKQ_PIDS[@]}")"` ) + + ZIO_REQ_NUL_PIDS=( `check_pid ${PID} ${NAME} "zio_req_nul" \ + "$(echo "${ZIO_REQ_NUL_PIDS[@]}")"` ) + + ZIO_IRQ_NUL_PIDS=( `check_pid ${PID} ${NAME} "zio_irq_nul" \ + "$(echo "${ZIO_IRQ_NUL_PIDS[@]}")"` ) + + ZIO_REQ_RD_PIDS=( `check_pid ${PID} ${NAME} "zio_req_rd" \ + "$(echo "${ZIO_REQ_RD_PIDS[@]}")"` ) + + ZIO_IRQ_RD_PIDS=( `check_pid ${PID} ${NAME} "zio_irq_rd" \ + "$(echo "${ZIO_IRQ_RD_PIDS[@]}")"` ) + + ZIO_REQ_WR_PIDS=( `check_pid ${PID} ${NAME} "zio_req_wr" \ + "$(echo "${ZIO_REQ_WR_PIDS[@]}")"` ) + + ZIO_IRQ_WR_PIDS=( `check_pid ${PID} ${NAME} "zio_irq_wr" \ + "$(echo "${ZIO_IRQ_WR_PIDS[@]}")"` ) + + ZIO_REQ_FR_PIDS=( `check_pid ${PID} ${NAME} "zio_req_fr" \ + "$(echo "${ZIO_REQ_FR_PIDS[@]}")"` ) + + ZIO_IRQ_FR_PIDS=( `check_pid ${PID} ${NAME} "zio_irq_fr" \ + "$(echo "${ZIO_IRQ_FR_PIDS[@]}")"` ) + + ZIO_REQ_CM_PIDS=( `check_pid ${PID} ${NAME} "zio_req_cm" \ + "$(echo "${ZIO_REQ_CM_PIDS[@]}")"` ) + + ZIO_IRQ_CM_PIDS=( `check_pid ${PID} ${NAME} "zio_irq_cm" \ + "$(echo "${ZIO_IRQ_CM_PIDS[@]}")"` ) + + ZIO_REQ_CTL_PIDS=( `check_pid ${PID} ${NAME} "zio_req_ctl" \ + "$(echo "${ZIO_REQ_CTL_PIDS[@]}")"` ) + + ZIO_IRQ_CTL_PIDS=( `check_pid ${PID} ${NAME} "zio_irq_ctl" \ + "$(echo "${ZIO_IRQ_CTL_PIDS[@]}")"` ) + + TXG_QUIESCE_PIDS=( `check_pid ${PID} ${NAME} "txg_quiesce" \ + "$(echo "${TXG_QUIESCE_PIDS[@]}")"` ) + + TXG_SYNC_PIDS=( `check_pid ${PID} ${NAME} "txg_sync" \ + "$(echo "${TXG_SYNC_PIDS[@]}")"` ) + + TXG_TIMELIMIT_PIDS=( `check_pid ${PID} ${NAME} "txg_timelimit" \ + "$(echo "${TXG_TIMELIMIT_PIDS[@]}")"` ) + + ARC_RECLAIM_PIDS=( `check_pid ${PID} ${NAME} "arc_reclaim" \ + "$(echo "${ARC_RECLAIM_PIDS[@]}")"` ) + + L2ARC_FEED_PIDS=( `check_pid ${PID} ${NAME} "l2arc_feed" \ + "$(echo "${L2ARC_FEED_PIDS[@]}")"` ) + done + + # Wait for kpios_io threads to start + kill -s SIGHUP ${PPID} + echo "* Waiting for kpios_io threads to start" + while [ ${RUN_DONE} -eq 0 ]; do + KPIOS_IO_PIDS=( `ps ax | grep kpios_io | grep -v grep | \ + sed 's/^ *//g' | cut -f1 -d' '` ) + if [ ${#KPIOS_IO_PIDS[@]} -gt 0 ]; then + break; + fi + sleep 0.1 + done + + echo "`show_pids`" >${RUN_LOG_DIR}/${RUN_ID}/pids.txt +} + +log_pids() { + echo "--- Logging ZFS profile to ${RUN_LOG_DIR}/${RUN_ID}/ ---" + ALL_PIDS=( ${ZIO_TASKQ_PIDS[@]} \ + ${ZIO_REQ_NUL_PIDS[@]} \ + ${ZIO_IRQ_NUL_PIDS[@]} \ + ${ZIO_REQ_RD_PID[@]} \ + ${ZIO_IRQ_RD_PIDS[@]} \ + ${ZIO_REQ_WR_PIDS[@]} \ + ${ZIO_IRQ_WR_PIDS[@]} \ + ${ZIO_REQ_FR_PIDS[@]} \ + ${ZIO_IRQ_FR_PIDS[@]} \ + ${ZIO_REQ_CM_PIDS[@]} \ + ${ZIO_IRQ_CM_PIDS[@]} \ + ${ZIO_REQ_CTL_PIDS[@]} \ + ${ZIO_IRQ_CTL_PIDS[@]} \ + ${TXG_QUIESCE_PIDS[@]} \ + ${TXG_SYNC_PIDS[@]} \ + ${TXG_TIMELIMIT_PIDS[@]} \ + ${ARC_RECLAIM_PIDS[@]} \ + ${L2ARC_FEED_PIDS[@]} \ + ${KPIOS_IO_PIDS[@]} ) + + while [ ${RUN_DONE} -eq 0 ]; do + NOW=`date +%s.%N` + LOG_PIDS="${RUN_LOG_DIR}/${RUN_ID}/pids-${NOW}" + LOG_DISK="${RUN_LOG_DIR}/${RUN_ID}/disk-${NOW}" + + for PID in "${ALL_PIDS[@]}"; do + if [ -z ${PID} ]; then + continue; + fi + + if [ -e /proc/${PID}/stat ]; then + cat /proc/${PID}/stat | head -n1 >>${LOG_PIDS} + else + echo "<${PID} exited>" >>${LOG_PIDS} + fi + done + + cat /proc/diskstats >${LOG_DISK} + + NOW2=`date +%s.%N` + DELTA=`echo "${POLL_INTERVAL}-(${NOW2}-${NOW})" | bc` + sleep ${DELTA} + done +} + +aquire_pids +log_pids + +exit 0 diff --git a/scripts/survey.sh b/scripts/survey.sh new file mode 100755 index 0000000..9198a41 --- /dev/null +++ b/scripts/survey.sh @@ -0,0 +1,102 @@ +#!/bin/bash + +prog=survey.sh +. ../.script-config + +LOG=/home/`whoami`/zpios-logs/`uname -r`/kpios-`date +%Y%m%d`/ +mkdir -p ${LOG} + +# Apply all tunings described below to generate some best case +# numbers for what is acheivable with some more elbow grease. +NAME="prefetch+zerocopy+checksum+pending1024+kmem" +echo "----------------------- ${NAME} ------------------------------" +./zpios.sh \ + "" \ + "zfs_prefetch_disable=1 zfs_vdev_max_pending=1024 zio_bulk_flags=0x100" \ + "--zerocopy" \ + ${LOG}/${NAME}/ \ + "${CMDDIR}/zfs/zfs set checksum=off lustre" | \ + tee ${LOG}/${NAME}.txt + +# Baseline number for an out of the box config with no manual tuning. +# Ideally, we will want things to be automatically tuned and for this +# number to approach the tweaked out results above. +NAME="baseline" +echo "----------------------- ${NAME} ------------------------------" +./zpios.sh \ + "" \ + "" \ + "" \ + ${LOG}/${NAME}/ | \ + tee ${LOG}/${NAME}.txt + +# Disable ZFS's prefetching. For some reason still not clear to me +# current prefetching policy is quite bad for a random workload. +# Allow the algorithm to detect a random workload and not do anything +# may be the way to address this issue. +NAME="prefetch" +echo "----------------------- ${NAME} ------------------------------" +./zpios.sh \ + "" \ + "zfs_prefetch_disable=1" \ + "" \ + ${LOG}/${NAME}/ | \ + tee ${LOG}/${NAME}.txt + +# As expected, simulating a zerocopy IO path improves performance +# by freeing up lots of CPU which is wasted move data between buffers. +NAME="zerocopy" +echo "----------------------- ${NAME} ------------------------------" +./zpios.sh \ + "" \ + "" \ + "--zerocopy" \ + ${LOG}/${NAME}/ | \ + tee ${LOG}/${NAME}.txt + +# Disabling checksumming should show some (if small) improvement +# simply due to freeing up a modest amount of CPU. +NAME="checksum" +echo "----------------------- ${NAME} ------------------------------" +./zpios.sh \ + "" \ + "" \ + "" \ + ${LOG}/${NAME}/ \ + "${CMDDIR}/zfs/zfs set checksum=off lustre" | \ + tee ${LOG}/${NAME}.txt + +# Increasing the pending IO depth also seems to improve things likely +# at the expense of latency. This should be exported more because I'm +# seeing a much bigger impact there that I would have expected. There +# may be some low hanging fruit to be found here. +NAME="pending" +echo "----------------------- ${NAME} ------------------------------" +./zpios.sh \ + "" \ + "zfs_vdev_max_pending=1024" \ + "" \ + ${LOG}/${NAME}/ | \ + tee ${LOG}/${NAME}.txt + +# To avoid memory fragmentation issues our slab implementation can be +# based on a virtual address space. Interestingly, we take a pretty +# substantial performance penalty for this somewhere in the low level +# IO drivers. If we back the slab with kmem pages we see far better +# read performance numbers at the cost of memory fragmention and general +# system instability due to large allocations. This may be because of +# an optimization in the low level drivers due to the contigeous kmem +# based memory. This needs to be explained. The good news here is that +# with zerocopy interfaces added at the DMU layer we could gaurentee +# kmem based memory for a pool of pages. +# +# 0x100 = KMC_KMEM - Force kmem_* based slab +# 0x200 = KMC_VMEM - Force vmem_* based slab +NAME="kmem" +echo "----------------------- ${NAME} ------------------------------" +./zpios.sh \ + "" \ + "zio_bulk_flags=0x100" \ + "" \ + ${LOG}/${NAME}/ | \ + tee ${LOG}/${NAME}.txt diff --git a/scripts/unload-zfs.sh b/scripts/unload-zfs.sh new file mode 100755 index 0000000..12e987b --- /dev/null +++ b/scripts/unload-zfs.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +prog=unload-zfs.sh +. ../.script-config + +spl_module=${SPLBUILD}/modules/spl/spl.ko +zlib_module=/lib/modules/${KERNELSRCVER}/kernel/lib/zlib_deflate/zlib_deflate.ko +zavl_module=${ZFSBUILD}/lib/libavl/zavl.ko +znvpair_module=${ZFSBUILD}/lib/libnvpair/znvpair.ko +zport_module=${ZFSBUILD}/lib/libport/zport.ko +zcommon_module=${ZFSBUILD}/lib/libzcommon/zcommon.ko +zpool_module=${ZFSBUILD}/lib/libzpool/zpool.ko +zctl_module=${ZFSBUILD}/lib/libdmu-ctl/zctl.ko +zpios_module=${ZFSBUILD}/lib/libzpios/zpios.ko + +die() { + echo "${prog}: $1" >&2 + exit 1 +} + +unload_module() { + echo "Unloading $1" + /sbin/rmmod $1 || die "Failed to unload $1" +} + +if [ $(id -u) != 0 ]; then + die "Must run as root" +fi + +unload_module ${zpios_module} +unload_module ${zctl_module} +unload_module ${zpool_module} +unload_module ${zcommon_module} +unload_module ${zport_module} +unload_module ${znvpair_module} +unload_module ${zavl_module} +unload_module ${zlib_module} + +# Set DUMP=1 to generate debug logs on unload +if [ -n "${DUMP}" ]; then + sysctl -w kernel.spl.debug.dump=1 + # This is racy, I don't like it, but for a helper script it will do. + SPL_LOG=`dmesg | tail -n 1 | cut -f5 -d' '` + ${SPLBUILD}/cmd/spl ${SPL_LOG} >${SPL_LOG}.log + echo + echo "Dumped debug log: ${SPL_LOG}.log" + tail -n1 ${SPL_LOG}.log + echo +fi + +unload_module ${spl_module} + +echo "Successfully unloaded ZFS module stack" + +exit 0 diff --git a/scripts/update-zfs.sh b/scripts/update-zfs.sh new file mode 100755 index 0000000..2bcc19b --- /dev/null +++ b/scripts/update-zfs.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +PROG=update-zfs.sh +ZFS_SRC=http://dlc.sun.com/osol/on/downloads/b89/on-src.tar.bz2 + +die() { + rm -Rf $SRC + echo "${PROG}: $1" >&2 + exit 1 +} + +DEST=`pwd` +if [ `basename $DEST` != "scripts" ]; then + die "Must be run from scripts directory" +fi + +SRC=`mktemp -d /tmp/zfs.XXXXXXXXXX` +DEST=`dirname $DEST` +DATE=`date +%Y%m%d%H%M%S` + +wget $ZFS_SRC + +echo "--- Updating ZFS source ---" +echo +echo "ZFS_REPO = $ZFS_REPO" +echo "ZFS_PATCH_REPO = $ZFS_PATCH_REPO" +echo "SRC = $SRC" +echo "DEST = $DEST" + +echo +echo "--- Cloning $ZFS_REPO ---" +cd $SRC || die "Failed to 'cd $SRC'" +hg clone $ZFS_REPO || die "Failed to clone $ZFS_REPO" + +echo +echo "--- Cloning $ZFS_PATCH_REPO ---" +hg clone $ZFS_PATCH_REPO patches || die "Failed to clone $ZFS_PATCH_REPO" + +echo +echo "--- Backing up existing files ---" +echo "$DEST/zfs -> $DEST/zfs.$DATE" +cp -Rf $DEST/zfs $DEST/zfs.$DATE || die "Failed to backup" +echo "$DEST/zfs_patches -> $DEST/zfs_patches.$DATE" +cp -Rf $DEST/zfs_patches $DEST/zfs_patches.$DATE || die "Failed to backup" + +echo +echo "--- Overwriting $DEST/zfs and $DEST/zfs_patches ---" +find $SRC/trunk/src/ -name SConstruct -type f -print | xargs /bin/rm -f +find $SRC/trunk/src/ -name SConscript -type f -print | xargs /bin/rm -f +find $SRC/trunk/src/ -name *.orig -type f -print | xargs /bin/rm -f +rm -f $SRC/trunk/src/myconfig.py +cp -Rf $SRC/trunk/src/* $DEST/zfs || die "Failed to overwrite" +cp -Rf $SRC/patches/*.patch $DEST/zfs_patches/patches/ || die "Failed to overwrite" +cp -f $SRC/patches/series $DEST/zfs_patches/series/zfs-lustre + +echo +echo "--- Removing $SRC ---" +rm -Rf $SRC + diff --git a/scripts/zpios-jbod.sh b/scripts/zpios-jbod.sh new file mode 100755 index 0000000..4cb960f --- /dev/null +++ b/scripts/zpios-jbod.sh @@ -0,0 +1,110 @@ +#!/bin/bash + +prog=zpios-jbod.sh +. ../.script-config + +SPL_OPTIONS=$1 +ZPOOL_OPTIONS=$2 +KPIOS_OPTIONS=$3 +PROFILE_KPIOS_LOGS=$4 +KPIOS_PRE=$5 +KPIOS_POST=$6 + +PROFILE_KPIOS_PRE=/home/behlendo/src/zfs/scripts/profile-kpios-pre.sh +PROFILE_KPIOS_POST=/home/behlendo/src/zfs/scripts/profile-kpios-post.sh + +echo ------------------------- ZFS TEST LOG --------------------------------- +echo -n "Date = "; date +echo -n "Kernel = "; uname -r +echo ------------------------------------------------------------------------ + +echo +./load-zfs.sh "${SPL_OPTIONS}" "${ZPOOL_OPTIONS}" + +sysctl -w kernel.spl.debug.mask=0 +sysctl -w kernel.spl.debug.subsystem=0 + +echo ---------------------- SPL Sysctl Tunings ------------------------------ +sysctl -A | grep spl +echo + +echo ------------------- SPL/ZPOOL Module Tunings --------------------------- +grep [0-9] /sys/module/spl/parameters/* +grep [0-9] /sys/module/zpool/parameters/* +echo + +DEVICES="/dev/sdn /dev/sdo /dev/sdp \ + /dev/sdq /dev/sdr /dev/sds \ + /dev/sdt /dev/sdu /dev/sdv \ + /dev/sdw /dev/sdx /dev/sdy" + +${CMDDIR}/zpool/zpool create -F lustre ${DEVICES} +${CMDDIR}/zpool/zpool status lustre + +if [ -n "${KPIOS_PRE}" ]; then + ${KPIOS_PRE} +fi + +# Usage: zpios +# --chunksize -c =values +# --chunksize_low -a =value +# --chunksize_high -b =value +# --chunksize_incr -g =value +# --offset -o =values +# --offset_low -m =value +# --offset_high -q =value +# --offset_incr -r =value +# --regioncount -n =values +# --regioncount_low -i =value +# --regioncount_high -j =value +# --regioncount_incr -k =value +# --threadcount -t =values +# --threadcount_low -l =value +# --threadcount_high -h =value +# --threadcount_incr -e =value +# --regionsize -s =values +# --regionsize_low -A =value +# --regionsize_high -B =value +# --regionsize_incr -C =value +# --cleanup -x +# --verify -V +# --zerocopy -z +# --threaddelay -T =jiffies +# --regionnoise -I =shift +# --chunknoise -N =bytes +# --prerun -P =pre-command +# --postrun -R =post-command +# --log -G =log directory +# --pool | --path -p =pool name +# --load -L =dmuio +# --help -? =this help +# --verbose -v =increase verbosity +# --threadcount=256,256,256,256,256 \ + +CMD="${CMDDIR}/zpios/zpios \ + --load=dmuio \ + --path=lustre \ + --chunksize=1M \ + --regionsize=4M \ + --regioncount=16384 \ + --threadcount=256 \ + --offset=4M \ + --cleanup \ + --verbose \ + --human-readable \ + ${KPIOS_OPTIONS} \ + --prerun=${PROFILE_KPIOS_PRE} \ + --postrun=${PROFILE_KPIOS_POST} \ + --log=${PROFILE_KPIOS_LOGS}" +echo +date +echo ${CMD} +$CMD +date + +if [ -n "${KPIOS_POST}" ]; then + ${KPIOS_POST} +fi + +${CMDDIR}/zpool/zpool destroy lustre +./unload-zfs.sh diff --git a/scripts/zpios.sh b/scripts/zpios.sh new file mode 100755 index 0000000..a22a33c --- /dev/null +++ b/scripts/zpios.sh @@ -0,0 +1,139 @@ +#!/bin/bash + +prog=zpios.sh +. ../.script-config + +SPL_OPTIONS="spl_debug_mask=0 spl_debug_subsys=0 ${1}" +ZPOOL_OPTIONS=$2 +KPIOS_OPTIONS=$3 +PROFILE_KPIOS_LOGS=$4 +KPIOS_PRE=$5 +KPIOS_POST=$6 + +PROFILE_KPIOS_PRE=/home/behlendo/src/zfs/scripts/profile-kpios-pre.sh +PROFILE_KPIOS_POST=/home/behlendo/src/zfs/scripts/profile-kpios-post.sh + +echo ------------------------- ZFS TEST LOG --------------------------------- +echo -n "Date = "; date +echo -n "Kernel = "; uname -r +echo ------------------------------------------------------------------------ + +echo +./load-zfs.sh "${SPL_OPTIONS}" "${ZPOOL_OPTIONS}" + +echo ---------------------- SPL Sysctl Tunings ------------------------------ +sysctl -A | grep spl +echo + +echo ------------------- SPL/ZPOOL Module Tunings --------------------------- +if [ -d /sys/module/spl/parameters ]; then + grep [0-9] /sys/module/spl/parameters/* + grep [0-9] /sys/module/zpool/parameters/* +else + grep [0-9] /sys/module/spl/* + grep [0-9] /sys/module/zpool/* +fi +echo + +# LOCAL HACK +if [ `hostname` = "ilc23" ]; then + DEVICES="/dev/sdy /dev/sdo /dev/sdp /dev/sdq /dev/sdr /dev/sds \ + /dev/sdt /dev/sdu /dev/sdv /dev/sdw /dev/sdx" +else + DEVICES="/dev/hda" +fi + +echo "${CMDDIR}/zpool/zpool create -F lustre ${DEVICES}" +${CMDDIR}/zpool/zpool create -F lustre ${DEVICES} + +echo "${CMDDIR}/zpool/zpool status lustre" +${CMDDIR}/zpool/zpool status lustre + +echo "Waiting for /dev/kpios to come up..." +while [ ! -c /dev/kpios ]; do + sleep 1 +done + +if [ -n "${KPIOS_PRE}" ]; then + ${KPIOS_PRE} +fi + +# Usage: zpios +# --chunksize -c =values +# --chunksize_low -a =value +# --chunksize_high -b =value +# --chunksize_incr -g =value +# --offset -o =values +# --offset_low -m =value +# --offset_high -q =value +# --offset_incr -r =value +# --regioncount -n =values +# --regioncount_low -i =value +# --regioncount_high -j =value +# --regioncount_incr -k =value +# --threadcount -t =values +# --threadcount_low -l =value +# --threadcount_high -h =value +# --threadcount_incr -e =value +# --regionsize -s =values +# --regionsize_low -A =value +# --regionsize_high -B =value +# --regionsize_incr -C =value +# --cleanup -x +# --verify -V +# --zerocopy -z +# --threaddelay -T =jiffies +# --regionnoise -I =shift +# --chunknoise -N =bytes +# --prerun -P =pre-command +# --postrun -R =post-command +# --log -G =log directory +# --pool | --path -p =pool name +# --load -L =dmuio +# --help -? =this help +# --verbose -v =increase verbosity + +# --prerun=${PROFILE_KPIOS_PRE} \ +# --postrun=${PROFILE_KPIOS_POST} \ + +CMD="${CMDDIR}/zpios/zpios \ + --load=dmuio \ + --path=lustre \ + --chunksize=1M \ + --regionsize=4M \ + --regioncount=16384 \ + --threadcount=256,256,256,256,256 \ + --offset=4M \ + --cleanup \ + --verbose \ + --human-readable \ + ${KPIOS_OPTIONS} \ + --log=${PROFILE_KPIOS_LOGS}" +echo +date +echo ${CMD} +$CMD +date + +if [ -n "${KPIOS_POST}" ]; then + ${KPIOS_POST} +fi + +${CMDDIR}/zpool/zpool destroy lustre + +echo ---------------------- SPL Sysctl Tunings ------------------------------ +sysctl -A | grep spl +echo + +echo ------------------------ KSTAT Statistics ------------------------------ +echo ARCSTATS +cat /proc/spl/kstat/zfs/arcstats +echo +echo VDEV_CACHE_STATS +cat /proc/spl/kstat/zfs/vdev_cache_stats +echo +echo SLAB +cat /proc/spl/kmem/slab +echo + +./unload-zfs.sh diff --git a/zfs/Makefile.in b/zfs/Makefile.in new file mode 100644 index 0000000..e0b30c8 --- /dev/null +++ b/zfs/Makefile.in @@ -0,0 +1,16 @@ +subdir-m += lib +subdir-m += zcmd + +all: +# Make the exported SPL symbols available to this module. There +# is probably a better way to do this, but this will have to do +# for now... an option to modpost perhaps. + cp @splsymvers@ . + +# Kick off the kernel build system + $(MAKE) -C @LINUX@ SUBDIRS=`pwd` @KERNELMAKE_PARAMS@ modules + +install uninstall clean distclean maintainer-clean distdir: + $(MAKE) -C @LINUX@ SUBDIRS=`pwd` @KERNELMAKE_PARAMS@ $@ + +check: diff --git a/zfs/lib/Makefile.in b/zfs/lib/Makefile.in new file mode 100644 index 0000000..324270d --- /dev/null +++ b/zfs/lib/Makefile.in @@ -0,0 +1,12 @@ +subdir-m += libuutil # User space util support +subdir-m += libumem # User space memory support +subdir-m += libzfs # User space library support +subdir-m += libsolcompat # User space compatibility library + +subdir-m += libzpool # Kernel DMU/SPA +subdir-m += libdmu-ctl # Kernel control interface + +subdir-m += libavl # Kernel + user space AVL tree support +subdir-m += libnvpair # Kernel + user space name/value support +subdir-m += libzcommon # Kernel + user space common support +subdir-m += libport # Kernel + user space linux support diff --git a/zfs/lib/libavl/Makefile.in b/zfs/lib/libavl/Makefile.in new file mode 100644 index 0000000..6b9d4d5 --- /dev/null +++ b/zfs/lib/libavl/Makefile.in @@ -0,0 +1,31 @@ +subdir-m += include +DISTFILES = avl.c + +MODULE := zavl +LIBRARY := libavl + +# Compile as kernel module. Needed symlinks created for all +# k* objects created by top level configure script. + +EXTRA_CFLAGS = @KERNELCPPFLAGS@ +EXTRA_CFLAGS += -I@LIBDIR@/libavl/include + +obj-m := ${MODULE}.o + +${MODULE}-objs += kavl.o # Generic AVL support + +# Compile as shared library. There's an extra useless host program +# here called 'zu' because it was the easiest way I could convince +# the kernel build system to construct a user space shared library. + +HOSTCFLAGS += @HOSTCFLAGS@ +HOSTCFLAGS += -I@LIBDIR@/libsolcompat/include +HOSTCFLAGS += -I@LIBDIR@/libport/include +HOSTCFLAGS += -I@LIBDIR@/libavl/include + +hostprogs-y := zu +always := $(hostprogs-y) + +zu-objs := zu.o ${LIBRARY}.so + +${LIBRARY}-objs += uavl.o diff --git a/zfs/lib/libavl/avl.c b/zfs/lib/libavl/avl.c new file mode 100644 index 0000000..ff3ad52 --- /dev/null +++ b/zfs/lib/libavl/avl.c @@ -0,0 +1,969 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL 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. + */ + + + + +/* + * AVL - generic AVL tree implementation for kernel use + * + * A complete description of AVL trees can be found in many CS textbooks. + * + * Here is a very brief overview. An AVL tree is a binary search tree that is + * almost perfectly balanced. By "almost" perfectly balanced, we mean that at + * any given node, the left and right subtrees are allowed to differ in height + * by at most 1 level. + * + * This relaxation from a perfectly balanced binary tree allows doing + * insertion and deletion relatively efficiently. Searching the tree is + * still a fast operation, roughly O(log(N)). + * + * The key to insertion and deletion is a set of tree maniuplations called + * rotations, which bring unbalanced subtrees back into the semi-balanced state. + * + * This implementation of AVL trees has the following peculiarities: + * + * - The AVL specific data structures are physically embedded as fields + * in the "using" data structures. To maintain generality the code + * must constantly translate between "avl_node_t *" and containing + * data structure "void *"s by adding/subracting the avl_offset. + * + * - Since the AVL data is always embedded in other structures, there is + * no locking or memory allocation in the AVL routines. This must be + * provided for by the enclosing data structure's semantics. Typically, + * avl_insert()/_add()/_remove()/avl_insert_here() require some kind of + * exclusive write lock. Other operations require a read lock. + * + * - The implementation uses iteration instead of explicit recursion, + * since it is intended to run on limited size kernel stacks. Since + * there is no recursion stack present to move "up" in the tree, + * there is an explicit "parent" link in the avl_node_t. + * + * - The left/right children pointers of a node are in an array. + * In the code, variables (instead of constants) are used to represent + * left and right indices. The implementation is written as if it only + * dealt with left handed manipulations. By changing the value assigned + * to "left", the code also works for right handed trees. The + * following variables/terms are frequently used: + * + * int left; // 0 when dealing with left children, + * // 1 for dealing with right children + * + * int left_heavy; // -1 when left subtree is taller at some node, + * // +1 when right subtree is taller + * + * int right; // will be the opposite of left (0 or 1) + * int right_heavy;// will be the opposite of left_heavy (-1 or 1) + * + * int direction; // 0 for "<" (ie. left child); 1 for ">" (right) + * + * Though it is a little more confusing to read the code, the approach + * allows using half as much code (and hence cache footprint) for tree + * manipulations and eliminates many conditional branches. + * + * - The avl_index_t is an opaque "cookie" used to find nodes at or + * adjacent to where a new value would be inserted in the tree. The value + * is a modified "avl_node_t *". The bottom bit (normally 0 for a + * pointer) is set to indicate if that the new node has a value greater + * than the value of the indicated "avl_node_t *". + */ + +#include +#include +#include +#include +#include + +/* + * Small arrays to translate between balance (or diff) values and child indeces. + * + * Code that deals with binary tree data structures will randomly use + * left and right children when examining a tree. C "if()" statements + * which evaluate randomly suffer from very poor hardware branch prediction. + * In this code we avoid some of the branch mispredictions by using the + * following translation arrays. They replace random branches with an + * additional memory reference. Since the translation arrays are both very + * small the data should remain efficiently in cache. + */ +static const int avl_child2balance[2] = {-1, 1}; +static const int avl_balance2child[] = {0, 0, 1}; + + +/* + * Walk from one node to the previous valued node (ie. an infix walk + * towards the left). At any given node we do one of 2 things: + * + * - If there is a left child, go to it, then to it's rightmost descendant. + * + * - otherwise we return thru parent nodes until we've come from a right child. + * + * Return Value: + * NULL - if at the end of the nodes + * otherwise next node + */ +void * +avl_walk(avl_tree_t *tree, void *oldnode, int left) +{ + size_t off = tree->avl_offset; + avl_node_t *node = AVL_DATA2NODE(oldnode, off); + int right = 1 - left; + int was_child; + + + /* + * nowhere to walk to if tree is empty + */ + if (node == NULL) + return (NULL); + + /* + * Visit the previous valued node. There are two possibilities: + * + * If this node has a left child, go down one left, then all + * the way right. + */ + if (node->avl_child[left] != NULL) { + for (node = node->avl_child[left]; + node->avl_child[right] != NULL; + node = node->avl_child[right]) + ; + /* + * Otherwise, return thru left children as far as we can. + */ + } else { + for (;;) { + was_child = AVL_XCHILD(node); + node = AVL_XPARENT(node); + if (node == NULL) + return (NULL); + if (was_child == right) + break; + } + } + + return (AVL_NODE2DATA(node, off)); +} + +/* + * Return the lowest valued node in a tree or NULL. + * (leftmost child from root of tree) + */ +void * +avl_first(avl_tree_t *tree) +{ + avl_node_t *node; + avl_node_t *prev = NULL; + size_t off = tree->avl_offset; + + for (node = tree->avl_root; node != NULL; node = node->avl_child[0]) + prev = node; + + if (prev != NULL) + return (AVL_NODE2DATA(prev, off)); + return (NULL); +} + +/* + * Return the highest valued node in a tree or NULL. + * (rightmost child from root of tree) + */ +void * +avl_last(avl_tree_t *tree) +{ + avl_node_t *node; + avl_node_t *prev = NULL; + size_t off = tree->avl_offset; + + for (node = tree->avl_root; node != NULL; node = node->avl_child[1]) + prev = node; + + if (prev != NULL) + return (AVL_NODE2DATA(prev, off)); + return (NULL); +} + +/* + * Access the node immediately before or after an insertion point. + * + * "avl_index_t" is a (avl_node_t *) with the bottom bit indicating a child + * + * Return value: + * NULL: no node in the given direction + * "void *" of the found tree node + */ +void * +avl_nearest(avl_tree_t *tree, avl_index_t where, int direction) +{ + int child = AVL_INDEX2CHILD(where); + avl_node_t *node = AVL_INDEX2NODE(where); + void *data; + size_t off = tree->avl_offset; + + if (node == NULL) { + ASSERT(tree->avl_root == NULL); + return (NULL); + } + data = AVL_NODE2DATA(node, off); + if (child != direction) + return (data); + + return (avl_walk(tree, data, direction)); +} + + +/* + * Search for the node which contains "value". The algorithm is a + * simple binary tree search. + * + * return value: + * NULL: the value is not in the AVL tree + * *where (if not NULL) is set to indicate the insertion point + * "void *" of the found tree node + */ +void * +avl_find(avl_tree_t *tree, void *value, avl_index_t *where) +{ + avl_node_t *node; + avl_node_t *prev = NULL; + int child = 0; + int diff; + size_t off = tree->avl_offset; + + for (node = tree->avl_root; node != NULL; + node = node->avl_child[child]) { + + prev = node; + + diff = tree->avl_compar(value, AVL_NODE2DATA(node, off)); + ASSERT(-1 <= diff && diff <= 1); + if (diff == 0) { +#ifdef DEBUG + if (where != NULL) + *where = 0; +#endif + return (AVL_NODE2DATA(node, off)); + } + child = avl_balance2child[1 + diff]; + + } + + if (where != NULL) + *where = AVL_MKINDEX(prev, child); + + return (NULL); +} + + +/* + * Perform a rotation to restore balance at the subtree given by depth. + * + * This routine is used by both insertion and deletion. The return value + * indicates: + * 0 : subtree did not change height + * !0 : subtree was reduced in height + * + * The code is written as if handling left rotations, right rotations are + * symmetric and handled by swapping values of variables right/left[_heavy] + * + * On input balance is the "new" balance at "node". This value is either + * -2 or +2. + */ +static int +avl_rotation(avl_tree_t *tree, avl_node_t *node, int balance) +{ + int left = !(balance < 0); /* when balance = -2, left will be 0 */ + int right = 1 - left; + int left_heavy = balance >> 1; + int right_heavy = -left_heavy; + avl_node_t *parent = AVL_XPARENT(node); + avl_node_t *child = node->avl_child[left]; + avl_node_t *cright; + avl_node_t *gchild; + avl_node_t *gright; + avl_node_t *gleft; + int which_child = AVL_XCHILD(node); + int child_bal = AVL_XBALANCE(child); + + /* BEGIN CSTYLED */ + /* + * case 1 : node is overly left heavy, the left child is balanced or + * also left heavy. This requires the following rotation. + * + * (node bal:-2) + * / \ + * / \ + * (child bal:0 or -1) + * / \ + * / \ + * cright + * + * becomes: + * + * (child bal:1 or 0) + * / \ + * / \ + * (node bal:-1 or 0) + * / \ + * / \ + * cright + * + * we detect this situation by noting that child's balance is not + * right_heavy. + */ + /* END CSTYLED */ + if (child_bal != right_heavy) { + + /* + * compute new balance of nodes + * + * If child used to be left heavy (now balanced) we reduced + * the height of this sub-tree -- used in "return...;" below + */ + child_bal += right_heavy; /* adjust towards right */ + + /* + * move "cright" to be node's left child + */ + cright = child->avl_child[right]; + node->avl_child[left] = cright; + if (cright != NULL) { + AVL_SETPARENT(cright, node); + AVL_SETCHILD(cright, left); + } + + /* + * move node to be child's right child + */ + child->avl_child[right] = node; + AVL_SETBALANCE(node, -child_bal); + AVL_SETCHILD(node, right); + AVL_SETPARENT(node, child); + + /* + * update the pointer into this subtree + */ + AVL_SETBALANCE(child, child_bal); + AVL_SETCHILD(child, which_child); + AVL_SETPARENT(child, parent); + if (parent != NULL) + parent->avl_child[which_child] = child; + else + tree->avl_root = child; + + return (child_bal == 0); + } + + /* BEGIN CSTYLED */ + /* + * case 2 : When node is left heavy, but child is right heavy we use + * a different rotation. + * + * (node b:-2) + * / \ + * / \ + * / \ + * (child b:+1) + * / \ + * / \ + * (gchild b: != 0) + * / \ + * / \ + * gleft gright + * + * becomes: + * + * (gchild b:0) + * / \ + * / \ + * / \ + * (child b:?) (node b:?) + * / \ / \ + * / \ / \ + * gleft gright + * + * computing the new balances is more complicated. As an example: + * if gchild was right_heavy, then child is now left heavy + * else it is balanced + */ + /* END CSTYLED */ + gchild = child->avl_child[right]; + gleft = gchild->avl_child[left]; + gright = gchild->avl_child[right]; + + /* + * move gright to left child of node and + * + * move gleft to right child of node + */ + node->avl_child[left] = gright; + if (gright != NULL) { + AVL_SETPARENT(gright, node); + AVL_SETCHILD(gright, left); + } + + child->avl_child[right] = gleft; + if (gleft != NULL) { + AVL_SETPARENT(gleft, child); + AVL_SETCHILD(gleft, right); + } + + /* + * move child to left child of gchild and + * + * move node to right child of gchild and + * + * fixup parent of all this to point to gchild + */ + balance = AVL_XBALANCE(gchild); + gchild->avl_child[left] = child; + AVL_SETBALANCE(child, (balance == right_heavy ? left_heavy : 0)); + AVL_SETPARENT(child, gchild); + AVL_SETCHILD(child, left); + + gchild->avl_child[right] = node; + AVL_SETBALANCE(node, (balance == left_heavy ? right_heavy : 0)); + AVL_SETPARENT(node, gchild); + AVL_SETCHILD(node, right); + + AVL_SETBALANCE(gchild, 0); + AVL_SETPARENT(gchild, parent); + AVL_SETCHILD(gchild, which_child); + if (parent != NULL) + parent->avl_child[which_child] = gchild; + else + tree->avl_root = gchild; + + return (1); /* the new tree is always shorter */ +} + + +/* + * Insert a new node into an AVL tree at the specified (from avl_find()) place. + * + * Newly inserted nodes are always leaf nodes in the tree, since avl_find() + * searches out to the leaf positions. The avl_index_t indicates the node + * which will be the parent of the new node. + * + * After the node is inserted, a single rotation further up the tree may + * be necessary to maintain an acceptable AVL balance. + */ +void +avl_insert(avl_tree_t *tree, void *new_data, avl_index_t where) +{ + avl_node_t *node; + avl_node_t *parent = AVL_INDEX2NODE(where); + int old_balance; + int new_balance; + int which_child = AVL_INDEX2CHILD(where); + size_t off = tree->avl_offset; + + ASSERT(tree); +#ifdef _LP64 + ASSERT(((uintptr_t)new_data & 0x7) == 0); +#endif + + node = AVL_DATA2NODE(new_data, off); + + /* + * First, add the node to the tree at the indicated position. + */ + ++tree->avl_numnodes; + + node->avl_child[0] = NULL; + node->avl_child[1] = NULL; + + AVL_SETCHILD(node, which_child); + AVL_SETBALANCE(node, 0); + AVL_SETPARENT(node, parent); + if (parent != NULL) { + ASSERT(parent->avl_child[which_child] == NULL); + parent->avl_child[which_child] = node; + } else { + ASSERT(tree->avl_root == NULL); + tree->avl_root = node; + } + /* + * Now, back up the tree modifying the balance of all nodes above the + * insertion point. If we get to a highly unbalanced ancestor, we + * need to do a rotation. If we back out of the tree we are done. + * If we brought any subtree into perfect balance (0), we are also done. + */ + for (;;) { + node = parent; + if (node == NULL) + return; + + /* + * Compute the new balance + */ + old_balance = AVL_XBALANCE(node); + new_balance = old_balance + avl_child2balance[which_child]; + + /* + * If we introduced equal balance, then we are done immediately + */ + if (new_balance == 0) { + AVL_SETBALANCE(node, 0); + return; + } + + /* + * If both old and new are not zero we went + * from -1 to -2 balance, do a rotation. + */ + if (old_balance != 0) + break; + + AVL_SETBALANCE(node, new_balance); + parent = AVL_XPARENT(node); + which_child = AVL_XCHILD(node); + } + + /* + * perform a rotation to fix the tree and return + */ + (void) avl_rotation(tree, node, new_balance); +} + +/* + * Insert "new_data" in "tree" in the given "direction" either after or + * before (AVL_AFTER, AVL_BEFORE) the data "here". + * + * Insertions can only be done at empty leaf points in the tree, therefore + * if the given child of the node is already present we move to either + * the AVL_PREV or AVL_NEXT and reverse the insertion direction. Since + * every other node in the tree is a leaf, this always works. + * + * To help developers using this interface, we assert that the new node + * is correctly ordered at every step of the way in DEBUG kernels. + */ +void +avl_insert_here( + avl_tree_t *tree, + void *new_data, + void *here, + int direction) +{ + avl_node_t *node; + int child = direction; /* rely on AVL_BEFORE == 0, AVL_AFTER == 1 */ +#ifdef DEBUG + int diff; +#endif + + ASSERT(tree != NULL); + ASSERT(new_data != NULL); + ASSERT(here != NULL); + ASSERT(direction == AVL_BEFORE || direction == AVL_AFTER); + + /* + * If corresponding child of node is not NULL, go to the neighboring + * node and reverse the insertion direction. + */ + node = AVL_DATA2NODE(here, tree->avl_offset); + +#ifdef DEBUG + diff = tree->avl_compar(new_data, here); + ASSERT(-1 <= diff && diff <= 1); + ASSERT(diff != 0); + ASSERT(diff > 0 ? child == 1 : child == 0); +#endif + + if (node->avl_child[child] != NULL) { + node = node->avl_child[child]; + child = 1 - child; + while (node->avl_child[child] != NULL) { +#ifdef DEBUG + diff = tree->avl_compar(new_data, + AVL_NODE2DATA(node, tree->avl_offset)); + ASSERT(-1 <= diff && diff <= 1); + ASSERT(diff != 0); + ASSERT(diff > 0 ? child == 1 : child == 0); +#endif + node = node->avl_child[child]; + } +#ifdef DEBUG + diff = tree->avl_compar(new_data, + AVL_NODE2DATA(node, tree->avl_offset)); + ASSERT(-1 <= diff && diff <= 1); + ASSERT(diff != 0); + ASSERT(diff > 0 ? child == 1 : child == 0); +#endif + } + ASSERT(node->avl_child[child] == NULL); + + avl_insert(tree, new_data, AVL_MKINDEX(node, child)); +} + +/* + * Add a new node to an AVL tree. + */ +void +avl_add(avl_tree_t *tree, void *new_node) +{ + avl_index_t where; + + /* + * This is unfortunate. We want to call panic() here, even for + * non-DEBUG kernels. In userland, however, we can't depend on anything + * in libc or else the rtld build process gets confused. So, all we can + * do in userland is resort to a normal ASSERT(). + */ + if (avl_find(tree, new_node, &where) != NULL) +#ifdef _KERNEL + panic("avl_find() succeeded inside avl_add()"); +#else + ASSERT(0); +#endif + avl_insert(tree, new_node, where); +} + +/* + * Delete a node from the AVL tree. Deletion is similar to insertion, but + * with 2 complications. + * + * First, we may be deleting an interior node. Consider the following subtree: + * + * d c c + * / \ / \ / \ + * b e b e b e + * / \ / \ / + * a c a a + * + * When we are deleting node (d), we find and bring up an adjacent valued leaf + * node, say (c), to take the interior node's place. In the code this is + * handled by temporarily swapping (d) and (c) in the tree and then using + * common code to delete (d) from the leaf position. + * + * Secondly, an interior deletion from a deep tree may require more than one + * rotation to fix the balance. This is handled by moving up the tree through + * parents and applying rotations as needed. The return value from + * avl_rotation() is used to detect when a subtree did not change overall + * height due to a rotation. + */ +void +avl_remove(avl_tree_t *tree, void *data) +{ + avl_node_t *delete; + avl_node_t *parent; + avl_node_t *node; + avl_node_t tmp; + int old_balance; + int new_balance; + int left; + int right; + int which_child; + size_t off = tree->avl_offset; + + ASSERT(tree); + + delete = AVL_DATA2NODE(data, off); + + /* + * Deletion is easiest with a node that has at most 1 child. + * We swap a node with 2 children with a sequentially valued + * neighbor node. That node will have at most 1 child. Note this + * has no effect on the ordering of the remaining nodes. + * + * As an optimization, we choose the greater neighbor if the tree + * is right heavy, otherwise the left neighbor. This reduces the + * number of rotations needed. + */ + if (delete->avl_child[0] != NULL && delete->avl_child[1] != NULL) { + + /* + * choose node to swap from whichever side is taller + */ + old_balance = AVL_XBALANCE(delete); + left = avl_balance2child[old_balance + 1]; + right = 1 - left; + + /* + * get to the previous value'd node + * (down 1 left, as far as possible right) + */ + for (node = delete->avl_child[left]; + node->avl_child[right] != NULL; + node = node->avl_child[right]) + ; + + /* + * create a temp placeholder for 'node' + * move 'node' to delete's spot in the tree + */ + tmp = *node; + + *node = *delete; + if (node->avl_child[left] == node) + node->avl_child[left] = &tmp; + + parent = AVL_XPARENT(node); + if (parent != NULL) + parent->avl_child[AVL_XCHILD(node)] = node; + else + tree->avl_root = node; + AVL_SETPARENT(node->avl_child[left], node); + AVL_SETPARENT(node->avl_child[right], node); + + /* + * Put tmp where node used to be (just temporary). + * It always has a parent and at most 1 child. + */ + delete = &tmp; + parent = AVL_XPARENT(delete); + parent->avl_child[AVL_XCHILD(delete)] = delete; + which_child = (delete->avl_child[1] != 0); + if (delete->avl_child[which_child] != NULL) + AVL_SETPARENT(delete->avl_child[which_child], delete); + } + + + /* + * Here we know "delete" is at least partially a leaf node. It can + * be easily removed from the tree. + */ + ASSERT(tree->avl_numnodes > 0); + --tree->avl_numnodes; + parent = AVL_XPARENT(delete); + which_child = AVL_XCHILD(delete); + if (delete->avl_child[0] != NULL) + node = delete->avl_child[0]; + else + node = delete->avl_child[1]; + + /* + * Connect parent directly to node (leaving out delete). + */ + if (node != NULL) { + AVL_SETPARENT(node, parent); + AVL_SETCHILD(node, which_child); + } + if (parent == NULL) { + tree->avl_root = node; + return; + } + parent->avl_child[which_child] = node; + + + /* + * Since the subtree is now shorter, begin adjusting parent balances + * and performing any needed rotations. + */ + do { + + /* + * Move up the tree and adjust the balance + * + * Capture the parent and which_child values for the next + * iteration before any rotations occur. + */ + node = parent; + old_balance = AVL_XBALANCE(node); + new_balance = old_balance - avl_child2balance[which_child]; + parent = AVL_XPARENT(node); + which_child = AVL_XCHILD(node); + + /* + * If a node was in perfect balance but isn't anymore then + * we can stop, since the height didn't change above this point + * due to a deletion. + */ + if (old_balance == 0) { + AVL_SETBALANCE(node, new_balance); + break; + } + + /* + * If the new balance is zero, we don't need to rotate + * else + * need a rotation to fix the balance. + * If the rotation doesn't change the height + * of the sub-tree we have finished adjusting. + */ + if (new_balance == 0) + AVL_SETBALANCE(node, new_balance); + else if (!avl_rotation(tree, node, new_balance)) + break; + } while (parent != NULL); +} + +/* + * initialize a new AVL tree + */ +void +avl_create(avl_tree_t *tree, int (*compar) (const void *, const void *), + size_t size, size_t offset) +{ + ASSERT(tree); + ASSERT(compar); + ASSERT(size > 0); + ASSERT(size >= offset + sizeof (avl_node_t)); +#ifdef _LP64 + ASSERT((offset & 0x7) == 0); +#endif + + tree->avl_compar = compar; + tree->avl_root = NULL; + tree->avl_numnodes = 0; + tree->avl_size = size; + tree->avl_offset = offset; +} + +/* + * Delete a tree. + */ +/* ARGSUSED */ +void +avl_destroy(avl_tree_t *tree) +{ + ASSERT(tree); + ASSERT(tree->avl_numnodes == 0); + ASSERT(tree->avl_root == NULL); +} + + +/* + * Return the number of nodes in an AVL tree. + */ +ulong_t +avl_numnodes(avl_tree_t *tree) +{ + ASSERT(tree); + return (tree->avl_numnodes); +} + + +#define CHILDBIT (1L) + +/* + * Post-order tree walk used to visit all tree nodes and destroy the tree + * in post order. This is used for destroying a tree w/o paying any cost + * for rebalancing it. + * + * example: + * + * void *cookie = NULL; + * my_data_t *node; + * + * while ((node = avl_destroy_nodes(tree, &cookie)) != NULL) + * free(node); + * avl_destroy(tree); + * + * The cookie is really an avl_node_t to the current node's parent and + * an indication of which child you looked at last. + * + * On input, a cookie value of CHILDBIT indicates the tree is done. + */ +void * +avl_destroy_nodes(avl_tree_t *tree, void **cookie) +{ + avl_node_t *node; + avl_node_t *parent; + int child; + void *first; + size_t off = tree->avl_offset; + + /* + * Initial calls go to the first node or it's right descendant. + */ + if (*cookie == NULL) { + first = avl_first(tree); + + /* + * deal with an empty tree + */ + if (first == NULL) { + *cookie = (void *)CHILDBIT; + return (NULL); + } + + node = AVL_DATA2NODE(first, off); + parent = AVL_XPARENT(node); + goto check_right_side; + } + + /* + * If there is no parent to return to we are done. + */ + parent = (avl_node_t *)((uintptr_t)(*cookie) & ~CHILDBIT); + if (parent == NULL) { + if (tree->avl_root != NULL) { + ASSERT(tree->avl_numnodes == 1); + tree->avl_root = NULL; + tree->avl_numnodes = 0; + } + return (NULL); + } + + /* + * Remove the child pointer we just visited from the parent and tree. + */ + child = (uintptr_t)(*cookie) & CHILDBIT; + parent->avl_child[child] = NULL; + ASSERT(tree->avl_numnodes > 1); + --tree->avl_numnodes; + + /* + * If we just did a right child or there isn't one, go up to parent. + */ + if (child == 1 || parent->avl_child[1] == NULL) { + node = parent; + parent = AVL_XPARENT(parent); + goto done; + } + + /* + * Do parent's right child, then leftmost descendent. + */ + node = parent->avl_child[1]; + while (node->avl_child[0] != NULL) { + parent = node; + node = node->avl_child[0]; + } + + /* + * If here, we moved to a left child. It may have one + * child on the right (when balance == +1). + */ +check_right_side: + if (node->avl_child[1] != NULL) { + ASSERT(AVL_XBALANCE(node) == 1); + parent = node; + node = node->avl_child[1]; + ASSERT(node->avl_child[0] == NULL && + node->avl_child[1] == NULL); + } else { + ASSERT(AVL_XBALANCE(node) <= 0); + } + +done: + if (parent == NULL) { + *cookie = (void *)CHILDBIT; + ASSERT(node == tree->avl_root); + } else { + *cookie = (void *)((uintptr_t)parent | AVL_XCHILD(node)); + } + + return (AVL_NODE2DATA(node, off)); +} diff --git a/zfs/lib/libavl/include/Makefile.in b/zfs/lib/libavl/include/Makefile.in new file mode 100644 index 0000000..6611e41 --- /dev/null +++ b/zfs/lib/libavl/include/Makefile.in @@ -0,0 +1 @@ +subdir-m += sys diff --git a/zfs/lib/libavl/include/sys/Makefile.in b/zfs/lib/libavl/include/sys/Makefile.in new file mode 100644 index 0000000..8149c38 --- /dev/null +++ b/zfs/lib/libavl/include/sys/Makefile.in @@ -0,0 +1 @@ +DISTFILES = avl.h avl_impl.h diff --git a/zfs/lib/libavl/include/sys/avl.h b/zfs/lib/libavl/include/sys/avl.h new file mode 100644 index 0000000..1c1f11b --- /dev/null +++ b/zfs/lib/libavl/include/sys/avl.h @@ -0,0 +1,298 @@ +/* + * 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. + */ + +#ifndef _AVL_H +#define _AVL_H + + + +/* + * This is a private header file. Applications should not directly include + * this file. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* + * This is a generic implemenatation of AVL trees for use in the Solaris kernel. + * The interfaces provide an efficient way of implementing an ordered set of + * data structures. + * + * AVL trees provide an alternative to using an ordered linked list. Using AVL + * trees will usually be faster, however they requires more storage. An ordered + * linked list in general requires 2 pointers in each data structure. The + * AVL tree implementation uses 3 pointers. The following chart gives the + * approximate performance of operations with the different approaches: + * + * Operation Link List AVL tree + * --------- -------- -------- + * lookup O(n) O(log(n)) + * + * insert 1 node constant constant + * + * delete 1 node constant between constant and O(log(n)) + * + * delete all nodes O(n) O(n) + * + * visit the next + * or prev node constant between constant and O(log(n)) + * + * + * The data structure nodes are anchored at an "avl_tree_t" (the equivalent + * of a list header) and the individual nodes will have a field of + * type "avl_node_t" (corresponding to list pointers). + * + * The type "avl_index_t" is used to indicate a position in the list for + * certain calls. + * + * The usage scenario is generally: + * + * 1. Create the list/tree with: avl_create() + * + * followed by any mixture of: + * + * 2a. Insert nodes with: avl_add(), or avl_find() and avl_insert() + * + * 2b. Visited elements with: + * avl_first() - returns the lowest valued node + * avl_last() - returns the highest valued node + * AVL_NEXT() - given a node go to next higher one + * AVL_PREV() - given a node go to previous lower one + * + * 2c. Find the node with the closest value either less than or greater + * than a given value with avl_nearest(). + * + * 2d. Remove individual nodes from the list/tree with avl_remove(). + * + * and finally when the list is being destroyed + * + * 3. Use avl_destroy_nodes() to quickly process/free up any remaining nodes. + * Note that once you use avl_destroy_nodes(), you can no longer + * use any routine except avl_destroy_nodes() and avl_destoy(). + * + * 4. Use avl_destroy() to destroy the AVL tree itself. + * + * Any locking for multiple thread access is up to the user to provide, just + * as is needed for any linked list implementation. + */ + + +/* + * Type used for the root of the AVL tree. + */ +typedef struct avl_tree avl_tree_t; + +/* + * The data nodes in the AVL tree must have a field of this type. + */ +typedef struct avl_node avl_node_t; + +/* + * An opaque type used to locate a position in the tree where a node + * would be inserted. + */ +typedef uintptr_t avl_index_t; + + +/* + * Direction constants used for avl_nearest(). + */ +#define AVL_BEFORE (0) +#define AVL_AFTER (1) + + + +/* + * Prototypes + * + * Where not otherwise mentioned, "void *" arguments are a pointer to the + * user data structure which must contain a field of type avl_node_t. + * + * Also assume the user data structures looks like: + * stuct my_type { + * ... + * avl_node_t my_link; + * ... + * }; + */ + +/* + * Initialize an AVL tree. Arguments are: + * + * tree - the tree to be initialized + * compar - function to compare two nodes, it must return exactly: -1, 0, or +1 + * -1 for <, 0 for ==, and +1 for > + * size - the value of sizeof(struct my_type) + * offset - the value of OFFSETOF(struct my_type, my_link) + */ +extern void avl_create(avl_tree_t *tree, + int (*compar) (const void *, const void *), size_t size, size_t offset); + + +/* + * Find a node with a matching value in the tree. Returns the matching node + * found. If not found, it returns NULL and then if "where" is not NULL it sets + * "where" for use with avl_insert() or avl_nearest(). + * + * node - node that has the value being looked for + * where - position for use with avl_nearest() or avl_insert(), may be NULL + */ +extern void *avl_find(avl_tree_t *tree, void *node, avl_index_t *where); + +/* + * Insert a node into the tree. + * + * node - the node to insert + * where - position as returned from avl_find() + */ +extern void avl_insert(avl_tree_t *tree, void *node, avl_index_t where); + +/* + * Insert "new_data" in "tree" in the given "direction" either after + * or before the data "here". + * + * This might be usefull for avl clients caching recently accessed + * data to avoid doing avl_find() again for insertion. + * + * new_data - new data to insert + * 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, + int direction); + + +/* + * Return the first or last valued node in the tree. Will return NULL + * if the tree is empty. + * + */ +extern void *avl_first(avl_tree_t *tree); +extern void *avl_last(avl_tree_t *tree); + + +/* + * Return the next or previous valued node in the tree. + * AVL_NEXT() will return NULL if at the last node. + * AVL_PREV() will return NULL if at the first node. + * + * node - the node from which the next or previous node is found + */ +#define AVL_NEXT(tree, node) avl_walk(tree, node, AVL_AFTER) +#define AVL_PREV(tree, node) avl_walk(tree, node, AVL_BEFORE) + + +/* + * Find the node with the nearest value either greater or less than + * the value from a previous avl_find(). Returns the node or NULL if + * there isn't a matching one. + * + * where - position as returned from avl_find() + * direction - either AVL_BEFORE or AVL_AFTER + * + * EXAMPLE get the greatest node that is less than a given value: + * + * avl_tree_t *tree; + * struct my_data look_for_value = {....}; + * struct my_data *node; + * struct my_data *less; + * avl_index_t where; + * + * node = avl_find(tree, &look_for_value, &where); + * if (node != NULL) + * less = AVL_PREV(tree, node); + * else + * less = avl_nearest(tree, where, AVL_BEFORE); + */ +extern void *avl_nearest(avl_tree_t *tree, avl_index_t where, int direction); + + +/* + * Add a single node to the tree. + * The node must not be in the tree, and it must not + * compare equal to any other node already in the tree. + * + * node - the node to add + */ +extern void avl_add(avl_tree_t *tree, void *node); + + +/* + * Remove a single node from the tree. The node must be in the tree. + * + * node - the node to remove + */ +extern void avl_remove(avl_tree_t *tree, void *node); + + +/* + * Return the number of nodes in the tree + */ +extern ulong_t avl_numnodes(avl_tree_t *tree); + + +/* + * Used to destroy any remaining nodes in a tree. The cookie argument should + * be initialized to NULL before the first call. Returns a node that has been + * removed from the tree and may be free()'d. Returns NULL when the tree is + * empty. + * + * Once you call avl_destroy_nodes(), you can only continuing calling it and + * finally avl_destroy(). No other AVL routines will be valid. + * + * cookie - a "void *" used to save state between calls to avl_destroy_nodes() + * + * EXAMPLE: + * avl_tree_t *tree; + * struct my_data *node; + * void *cookie; + * + * cookie = NULL; + * while ((node = avl_destroy_nodes(tree, &cookie)) != NULL) + * free(node); + * avl_destroy(tree); + */ +extern void *avl_destroy_nodes(avl_tree_t *tree, void **cookie); + + +/* + * Final destroy of an AVL tree. Arguments are: + * + * tree - the empty tree to destroy + */ +extern void avl_destroy(avl_tree_t *tree); + + + +#ifdef __cplusplus +} +#endif + +#endif /* _AVL_H */ diff --git a/zfs/lib/libavl/include/sys/avl_impl.h b/zfs/lib/libavl/include/sys/avl_impl.h new file mode 100644 index 0000000..fddf769 --- /dev/null +++ b/zfs/lib/libavl/include/sys/avl_impl.h @@ -0,0 +1,164 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _AVL_IMPL_H +#define _AVL_IMPL_H + + + +/* + * This is a private header file. Applications should not directly include + * this file. + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * generic AVL tree implementation for kernel use + * + * There are 5 pieces of information stored for each node in an AVL tree + * + * pointer to less than child + * pointer to greater than child + * a pointer to the parent of this node + * an indication [0/1] of which child I am of my parent + * a "balance" (-1, 0, +1) indicating which child tree is taller + * + * Since they only need 3 bits, the last two fields are packed into the + * bottom bits of the parent pointer on 64 bit machines to save on space. + */ + +#ifndef _LP64 + +struct avl_node { + struct avl_node *avl_child[2]; /* left/right children */ + struct avl_node *avl_parent; /* this node's parent */ + unsigned short avl_child_index; /* my index in parent's avl_child[] */ + short avl_balance; /* balance value: -1, 0, +1 */ +}; + +#define AVL_XPARENT(n) ((n)->avl_parent) +#define AVL_SETPARENT(n, p) ((n)->avl_parent = (p)) + +#define AVL_XCHILD(n) ((n)->avl_child_index) +#define AVL_SETCHILD(n, c) ((n)->avl_child_index = (unsigned short)(c)) + +#define AVL_XBALANCE(n) ((n)->avl_balance) +#define AVL_SETBALANCE(n, b) ((n)->avl_balance = (short)(b)) + +#else /* _LP64 */ + +/* + * for 64 bit machines, avl_pcb contains parent pointer, balance and child_index + * values packed in the following manner: + * + * |63 3| 2 |1 0 | + * |-------------------------------------|-----------------|-------------| + * | avl_parent hi order bits | avl_child_index | avl_balance | + * | | | + 1 | + * |-------------------------------------|-----------------|-------------| + * + */ +struct avl_node { + struct avl_node *avl_child[2]; /* left/right children nodes */ + uintptr_t avl_pcb; /* parent, child_index, balance */ +}; + +/* + * macros to extract/set fields in avl_pcb + * + * pointer to the parent of the current node is the high order bits + */ +#define AVL_XPARENT(n) ((struct avl_node *)((n)->avl_pcb & ~7)) +#define AVL_SETPARENT(n, p) \ + ((n)->avl_pcb = (((n)->avl_pcb & 7) | (uintptr_t)(p))) + +/* + * index of this node in its parent's avl_child[]: bit #2 + */ +#define AVL_XCHILD(n) (((n)->avl_pcb >> 2) & 1) +#define AVL_SETCHILD(n, c) \ + ((n)->avl_pcb = (uintptr_t)(((n)->avl_pcb & ~4) | ((c) << 2))) + +/* + * balance indication for a node, lowest 2 bits. A valid balance is + * -1, 0, or +1, and is encoded by adding 1 to the value to get the + * unsigned values of 0, 1, 2. + */ +#define AVL_XBALANCE(n) ((int)(((n)->avl_pcb & 3) - 1)) +#define AVL_SETBALANCE(n, b) \ + ((n)->avl_pcb = (uintptr_t)((((n)->avl_pcb & ~3) | ((b) + 1)))) + +#endif /* _LP64 */ + + + +/* + * switch between a node and data pointer for a given tree + * the value of "o" is tree->avl_offset + */ +#define AVL_NODE2DATA(n, o) ((void *)((uintptr_t)(n) - (o))) +#define AVL_DATA2NODE(d, o) ((struct avl_node *)((uintptr_t)(d) + (o))) + + + +/* + * macros used to create/access an avl_index_t + */ +#define AVL_INDEX2NODE(x) ((avl_node_t *)((x) & ~1)) +#define AVL_INDEX2CHILD(x) ((x) & 1) +#define AVL_MKINDEX(n, c) ((avl_index_t)(n) | (c)) + + +/* + * The tree structure. The fields avl_root, avl_compar, and avl_offset come + * first since they are needed for avl_find(). We want them to fit into + * a single 64 byte cache line to make avl_find() as fast as possible. + */ +struct avl_tree { + struct avl_node *avl_root; /* root node in tree */ + int (*avl_compar)(const void *, const void *); + size_t avl_offset; /* offsetof(type, avl_link_t field) */ + ulong_t avl_numnodes; /* number of nodes in the tree */ + size_t avl_size; /* sizeof user type struct */ +}; + + +/* + * This will only by used via AVL_NEXT() or AVL_PREV() + */ +extern void *avl_walk(struct avl_tree *, void *, int); + +#ifdef __cplusplus +} +#endif + +#endif /* _AVL_IMPL_H */ diff --git a/zfs/lib/libdmu-ctl/Makefile.in b/zfs/lib/libdmu-ctl/Makefile.in new file mode 100644 index 0000000..c4017b2 --- /dev/null +++ b/zfs/lib/libdmu-ctl/Makefile.in @@ -0,0 +1,28 @@ +# NOTE: dctl_client.c, dctl_common.c, dctl_server.c, dctl_thrpool.c unused +# by kernel port. Potentially they should just be removed if we don't care +# able user space lustre intergration from this source base. + +# NOTE: For clarity this directly should simply be renamed libzpl and +# the full kernel implementation should be minimally stubbed out. + +subdir-m += include +DISTFILES = dctl_client.c dctl_common.c dctl_server.c dctl_thrpool.c +DISTFILES += dmu_send.c rrwlock.c zfs_acl.c zfs_ctldir.c +DISTFILES += zfs_dir.c zfs_fuid.c zfs_ioctl.c zfs_log.c zfs_replay.c +DISTFILES += zfs_rlock.c zfs_vfsops.c zfs_vnops.c zvol.c + +MODULE := zctl + +EXTRA_CFLAGS = @KERNELCPPFLAGS@ +EXTRA_CFLAGS += -I@LIBDIR@/libzcommon/include +EXTRA_CFLAGS += -I@LIBDIR@/libdmu-ctl/include +EXTRA_CFLAGS += -I@LIBDIR@/libavl/include +EXTRA_CFLAGS += -I@LIBDIR@/libport/include +EXTRA_CFLAGS += -I@LIBDIR@/libnvpair/include + +obj-m := ${MODULE}.o + +${MODULE}-objs += zvol.o # Volume emulation interface +${MODULE}-objs += zfs_ioctl.o # /dev/zfs_ioctl interface +${MODULE}-objs += zfs_vfsops.o +${MODULE}-objs += dmu_send.o diff --git a/zfs/lib/libdmu-ctl/dctl_client.c b/zfs/lib/libdmu-ctl/dctl_client.c new file mode 100644 index 0000000..e3d8f30 --- /dev/null +++ b/zfs/lib/libdmu-ctl/dctl_client.c @@ -0,0 +1,263 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * Try to connect to the socket given in path. + * + * For nftw() convenience, returns 0 if unsuccessful, otherwise + * returns the socket descriptor. + */ +static int try_connect(const char *path) +{ + struct sockaddr_un name; + int sock; + + sock = socket(PF_UNIX, SOCK_STREAM, 0); + if (sock == -1) { + perror("socket"); + return 0; + } + + /* + * The socket fd cannot be 0 otherwise nftw() will not interpret the + * return code correctly. + */ + VERIFY(sock != 0); + + name.sun_family = AF_UNIX; + strncpy(name.sun_path, path, sizeof(name.sun_path)); + + name.sun_path[sizeof(name.sun_path) - 1] = '\0'; + + if (connect(sock, (struct sockaddr *) &name, sizeof(name)) == -1) { + close(sock); + return 0; + } + + return sock; +} + +/* + * nftw() callback. + */ +static int nftw_cb(const char *fpath, const struct stat *sb, int typeflag, + struct FTW *ftwbuf) +{ + if (!S_ISSOCK(sb->st_mode)) + return 0; + + if (strcmp(&fpath[ftwbuf->base], SOCKNAME) != 0) + return 0; + + return try_connect(fpath); +} + +/* + * For convenience, if check_subdirs is true we walk the directory tree to + * find a good socket. + */ +int dctlc_connect(const char *dir, boolean_t check_subdirs) +{ + char *fpath; + int fd; + + if (check_subdirs) + fd = nftw(dir, nftw_cb, 10, FTW_PHYS); + else { + fpath = malloc(strlen(dir) + strlen(SOCKNAME) + 2); + if (fpath == NULL) + return -1; + + strcpy(fpath, dir); + strcat(fpath, "/" SOCKNAME); + + fd = try_connect(fpath); + + free(fpath); + } + + return fd == 0 ? -1 : fd; +} + +void dctlc_disconnect(int fd) +{ + (void) shutdown(fd, SHUT_RDWR); +} + +static int dctl_reply_copyin(int fd, dctl_cmd_t *cmd) +{ + return dctl_send_data(fd, (void *)(uintptr_t) cmd->u.dcmd_copy.ptr, + cmd->u.dcmd_copy.size); +} + +static int dctl_reply_copyinstr(int fd, dctl_cmd_t *cmd) +{ + dctl_cmd_t reply; + char *from; + size_t len, buflen, to_copy; + int error; + + reply.dcmd_msg = DCTL_GEN_REPLY; + + from = (char *)(uintptr_t) cmd->u.dcmd_copy.ptr; + + buflen = cmd->u.dcmd_copy.size; + to_copy = strnlen(from, buflen - 1); + + reply.u.dcmd_reply.rc = from[to_copy] == '\0' ? 0 : ENAMETOOLONG; + reply.u.dcmd_reply.size = to_copy; + + error = dctl_send_msg(fd, &reply); + + if (!error && to_copy > 0) + error = dctl_send_data(fd, from, to_copy); + + return error; +} + +static int dctl_reply_copyout(int fd, dctl_cmd_t *cmd) +{ + return dctl_read_data(fd, (void *)(uintptr_t) cmd->u.dcmd_copy.ptr, + cmd->u.dcmd_copy.size); +} + +static int dctl_reply_fd_read(int fd, dctl_cmd_t *cmd) +{ + dctl_cmd_t reply; + void *buf; + int error; + ssize_t rrc, size = cmd->u.dcmd_fd_io.size; + + buf = malloc(size); + if (buf == NULL) + return ENOMEM; + + rrc = read(cmd->u.dcmd_fd_io.fd, buf, size); + + reply.dcmd_msg = DCTL_GEN_REPLY; + reply.u.dcmd_reply.rc = rrc == -1 ? errno : 0; + reply.u.dcmd_reply.size = rrc; + + error = dctl_send_msg(fd, &reply); + + if (!error && rrc > 0) + error = dctl_send_data(fd, buf, rrc); + +out: + free(buf); + + return error; +} + +static int dctl_reply_fd_write(int fd, dctl_cmd_t *cmd) +{ + dctl_cmd_t reply; + void *buf; + int error; + ssize_t wrc, size = cmd->u.dcmd_fd_io.size; + + buf = malloc(size); + if (buf == NULL) + return ENOMEM; + + error = dctl_read_data(fd, buf, size); + if (error) + goto out; + + wrc = write(cmd->u.dcmd_fd_io.fd, buf, size); + + reply.dcmd_msg = DCTL_GEN_REPLY; + reply.u.dcmd_reply.rc = wrc == -1 ? errno : 0; + reply.u.dcmd_reply.size = wrc; + + error = dctl_send_msg(fd, &reply); + +out: + free(buf); + + return error; +} + +int dctlc_ioctl(int fd, int32_t request, void *arg) +{ + int error; + dctl_cmd_t cmd; + + ASSERT(fd != 0); + + cmd.dcmd_msg = DCTL_IOCTL; + + cmd.u.dcmd_ioctl.cmd = request; + cmd.u.dcmd_ioctl.arg = (uintptr_t) arg; + + error = dctl_send_msg(fd, &cmd); + + while (!error && (error = dctl_read_msg(fd, &cmd)) == 0) { + switch (cmd.dcmd_msg) { + case DCTL_IOCTL_REPLY: + error = cmd.u.dcmd_reply.rc; + goto out; + case DCTL_COPYIN: + error = dctl_reply_copyin(fd, &cmd); + break; + case DCTL_COPYINSTR: + error = dctl_reply_copyinstr(fd, &cmd); + break; + case DCTL_COPYOUT: + error = dctl_reply_copyout(fd, &cmd); + break; + case DCTL_FD_READ: + error = dctl_reply_fd_read(fd, &cmd); + break; + case DCTL_FD_WRITE: + error = dctl_reply_fd_write(fd, &cmd); + break; + default: + fprintf(stderr, "%s(): invalid message " + "received.\n", __func__); + error = EINVAL; + goto out; + } + } + +out: + errno = error; + return error ? -1 : 0; +} diff --git a/zfs/lib/libdmu-ctl/dctl_common.c b/zfs/lib/libdmu-ctl/dctl_common.c new file mode 100644 index 0000000..8de37dc --- /dev/null +++ b/zfs/lib/libdmu-ctl/dctl_common.c @@ -0,0 +1,109 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include +#include +#include +#include + +#include +#include + +int dctl_read_msg(int fd, dctl_cmd_t *cmd) +{ + int error; + + /* + * First, read only the magic number and the protocol version. + * + * This prevents blocking forever in case the size of dctl_cmd_t + * shrinks in future protocol versions. + */ + error = dctl_read_data(fd, cmd, DCTL_CMD_HEADER_SIZE); + + if (!error &&cmd->dcmd_magic != DCTL_MAGIC) { + fprintf(stderr, "%s(): invalid magic number\n", __func__); + error = EIO; + } + + if (!error && cmd->dcmd_version != DCTL_PROTOCOL_VER) { + fprintf(stderr, "%s(): invalid protocol version\n", __func__); + error = ENOTSUP; + } + + if (error) + return error; + + /* Get the rest of the command */ + return dctl_read_data(fd, (caddr_t) cmd + DCTL_CMD_HEADER_SIZE, + sizeof(dctl_cmd_t) - DCTL_CMD_HEADER_SIZE); +} + +int dctl_send_msg(int fd, dctl_cmd_t *cmd) +{ + cmd->dcmd_magic = DCTL_MAGIC; + cmd->dcmd_version = DCTL_PROTOCOL_VER; + + return dctl_send_data(fd, cmd, sizeof(dctl_cmd_t)); +} + +int dctl_read_data(int fd, void *ptr, size_t size) +{ + size_t read = 0; + size_t left = size; + ssize_t rc; + + while (left > 0) { + rc = recv(fd, (caddr_t) ptr + read, left, 0); + + /* File descriptor closed */ + if (rc == 0) + return ECONNRESET; + + if (rc == -1) { + if (errno == EINTR) + continue; + return errno; + } + + read += rc; + left -= rc; + } + + return 0; +} + +int dctl_send_data(int fd, const void *ptr, size_t size) +{ + ssize_t rc; + + do { + rc = send(fd, ptr, size, MSG_NOSIGNAL); + } while(rc == -1 && errno == EINTR); + + return rc == size ? 0 : EIO; +} + diff --git a/zfs/lib/libdmu-ctl/dctl_server.c b/zfs/lib/libdmu-ctl/dctl_server.c new file mode 100644 index 0000000..0162785 --- /dev/null +++ b/zfs/lib/libdmu-ctl/dctl_server.c @@ -0,0 +1,476 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static dctl_sock_info_t ctl_sock = { + .dsi_mtx = PTHREAD_MUTEX_INITIALIZER, + .dsi_fd = -1 +}; + +static int dctl_create_socket_common(); + +/* + * Routines from zfs_ioctl.c + */ +extern int zfs_ioctl_init(); +extern int zfs_ioctl_fini(); +extern int zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, + int *rvalp); + +/* + * We can't simply put the client file descriptor in wthr_info_t because we + * have no way of accessing it from the DMU code without extensive + * modifications. + * + * Therefore each worker thread will have it's own global thread-specific + * client_fd variable. + */ +static __thread int client_fd = -1; + +int dctls_copyin(const void *src, void *dest, size_t size) +{ + dctl_cmd_t cmd; + + VERIFY(client_fd >= 0); + + cmd.dcmd_msg = DCTL_COPYIN; + cmd.u.dcmd_copy.ptr = (uintptr_t) src; + cmd.u.dcmd_copy.size = size; + + if (dctl_send_msg(client_fd, &cmd) != 0) + return EFAULT; + + if (dctl_read_data(client_fd, dest, size) != 0) + return EFAULT; + + return 0; +} + +int dctls_copyinstr(const char *from, char *to, size_t max, size_t *len) +{ + dctl_cmd_t msg; + size_t copied; + + VERIFY(client_fd >= 0); + + if (max == 0) + return ENAMETOOLONG; + if (max < 0) + return EFAULT; + + msg.dcmd_msg = DCTL_COPYINSTR; + msg.u.dcmd_copy.ptr = (uintptr_t) from; + msg.u.dcmd_copy.size = max; + + if (dctl_send_msg(client_fd, &msg) != 0) + return EFAULT; + + if (dctl_read_msg(client_fd, &msg) != 0) + return EFAULT; + + if (msg.dcmd_msg != DCTL_GEN_REPLY) + return EFAULT; + + copied = msg.u.dcmd_reply.size; + + if (copied >= max) + return EFAULT; + + if (copied > 0) + if (dctl_read_data(client_fd, to, copied) != 0) + return EFAULT; + + to[copied] = '\0'; + + if (len != NULL) + *len = copied + 1; + + return msg.u.dcmd_reply.rc; +} + +int dctls_copyout(const void *src, void *dest, size_t size) +{ + dctl_cmd_t cmd; + + VERIFY(client_fd >= 0); + + cmd.dcmd_msg = DCTL_COPYOUT; + cmd.u.dcmd_copy.ptr = (uintptr_t) dest; + cmd.u.dcmd_copy.size = size; + + if (dctl_send_msg(client_fd, &cmd) != 0) + return EFAULT; + + if (dctl_send_data(client_fd, src, size) != 0) + return EFAULT; + + return 0; +} + +int dctls_fd_read(int fd, void *buf, ssize_t len, ssize_t *residp) +{ + dctl_cmd_t msg; + uint64_t dsize; + int error; + + VERIFY(client_fd >= 0); + + msg.dcmd_msg = DCTL_FD_READ; + msg.u.dcmd_fd_io.fd = fd; + msg.u.dcmd_fd_io.size = len; + + if ((error = dctl_send_msg(client_fd, &msg)) != 0) + return error; + + if ((error = dctl_read_msg(client_fd, &msg)) != 0) + return error; + + if (msg.dcmd_msg != DCTL_GEN_REPLY) + return EIO; + + if (msg.u.dcmd_reply.rc != 0) + return msg.u.dcmd_reply.rc; + + dsize = msg.u.dcmd_reply.size; + + if (dsize > 0) + error = dctl_read_data(client_fd, buf, dsize); + + *residp = len - dsize; + + return error; +} + +int dctls_fd_write(int fd, const void *src, ssize_t len) +{ + dctl_cmd_t msg; + int error; + + VERIFY(client_fd >= 0); + + msg.dcmd_msg = DCTL_FD_WRITE; + msg.u.dcmd_fd_io.fd = fd; + msg.u.dcmd_fd_io.size = len; + + error = dctl_send_msg(client_fd, &msg); + + if (!error) + error = dctl_send_data(client_fd, src, len); + + if (!error) + error = dctl_read_msg(client_fd, &msg); + + if (error) + return error; + + if (msg.dcmd_msg != DCTL_GEN_REPLY) + return EIO; + + if (msg.u.dcmd_reply.rc != 0) + return msg.u.dcmd_reply.rc; + + /* + * We have to do this because the original upstream code + * does not check if residp == len. + */ + if (msg.u.dcmd_reply.size != len) + return EIO; + + return 0; +} + +/* Handle a new connection */ +static void dctl_handle_conn(int sock_fd) +{ + dctl_cmd_t cmd; + dev_t dev = { 0 }; + int rc; + + client_fd = sock_fd; + + while (dctl_read_msg(sock_fd, &cmd) == 0) { + if (cmd.dcmd_msg != DCTL_IOCTL) { + fprintf(stderr, "%s(): unexpected message type.\n", + __func__); + break; + } + + rc = zfsdev_ioctl(dev, cmd.u.dcmd_ioctl.cmd, + (intptr_t) cmd.u.dcmd_ioctl.arg, 0, NULL, NULL); + + cmd.dcmd_msg = DCTL_IOCTL_REPLY; + cmd.u.dcmd_reply.rc = rc; + + if (dctl_send_msg(sock_fd, &cmd) != 0) + break; + } + close(sock_fd); + + client_fd = -1; +} + +/* Main worker thread loop */ +static void *dctl_thread(void *arg) +{ + wthr_info_t *thr = arg; + struct pollfd fds[1]; + + fds[0].events = POLLIN; + + pthread_mutex_lock(&ctl_sock.dsi_mtx); + + while (!thr->wthr_exit) { + /* Clean-up dead threads */ + dctl_thr_join(); + + /* The file descriptor might change in the thread lifetime */ + fds[0].fd = ctl_sock.dsi_fd; + + /* Poll socket with 1-second timeout */ + int rc = poll(fds, 1, 1000); + if (rc == 0 || (rc == -1 && errno == EINTR)) + continue; + + /* Recheck the exit flag */ + if (thr->wthr_exit) + break; + + if (rc == -1) { + /* Unknown error, let's try to recreate the socket */ + close(ctl_sock.dsi_fd); + ctl_sock.dsi_fd = -1; + + if (dctl_create_socket_common() != 0) + break; + + continue; + } + ASSERT(rc == 1); + + short rev = fds[0].revents; + if (rev == 0) + continue; + ASSERT(rev == POLLIN); + + /* + * At this point there should be a connection ready to be + * accepted. + */ + int client_fd = accept(ctl_sock.dsi_fd, NULL, NULL); + /* Many possible errors here, we'll just retry */ + if (client_fd == -1) + continue; + + /* + * Now lets handle the request. This can take a very + * long time (hours even), so we'll let other threads + * handle new connections. + */ + pthread_mutex_unlock(&ctl_sock.dsi_mtx); + + dctl_thr_rebalance(thr, B_FALSE); + dctl_handle_conn(client_fd); + dctl_thr_rebalance(thr, B_TRUE); + + pthread_mutex_lock(&ctl_sock.dsi_mtx); + } + pthread_mutex_unlock(&ctl_sock.dsi_mtx); + + dctl_thr_die(thr); + + return NULL; +} + +static int dctl_create_socket_common() +{ + dctl_sock_info_t *s = &ctl_sock; + size_t size; + int error; + + ASSERT(s->dsi_fd == -1); + + /* + * Unlink old socket, in case it exists. + * We don't care about errors here. + */ + unlink(s->dsi_path); + + /* Create the socket */ + s->dsi_fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (s->dsi_fd == -1) { + error = errno; + perror("socket"); + return error; + } + + s->dsi_addr.sun_family = AF_UNIX; + + size = sizeof(s->dsi_addr.sun_path) - 1; + strncpy(s->dsi_addr.sun_path, s->dsi_path, size); + + s->dsi_addr.sun_path[size] = '\0'; + + if (bind(s->dsi_fd, (struct sockaddr *) &s->dsi_addr, + sizeof(s->dsi_addr)) != 0) { + error = errno; + perror("bind"); + return error; + } + + if (listen(s->dsi_fd, LISTEN_BACKLOG) != 0) { + error = errno; + perror("listen"); + unlink(s->dsi_path); + return error; + } + + return 0; +} + +static int dctl_create_socket(const char *cfg_dir) +{ + int error; + dctl_sock_info_t *s = &ctl_sock; + + ASSERT(s->dsi_path == NULL); + ASSERT(s->dsi_fd == -1); + + int pathsize = strlen(cfg_dir) + strlen(SOCKNAME) + 2; + if (pathsize > sizeof(s->dsi_addr.sun_path)) + return ENAMETOOLONG; + + s->dsi_path = malloc(pathsize); + if (s->dsi_path == NULL) + return ENOMEM; + + strcpy(s->dsi_path, cfg_dir); + strcat(s->dsi_path, "/" SOCKNAME); + + /* + * For convenience, create the directory in case it doesn't exist. + * We don't care about errors here. + */ + mkdir(cfg_dir, 0770); + + error = dctl_create_socket_common(); + + if (error) { + free(s->dsi_path); + + if (s->dsi_fd != -1) { + close(s->dsi_fd); + s->dsi_fd = -1; + } + } + + return error; +} + +static void dctl_destroy_socket() +{ + dctl_sock_info_t *s = &ctl_sock; + + ASSERT(s->dsi_path != NULL); + ASSERT(s->dsi_fd != -1); + + close(s->dsi_fd); + s->dsi_fd = -1; + + unlink(s->dsi_path); + free(s->dsi_path); +} + +/* + * Initialize the DMU userspace control interface. + * This should be called after kernel_init(). + * + * Note that only very rarely we have more than a couple of simultaneous + * lzfs/lzpool connections. Since the thread pool grows automatically when all + * threads are busy, a good value for min_thr and max_free_thr is 2. + */ +int dctl_server_init(const char *cfg_dir, int min_thr, int max_free_thr) +{ + int error; + + ASSERT(min_thr > 0); + ASSERT(max_free_thr >= min_thr); + + error = zfs_ioctl_init(); + if (error) + return error; + + error = dctl_create_socket(cfg_dir); + if (error) { + (void) zfs_ioctl_fini(); + return error; + } + + error = dctl_thr_pool_create(min_thr, max_free_thr, dctl_thread); + if (error) { + (void) zfs_ioctl_fini(); + dctl_destroy_socket(); + return error; + } + + return 0; +} + +/* + * Terminate control interface. + * This should be called after closing all objsets, but before calling + * kernel_fini(). + * May return EBUSY if the SPA is busy. + * + * Thread pool destruction can take a while due to poll() + * timeout or due to a thread being busy (e.g. a backup is being taken). + */ +int dctl_server_fini() +{ + dctl_thr_pool_stop(); + dctl_destroy_socket(); + + return zfs_ioctl_fini(); +} diff --git a/zfs/lib/libdmu-ctl/dctl_thrpool.c b/zfs/lib/libdmu-ctl/dctl_thrpool.c new file mode 100644 index 0000000..7b2f9b4 --- /dev/null +++ b/zfs/lib/libdmu-ctl/dctl_thrpool.c @@ -0,0 +1,253 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static dctl_thr_info_t thr_pool = { + .dti_mtx = PTHREAD_MUTEX_INITIALIZER +}; + +/* + * Create n threads. + * Callers must acquire thr_pool.dti_mtx first. + */ +static int dctl_thr_create(int n) +{ + dctl_thr_info_t *p = &thr_pool; + int error; + + for (int i = 0; i < n; i++) { + wthr_info_t *thr = malloc(sizeof(wthr_info_t)); + if (thr == NULL) + return ENOMEM; + + thr->wthr_exit = B_FALSE; + thr->wthr_free = B_TRUE; + + error = pthread_create(&thr->wthr_id, NULL, p->dti_thr_func, + thr); + if (error) { + free(thr); + return error; + } + + p->dti_free++; + + list_insert_tail(&p->dti_list, thr); + } + return 0; +} + +/* + * Mark the thread as dead. + * Must be called right before exiting the main thread function. + */ +void dctl_thr_die(wthr_info_t *thr) +{ + dctl_thr_info_t *p = &thr_pool; + + thr->wthr_exit = B_TRUE; + dctl_thr_rebalance(thr, B_FALSE); + + pthread_mutex_lock(&p->dti_mtx); + + list_remove(&p->dti_list, thr); + list_insert_tail(&p->dti_join_list, thr); + + pthread_mutex_unlock(&p->dti_mtx); +} + +/* + * Clean-up dead threads. + */ +void dctl_thr_join() +{ + dctl_thr_info_t *p = &thr_pool; + wthr_info_t *thr; + + pthread_mutex_lock(&p->dti_mtx); + + while ((thr = list_head(&p->dti_join_list))) { + list_remove(&p->dti_join_list, thr); + + ASSERT(!pthread_equal(thr->wthr_id, pthread_self())); + + /* + * This should not block because all the threads + * on this list should have died already. + * + * pthread_join() can only return an error if + * we made a programming mistake. + */ + VERIFY(pthread_join(thr->wthr_id, NULL) == 0); + + ASSERT(thr->wthr_exit); + ASSERT(!thr->wthr_free); + + free(thr); + } + + pthread_mutex_unlock(&p->dti_mtx); +} + +/* + * Adjust the number of free threads in the pool and the thread status. + * + * Callers must acquire thr_pool.dti_mtx first. + */ +static void dctl_thr_adjust_free(wthr_info_t *thr, boolean_t set_free) +{ + dctl_thr_info_t *p = &thr_pool; + + ASSERT(p->dti_free >= 0); + + if (!thr->wthr_free && set_free) + p->dti_free++; + else if (thr->wthr_free && !set_free) + p->dti_free--; + + ASSERT(p->dti_free >= 0); + + thr->wthr_free = set_free; +} + +/* + * Rebalance threads. Also adjusts the free status of the thread. + * Will set the thread exit flag if the number of free threads is above + * the limit. + */ +void dctl_thr_rebalance(wthr_info_t *thr, boolean_t set_free) +{ + dctl_thr_info_t *p = &thr_pool; + + pthread_mutex_lock(&p->dti_mtx); + + if (p->dti_exit || p->dti_free > p->dti_max_free) + thr->wthr_exit = B_TRUE; + + if (thr->wthr_exit) + set_free = B_FALSE; + + dctl_thr_adjust_free(thr, set_free); + + if (!p->dti_exit && p->dti_free == 0) + dctl_thr_create(1); + + pthread_mutex_unlock(&p->dti_mtx); +} + +/* + * Stop the thread pool. + * + * This can take a while since it actually waits for all threads to exit. + */ +void dctl_thr_pool_stop() +{ + dctl_thr_info_t *p = &thr_pool; + wthr_info_t *thr; + struct timespec ts; + + pthread_mutex_lock(&p->dti_mtx); + + ASSERT(!p->dti_exit); + p->dti_exit = B_TRUE; + + /* Let's flag the threads first */ + thr = list_head(&p->dti_list); + while (thr != NULL) { + thr->wthr_exit = B_TRUE; + dctl_thr_adjust_free(thr, B_FALSE); + + thr = list_next(&p->dti_list, thr); + } + + pthread_mutex_unlock(&p->dti_mtx); + + /* Now let's wait for them to exit */ + ts.tv_sec = 0; + ts.tv_nsec = 50000000; /* 50ms */ + do { + nanosleep(&ts, NULL); + + pthread_mutex_lock(&p->dti_mtx); + thr = list_head(&p->dti_list); + pthread_mutex_unlock(&p->dti_mtx); + + dctl_thr_join(); + } while(thr != NULL); + + ASSERT(p->dti_free == 0); + + ASSERT(list_is_empty(&p->dti_list)); + ASSERT(list_is_empty(&p->dti_join_list)); + + list_destroy(&p->dti_list); + list_destroy(&p->dti_join_list); +} + +/* + * Create thread pool. + * + * If at least one thread creation fails, it will stop all previous + * threads and return a non-zero value. + */ +int dctl_thr_pool_create(int min_thr, int max_free_thr, + thr_func_t *thr_func) +{ + int error; + dctl_thr_info_t *p = &thr_pool; + + ASSERT(p->dti_free == 0); + + /* Initialize global variables */ + p->dti_min = min_thr; + p->dti_max_free = max_free_thr; + p->dti_exit = B_FALSE; + p->dti_thr_func = thr_func; + + list_create(&p->dti_list, sizeof(wthr_info_t), offsetof(wthr_info_t, + wthr_node)); + list_create(&p->dti_join_list, sizeof(wthr_info_t), + offsetof(wthr_info_t, wthr_node)); + + pthread_mutex_lock(&p->dti_mtx); + error = dctl_thr_create(min_thr); + pthread_mutex_unlock(&p->dti_mtx); + + if (error) + dctl_thr_pool_stop(); + + return error; +} diff --git a/zfs/lib/libdmu-ctl/dmu_send.c b/zfs/lib/libdmu-ctl/dmu_send.c new file mode 100644 index 0000000..1c72f95 --- /dev/null +++ b/zfs/lib/libdmu-ctl/dmu_send.c @@ -0,0 +1,1249 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL 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 "@(#)dmu_send.c 1.14 08/04/27 SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *dmu_recv_tag = "dmu_recv_tag"; + +struct backuparg { + dmu_replay_record_t *drr; + vnode_t *vp; + offset_t *off; + objset_t *os; + zio_cksum_t zc; + int err; +}; + +static int +dump_bytes(struct backuparg *ba, void *buf, int len) +{ + ssize_t resid; /* have to get resid to get detailed errno */ + ASSERT3U(len % 8, ==, 0); + + fletcher_4_incremental_native(buf, len, &ba->zc); + ba->err = vn_rdwr(UIO_WRITE, ba->vp, + (caddr_t)buf, len, + 0, UIO_SYSSPACE, FAPPEND, RLIM64_INFINITY, CRED(), &resid); + *ba->off += len; + return (ba->err); +} + +static int +dump_free(struct backuparg *ba, uint64_t object, uint64_t offset, + uint64_t length) +{ + /* write a FREE record */ + bzero(ba->drr, sizeof (dmu_replay_record_t)); + ba->drr->drr_type = DRR_FREE; + ba->drr->drr_u.drr_free.drr_object = object; + ba->drr->drr_u.drr_free.drr_offset = offset; + ba->drr->drr_u.drr_free.drr_length = length; + + if (dump_bytes(ba, ba->drr, sizeof (dmu_replay_record_t))) + return (EINTR); + return (0); +} + +static int +dump_data(struct backuparg *ba, dmu_object_type_t type, + uint64_t object, uint64_t offset, int blksz, void *data) +{ + /* write a DATA record */ + bzero(ba->drr, sizeof (dmu_replay_record_t)); + ba->drr->drr_type = DRR_WRITE; + ba->drr->drr_u.drr_write.drr_object = object; + ba->drr->drr_u.drr_write.drr_type = type; + ba->drr->drr_u.drr_write.drr_offset = offset; + ba->drr->drr_u.drr_write.drr_length = blksz; + + if (dump_bytes(ba, ba->drr, sizeof (dmu_replay_record_t))) + return (EINTR); + if (dump_bytes(ba, data, blksz)) + return (EINTR); + return (0); +} + +static int +dump_freeobjects(struct backuparg *ba, uint64_t firstobj, uint64_t numobjs) +{ + /* write a FREEOBJECTS record */ + bzero(ba->drr, sizeof (dmu_replay_record_t)); + ba->drr->drr_type = DRR_FREEOBJECTS; + ba->drr->drr_u.drr_freeobjects.drr_firstobj = firstobj; + ba->drr->drr_u.drr_freeobjects.drr_numobjs = numobjs; + + if (dump_bytes(ba, ba->drr, sizeof (dmu_replay_record_t))) + return (EINTR); + return (0); +} + +static int +dump_dnode(struct backuparg *ba, uint64_t object, dnode_phys_t *dnp) +{ + if (dnp == NULL || dnp->dn_type == DMU_OT_NONE) + return (dump_freeobjects(ba, object, 1)); + + /* write an OBJECT record */ + bzero(ba->drr, sizeof (dmu_replay_record_t)); + ba->drr->drr_type = DRR_OBJECT; + ba->drr->drr_u.drr_object.drr_object = object; + ba->drr->drr_u.drr_object.drr_type = dnp->dn_type; + ba->drr->drr_u.drr_object.drr_bonustype = dnp->dn_bonustype; + ba->drr->drr_u.drr_object.drr_blksz = + dnp->dn_datablkszsec << SPA_MINBLOCKSHIFT; + ba->drr->drr_u.drr_object.drr_bonuslen = dnp->dn_bonuslen; + ba->drr->drr_u.drr_object.drr_checksum = dnp->dn_checksum; + ba->drr->drr_u.drr_object.drr_compress = dnp->dn_compress; + + if (dump_bytes(ba, ba->drr, sizeof (dmu_replay_record_t))) + return (EINTR); + + if (dump_bytes(ba, DN_BONUS(dnp), P2ROUNDUP(dnp->dn_bonuslen, 8))) + return (EINTR); + + /* free anything past the end of the file */ + if (dump_free(ba, object, (dnp->dn_maxblkid + 1) * + (dnp->dn_datablkszsec << SPA_MINBLOCKSHIFT), -1ULL)) + return (EINTR); + if (ba->err) + return (EINTR); + return (0); +} + +#define BP_SPAN(dnp, level) \ + (((uint64_t)dnp->dn_datablkszsec) << (SPA_MINBLOCKSHIFT + \ + (level) * (dnp->dn_indblkshift - SPA_BLKPTRSHIFT))) + +static int +backup_cb(traverse_blk_cache_t *bc, spa_t *spa, void *arg) +{ + struct backuparg *ba = arg; + uint64_t object = bc->bc_bookmark.zb_object; + int level = bc->bc_bookmark.zb_level; + uint64_t blkid = bc->bc_bookmark.zb_blkid; + blkptr_t *bp = bc->bc_blkptr.blk_birth ? &bc->bc_blkptr : NULL; + dmu_object_type_t type = bp ? BP_GET_TYPE(bp) : DMU_OT_NONE; + void *data = bc->bc_data; + int err = 0; + + if (issig(JUSTLOOKING) && issig(FORREAL)) + return (EINTR); + + ASSERT(data || bp == NULL); + + if (bp == NULL && object == 0) { + uint64_t span = BP_SPAN(bc->bc_dnode, level); + uint64_t dnobj = (blkid * span) >> DNODE_SHIFT; + err = dump_freeobjects(ba, dnobj, span >> DNODE_SHIFT); + } else if (bp == NULL) { + uint64_t span = BP_SPAN(bc->bc_dnode, level); + err = dump_free(ba, object, blkid * span, span); + } else if (data && level == 0 && type == DMU_OT_DNODE) { + dnode_phys_t *blk = data; + int i; + int blksz = BP_GET_LSIZE(bp); + + for (i = 0; i < blksz >> DNODE_SHIFT; i++) { + uint64_t dnobj = + (blkid << (DNODE_BLOCK_SHIFT - DNODE_SHIFT)) + i; + err = dump_dnode(ba, dnobj, blk+i); + if (err) + break; + } + } else if (level == 0 && + type != DMU_OT_DNODE && type != DMU_OT_OBJSET) { + int blksz = BP_GET_LSIZE(bp); + if (data == NULL) { + uint32_t aflags = ARC_WAIT; + arc_buf_t *abuf; + zbookmark_t zb; + + zb.zb_objset = ba->os->os->os_dsl_dataset->ds_object; + 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); + + if (abuf) { + err = dump_data(ba, type, object, blkid * blksz, + blksz, abuf->b_data); + (void) arc_buf_remove_ref(abuf, &abuf); + } + } else { + err = dump_data(ba, type, object, blkid * blksz, + blksz, data); + } + } + + ASSERT(err == 0 || err == EINTR); + return (err); +} + +int +dmu_sendbackup(objset_t *tosnap, objset_t *fromsnap, boolean_t fromorigin, + vnode_t *vp, 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) + return (EINVAL); + + /* 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)) + return (EXDEV); + + if (fromorigin) { + if (fromsnap) + return (EINVAL); + + if (ds->ds_dir->dd_phys->dd_origin_obj != NULL) { + dsl_pool_t *dp = ds->ds_dir->dd_pool; + rw_enter(&dp->dp_config_rwlock, RW_READER); + err = dsl_dataset_open_obj(dp, + ds->ds_dir->dd_phys->dd_origin_obj, NULL, + DS_MODE_NONE, 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_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_close(fromds, DS_MODE_NONE, FTAG); + + ba.drr = drr; + ba.vp = vp; + 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))) { + kmem_free(drr, sizeof (dmu_replay_record_t)); + return (ba.err); + } + + err = traverse_dsl_dataset(ds, fromtxg, + ADVANCE_PRE | ADVANCE_HOLES | ADVANCE_DATA | ADVANCE_NOLOCK, + backup_cb, &ba); + + if (err) { + if (err == EINTR && ba.err) + err = ba.err; + kmem_free(drr, sizeof (dmu_replay_record_t)); + return (err); + } + + bzero(drr, sizeof (dmu_replay_record_t)); + drr->drr_type = DRR_END; + drr->drr_u.drr_end.drr_checksum = ba.zc; + + if (dump_bytes(&ba, drr, sizeof (dmu_replay_record_t))) { + kmem_free(drr, sizeof (dmu_replay_record_t)); + return (ba.err); + } + + kmem_free(drr, sizeof (dmu_replay_record_t)); + + return (0); +} + +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; + + VERIFY(0 == dsl_dataset_open_obj(dp, dsobj, NULL, + DS_MODE_EXCLUSIVE, 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, + ds->ds_dir->dd_pool->dp_spa, tx, cr, "dataset = %lld", + ds->ds_phys->ds_dir_obj); + + return (ds); +} + +/* ARGSUSED */ +static int +recv_full_check(void *arg1, void *arg2, dmu_tx_t *tx) +{ + dsl_dir_t *dd = arg1; + struct recvbeginsyncarg *rbsa = arg2; + objset_t *mos = dd->dd_pool->dp_meta_objset; + uint64_t val; + int err; + + err = zap_lookup(mos, dd->dd_phys->dd_child_dir_zapobj, + strrchr(rbsa->tofs, '/') + 1, sizeof (uint64_t), 1, &val); + + if (err != ENOENT) + return (err ? err : 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); + } + + 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 dsobj; + uint64_t flags = DS_FLAG_INCONSISTENT; + + flags |= rbsa->dsflags; + + 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 (ds->ds_prev != NULL) + 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); +} + +static void +recv_full_existing_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx) +{ + dsl_dataset_t *ds = arg1; + struct recvbeginsyncarg *rbsa = arg2; + dsl_dir_t *dd = ds->ds_dir; + uint64_t dsobj; + uint64_t flags = DS_FLAG_INCONSISTENT; + + flags |= rbsa->dsflags; + + /* + * 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_impl(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 +recv_incremental_check(void *arg1, void *arg2, dmu_tx_t *tx) +{ + dsl_dataset_t *ds = arg1; + struct recvbeginsyncarg *rbsa = arg2; + int err; + uint64_t val; + + /* 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); + + /* 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 +recv_online_incremental_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx) +{ + 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 dsobj; + uint64_t flags = DS_FLAG_INCONSISTENT; + + flags |= rbsa->dsflags; + + /* create the temporary clone */ + VERIFY(0 == dsl_dataset_open_obj(dp, ohds->ds_phys->ds_prev_snap_obj, + NULL, DS_MODE_STANDARD, FTAG, &ods)); + dsobj = dsl_dataset_create_sync(ohds->ds_dir, + rbsa->clonelastname, ods, flags, cr, tx); + dsl_dataset_close(ods, DS_MODE_STANDARD, FTAG); + + /* open the temporary clone */ + VERIFY(0 == dsl_dataset_open_obj(dp, dsobj, NULL, + DS_MODE_EXCLUSIVE, dmu_recv_tag, &cds)); + + /* 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); + + rbsa->ds = cds; + + spa_history_internal_log(LOG_DS_REPLAY_INC_SYNC, + dp->dp_spa, tx, cr, "dataset = %lld", + cds->ds_phys->ds_dir_obj); +} + +/* 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; + + spa_history_internal_log(LOG_DS_REPLAY_INC_SYNC, + ds->ds_dir->dd_pool->dp_spa, tx, cr, "dataset = %lld", + ds->ds_phys->ds_dir_obj); +} + +/* + * 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) +{ + int err = 0; + boolean_t byteswap; + struct recvbeginsyncarg rbsa; + uint64_t version; + int flags; + dsl_dataset_t *ds; + + if (drrb->drr_magic == DMU_BACKUP_MAGIC) + byteswap = FALSE; + else if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) + byteswap = TRUE; + else + return (EINVAL); + + 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); + + if (flags & DRR_FLAG_CI_DATA) + rbsa.dsflags = DS_FLAG_CI_DATASET; + + bzero(drc, sizeof (dmu_recv_cookie_t)); + drc->drc_drrb = drrb; + drc->drc_tosnap = tosnap; + drc->drc_force = force; + + /* + * Process the begin in syncing context. + */ + if (rbsa.fromguid && !(flags & DRR_FLAG_CLONE) && !online) { + /* offline incremental receive */ + err = dsl_dataset_open(tofs, + DS_MODE_EXCLUSIVE, dmu_recv_tag, &ds); + if (err) + return (err); + + /* + * 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_close(ds, DS_MODE_EXCLUSIVE, + 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_close(ds, DS_MODE_EXCLUSIVE, 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 */ + + /* tmp clone name is: tofs/%tosnap" */ + (void) snprintf(rbsa.clonelastname, sizeof (rbsa.clonelastname), + "%%%s", tosnap); + + /* open the dataset we are logically receiving into */ + err = dsl_dataset_open(tofs, + DS_MODE_STANDARD, dmu_recv_tag, &ds); + if (err) + return (err); + + 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_close(ds, DS_MODE_STANDARD, 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_open_obj(dd->dd_pool, + dd->dd_phys->dd_head_dataset_obj, NULL, + DS_MODE_EXCLUSIVE | DS_MODE_INCONSISTENT, + FTAG, &ds); + rw_exit(&dd->dd_pool->dp_config_rwlock); + if (err) { + dsl_dir_close(dd, FTAG); + return (err); + } + + err = dsl_sync_task_do(dd->dd_pool, + recv_full_existing_check, + recv_full_existing_sync, ds, &rbsa, 5); + /* if successful, sync task closes the ds for us */ + if (err) + dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG); + } else { + err = dsl_sync_task_do(dd->dd_pool, recv_full_check, + recv_full_sync, dd, &rbsa, 5); + if (err) + return (err); + } + dsl_dir_close(dd, FTAG); + if (err) + return (err); + drc->drc_logical_ds = drc->drc_real_ds = rbsa.ds; + drc->drc_newfs = B_TRUE; + } + + /* downgrade our hold on the ds from EXCLUSIVE to PRIMARY */ + dsl_dataset_downgrade(drc->drc_real_ds, + DS_MODE_EXCLUSIVE, DS_MODE_PRIMARY); + + return (0); +} + +struct restorearg { + int err; + int byteswap; + vnode_t *vp; + char *buf; + uint64_t voff; + int bufsize; /* amount of memory allocated for buf */ + zio_cksum_t cksum; +}; + +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 (done < len) { + ssize_t resid; + + ra->err = vn_rdwr(UIO_READ, ra->vp, + (caddr_t)ra->buf + done, len - done, + ra->voff, UIO_SYSSPACE, FAPPEND, + RLIM64_INFINITY, CRED(), &resid); + + if (resid == len - done) + ra->err = EINVAL; + ra->voff += len - done - resid; + done = len - resid; + if (ra->err) + return (NULL); + } + + ASSERT3U(done, ==, len); + rv = ra->buf; + if (ra->byteswap) + fletcher_4_incremental_byteswap(rv, len, &ra->cksum); + else + fletcher_4_incremental_native(rv, len, &ra->cksum); + return (rv); +} + +static void +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; + case DRR_OBJECT: + DO64(drr_object.drr_object); + /* DO64(drr_object.drr_allocation_txg); */ + DO32(drr_object.drr_type); + DO32(drr_object.drr_bonustype); + DO32(drr_object.drr_blksz); + DO32(drr_object.drr_bonuslen); + break; + case DRR_FREEOBJECTS: + DO64(drr_freeobjects.drr_firstobj); + DO64(drr_freeobjects.drr_numobjs); + break; + case DRR_WRITE: + DO64(drr_write.drr_object); + DO32(drr_write.drr_type); + DO64(drr_write.drr_offset); + DO64(drr_write.drr_length); + break; + case DRR_FREE: + DO64(drr_free.drr_object); + DO64(drr_free.drr_offset); + DO64(drr_free.drr_length); + break; + case DRR_END: + DO64(drr_end.drr_checksum.zc_word[0]); + DO64(drr_end.drr_checksum.zc_word[1]); + DO64(drr_end.drr_checksum.zc_word[2]); + DO64(drr_end.drr_checksum.zc_word[3]); + break; + } +#undef DO64 +#undef DO32 +} + +static int +restore_object(struct restorearg *ra, objset_t *os, struct drr_object *drro) +{ + int err; + dmu_tx_t *tx; + + err = dmu_object_info(os, drro->drr_object, NULL); + + if (err != 0 && err != ENOENT) + return (EINVAL); + + if (drro->drr_type == DMU_OT_NONE || + drro->drr_type >= DMU_OT_NUMTYPES || + drro->drr_bonustype >= DMU_OT_NUMTYPES || + drro->drr_checksum >= ZIO_CHECKSUM_FUNCTIONS || + drro->drr_compress >= ZIO_COMPRESS_FUNCTIONS || + P2PHASE(drro->drr_blksz, SPA_MINBLOCKSIZE) || + drro->drr_blksz < SPA_MINBLOCKSIZE || + drro->drr_blksz > SPA_MAXBLOCKSIZE || + drro->drr_bonuslen > DN_MAX_BONUSLEN) { + return (EINVAL); + } + + tx = dmu_tx_create(os); + + if (err == ENOENT) { + /* currently free, want to be allocated */ + dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT); + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, 1); + err = dmu_tx_assign(tx, TXG_WAIT); + if (err) { + dmu_tx_abort(tx); + return (err); + } + err = dmu_object_claim(os, drro->drr_object, + drro->drr_type, drro->drr_blksz, + drro->drr_bonustype, drro->drr_bonuslen, tx); + } else { + /* currently allocated, want to be allocated */ + dmu_tx_hold_bonus(tx, drro->drr_object); + /* + * We may change blocksize, so need to + * hold_write + */ + dmu_tx_hold_write(tx, drro->drr_object, 0, 1); + err = dmu_tx_assign(tx, TXG_WAIT); + if (err) { + dmu_tx_abort(tx); + return (err); + } + + err = dmu_object_reclaim(os, drro->drr_object, + drro->drr_type, drro->drr_blksz, + drro->drr_bonustype, drro->drr_bonuslen, tx); + } + if (err) { + dmu_tx_commit(tx); + return (EINVAL); + } + + dmu_object_set_checksum(os, drro->drr_object, drro->drr_checksum, tx); + dmu_object_set_compress(os, drro->drr_object, drro->drr_compress, tx); + + if (drro->drr_bonuslen) { + dmu_buf_t *db; + void *data; + 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(drro->drr_bonuslen, 8)); + if (data == NULL) { + dmu_tx_commit(tx); + return (ra->err); + } + bcopy(data, db->db_data, drro->drr_bonuslen); + if (ra->byteswap) { + dmu_ot[drro->drr_bonustype].ot_byteswap(db->db_data, + drro->drr_bonuslen); + } + dmu_buf_rele(db, FTAG); + } + dmu_tx_commit(tx); + return (0); +} + +/* ARGSUSED */ +static int +restore_freeobjects(struct restorearg *ra, objset_t *os, + struct drr_freeobjects *drrfo) +{ + uint64_t obj; + + if (drrfo->drr_firstobj + drrfo->drr_numobjs < drrfo->drr_firstobj) + return (EINVAL); + + 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); + return (err); + } + err = dmu_object_free(os, obj, tx); + dmu_tx_commit(tx); + if (err && err != ENOENT) + return (EINVAL); + } + return (0); +} + +static int +restore_write(struct restorearg *ra, objset_t *os, + struct drr_write *drrw) +{ + dmu_tx_t *tx; + void *data; + int err; + + if (drrw->drr_offset + drrw->drr_length < drrw->drr_offset || + drrw->drr_type >= DMU_OT_NUMTYPES) + return (EINVAL); + + data = restore_read(ra, drrw->drr_length); + if (data == NULL) + return (ra->err); + + if (dmu_object_info(os, drrw->drr_object, NULL) != 0) + return (EINVAL); + + tx = dmu_tx_create(os); + + dmu_tx_hold_write(tx, drrw->drr_object, + drrw->drr_offset, drrw->drr_length); + err = dmu_tx_assign(tx, TXG_WAIT); + if (err) { + dmu_tx_abort(tx); + return (err); + } + if (ra->byteswap) + dmu_ot[drrw->drr_type].ot_byteswap(data, drrw->drr_length); + dmu_write(os, drrw->drr_object, + drrw->drr_offset, drrw->drr_length, data, tx); + dmu_tx_commit(tx); + return (0); +} + +/* ARGSUSED */ +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 && + drrf->drr_offset + drrf->drr_length < drrf->drr_offset) + return (EINVAL); + + 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, + 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_close(drc->drc_logical_ds, + DS_MODE_STANDARD, dmu_recv_tag); + } + } else { + /* + * offline incremental: rollback to most recent snapshot. + */ + int lmode = DS_MODE_PRIMARY; + if (dsl_dataset_tryupgrade(drc->drc_real_ds, + DS_MODE_PRIMARY, DS_MODE_EXCLUSIVE)) { + lmode = DS_MODE_EXCLUSIVE; + (void) dsl_dataset_rollback(drc->drc_real_ds, + DMU_OST_NONE); + } + dsl_dataset_close(drc->drc_real_ds, lmode, FTAG); + } +} + +/* + * NB: callers *must* call dmu_recv_end() if this succeeds. + */ +int +dmu_recv_stream(dmu_recv_cookie_t *drc, vnode_t *vp, offset_t *voffp) +{ + struct restorearg ra = { 0 }; + dmu_replay_record_t *drr; + objset_t *os; + zio_cksum_t pcksum; + + if (drc->drc_drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) + ra.byteswap = TRUE; + + { + /* 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)); + } + + 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); + drrb->drr_type = BSWAP_32(drrb->drr_type); + drrb->drr_toguid = BSWAP_64(drrb->drr_toguid); + drrb->drr_fromguid = BSWAP_64(drrb->drr_fromguid); + } + + ra.vp = vp; + ra.voff = *voffp; + ra.bufsize = 1<<20; + ra.buf = kmem_alloc(ra.bufsize, KM_SLEEP); + + /* 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); + + ASSERT(drc->drc_real_ds->ds_phys->ds_flags & DS_FLAG_INCONSISTENT); + + /* + * Read records and process them. + */ + pcksum = ra.cksum; + while (ra.err == 0 && + NULL != (drr = restore_read(&ra, sizeof (*drr)))) { + if (issig(JUSTLOOKING) && issig(FORREAL)) { + ra.err = EINTR; + goto out; + } + + if (ra.byteswap) + backup_byteswap(drr); + + switch (drr->drr_type) { + case DRR_OBJECT: + { + /* + * We need to make a copy of the record header, + * because restore_{object,write} may need to + * restore_read(), which will invalidate drr. + */ + struct drr_object drro = drr->drr_u.drr_object; + ra.err = restore_object(&ra, os, &drro); + break; + } + case DRR_FREEOBJECTS: + { + struct drr_freeobjects drrfo = + drr->drr_u.drr_freeobjects; + ra.err = restore_freeobjects(&ra, os, &drrfo); + break; + } + case DRR_WRITE: + { + struct drr_write drrw = drr->drr_u.drr_write; + ra.err = restore_write(&ra, os, &drrw); + break; + } + case DRR_FREE: + { + struct drr_free drrf = drr->drr_u.drr_free; + ra.err = restore_free(&ra, os, &drrf); + break; + } + case DRR_END: + { + struct drr_end drre = drr->drr_u.drr_end; + /* + * We compare against the *previous* checksum + * value, because the stored checksum is of + * everything before the DRR_END record. + */ + if (!ZIO_CHECKSUM_EQUAL(drre.drr_checksum, pcksum)) + ra.err = ECKSUM; + goto out; + } + default: + ra.err = EINVAL; + goto out; + } + pcksum = ra.cksum; + } + ASSERT(ra.err != 0); + +out: + dmu_objset_close(os); + + if (ra.err != 0) { + /* + * rollback or destroy what we created, so we don't + * leave it in the restoring state. + */ + txg_wait_synced(drc->drc_real_ds->ds_dir->dd_pool, 0); + dmu_recv_abort_cleanup(drc); + } + + kmem_free(ra.buf, ra.bufsize); + *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) +{ + int err = 0; + int lmode; + + /* + * 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(drc->drc_real_ds->ds_dir->dd_pool, 0); + + if (dsl_dataset_tryupgrade(drc->drc_real_ds, + DS_MODE_PRIMARY, DS_MODE_EXCLUSIVE)) { + lmode = DS_MODE_EXCLUSIVE; + } else { + dmu_recv_abort_cleanup(drc); + return (EBUSY); + } + + if (drc->drc_logical_ds != drc->drc_real_ds) { + if (err == 0 && dsl_dataset_tryupgrade(drc->drc_logical_ds, + DS_MODE_STANDARD, DS_MODE_EXCLUSIVE)) { + lmode = DS_MODE_EXCLUSIVE; + err = dsl_dataset_clone_swap(drc->drc_real_ds, + drc->drc_logical_ds, drc->drc_force); + } else { + lmode = DS_MODE_STANDARD; + err = EBUSY; + } + } + + if (err == 0) { + struct recvendsyncarg resa; + + 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(drc->drc_real_ds->ds_dir->dd_pool, + recv_end_check, recv_end_sync, + drc->drc_logical_ds, &resa, 3); + if (err) { + if (drc->drc_newfs) { + ASSERT(drc->drc_logical_ds == drc->drc_real_ds); + (void) dsl_dataset_destroy(drc->drc_real_ds, + dmu_recv_tag); + return (err); + } else { + (void) dsl_dataset_rollback(drc->drc_logical_ds, + DMU_OST_NONE); + } + } + } + + if (drc->drc_logical_ds != drc->drc_real_ds) { + /* dsl_dataset_destroy() will close the ds */ + (void) dsl_dataset_destroy(drc->drc_real_ds, dmu_recv_tag); + } + /* close the hold from dmu_recv_begin */ + dsl_dataset_close(drc->drc_logical_ds, lmode, dmu_recv_tag); + return (err); +} diff --git a/zfs/lib/libdmu-ctl/include/Makefile.in b/zfs/lib/libdmu-ctl/include/Makefile.in new file mode 100644 index 0000000..6611e41 --- /dev/null +++ b/zfs/lib/libdmu-ctl/include/Makefile.in @@ -0,0 +1 @@ +subdir-m += sys diff --git a/zfs/lib/libdmu-ctl/include/sys/Makefile.in b/zfs/lib/libdmu-ctl/include/sys/Makefile.in new file mode 100644 index 0000000..8f97ae6 --- /dev/null +++ b/zfs/lib/libdmu-ctl/include/sys/Makefile.in @@ -0,0 +1 @@ +DISTFILES = dmu_ctl.h dmu_ctl_impl.h diff --git a/zfs/lib/libdmu-ctl/include/sys/dmu_ctl.h b/zfs/lib/libdmu-ctl/include/sys/dmu_ctl.h new file mode 100644 index 0000000..c2044ba --- /dev/null +++ b/zfs/lib/libdmu-ctl/include/sys/dmu_ctl.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, 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_DMU_CTL_H +#define _SYS_DMU_CTL_H + +#include + +/* Default directory where the clients search for sockets to connect */ +#define DMU_CTL_DEFAULT_DIR "/var/run/zfs/udmu" + +/* + * These functions are called by the server process. + * + * kernel_init() must be called before dctl_server_init(). + * kernel_fini() must not be called before dctl_server_fini(). + * + * All objsets must be closed and object references be released before calling + * dctl_server_fini(), otherwise it will return EBUSY. + * + * Note: On Solaris, it is highly recommended to either catch or ignore the + * SIGPIPE signal, otherwise the server process will die if the client is + * killed. + */ +int dctl_server_init(const char *cfg_dir, int min_threads, + int max_free_threads); +int dctl_server_fini(); + +/* + * The following functions are called by the DMU from the server process context + * (in the worker threads). + */ +int dctls_copyin(const void *src, void *dest, size_t size); +int dctls_copyinstr(const char *from, char *to, size_t max, + size_t *len); +int dctls_copyout(const void *src, void *dest, size_t size); +int dctls_fd_read(int fd, void *buf, ssize_t len, ssize_t *residp); +int dctls_fd_write(int fd, const void *src, ssize_t len); + +/* + * These functions are called by the client process (libzfs). + */ +int dctlc_connect(const char *dir, boolean_t check_subdirs); +void dctlc_disconnect(int fd); + +int dctlc_ioctl(int fd, int32_t request, void *arg); + +#endif diff --git a/zfs/lib/libdmu-ctl/include/sys/dmu_ctl_impl.h b/zfs/lib/libdmu-ctl/include/sys/dmu_ctl_impl.h new file mode 100644 index 0000000..6b4a564 --- /dev/null +++ b/zfs/lib/libdmu-ctl/include/sys/dmu_ctl_impl.h @@ -0,0 +1,144 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_DMU_CTL_IMPL_H +#define _SYS_DMU_CTL_IMPL_H + +#include +#include +#include +#include +#include + +#define SOCKNAME "dmu_socket" + +#define DCTL_PROTOCOL_VER 1 +#define DCTL_MAGIC 0xdc71b1070c01dc71ll + +/* Message types */ +enum { + DCTL_IOCTL, + DCTL_IOCTL_REPLY, + DCTL_COPYIN, + DCTL_COPYINSTR, + DCTL_COPYOUT, + DCTL_FD_READ, + DCTL_FD_WRITE, + DCTL_GEN_REPLY /* generic reply */ +}; + +/* On-the-wire message */ +typedef struct dctl_cmd { + uint64_t dcmd_magic; + int8_t dcmd_version; + int8_t dcmd_msg; + uint8_t dcmd_pad[6]; + union { + struct dcmd_ioctl { + uint64_t arg; + int32_t cmd; + uint8_t pad[4]; + } dcmd_ioctl; + + struct dcmd_copy_req { + uint64_t ptr; + uint64_t size; + } dcmd_copy; + + struct dcmd_fd_req { + int64_t size; + int32_t fd; + uint8_t pad[4]; + } dcmd_fd_io; + + struct dcmd_reply { + uint64_t size; /* used by reply to DCTL_COPYINSTR, + DCTL_FD_READ and DCTL_FD_WRITE */ + int32_t rc; /* return code */ + uint8_t pad[4]; + } dcmd_reply; + } u; +} dctl_cmd_t; + +#define DCTL_CMD_HEADER_SIZE (sizeof(uint64_t) + sizeof(uint8_t)) + +/* + * The following definitions are only used by the server code. + */ + +#define LISTEN_BACKLOG 5 + +/* Worker thread data */ +typedef struct wthr_info { + list_node_t wthr_node; + pthread_t wthr_id; + boolean_t wthr_exit; /* termination flag */ + boolean_t wthr_free; +} wthr_info_t; + +/* Control socket data */ +typedef struct dctl_sock_info { + pthread_mutex_t dsi_mtx; + char *dsi_path; + struct sockaddr_un dsi_addr; + int dsi_fd; +} dctl_sock_info_t; + +typedef void *thr_func_t(void *); + +/* Thread pool data */ +typedef struct dctl_thr_info { + thr_func_t *dti_thr_func; + + pthread_mutex_t dti_mtx; /* protects the thread lists and dti_free */ + list_t dti_list; /* list of threads in the thread pool */ + list_t dti_join_list; /* list of threads that are waiting to be + joined */ + int dti_free; /* number of free worker threads */ + + int dti_min; + int dti_max_free; + + boolean_t dti_exit; /* global termination flag */ +} dctl_thr_info_t; + +/* Messaging functions functions */ +int dctl_read_msg(int fd, dctl_cmd_t *cmd); +int dctl_send_msg(int fd, dctl_cmd_t *cmd); + +int dctl_read_data(int fd, void *ptr, size_t size); +int dctl_send_data(int fd, const void *ptr, size_t size); + +/* Thread pool functions */ +int dctl_thr_pool_create(int min_thr, int max_free_thr, + thr_func_t *thr_func); +void dctl_thr_pool_stop(); + +void dctl_thr_join(); +void dctl_thr_die(wthr_info_t *thr); +void dctl_thr_rebalance(wthr_info_t *thr, boolean_t set_free); + +#endif diff --git a/zfs/lib/libdmu-ctl/rrwlock.c b/zfs/lib/libdmu-ctl/rrwlock.c new file mode 100644 index 0000000..c46ed81 --- /dev/null +++ b/zfs/lib/libdmu-ctl/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 "@(#)rrwlock.c 1.1 07/10/24 SMI" + +#include +#include + +/* + * 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 (NULL); + + 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/zfs/lib/libdmu-ctl/zfs_acl.c b/zfs/lib/libdmu-ctl/zfs_acl.c new file mode 100644 index 0000000..cc2f97e --- /dev/null +++ b/zfs/lib/libdmu-ctl/zfs_acl.c @@ -0,0 +1,2641 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL 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 "@(#)zfs_acl.c 1.25 08/04/08 SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fs/fs_subr.h" +#include + +#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 OWNING_GROUP (ACE_GROUP|ACE_IDENTIFIER_GROUP) +#define EVERYONE_ALLOW_MASK (ACE_READ_ACL|ACE_READ_ATTRIBUTES | \ + ACE_READ_NAMED_ATTRS|ACE_SYNCHRONIZE) +#define EVERYONE_DENY_MASK (ACE_WRITE_ACL|ACE_WRITE_OWNER | \ + 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_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) + +#define OGE_CLEAR (ACE_READ_DATA|ACE_LIST_DIRECTORY|ACE_WRITE_DATA| \ + ACE_ADD_FILE|ACE_APPEND_DATA|ACE_ADD_SUBDIRECTORY|ACE_EXECUTE) + +#define OKAY_MASK_BITS (ACE_READ_DATA|ACE_LIST_DIRECTORY|ACE_WRITE_DATA| \ + 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_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; +} + +static void +zfs_ace_v0_set_flags(void *acep, uint16_t flags) +{ + ((zfs_oldace_t *)acep)->z_flags = flags; +} + +static void +zfs_ace_v0_set_mask(void *acep, uint32_t mask) +{ + ((zfs_oldace_t *)acep)->z_access_mask = mask; +} + +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 == (ACE_GROUP | ACE_IDENTIFIER_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 vers) +{ + zfs_acl_t *aclp; + + aclp = kmem_zalloc(sizeof (zfs_acl_t), KM_SLEEP); + 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) +{ + zfs_acl_release_nodes(aclp); + list_destroy(&aclp->z_acl); + kmem_free(aclp, sizeof (zfs_acl_t)); +} + +static boolean_t +zfs_ace_valid(vtype_t obj_type, zfs_acl_t *aclp, uint16_t type, uint16_t iflags) +{ + /* + * first check type of entry + */ + + switch (iflags & ACE_TYPE_FLAGS) { + case ACE_OWNER: + case (ACE_IDENTIFIER_GROUP | ACE_GROUP): + case ACE_IDENTIFIER_GROUP: + case ACE_EVERYONE: + case 0: /* User entry */ + break; + default: + return (B_FALSE); + + } + + /* + * next check inheritance level flags + */ + + if (type != ALLOW && type > MAX_ACE_TYPE) { + 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; + } + + /* + * Only directories should have inheritance flags. + */ + if (obj_type != VDIR && (iflags & + (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE| + ACE_INHERIT_ONLY_ACE|ACE_NO_PROPAGATE_INHERIT_ACE))) { + return (B_FALSE); + } + + if (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; + *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 + + aclp->z_ops.ace_size(acep); + 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 != (ACE_GROUP | ACE_IDENTIFIER_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; + +} + +/* + * Convert unix access mask to v4 access mask + */ +static uint32_t +zfs_unix_to_v4(uint32_t access_mask) +{ + uint32_t new_mask = 0; + + 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(zfs_acl_t *aclp, void *acep, uint32_t access_mask, + uint16_t access_type, uint64_t fuid, uint16_t 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 != (ACE_GROUP | ACE_IDENTIFIER_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_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)) { + + /* + * Skip over inherit only ACEs + */ + if (iflags & ACE_INHERIT_ONLY_ACE) + continue; + + entry_type = (iflags & ACE_TYPE_FLAGS); + + if (entry_type == ACE_OWNER) { + if ((access_mask & ACE_READ_DATA) && + (!(seen & S_IRUSR))) { + seen |= S_IRUSR; + if (type == ALLOW) { + mode |= S_IRUSR; + } + } + if ((access_mask & ACE_WRITE_DATA) && + (!(seen & S_IWUSR))) { + seen |= S_IWUSR; + if (type == ALLOW) { + mode |= S_IWUSR; + } + } + if ((access_mask & ACE_EXECUTE) && + (!(seen & S_IXUSR))) { + seen |= S_IXUSR; + if (type == ALLOW) { + mode |= S_IXUSR; + } + } + } else if (entry_type == OWNING_GROUP) { + if ((access_mask & ACE_READ_DATA) && + (!(seen & S_IRGRP))) { + seen |= S_IRGRP; + if (type == ALLOW) { + mode |= S_IRGRP; + } + } + if ((access_mask & ACE_WRITE_DATA) && + (!(seen & S_IWGRP))) { + seen |= S_IWGRP; + if (type == ALLOW) { + mode |= S_IWGRP; + } + } + if ((access_mask & ACE_EXECUTE) && + (!(seen & S_IXGRP))) { + seen |= S_IXGRP; + if (type == ALLOW) { + mode |= S_IXGRP; + } + } + } else if (entry_type == ACE_EVERYONE) { + if ((access_mask & ACE_READ_DATA)) { + if (!(seen & S_IRUSR)) { + seen |= S_IRUSR; + if (type == ALLOW) { + mode |= S_IRUSR; + } + } + if (!(seen & S_IRGRP)) { + seen |= S_IRGRP; + if (type == ALLOW) { + mode |= S_IRGRP; + } + } + if (!(seen & S_IROTH)) { + seen |= S_IROTH; + if (type == ALLOW) { + mode |= S_IROTH; + } + } + } + if ((access_mask & ACE_WRITE_DATA)) { + if (!(seen & S_IWUSR)) { + seen |= S_IWUSR; + if (type == ALLOW) { + mode |= S_IWUSR; + } + } + if (!(seen & S_IWGRP)) { + seen |= S_IWGRP; + if (type == ALLOW) { + mode |= S_IWGRP; + } + } + if (!(seen & S_IWOTH)) { + seen |= S_IWOTH; + if (type == ALLOW) { + mode |= S_IWOTH; + } + } + } + if ((access_mask & ACE_EXECUTE)) { + if (!(seen & S_IXUSR)) { + seen |= S_IXUSR; + if (type == ALLOW) { + mode |= S_IXUSR; + } + } + if (!(seen & S_IXGRP)) { + seen |= S_IXGRP; + if (type == ALLOW) { + mode |= S_IXGRP; + } + } + if (!(seen & S_IXOTH)) { + seen |= S_IXOTH; + 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, boolean_t will_modify) +{ + zfs_acl_t *aclp; + zfs_acl_node_t *aclnode; + + 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); +} + +/* + * Read an external acl object. + */ +static int +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, will_modify); + return (0); + } + + 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, + 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); + return (error); + } + + *aclpp = aclp; + return (0); +} + +/* + * 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, cred_t *cr, + zfs_fuid_info_t **fuidp, dmu_tx_t *tx) +{ + int error; + znode_phys_t *zphys = zp->z_phys; + 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)); + + dmu_buf_will_dirty(zp->z_dbuf, tx); + + zphys->zp_mode = zfs_mode_fuid_compute(zp, aclp, cr, fuidp, tx); + + /* + * Decide which opbject type to use. If we are forced to + * use old ACL format than transform ACL into zfs_oldace_t + * layout. + */ + 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, + 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, + aclp->z_acl_bytes, 0, tx); + } + zphys->zp_acl.z_acl_extern_obj = aoid; + 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); + if (error) + return (error); + zphys->zp_acl.z_acl_extern_obj = 0; + } + + 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; + } + } + + /* + * 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_acl.z_acl_version = aclp->z_version; + + /* + * Replace ACL wide bits, but first clear them. + */ + zp->z_phys->zp_flags &= ~ZFS_ACL_WIDE_FLAGS; + + zp->z_phys->zp_flags |= aclp->z_hints; + + if (ace_trivial_common(aclp, 0, zfs_ace_walk) == 0) + zp->z_phys->zp_flags |= ZFS_ACL_TRIVIAL; + + zfs_time_stamper_locked(zp, STATE_CHANGED, tx); + return (0); +} + +/* + * Update access mask for prepended ACE + * + * This applies the "groupmask" value for aclmode property. + */ +static void +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; + + 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 && (fuid == owner)) { + rmask = S_IRUSR; + wmask = S_IWUSR; + xmask = S_IXUSR; + } else { + rmask = S_IRGRP; + wmask = S_IWGRP; + xmask = S_IXGRP; + } + + if (origmask & ACE_READ_DATA) { + if (mode & rmask) { + acepmask &= ~ACE_READ_DATA; + } else { + acepmask |= ACE_READ_DATA; + } + } + + if (origmask & ACE_WRITE_DATA) { + if (mode & wmask) { + acepmask &= ~ACE_WRITE_DATA; + } else { + acepmask |= ACE_WRITE_DATA; + } + } + + if (origmask & ACE_APPEND_DATA) { + if (mode & wmask) { + acepmask &= ~ACE_APPEND_DATA; + } else { + acepmask |= ACE_APPEND_DATA; + } + } + + if (origmask & ACE_EXECUTE) { + if (mode & xmask) { + acepmask &= ~ACE_EXECUTE; + } else { + acepmask |= ACE_EXECUTE; + } + } + aclp->z_ops.ace_mask_set(acep, acepmask); +} + +/* + * Apply mode to canonical six ACEs. + */ +static void +zfs_acl_fixup_canonical_six(zfs_acl_t *aclp, mode_t mode) +{ + 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); + + acep = (void *)((caddr_t)aclnode->z_acldata + + aclnode->z_size - (abstract_size * 6)); + + /* + * Fixup final ACEs to match the mode + */ + + 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(zfs_acl_t *aclp, void *acep, int allow_deny, + int entry_type, int accessmask) +{ + 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(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 (prevacep == NULL) + return (B_FALSE); + + 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 (prevflags != (flags & ACE_IDENTIFIER_GROUP)) + return (B_FALSE); + + okay_masks = (mask & OKAY_MASK_BITS); + + if (prevmask & ~okay_masks) + return (B_FALSE); + + return (B_TRUE); +} + + +/* + * 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 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; + } + + 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, void *acep, + mode_t mode) +{ + 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); +} + +/* + * Split an inherited ACE into inherit_only ACE + * and original ACE with inheritance flags stripped off. + */ +static void +zfs_acl_split_ace(zfs_acl_t *aclp, zfs_ace_hdr_t *acep) +{ + 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) +{ + 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(); + + 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(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(aclp, (caddr_t)acep + (abstract_size * i++), + ALLOW, ACE_EVERYONE, EVERYONE_ALLOW_MASK))) { + return (1); + } else { + return (0); + } +} + + +/* + * Apply step 1g, to group entries + * + * Need to deal with corner case where group may have + * greater permissions than owner. If so then limit + * group permissions, based on what extra permissions + * group has. + */ +static void +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 (prevflags & ACE_IDENTIFIER_GROUP) { + + extramode &= ~ownermode; + + if (extramode) { + if (extramode & S_IROTH) { + prevmask &= ~ACE_READ_DATA; + mask &= ~ACE_READ_DATA; + } + if (extramode & S_IWOTH) { + prevmask &= ~(ACE_WRITE_DATA|ACE_APPEND_DATA); + mask &= ~(ACE_WRITE_DATA|ACE_APPEND_DATA); + } + 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 void +zfs_acl_chmod(znode_t *zp, uint64_t mode, zfs_acl_t *aclp) +{ + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + void *acep = NULL, *prevacep = NULL; + uint64_t who; + int i; + int entry_type; + int reuse_deny; + int need_canonical_six = 1; + uint16_t iflags, type; + uint32_t access_mask; + + ASSERT(MUTEX_HELD(&zp->z_acl_lock)); + ASSERT(MUTEX_HELD(&zp->z_lock)); + + aclp->z_hints = (zp->z_phys->zp_flags & V4_ACL_WIDE_FLAGS); + + /* + * 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)) { + + 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; + } + + /* + * Need to split ace into two? + */ + if ((iflags & (ACE_FILE_INHERIT_ACE| + ACE_DIRECTORY_INHERIT_ACE)) && + (!(iflags & ACE_INHERIT_ONLY_ACE))) { + 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)) { + access_mask &= ~OGE_CLEAR; + aclp->z_ops.ace_mask_set(acep, access_mask); + goto nextace; + } else { + reuse_deny = B_TRUE; + if (type == ALLOW) { + + /* + * Check preceding ACE if any, to see + * if we need to prepend a DENY ACE. + * This is only applicable when the acl_mode + * property == groupmask. + */ + if (zfsvfs->z_acl_mode == ZFS_ACL_GROUPMASK) { + + reuse_deny = zfs_reuse_deny(aclp, acep, + prevacep); + + if (!reuse_deny) { + prevacep = + zfs_acl_prepend_deny(zp, + aclp, acep, mode); + } else { + zfs_acl_prepend_fixup( + aclp, prevacep, + acep, mode, + zp->z_phys->zp_uid); + } + zfs_fixup_group_entries(aclp, acep, + prevacep, mode); + + } + } + } +nextace: + prevacep = acep; + } + + /* + * Check out last six aces, if we have six. + */ + + if (aclp->z_acl_count >= 6) { + if (zfs_have_canonical_six(aclp)) { + need_canonical_six = 0; + } + } + + if (need_canonical_six) { + 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); +} + +int +zfs_acl_chmod_setattr(znode_t *zp, zfs_acl_t **aclp, uint64_t mode) +{ + int error; + + mutex_enter(&zp->z_lock); + mutex_enter(&zp->z_acl_lock); + *aclp = NULL; + error = zfs_acl_node_read(zp, aclp, B_TRUE); + if (error == 0) + zfs_acl_chmod(zp, mode, *aclp); + mutex_exit(&zp->z_acl_lock); + mutex_exit(&zp->z_lock); + return (error); +} + +/* + * strip off write_owner and write_acl + */ +static void +zfs_restricted_update(zfsvfs_t *zfsvfs, zfs_acl_t *aclp, void *acep) +{ + 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, boolean_t *need_chmod) +{ + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + void *pacep; + void *acep, *acep2; + zfs_acl_node_t *aclnode, *aclnode2; + zfs_acl_t *aclp = NULL; + 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(zfs_acl_version_zp(zp)); + if (zfsvfs->z_acl_inherit != ZFS_ACL_DISCARD) { + while (pacep = zfs_acl_next_ace(paclp, pacep, &who, + &access_mask, &iflags, &type)) { + + if (zfsvfs->z_acl_inherit == ZFS_ACL_NOALLOW && + type == ALLOW) + continue; + + ace_size = aclp->z_ops.ace_size(pacep); + + if (!zfs_ace_can_use(zp, iflags)) + continue; + + /* + * 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); + + /* + * 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(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); + + /* + * Copy special opaque data if any + */ + if ((data1sz = aclp->z_ops.ace_data(acep, + &data1)) != 0) { + VERIFY((data2sz = + aclp->z_ops.ace_data(acep2, + &data2)) == data1sz); + bcopy(data1, data2, data1sz); + } + 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); + } + } + } + return (aclp); +} + +/* + * Create file system object initial permissions + * including inheritable ACEs. + */ +void +zfs_perm_init(znode_t *zp, znode_t *parent, int flag, + vattr_t *vap, dmu_tx_t *tx, cred_t *cr, + zfs_acl_t *setaclp, zfs_fuid_info_t **fuidp) +{ + uint64_t mode, fuid, fgid; + int error; + 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); + + /* + * Determine uid and gid. + */ + if ((flag & (IS_ROOT_NODE | IS_REPLAY)) || + ((flag & IS_XATTR) && (vap->va_type == VDIR))) { + 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 { + 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; + 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); + gid = crgetgid(cr); + } + } + } + + /* + * If we're creating a directory, and the parent directory has the + * set-GID bit set, set in on the new directory. + * Otherwise, if the user is neither privileged nor a member of the + * file's new group, clear the file's set-GID bit. + */ + + if ((parent->z_phys->zp_mode & S_ISGID) && (vap->va_type == VDIR)) { + mode |= S_ISGID; + } else { + if ((mode & S_ISGID) && + secpolicy_vnode_setids_setgids(cr, gid) != 0) + mode &= ~S_ISGID; + } + + zp->z_phys->zp_uid = fuid; + zp->z_phys->zp_gid = fgid; + zp->z_phys->zp_mode = mode; + + if (aclp == NULL) { + mutex_enter(&parent->z_lock); + if (parent->z_phys->zp_flags & ZFS_INHERIT_ACE) { + 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 { + mutex_enter(&zp->z_lock); + mutex_enter(&zp->z_acl_lock); + } + + /* 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); + + if (aclp != setaclp) + zfs_acl_free(aclp); +} + +/* + * Retrieve a files ACL + */ +int +zfs_getacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr) +{ + zfs_acl_t *aclp; + ulong_t mask; + int error; + int count = 0; + int largeace = 0; + + 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, 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 = count; + } + + if (mask & VSA_ACE) { + 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); + + zfs_acl_free(aclp); + + return (0); +} + +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); +} + +/* + * Set a files ACL + */ +int +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; + ulong_t mask = vsecp->vsa_mask & (VSA_ACE | VSA_ACECNT); + dmu_tx_t *tx; + int error; + zfs_acl_t *aclp; + zfs_fuid_info_t *fuidp = NULL; + + if (mask == 0) + return (ENOSYS); + + 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: + if (error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr)) { + zfs_acl_free(aclp); + return (error); + } + + mutex_enter(&zp->z_lock); + mutex_enter(&zp->z_acl_lock); + + tx = dmu_tx_create(zfsvfs->z_os); + dmu_tx_hold_bonus(tx, zp->z_id); + + if (zp->z_phys->zp_acl.z_acl_extern_obj) { + /* 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); + if (error) { + mutex_exit(&zp->z_acl_lock); + mutex_exit(&zp->z_lock); + + if (error == ERESTART && zfsvfs->z_assign == TXG_NOWAIT) { + dmu_tx_wait(tx); + dmu_tx_abort(tx); + goto top; + } + dmu_tx_abort(tx); + zfs_acl_free(aclp); + return (error); + } + + 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); + dmu_tx_commit(tx); +done: + mutex_exit(&zp->z_acl_lock); + mutex_exit(&zp->z_lock); + + return (error); +} + +/* + * working_mode returns the permissions that were not granted + */ +static int +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; + int error; + 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; + return (0); + } + + *working_mode = v4_mode; + + 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, 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)) { + + if (iflags & ACE_INHERIT_ONLY_ACE) + continue; + + entry_type = (iflags & ACE_TYPE_FLAGS); + + checkit = B_FALSE; + + switch (entry_type) { + case ACE_OWNER: + if (uid == fowner) + checkit = B_TRUE; + break; + case OWNING_GROUP: + who = gowner; + /*FALLTHROUGH*/ + case ACE_IDENTIFIER_GROUP: + checkit = zfs_groupmember(zfsvfs, who, cr); + break; + case ACE_EVERYONE: + checkit = B_TRUE; + break; + + /* USER Entry */ + default: + if (entry_type == 0) { + 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; + } + } + + /* Are we done? */ + if (*working_mode == 0) + break; + } + + mutex_exit(&zp->z_acl_lock); + zfs_acl_free(aclp); + + /* Put the found 'denies' back on the working mode */ + *working_mode |= deny_mask; + + if (*working_mode) + return (EACCES); + + 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, int flags, boolean_t skipaclchk, cred_t *cr) +{ + 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)); + + /* + * If attribute then validate against base file + */ + if (is_attr) { + if ((error = zfs_zget(zp->z_zfsvfs, + zp->z_phys->zp_parent, &xzp)) != 0) { + return (error); + } + + check_zp = xzp; + + /* + * fixup mode to map to xattr perms + */ + + if (mode & (ACE_WRITE_DATA|ACE_APPEND_DATA)) { + mode &= ~(ACE_WRITE_DATA|ACE_APPEND_DATA); + mode |= ACE_WRITE_NAMED_ATTRS; + } + + if (mode & (ACE_READ_DATA|ACE_EXECUTE)) { + mode &= ~(ACE_READ_DATA|ACE_EXECUTE); + mode |= ACE_READ_NAMED_ATTRS; + } + } + + 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 && !check_privs) { + if (is_attr) + VN_RELE(ZTOV(xzp)); + return (error); + } + + 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)) + checkmode |= VREAD; + if (working_mode & (ACE_WRITE_DATA|ACE_WRITE_NAMED_ATTRS| + ACE_APPEND_DATA|ACE_WRITE_ATTRIBUTES)) + 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_create_gid(cr); + if (error == 0 && (working_mode & ACE_WRITE_ACL)) + error = secpolicy_vnode_setdac(cr, owner); + + if (error == 0 && (working_mode & + (ACE_DELETE|ACE_DELETE_CHILD))) + error = secpolicy_vnode_remove(cr); + + if (error == 0 && (working_mode & ACE_SYNCHRONIZE)) + error = secpolicy_vnode_owner(cr, owner); + + 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) + VN_RELE(ZTOV(xzp)); + + return (error); +} + +/* + * Translate traditional unix VREAD/VWRITE/VEXEC mode into + * native ACL format and call zfs_zaccess() + */ +int +zfs_zaccess_rwx(znode_t *zp, mode_t mode, int flags, cred_t *cr) +{ + return (zfs_zaccess(zp, zfs_unix_to_v4(mode >> 6), flags, B_FALSE, cr)); +} + +/* + * Access function for secpolicy_vnode_setattr + */ +int +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, 0, B_FALSE, cr)); +} + +static int +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; + + 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); + + return (error); +} + +/* + * Determine whether Access should be granted/deny, without + * consulting least priv subsystem. + * + * + * The following chart is the recommended NFSv4 enforcement for + * ability to delete an object. + * + * ------------------------------------------------------- + * | Parent Dir | Target Object Permissions | + * | permissions | | + * ------------------------------------------------------- + * | | ACL Allows | ACL Denies| Delete | + * | | Delete | Delete | unspecified| + * ------------------------------------------------------- + * | ACL Allows | Permit | Permit | Permit | + * | DELETE_CHILD | | + * ------------------------------------------------------- + * | ACL Denies | Permit | Deny | Deny | + * | DELETE_CHILD | | | | + * ------------------------------------------------------- + * | ACL specifies | | | | + * | only allow | Permit | Permit | Permit | + * | write and | | | | + * | execute | | | | + * ------------------------------------------------------- + * | ACL denies | | | | + * | write and | Permit | Deny | Deny | + * | execute | | | | + * ------------------------------------------------------- + * ^ + * | + * No search privilege, can't even look up file? + * + */ +int +zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr) +{ + 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; + + /* + * 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. + */ + + if (zp->z_phys->zp_flags & (ZFS_IMMUTABLE | ZFS_NOUNLINK)) + return (EPERM); + + /* + * If the directory permissions allow the delete, we are done. + */ + if ((dzp_error = zfs_zaccess_common(dzp, + ACE_DELETE_CHILD|ACE_EXECUTE|ACE_WRITE_DATA, + &dzp_working_mode, &dzpcheck_privs, B_FALSE, cr)) == 0) + return (0); + + /* + * If target object has delete permission then we are done + */ + if ((zp_error = zfs_zaccess_common(zp, ACE_DELETE, &zp_working_mode, + &zpcheck_privs, B_FALSE, cr)) == 0) + return (0); + + if (!dzpcheck_privs) + return (dzp_error); + else if (!zpcheck_privs) + return (zp_error); + + /* + * First check the first row. + * We only need to see if parent Allows delete_child + */ + if ((dzp_working_mode & ACE_DELETE_CHILD) == 0) + return (0); + + /* + * Second row + * we already have the necessary information in + * zp_working_mode, zp_error and dzp_error. + */ + + if ((zp_working_mode & ACE_DELETE) == 0) + return (0); + + /* + * determine the needed permissions based off of the directories + * working mode + */ + + missing_perms = (dzp_working_mode & ACE_WRITE_DATA) ? VWRITE : 0; + missing_perms |= (dzp_working_mode & ACE_EXECUTE) ? VEXEC : 0; + + if (dzp_error == EACCES) + return (zfs_delete_final_check(zp, dzp, missing_perms, cr)); + + /* + * Third Row + * only need to see if we have write/execute on directory. + */ + + if (missing_perms == 0) + return (zfs_sticky_remove_access(dzp, zp, cr)); + + /* + * Fourth Row + */ + + if (missing_perms && ((zp_working_mode & ACE_DELETE) == 0)) + return (zfs_sticky_remove_access(dzp, zp, cr)); + + return (zfs_delete_final_check(zp, dzp, missing_perms, cr)); +} + +int +zfs_zaccess_rename(znode_t *sdzp, znode_t *szp, znode_t *tdzp, + znode_t *tzp, cred_t *cr) +{ + 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; + + /* + * Rename permissions are combination of delete permission + + * add file/subdir permission. + */ + + /* + * first make sure we do the delete portion. + * + * If that succeeds then check for add_file/add_subdir permissions + */ + + if (error = zfs_zaccess_delete(sdzp, szp, cr)) + return (error); + + /* + * If we have a tzp, see if we can delete it? + */ + if (tzp) { + if (error = zfs_zaccess_delete(tdzp, tzp, cr)) + return (error); + } + + /* + * Now check for add permissions + */ + error = zfs_zaccess(tdzp, add_perm, 0, B_FALSE, cr); + + return (error); +} diff --git a/zfs/lib/libdmu-ctl/zfs_ctldir.c b/zfs/lib/libdmu-ctl/zfs_ctldir.c new file mode 100644 index 0000000..45de481 --- /dev/null +++ b/zfs/lib/libdmu-ctl/zfs_ctldir.c @@ -0,0 +1,1147 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL 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 "@(#)zfs_ctldir.c 1.20 08/04/27 SMI" + +/* + * ZFS control directory (a.k.a. ".zfs") + * + * This directory provides a common location for all ZFS meta-objects. + * Currently, this is only the 'snapshot' directory, but this may expand in the + * future. The elements are built using the GFS primitives, as the hierarchy + * does not actually exist on disk. + * + * For 'snapshot', we don't want to have all snapshots always mounted, because + * this would take up a huge amount of space in /etc/mnttab. We have three + * types of objects: + * + * ctldir ------> snapshotdir -------> snapshot + * | + * | + * V + * mounted fs + * + * The 'snapshot' node contains just enough information to lookup '..' and act + * as a mountpoint for the snapshot. Whenever we lookup a specific snapshot, we + * perform an automount of the underlying filesystem and return the + * corresponding vnode. + * + * All mounts are handled automatically by the kernel, but unmounts are + * (currently) handled from user land. The main reason is that there is no + * 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/') 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/' + * (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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; + vnode_t *se_root; + avl_node_t se_node; +} zfs_snapentry_t; + +static int +snapentry_compare(const void *a, const void *b) +{ + const zfs_snapentry_t *sa = a; + const zfs_snapentry_t *sb = b; + int ret = strcmp(sa->se_name, sb->se_name); + + if (ret < 0) + return (-1); + else if (ret > 0) + return (1); + else + return (0); +} + +vnodeops_t *zfsctl_ops_root; +vnodeops_t *zfsctl_ops_snapdir; +vnodeops_t *zfsctl_ops_snapshot; + +static const fs_operation_def_t zfsctl_tops_root[]; +static const fs_operation_def_t zfsctl_tops_snapdir[]; +static const fs_operation_def_t zfsctl_tops_snapshot[]; + +static vnode_t *zfsctl_mknode_snapdir(vnode_t *); +static vnode_t *zfsctl_snapshot_mknode(vnode_t *, uint64_t objset); +static int zfsctl_unmount_snap(zfs_snapentry_t *, int, cred_t *); + +static gfs_opsvec_t zfsctl_opsvec[] = { + { ".zfs", zfsctl_tops_root, &zfsctl_ops_root }, + { ".zfs/snapshot", zfsctl_tops_snapdir, &zfsctl_ops_snapdir }, + { ".zfs/snapshot/vnode", zfsctl_tops_snapshot, &zfsctl_ops_snapshot }, + { NULL } +}; + +/* + * Root directory elements. We have only a single static entry, 'snapshot'. + */ +static gfs_dirent_t zfsctl_root_entries[] = { + { "snapshot", zfsctl_mknode_snapdir, GFS_CACHE_VNODE }, + { NULL } +}; + +/* include . and .. in the calculation */ +#define NROOT_ENTRIES ((sizeof (zfsctl_root_entries) / \ + sizeof (gfs_dirent_t)) + 1) + + +/* + * Initialize the various GFS pieces we'll need to create and manipulate .zfs + * directories. This is called from the ZFS init routine, and initializes the + * vnode ops vectors that we'll be using. + */ +void +zfsctl_init(void) +{ + VERIFY(gfs_make_opsvec(zfsctl_opsvec) == 0); +} + +void +zfsctl_fini(void) +{ + /* + * Remove vfsctl vnode ops + */ + if (zfsctl_ops_root) + vn_freevnodeops(zfsctl_ops_root); + if (zfsctl_ops_snapdir) + vn_freevnodeops(zfsctl_ops_snapdir); + if (zfsctl_ops_snapshot) + vn_freevnodeops(zfsctl_ops_snapshot); + + zfsctl_ops_root = NULL; + zfsctl_ops_snapdir = NULL; + zfsctl_ops_snapshot = NULL; +} + +/* + * Return the inode number associated with the 'snapshot' directory. + */ +/* ARGSUSED */ +static ino64_t +zfsctl_root_inode_cb(vnode_t *vp, int index) +{ + ASSERT(index == 0); + return (ZFSCTL_INO_SNAPDIR); +} + +/* + * Create the '.zfs' directory. This directory is cached as part of the VFS + * structure. This results in a hold on the vfs_t. The code in zfs_umount() + * therefore checks against a vfs_count of 2 instead of 1. This reference + * is removed when the ctldir is destroyed in the unmount. + */ +void +zfsctl_create(zfsvfs_t *zfsvfs) +{ + vnode_t *vp, *rvp; + zfsctl_node_t *zcp; + + ASSERT(zfsvfs->z_ctldir == NULL); + + vp = gfs_root_create(sizeof (zfsctl_node_t), zfsvfs->z_vfs, + zfsctl_ops_root, ZFSCTL_INO_ROOT, zfsctl_root_entries, + zfsctl_root_inode_cb, MAXNAMELEN, NULL, NULL); + zcp = vp->v_data; + zcp->zc_id = ZFSCTL_INO_ROOT; + + VERIFY(VFS_ROOT(zfsvfs->z_vfs, &rvp) == 0); + ZFS_TIME_DECODE(&zcp->zc_cmtime, VTOZ(rvp)->z_phys->zp_crtime); + VN_RELE(rvp); + + /* + * We're only faking the fact that we have a root of a filesystem for + * the sake of the GFS interfaces. Undo the flag manipulation it did + * for us. + */ + vp->v_flag &= ~(VROOT | VNOCACHE | VNOMAP | VNOSWAP | VNOMOUNT); + + zfsvfs->z_ctldir = vp; +} + +/* + * Destroy the '.zfs' directory. Only called when the filesystem is unmounted. + * There might still be more references if we were force unmounted, but only + * new zfs_inactive() calls can occur and they don't reference .zfs + */ +void +zfsctl_destroy(zfsvfs_t *zfsvfs) +{ + VN_RELE(zfsvfs->z_ctldir); + zfsvfs->z_ctldir = NULL; +} + +/* + * Given a root znode, retrieve the associated .zfs directory. + * Add a hold to the vnode and return it. + */ +vnode_t * +zfsctl_root(znode_t *zp) +{ + ASSERT(zfs_has_ctldir(zp)); + VN_HOLD(zp->z_zfsvfs->z_ctldir); + return (zp->z_zfsvfs->z_ctldir); +} + +/* + * Common open routine. Disallow any write access. + */ +/* ARGSUSED */ +static int +zfsctl_common_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct) +{ + if (flags & FWRITE) + return (EACCES); + + return (0); +} + +/* + * Common close routine. Nothing to do here. + */ +/* ARGSUSED */ +static int +zfsctl_common_close(vnode_t *vpp, int flags, int count, offset_t off, + cred_t *cr, caller_context_t *ct) +{ + return (0); +} + +/* + * Common access routine. Disallow writes. + */ +/* ARGSUSED */ +static int +zfsctl_common_access(vnode_t *vp, int mode, int flags, cred_t *cr, + caller_context_t *ct) +{ + if (mode & VWRITE) + return (EACCES); + + return (0); +} + +/* + * Common getattr function. Fill in basic information. + */ +static void +zfsctl_common_getattr(vnode_t *vp, vattr_t *vap) +{ + zfsctl_node_t *zcp = vp->v_data; + timestruc_t now; + + vap->va_uid = 0; + vap->va_gid = 0; + vap->va_rdev = 0; + /* + * We are a purly virtual object, so we have no + * blocksize or allocated blocks. + */ + vap->va_blksize = 0; + vap->va_nblocks = 0; + vap->va_seq = 0; + vap->va_fsid = vp->v_vfsp->vfs_dev; + vap->va_mode = S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | + S_IROTH | S_IXOTH; + vap->va_type = VDIR; + /* + * We live in the now (for atime). + */ + gethrestime(&now); + vap->va_atime = now; + vap->va_mtime = vap->va_ctime = zcp->zc_cmtime; +} + +/*ARGSUSED*/ +static int +zfsctl_common_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct) +{ + zfsvfs_t *zfsvfs = vp->v_vfsp->vfs_data; + zfsctl_node_t *zcp = vp->v_data; + uint64_t object = zcp->zc_id; + zfid_short_t *zfid; + int i; + + ZFS_ENTER(zfsvfs); + + if (fidp->fid_len < SHORT_FID_LEN) { + fidp->fid_len = SHORT_FID_LEN; + ZFS_EXIT(zfsvfs); + return (ENOSPC); + } + + zfid = (zfid_short_t *)fidp; + + zfid->zf_len = SHORT_FID_LEN; + + for (i = 0; i < sizeof (zfid->zf_object); i++) + zfid->zf_object[i] = (uint8_t)(object >> (8 * i)); + + /* .zfs znodes always have a generation number of 0 */ + for (i = 0; i < sizeof (zfid->zf_gen); i++) + zfid->zf_gen[i] = 0; + + ZFS_EXIT(zfsvfs); + return (0); +} + +/* + * .zfs inode namespace + * + * We need to generate unique inode numbers for all files and directories + * within the .zfs pseudo-filesystem. We use the following scheme: + * + * ENTRY ZFSCTL_INODE + * .zfs 1 + * .zfs/snapshot 2 + * .zfs/snapshot/ objectid(snap) + */ + +#define ZFSCTL_INO_SNAP(id) (id) + +/* + * Get root directory attributes. + */ +/* ARGSUSED */ +static int +zfsctl_root_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) +{ + zfsvfs_t *zfsvfs = vp->v_vfsp->vfs_data; + + ZFS_ENTER(zfsvfs); + vap->va_nodeid = ZFSCTL_INO_ROOT; + vap->va_nlink = vap->va_size = NROOT_ENTRIES; + + zfsctl_common_getattr(vp, vap); + ZFS_EXIT(zfsvfs); + + return (0); +} + +/* + * Special case the handling of "..". + */ +/* ARGSUSED */ +int +zfsctl_root_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) +{ + 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) { + err = VFS_ROOT(dvp->v_vfsp, vpp); + } else { + err = gfs_vop_lookup(dvp, nm, vpp, pnp, flags, rdir, + cr, ct, direntflags, realpnp); + } + + ZFS_EXIT(zfsvfs); + + return (err); +} + +static const fs_operation_def_t zfsctl_tops_root[] = { + { VOPNAME_OPEN, { .vop_open = zfsctl_common_open } }, + { VOPNAME_CLOSE, { .vop_close = zfsctl_common_close } }, + { VOPNAME_IOCTL, { .error = fs_inval } }, + { VOPNAME_GETATTR, { .vop_getattr = zfsctl_root_getattr } }, + { VOPNAME_ACCESS, { .vop_access = zfsctl_common_access } }, + { VOPNAME_READDIR, { .vop_readdir = gfs_vop_readdir } }, + { VOPNAME_LOOKUP, { .vop_lookup = zfsctl_root_lookup } }, + { VOPNAME_SEEK, { .vop_seek = fs_seek } }, + { VOPNAME_INACTIVE, { .vop_inactive = gfs_vop_inactive } }, + { VOPNAME_FID, { .vop_fid = zfsctl_common_fid } }, + { NULL } +}; + +static int +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; + + dmu_objset_name(os, zname); + if (strlen(zname) + 1 + strlen(name) >= len) + return (ENAMETOOLONG); + (void) strcat(zname, "@"); + (void) strcat(zname, name); + return (0); +} + +static int +zfsctl_unmount_snap(zfs_snapentry_t *sep, int fflags, cred_t *cr) +{ + vnode_t *svp = sep->se_root; + int error; + + ASSERT(vn_ismntpt(svp)); + + /* this will be dropped by dounmount() */ + if ((error = vn_vfswlock(svp)) != 0) + return (error); + + VN_HOLD(svp); + error = dounmount(vn_mountedvfs(svp), fflags, cr); + if (error) { + VN_RELE(svp); + return (error); + } + VFS_RELE(svp->v_vfsp); + /* + * We can't use VN_RELE(), as that will try to invoke + * zfsctl_snapdir_inactive(), which would cause us to destroy + * the sd_lock mutex held by our caller. + */ + ASSERT(svp->v_count == 1); + gfs_vop_inactive(svp, cr, NULL); + + kmem_free(sep->se_name, strlen(sep->se_name) + 1); + kmem_free(sep, sizeof (zfs_snapentry_t)); + + return (0); +} + +static void +zfsctl_rename_snap(zfsctl_snapdir_t *sdp, zfs_snapentry_t *sep, const char *nm) +{ + avl_index_t where; + vfs_t *vfsp; + refstr_t *pathref; + char newpath[MAXNAMELEN]; + char *tail; + + ASSERT(MUTEX_HELD(&sdp->sd_lock)); + ASSERT(sep != NULL); + + vfsp = vn_mountedvfs(sep->se_root); + ASSERT(vfsp != NULL); + + vfs_lock_wait(vfsp); + + /* + * Change the name in the AVL tree. + */ + avl_remove(&sdp->sd_snaps, sep); + kmem_free(sep->se_name, strlen(sep->se_name) + 1); + sep->se_name = kmem_alloc(strlen(nm) + 1, KM_SLEEP); + (void) strcpy(sep->se_name, nm); + VERIFY(avl_find(&sdp->sd_snaps, sep, &where) == NULL); + avl_insert(&sdp->sd_snaps, sep, where); + + /* + * Change the current mountpoint info: + * - update the tail of the mntpoint path + * - update the tail of the resource path + */ + pathref = vfs_getmntpoint(vfsp); + (void) strncpy(newpath, refstr_value(pathref), sizeof (newpath)); + VERIFY((tail = strrchr(newpath, '/')) != NULL); + *(tail+1) = '\0'; + ASSERT3U(strlen(newpath) + strlen(nm), <, sizeof (newpath)); + (void) strcat(newpath, nm); + refstr_rele(pathref); + vfs_setmntpoint(vfsp, newpath); + + pathref = vfs_getresource(vfsp); + (void) strncpy(newpath, refstr_value(pathref), sizeof (newpath)); + VERIFY((tail = strrchr(newpath, '@')) != NULL); + *(tail+1) = '\0'; + ASSERT3U(strlen(newpath) + strlen(nm), <, sizeof (newpath)); + (void) strcat(newpath, nm); + refstr_rele(pathref); + vfs_setresource(vfsp, newpath); + + vfs_unlock(vfsp); +} + +/*ARGSUSED*/ +static int +zfsctl_snapdir_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, + 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) + err = zfsctl_snapshot_zname(tdvp, tnm, MAXNAMELEN, to); + if (!err) + err = zfs_secpolicy_rename_perms(from, to, cr); + if (err) + return (err); + + /* + * Cannot move snapshots out of the snapdir. + */ + if (sdvp != tdvp) + return (EINVAL); + + if (strcmp(snm, tnm) == 0) + return (0); + + mutex_enter(&sdp->sd_lock); + + search.se_name = (char *)snm; + if ((sep = avl_find(&sdp->sd_snaps, &search, &where)) == NULL) { + mutex_exit(&sdp->sd_lock); + return (ENOENT); + } + + err = dmu_objset_rename(from, to, B_FALSE); + if (err == 0) + zfsctl_rename_snap(sdp, sep, tnm); + + mutex_exit(&sdp->sd_lock); + + return (err); +} + +/* ARGSUSED */ +static int +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) + err = zfs_secpolicy_destroy_perms(snapname, cr); + if (err) + return (err); + + mutex_enter(&sdp->sd_lock); + + 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; + } + + mutex_exit(&sdp->sd_lock); + + return (err); +} + +/* + * 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; + + 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); +} + +/* + * 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. + */ +/* ARGSUSED */ +static int +zfsctl_snapdir_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) +{ + zfsctl_snapdir_t *sdp = dvp->v_data; + objset_t *snap; + char snapname[MAXNAMELEN]; + char real[MAXNAMELEN]; + char *mountpoint; + zfs_snapentry_t *sep, search; + struct mounta margs; + vfs_t *vfsp; + size_t mountpoint_len; + avl_index_t where; + zfsvfs_t *zfsvfs = dvp->v_vfsp->vfs_data; + int err; + + /* + * No extended attributes allowed under .zfs + */ + if (flags & LOOKUP_XATTR) + return (EINVAL); + + ASSERT(dvp->v_type == VDIR); + + if (gfs_lookup_dot(vpp, dvp, zfsvfs->z_ctldir, nm) == 0) + return (0); + + /* + * If we get a recursive call, that means we got called + * from the domount() code while it was trying to look up the + * spec (which looks like a local path for zfs). We need to + * add some flag to domount() to tell it not to do this lookup. + */ + if (MUTEX_HELD(&sdp->sd_lock)) + return (ENOENT); + + 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) { + nm = real; + } else if (err != ENOTSUP) { + ZFS_EXIT(zfsvfs); + return (err); + } + if (realpnp) + (void) strlcpy(realpnp->pn_buf, nm, + realpnp->pn_bufsize); + if (conflict && direntflags) + *direntflags = ED_CASE_CONFLICT; + } + + mutex_enter(&sdp->sd_lock); + search.se_name = (char *)nm; + if ((sep = avl_find(&sdp->sd_snaps, &search, &where)) != NULL) { + *vpp = sep->se_root; + VN_HOLD(*vpp); + err = traverse(vpp); + if (err) { + VN_RELE(*vpp); + *vpp = NULL; + } else if (*vpp == sep->se_root) { + /* + * The snapshot was unmounted behind our backs, + * 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; + } + mutex_exit(&sdp->sd_lock); + ZFS_EXIT(zfsvfs); + return (err); + } + + /* + * The requested snapshot is not currently mounted, look it up. + */ + err = zfsctl_snapshot_zname(dvp, nm, MAXNAMELEN, snapname); + if (err) { + mutex_exit(&sdp->sd_lock); + ZFS_EXIT(zfsvfs); + return (err); + } + if (dmu_objset_open(snapname, DMU_OST_ZFS, + DS_MODE_STANDARD | DS_MODE_READONLY, &snap) != 0) { + mutex_exit(&sdp->sd_lock); + ZFS_EXIT(zfsvfs); + return (ENOENT); + } + + sep = kmem_alloc(sizeof (zfs_snapentry_t), KM_SLEEP); + sep->se_name = kmem_alloc(strlen(nm) + 1, KM_SLEEP); + (void) strcpy(sep->se_name, nm); + *vpp = sep->se_root = zfsctl_snapshot_mknode(dvp, dmu_objset_id(snap)); + avl_insert(&sdp->sd_snaps, sep, where); + + dmu_objset_close(snap); +domount: + mountpoint_len = strlen(refstr_value(dvp->v_vfsp->vfs_mntpt)) + + strlen("/.zfs/snapshot/") + strlen(nm) + 1; + mountpoint = kmem_alloc(mountpoint_len, KM_SLEEP); + (void) snprintf(mountpoint, mountpoint_len, "%s/.zfs/snapshot/%s", + refstr_value(dvp->v_vfsp->vfs_mntpt), nm); + + margs.spec = snapname; + margs.dir = mountpoint; + margs.flags = MS_SYSSPACE | MS_NOMNTTAB; + margs.fstype = "zfs"; + margs.dataptr = NULL; + margs.datalen = 0; + margs.optptr = NULL; + margs.optlen = 0; + + err = domount("zfs", &margs, *vpp, kcred, &vfsp); + kmem_free(mountpoint, mountpoint_len); + + if (err == 0) { + /* + * Return the mounted root rather than the covered mount point. + * Takes the GFS vnode at .zfs/snapshot/ and returns + * the ZFS vnode mounted on top of the GFS node. This ZFS + * vnode is the root the newly created vfsp. + */ + VFS_RELE(vfsp); + err = traverse(vpp); + } + + if (err == 0) { + /* + * Fix up the root vnode mounted on .zfs/snapshot/. + * + * This is where we lie about our v_vfsp in order to + * make .zfs/snapshot/ accessible over NFS + * without requiring manual mounts of . + */ + ASSERT(VTOZ(*vpp)->z_zfsvfs != zfsvfs); + VTOZ(*vpp)->z_zfsvfs->z_parent = zfsvfs; + (*vpp)->v_vfsp = zfsvfs->z_vfs; + (*vpp)->v_flag &= ~VROOT; + } + mutex_exit(&sdp->sd_lock); + ZFS_EXIT(zfsvfs); + + /* + * If we had an error, drop our hold on the vnode and + * zfsctl_snapshot_inactive() will clean up. + */ + if (err) { + VN_RELE(*vpp); + *vpp = NULL; + } + return (err); +} + +/* ARGSUSED */ +static int +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; + error = dmu_snapshot_list_next(zfsvfs->z_os, MAXNAMELEN, snapname, &id, + &cookie, &case_conflict); + if (error) { + ZFS_EXIT(zfsvfs); + if (error == ENOENT) { + *eofp = 1; + return (0); + } + return (error); + } + + 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); + + 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) +{ + vnode_t *vp; + zfsctl_snapdir_t *sdp; + + vp = gfs_dir_create(sizeof (zfsctl_snapdir_t), pvp, + zfsctl_ops_snapdir, NULL, NULL, MAXNAMELEN, + zfsctl_snapdir_readdir_cb, NULL); + sdp = vp->v_data; + sdp->sd_node.zc_id = ZFSCTL_INO_SNAPDIR; + sdp->sd_node.zc_cmtime = ((zfsctl_node_t *)pvp->v_data)->zc_cmtime; + mutex_init(&sdp->sd_lock, NULL, MUTEX_DEFAULT, NULL); + avl_create(&sdp->sd_snaps, snapentry_compare, + sizeof (zfs_snapentry_t), offsetof(zfs_snapentry_t, se_node)); + return (vp); +} + +/* ARGSUSED */ +static int +zfsctl_snapdir_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) +{ + zfsvfs_t *zfsvfs = vp->v_vfsp->vfs_data; + zfsctl_snapdir_t *sdp = vp->v_data; + + ZFS_ENTER(zfsvfs); + zfsctl_common_getattr(vp, vap); + vap->va_nodeid = gfs_file_inode(vp); + vap->va_nlink = vap->va_size = avl_numnodes(&sdp->sd_snaps) + 2; + ZFS_EXIT(zfsvfs); + + return (0); +} + +/* ARGSUSED */ +static void +zfsctl_snapdir_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) +{ + zfsctl_snapdir_t *sdp = vp->v_data; + void *private; + + private = gfs_dir_inactive(vp); + if (private != NULL) { + ASSERT(avl_numnodes(&sdp->sd_snaps) == 0); + mutex_destroy(&sdp->sd_lock); + avl_destroy(&sdp->sd_snaps); + kmem_free(private, sizeof (zfsctl_snapdir_t)); + } +} + +static const fs_operation_def_t zfsctl_tops_snapdir[] = { + { VOPNAME_OPEN, { .vop_open = zfsctl_common_open } }, + { VOPNAME_CLOSE, { .vop_close = zfsctl_common_close } }, + { VOPNAME_IOCTL, { .error = fs_inval } }, + { VOPNAME_GETATTR, { .vop_getattr = zfsctl_snapdir_getattr } }, + { VOPNAME_ACCESS, { .vop_access = zfsctl_common_access } }, + { VOPNAME_RENAME, { .vop_rename = zfsctl_snapdir_rename } }, + { VOPNAME_RMDIR, { .vop_rmdir = zfsctl_snapdir_remove } }, + { VOPNAME_MKDIR, { .vop_mkdir = zfsctl_snapdir_mkdir } }, + { VOPNAME_READDIR, { .vop_readdir = gfs_vop_readdir } }, + { VOPNAME_LOOKUP, { .vop_lookup = zfsctl_snapdir_lookup } }, + { VOPNAME_SEEK, { .vop_seek = fs_seek } }, + { VOPNAME_INACTIVE, { .vop_inactive = zfsctl_snapdir_inactive } }, + { VOPNAME_FID, { .vop_fid = zfsctl_common_fid } }, + { NULL } +}; + +/* + * 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) +{ + vnode_t *vp; + zfsctl_node_t *zcp; + + vp = gfs_dir_create(sizeof (zfsctl_node_t), pvp, + zfsctl_ops_snapshot, NULL, NULL, MAXNAMELEN, NULL, NULL); + zcp = vp->v_data; + zcp->zc_id = objset; + VFS_HOLD(vp->v_vfsp); + + return (vp); +} + +static void +zfsctl_snapshot_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) +{ + zfsctl_snapdir_t *sdp; + zfs_snapentry_t *sep, *next; + vnode_t *dvp; + + VERIFY(gfs_dir_lookup(vp, "..", &dvp, cr, 0, NULL, NULL) == 0); + sdp = dvp->v_data; + + mutex_enter(&sdp->sd_lock); + + if (vp->v_count > 1) { + mutex_exit(&sdp->sd_lock); + return; + } + ASSERT(!vn_ismntpt(vp)); + + sep = avl_first(&sdp->sd_snaps); + while (sep != NULL) { + next = AVL_NEXT(&sdp->sd_snaps, sep); + + if (sep->se_root == vp) { + avl_remove(&sdp->sd_snaps, sep); + kmem_free(sep->se_name, strlen(sep->se_name) + 1); + kmem_free(sep, sizeof (zfs_snapentry_t)); + break; + } + sep = next; + } + ASSERT(sep != NULL); + + mutex_exit(&sdp->sd_lock); + VN_RELE(dvp); + VFS_RELE(vp->v_vfsp); + + /* + * Dispose of the vnode for the snapshot mount point. + * This is safe to do because once this entry has been removed + * from the AVL tree, it can't be found again, so cannot become + * "active". If we lookup the same name again we will end up + * creating a new vnode. + */ + gfs_vop_inactive(vp, cr, ct); +} + + +/* + * These VP's should never see the light of day. They should always + * be covered. + */ +static const fs_operation_def_t zfsctl_tops_snapshot[] = { + VOPNAME_INACTIVE, { .vop_inactive = zfsctl_snapshot_inactive }, + NULL, NULL +}; + +int +zfsctl_lookup_objset(vfs_t *vfsp, uint64_t objsetid, zfsvfs_t **zfsvfsp) +{ + zfsvfs_t *zfsvfs = vfsp->vfs_data; + vnode_t *dvp, *vp; + zfsctl_snapdir_t *sdp; + zfsctl_node_t *zcp; + zfs_snapentry_t *sep; + int error; + + ASSERT(zfsvfs->z_ctldir != NULL); + error = zfsctl_root_lookup(zfsvfs->z_ctldir, "snapshot", &dvp, + NULL, 0, NULL, kcred, NULL, NULL, NULL); + if (error != 0) + return (error); + sdp = dvp->v_data; + + mutex_enter(&sdp->sd_lock); + sep = avl_first(&sdp->sd_snaps); + while (sep != NULL) { + vp = sep->se_root; + zcp = vp->v_data; + if (zcp->zc_id == objsetid) + break; + + sep = AVL_NEXT(&sdp->sd_snaps, sep); + } + + if (sep != NULL) { + VN_HOLD(vp); + /* + * Return the mounted root rather than the covered mount point. + * Takes the GFS vnode at .zfs/snapshot/ + * 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); + if (error == 0) { + if (vp == sep->se_root) + error = EINVAL; + else + *zfsvfsp = VTOZ(vp)->z_zfsvfs; + } + mutex_exit(&sdp->sd_lock); + VN_RELE(vp); + } else { + error = EINVAL; + mutex_exit(&sdp->sd_lock); + } + + VN_RELE(dvp); + + return (error); +} + +/* + * Unmount any snapshots for the given filesystem. This is called from + * zfs_umount() - if we have a ctldir, then go through and unmount all the + * snapshots. + */ +int +zfsctl_umount_snapshots(vfs_t *vfsp, int fflags, cred_t *cr) +{ + zfsvfs_t *zfsvfs = vfsp->vfs_data; + 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, NULL, NULL); + if (error != 0) + return (error); + sdp = dvp->v_data; + + mutex_enter(&sdp->sd_lock); + + sep = avl_first(&sdp->sd_snaps); + while (sep != NULL) { + next = AVL_NEXT(&sdp->sd_snaps, sep); + + /* + * If this snapshot is not mounted, then it must + * have just been unmounted by somebody else, and + * will be cleaned up by zfsctl_snapdir_inactive(). + */ + if (vn_ismntpt(sep->se_root)) { + avl_remove(&sdp->sd_snaps, sep); + error = zfsctl_unmount_snap(sep, fflags, cr); + if (error) { + avl_add(&sdp->sd_snaps, sep); + break; + } + } + sep = next; + } + + mutex_exit(&sdp->sd_lock); + VN_RELE(dvp); + + return (error); +} diff --git a/zfs/lib/libdmu-ctl/zfs_dir.c b/zfs/lib/libdmu-ctl/zfs_dir.c new file mode 100644 index 0000000..6f22e2a --- /dev/null +++ b/zfs/lib/libdmu-ctl/zfs_dir.c @@ -0,0 +1,968 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL 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 "@(#)zfs_dir.c 1.25 08/04/27 SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fs/fs_subr.h" +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 protects that name + * in dzp's directory zap object. As long as you hold a dirlock, you can + * assume two things: (1) dzp cannot be reaped, and (2) no other thread + * can change the zap entry for (i.e. link or unlink) this name. + * + * Input arguments: + * dzp - znode for directory + * name - name of entry to lock + * flag - ZNEW: if the entry already exists, fail with EEXIST. + * 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 *direntflags, pathname_t *realpnp) +{ + zfsvfs_t *zfsvfs = dzp->z_zfsvfs; + zfs_dirlock_t *dl; + boolean_t update; + boolean_t exact; + uint64_t zoid; + vnode_t *vp = NULL; + int error = 0; + int cmpflags; + + *zpp = NULL; + *dlpp = NULL; + + /* + * Verify that we are not trying to lock '.', '..', or '.zfs' + */ + if (name[0] == '.' && + (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')) || + zfs_has_ctldir(dzp) && strcmp(name, ZFS_CTLDIR_NAME) == 0) + 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); + mutex_enter(&dzp->z_lock); + for (;;) { + if (dzp->z_unlinked) { + mutex_exit(&dzp->z_lock); + rw_exit(&dzp->z_name_lock); + return (ENOENT); + } + 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. + */ + dl = kmem_alloc(sizeof (zfs_dirlock_t), KM_SLEEP); + cv_init(&dl->dl_cv, NULL, CV_DEFAULT, NULL); + dl->dl_name = name; + dl->dl_sharecnt = 0; + dl->dl_namesize = 0; + dl->dl_dzp = dzp; + dl->dl_next = dzp->z_dirlocks; + dzp->z_dirlocks = dl; + break; + } + if ((flag & ZSHARED) && dl->dl_sharecnt != 0) + break; + cv_wait(&dl->dl_cv, &dzp->z_lock); + } + + if ((flag & ZSHARED) && ++dl->dl_sharecnt > 1 && dl->dl_namesize == 0) { + /* + * We're the second shared reference to dl. Make a copy of + * dl_name in case the first thread goes away before we do. + * Note that we initialize the new name before storing its + * pointer into dl_name, because the first thread may load + * dl->dl_name at any time. He'll either see the old value, + * which is his, or the new shared copy; either is OK. + */ + dl->dl_namesize = strlen(dl->dl_name) + 1; + name = kmem_alloc(dl->dl_namesize, KM_SLEEP); + bcopy(dl->dl_name, name, dl->dl_namesize); + dl->dl_name = name; + } + + mutex_exit(&dzp->z_lock); + + /* + * We have a dirlock on the name. (Note that it is the dirlock, + * not the dzp's z_lock, that protects the name in the zap object.) + * See if there's an object by this name; if so, put a hold on it. + */ + if (flag & ZXATTR) { + zoid = dzp->z_phys->zp_xattr; + error = (zoid == 0 ? ENOENT : 0); + } else { + if (update) + vp = dnlc_lookup(ZTOV(dzp), name); + if (vp == DNLC_NO_VNODE) { + VN_RELE(vp); + error = ENOENT; + } else if (vp) { + if (flag & ZNEW) { + zfs_dirent_unlock(dl); + VN_RELE(vp); + return (EEXIST); + } + *dlpp = dl; + *zpp = VTOZ(vp); + return (0); + } else { + error = zfs_match_find(zfsvfs, dzp, name, exact, + update, direntflags, realpnp, &zoid); + } + } + if (error) { + if (error != ENOENT || (flag & ZEXISTS)) { + zfs_dirent_unlock(dl); + return (error); + } + } else { + if (flag & ZNEW) { + zfs_dirent_unlock(dl); + return (EEXIST); + } + error = zfs_zget(zfsvfs, zoid, zpp); + if (error) { + zfs_dirent_unlock(dl); + return (error); + } + if (!(flag & ZXATTR) && update) + dnlc_update(ZTOV(dzp), name, ZTOV(*zpp)); + } + + *dlpp = dl; + + return (0); +} + +/* + * Unlock this directory entry and wake anyone who was waiting for it. + */ +void +zfs_dirent_unlock(zfs_dirlock_t *dl) +{ + znode_t *dzp = dl->dl_dzp; + zfs_dirlock_t **prev_dl, *cur_dl; + + mutex_enter(&dzp->z_lock); + rw_exit(&dzp->z_name_lock); + if (dl->dl_sharecnt > 1) { + dl->dl_sharecnt--; + mutex_exit(&dzp->z_lock); + return; + } + prev_dl = &dzp->z_dirlocks; + while ((cur_dl = *prev_dl) != dl) + prev_dl = &cur_dl->dl_next; + *prev_dl = dl->dl_next; + cv_broadcast(&dl->dl_cv); + mutex_exit(&dzp->z_lock); + + if (dl->dl_namesize != 0) + kmem_free(dl->dl_name, dl->dl_namesize); + cv_destroy(&dl->dl_cv); + kmem_free(dl, sizeof (*dl)); +} + +/* + * Look up an entry in a directory. + * + * NOTE: '.' and '..' are handled as special cases because + * no directory entries are actually stored for them. If this is + * the root of a filesystem, then '.zfs' is also treated as a + * special pseudo-directory. + */ +int +zfs_dirlook(znode_t *dzp, char *name, vnode_t **vpp, int flags, + int *deflg, pathname_t *rpnp) +{ + zfs_dirlock_t *dl; + znode_t *zp; + int error = 0; + + if (name[0] == 0 || (name[0] == '.' && name[1] == 0)) { + *vpp = ZTOV(dzp); + VN_HOLD(*vpp); + } else if (name[0] == '.' && name[1] == '.' && name[2] == 0) { + zfsvfs_t *zfsvfs = dzp->z_zfsvfs; + /* + * If we are a snapshot mounted under .zfs, return + * the vp for the snapshot directory. + */ + 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, + NULL, NULL, NULL); + return (error); + } + rw_enter(&dzp->z_parent_lock, RW_READER); + error = zfs_zget(zfsvfs, dzp->z_phys->zp_parent, &zp); + if (error == 0) + *vpp = ZTOV(zp); + rw_exit(&dzp->z_parent_lock); + } else if (zfs_has_ctldir(dzp) && strcmp(name, ZFS_CTLDIR_NAME) == 0) { + *vpp = zfsctl_root(dzp); + } else { + 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; + } + + if ((flags & FIGNORECASE) && rpnp && !error) + (void) strlcpy(rpnp->pn_buf, name, rpnp->pn_bufsize); + + 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); + + return (name); +} + +/* + * unlinked Set (formerly known as the "delete queue") Error Handling + * + * When dealing with the unlinked set, we dmu_tx_hold_zap(), but we + * don't specify the name of the entry that we will be manipulating. We + * also fib and say that we won't be adding any new entries to the + * unlinked set, even though we might (this is to lower the minimum file + * size that can be deleted in a full filesystem). So on the small + * chance that the nlink list is using a fat zap (ie. has more than + * 2000 entries), we *may* not pre-read a block that's needed. + * Therefore it is remotely possible for some of the assertions + * regarding the unlinked set below to fail due to i/o error. On a + * nondebug system, this will result in the space being leaked. + */ +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); +} + +/* + * Clean up any znodes that had no links when we either crashed or + * (force) umounted the file system. + */ +void +zfs_unlinked_drain(zfsvfs_t *zfsvfs) +{ + zap_cursor_t zc; + zap_attribute_t zap; + dmu_object_info_t doi; + znode_t *zp; + int error; + + /* + * Interate over the contents of the unlinked set. + */ + for (zap_cursor_init(&zc, zfsvfs->z_os, zfsvfs->z_unlinkedobj); + zap_cursor_retrieve(&zc, &zap) == 0; + zap_cursor_advance(&zc)) { + + /* + * See what kind of object we have in list + */ + + error = dmu_object_info(zfsvfs->z_os, + zap.za_first_integer, &doi); + if (error != 0) + continue; + + ASSERT((doi.doi_type == DMU_OT_PLAIN_FILE_CONTENTS) || + (doi.doi_type == DMU_OT_DIRECTORY_CONTENTS)); + /* + * We need to re-mark these list entries for deletion, + * so we pull them back into core and set zp->z_unlinked. + */ + error = zfs_zget(zfsvfs, zap.za_first_integer, &zp); + + /* + * We may pick up znodes that are already marked for deletion. + * This could happen during the purge of an extended attribute + * directory. All we need to do is skip over them, since they + * are already in the system marked z_unlinked. + */ + if (error != 0) + continue; + + zp->z_unlinked = B_TRUE; + VN_RELE(ZTOV(zp)); + } + zap_cursor_fini(&zc); +} + +/* + * Delete the entire contents of a directory. Return a count + * 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. + * Also, it assumes the directory contents is *only* regular + * files. + */ +static int +zfs_purgedir(znode_t *dzp) +{ + zap_cursor_t zc; + zap_attribute_t zap; + znode_t *xzp; + dmu_tx_t *tx; + zfsvfs_t *zfsvfs = dzp->z_zfsvfs; + zfs_dirlock_t dl; + int skipped = 0; + int error; + + for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id); + (error = zap_cursor_retrieve(&zc, &zap)) == 0; + zap_cursor_advance(&zc)) { + error = zfs_zget(zfsvfs, + ZFS_DIRENT_OBJ(zap.za_first_integer), &xzp); + if (error) { + skipped += 1; + continue; + } + + ASSERT((ZTOV(xzp)->v_type == VREG) || + (ZTOV(xzp)->v_type == VLNK)); + + tx = dmu_tx_create(zfsvfs->z_os); + dmu_tx_hold_bonus(tx, dzp->z_id); + dmu_tx_hold_zap(tx, dzp->z_id, FALSE, zap.za_name); + dmu_tx_hold_bonus(tx, xzp->z_id); + dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL); + error = dmu_tx_assign(tx, TXG_WAIT); + if (error) { + dmu_tx_abort(tx); + VN_RELE(ZTOV(xzp)); + skipped += 1; + continue; + } + bzero(&dl, sizeof (dl)); + dl.dl_dzp = dzp; + dl.dl_name = zap.za_name; + + error = zfs_link_destroy(&dl, xzp, tx, 0, NULL); + if (error) + skipped += 1; + dmu_tx_commit(tx); + + VN_RELE(ZTOV(xzp)); + } + zap_cursor_fini(&zc); + if (error != ENOENT) + skipped += 1; + return (skipped); +} + +void +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; + + ASSERT(ZTOV(zp)->v_count == 0); + ASSERT(zp->z_phys->zp_links == 0); + + /* + * If this is an attribute directory, purge its contents. + */ + if (ZTOV(zp)->v_type == VDIR && (zp->z_phys->zp_flags & ZFS_XATTR)) { + if (zfs_purgedir(zp) != 0) { + /* + * Not enough space to delete some xattrs. + * Leave it on 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. + */ + if (zp->z_phys->zp_xattr) { + error = zfs_zget(zfsvfs, zp->z_phys->zp_xattr, &xzp); + ASSERT(error == 0); + } + + acl_obj = zp->z_phys->zp_acl.z_acl_extern_obj; + + /* + * Set up the transaction. + */ + tx = dmu_tx_create(os); + dmu_tx_hold_free(tx, zp->z_id, 0, DMU_OBJECT_END); + dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL); + if (xzp) { + dmu_tx_hold_bonus(tx, xzp->z_id); + dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, TRUE, NULL); + } + if (acl_obj) + dmu_tx_hold_free(tx, acl_obj, 0, DMU_OBJECT_END); + error = dmu_tx_assign(tx, TXG_WAIT); + if (error) { + /* + * Not enough space to delete the file. Leave it in the + * unlinked set, leaking it until the fs is remounted (at + * which point we'll call zfs_unlinked_drain() to process it). + */ + dmu_tx_abort(tx); + zfs_znode_dmu_fini(zp); + zfs_znode_free(zp); + goto out; + } + + if (xzp) { + dmu_buf_will_dirty(xzp->z_dbuf, tx); + mutex_enter(&xzp->z_lock); + xzp->z_unlinked = B_TRUE; /* mark xzp for deletion */ + xzp->z_phys->zp_links = 0; /* no more links to it */ + mutex_exit(&xzp->z_lock); + zfs_unlinked_add(xzp, tx); + } + + /* 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); + + zfs_znode_delete(zp, tx); + + dmu_tx_commit(tx); +out: + if (xzp) + VN_RELE(ZTOV(xzp)); +} + +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. + */ +int +zfs_link_create(zfs_dirlock_t *dl, znode_t *zp, dmu_tx_t *tx, int flag) +{ + znode_t *dzp = dl->dl_dzp; + vnode_t *vp = ZTOV(zp); + uint64_t value; + int zp_is_dir = (vp->v_type == VDIR); + int error; + + dmu_buf_will_dirty(zp->z_dbuf, tx); + mutex_enter(&zp->z_lock); + + if (!(flag & ZRENAMING)) { + if (zp->z_unlinked) { /* no new links to unlinked zp */ + ASSERT(!(flag & (ZNEW | ZEXISTS))); + mutex_exit(&zp->z_lock); + return (ENOENT); + } + zp->z_phys->zp_links++; + } + zp->z_phys->zp_parent = dzp->z_id; /* dzp is now zp's parent */ + + if (!(flag & ZNEW)) + zfs_time_stamper_locked(zp, STATE_CHANGED, tx); + mutex_exit(&zp->z_lock); + + dmu_buf_will_dirty(dzp->z_dbuf, tx); + mutex_enter(&dzp->z_lock); + dzp->z_phys->zp_size++; /* one dirent added */ + dzp->z_phys->zp_links += zp_is_dir; /* ".." link from zp */ + zfs_time_stamper_locked(dzp, CONTENT_MODIFIED, tx); + mutex_exit(&dzp->z_lock); + + 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); + + dnlc_update(ZTOV(dzp), dl->dl_name, vp); + + return (0); +} + +/* + * Unlink zp from dl, and mark zp for deletion if this was the last link. + * Can fail if zp is a mount point (EBUSY) or a non-empty directory (EEXIST). + * If 'unlinkedp' is NULL, we put unlinked znodes on the unlinked list. + * If it's non-NULL, we use it to indicate whether the znode needs deletion, + * and it's the caller's job to do it. + */ +int +zfs_link_destroy(zfs_dirlock_t *dl, znode_t *zp, dmu_tx_t *tx, int flag, + boolean_t *unlinkedp) +{ + znode_t *dzp = dl->dl_dzp; + vnode_t *vp = ZTOV(zp); + int zp_is_dir = (vp->v_type == VDIR); + boolean_t unlinked = B_FALSE; + int error; + + dnlc_remove(ZTOV(dzp), dl->dl_name); + + if (!(flag & ZRENAMING)) { + dmu_buf_will_dirty(zp->z_dbuf, tx); + + if (vn_vfswlock(vp)) /* prevent new mounts on zp */ + return (EBUSY); + + if (vn_ismntpt(vp)) { /* don't remove mount point */ + vn_vfsunlock(vp); + return (EBUSY); + } + + mutex_enter(&zp->z_lock); + if (zp_is_dir && !zfs_dirempty(zp)) { /* dir not empty */ + mutex_exit(&zp->z_lock); + vn_vfsunlock(vp); + return (EEXIST); + } + if (zp->z_phys->zp_links <= zp_is_dir) { + zfs_panic_recover("zfs: link count on %s is %u, " + "should be at least %u", + zp->z_vnode->v_path ? zp->z_vnode->v_path : + "", (int)zp->z_phys->zp_links, + zp_is_dir + 1); + zp->z_phys->zp_links = zp_is_dir + 1; + } + if (--zp->z_phys->zp_links == zp_is_dir) { + zp->z_unlinked = B_TRUE; + zp->z_phys->zp_links = 0; + unlinked = B_TRUE; + } else { + zfs_time_stamper_locked(zp, STATE_CHANGED, tx); + } + mutex_exit(&zp->z_lock); + vn_vfsunlock(vp); + } + + dmu_buf_will_dirty(dzp->z_dbuf, tx); + mutex_enter(&dzp->z_lock); + dzp->z_phys->zp_size--; /* one dirent removed */ + dzp->z_phys->zp_links -= zp_is_dir; /* ".." link from zp */ + zfs_time_stamper_locked(dzp, CONTENT_MODIFIED, tx); + mutex_exit(&dzp->z_lock); + + 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) + *unlinkedp = unlinked; + else if (unlinked) + zfs_unlinked_add(zp, tx); + + return (0); +} + +/* + * Indicate whether the directory is empty. Works with or without z_lock + * held, but can only be consider a hint in the latter case. Returns true + * if only "." and ".." remain and there's no work in progress. + */ +boolean_t +zfs_dirempty(znode_t *dzp) +{ + return (dzp->z_phys->zp_size == 2 && dzp->z_dirlocks == 0); +} + +int +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; + int error; + zfs_fuid_info_t *fuidp = NULL; + + *xvpp = NULL; + + 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) + dmu_tx_wait(tx); + dmu_tx_abort(tx); + return (error); + } + 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 = xzp->z_id; + + (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); + + return (0); +} + +/* + * Return a znode for the extended attribute directory for zp. + * ** If the directory does not already exist, it is created ** + * + * IN: zp - znode to obtain attribute directory from + * cr - credentials of caller + * flags - flags from the VOP_LOOKUP call + * + * OUT: xzpp - pointer to extended attribute znode + * + * RETURN: 0 on success + * error number on failure + */ +int +zfs_get_xattrdir(znode_t *zp, vnode_t **xvpp, cred_t *cr, int flags) +{ + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + znode_t *xzp; + zfs_dirlock_t *dl; + vattr_t va; + int error; +top: + error = zfs_dirent_lock(&dl, zp, "", &xzp, ZXATTR, NULL, NULL); + if (error) + return (error); + + if (xzp != NULL) { + *xvpp = ZTOV(xzp); + zfs_dirent_unlock(dl); + return (0); + } + + ASSERT(zp->z_phys->zp_xattr == 0); + + if (!(flags & CREATE_XATTR_DIR)) { + zfs_dirent_unlock(dl); + return (ENOENT); + } + + if (zfsvfs->z_vfs->vfs_flag & VFS_RDONLY) { + zfs_dirent_unlock(dl); + return (EROFS); + } + + /* + * The ability to 'create' files in an attribute + * directory comes from the write_xattr permission on the base file. + * + * The ability to 'search' an attribute directory requires + * read_xattr permission on the base file. + * + * Once in a directory the ability to read/write attributes + * is controlled by the permissions on the attribute file. + */ + va.va_mask = AT_TYPE | AT_MODE | AT_UID | AT_GID; + va.va_type = VDIR; + va.va_mode = S_IFDIR | S_ISVTX | 0777; + zfs_fuid_map_ids(zp, cr, &va.va_uid, &va.va_gid); + + error = zfs_make_xattrdir(zp, &va, xvpp, cr); + zfs_dirent_unlock(dl); + + if (error == ERESTART && zfsvfs->z_assign == TXG_NOWAIT) { + /* NB: we already did dmu_tx_wait() if necessary */ + goto top; + } + + return (error); +} + +/* + * Decide whether it is okay to remove within a sticky directory. + * + * In sticky directories, write access is not sufficient; + * you can remove entries from a directory only if: + * + * you own the directory, + * you own the entry, + * the entry is a plain file and you have write access, + * or you are privileged (checked in secpolicy...). + * + * The function returns 0 if remove access is granted. + */ +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) + 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, 0, B_FALSE, cr) == 0)) + return (0); + else + return (secpolicy_vnode_remove(cr)); +} diff --git a/zfs/lib/libdmu-ctl/zfs_fuid.c b/zfs/lib/libdmu-ctl/zfs_fuid.c new file mode 100644 index 0000000..59c9adf --- /dev/null +++ b/zfs/lib/libdmu-ctl/zfs_fuid.c @@ -0,0 +1,688 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL 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 "@(#)zfs_fuid.c 1.5 08/01/31 SMI" + +#include +#include +#include +#include +#include +#include +#include +#ifdef _KERNEL +#include +#include +#include +#include +#endif +#include + +/* + * 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; + +/* + * 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->f_ksid->kd_name); +} + +#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); + } + } + + 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; + + /* + * 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 = ""; + 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); + + rw_enter(&zfsvfs->z_fuid_lock, RW_READER); + findnode = avl_find(&zfsvfs->z_fuid_domain, &searchnode, &loc); + rw_exit(&zfsvfs->z_fuid_lock); + + if (findnode) { + 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; + + domnode = kmem_alloc(sizeof (fuid_domain_t), KM_SLEEP); + domnode->f_ksid = searchnode.f_ksid; + + rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER); + 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); + domain = zfs_fuid_idx_domain(&zfsvfs->z_fuid_idx, idx); + 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); + + 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); + } + 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); + + 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)); +} + +/* + * 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 { + 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 = ""; + } + } + + 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; + + 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); + } + } + } + + /* + * 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/zfs/lib/libdmu-ctl/zfs_ioctl.c b/zfs/lib/libdmu-ctl/zfs_ioctl.c new file mode 100644 index 0000000..e4d2534 --- /dev/null +++ b/zfs/lib/libdmu-ctl/zfs_ioctl.c @@ -0,0 +1,3055 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL 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 "@(#)zfs_ioctl.c 1.61 08/04/27 SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zfs_namecheck.h" +#include "zfs_prop.h" +#include "zfs_deleg.h" + +extern struct modlfs zfs_modlfs; + +extern void zfs_init(void); +extern void zfs_fini(void); + +ldi_ident_t zfs_li = NULL; +dev_info_t *zfs_dip; + +typedef int zfs_ioc_func_t(zfs_cmd_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; + boolean_t zvec_his_log; +} zfs_ioc_vec_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, ...) +{ + const char *newfile; + char buf[256]; + va_list adx; + + /* + * Get rid of annoying "../common/" prefix to filename. + */ + newfile = strrchr(file, '/'); + if (newfile != NULL) { + newfile = newfile + 1; /* Get rid of leading / */ + } else { + newfile = file; + } + + va_start(adx, fmt); + (void) vsnprintf(buf, sizeof (buf), fmt, adx); + va_end(adx); + + /* + * To get this data, use the zfs-dprintf probe as so: + * dtrace -q -n 'zfs-dprintf \ + * /stringof(arg0) == "dbuf.c"/ \ + * {printf("%s: %s", stringof(arg1), stringof(arg3))}' + * arg0 = file name + * arg1 = function name + * arg2 = line number + * arg3 = message + */ + DTRACE_PROBE4(zfs__dprintf, + 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 == NULL) + 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); +} + +/* + * zfs_check_version + * + * Return non-zero if the spa version is less than requested version. + */ +static int +zfs_check_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_check_version + * + * Return non-zero if the ZPL version is less than requested version. + */ +static int +zpl_check_version(const char *name, int version) +{ + objset_t *os; + int rc = 1; + + if (dmu_objset_open(name, DMU_OST_ANY, + DS_MODE_STANDARD | DS_MODE_READONLY, &os) == 0) { + uint64_t propversion; + + if (zfs_get_zplprop(os, ZFS_PROP_VERSION, + &propversion) == 0) { + rc = !(propversion >= 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(zfs_cmd_t *zc, cred_t *cr) +{ + return (0); +} + +/* + * Policy for dataset read operations (list children, get statistics). Requires + * no privileges, but must be visible in the local zone. + */ +/* ARGSUSED */ +static int +zfs_secpolicy_read(zfs_cmd_t *zc, cred_t *cr) +{ + if (INGLOBALZONE(curproc) || + zone_dataset_visible(zc->zc_name, NULL)) + return (0); + + return (ENOENT); +} + +static int +zfs_dozonecheck(const char *dataset, cred_t *cr) +{ + uint64_t zoned; + int writable = 1; + + /* + * 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) && + !zone_dataset_visible(dataset, &writable)) + return (ENOENT); + + if (dsl_prop_get_integer(dataset, "zoned", &zoned, NULL)) + return (ENOENT); + + if (INGLOBALZONE(curproc)) { + /* + * If the fs is zoned, only root can access it from the + * global zone. + */ + if (secpolicy_zfs(cr) && zoned) + return (EPERM); + } else { + /* + * If we are in a local zone, the 'zoned' property must be set. + */ + if (!zoned) + return (EPERM); + + /* must be writable by this zone */ + if (!writable) + return (EPERM); + } + return (0); +} + +int +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(curproc)) + return (EPERM); + break; + + case ZFS_PROP_QUOTA: + if (!INGLOBALZONE(curproc)) { + 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; + + error = zfs_dozonecheck(zc->zc_name, cr); + if (error) + return (error); + + /* + * 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(curproc)) + 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 (vp->v_vfsp->vfs_fstype != zfsfstype || + (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)); + } +} + +static int +zfs_get_parent(const char *datasetname, char *parent, int parentsize) +{ + char *cp; + + /* + * Remove the @bla or /bla from the end of the name to get the parent. + */ + (void) strncpy(parent, datasetname, parentsize); + cp = strrchr(parent, '@'); + if (cp != NULL) { + cp[0] = '\0'; + } else { + 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_STANDARD | 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_open_obj(dd->dd_pool, + dd->dd_phys->dd_origin_obj, NULL, + DS_MODE_NONE, 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_close(pclone, DS_MODE_NONE, 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_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); +} + +/* + * Policy for pool operations - create/destroy pools, add vdevs, etc. Requires + * SYS_CONFIG privilege, which is not available in a local zone. + */ +/* ARGSUSED */ +static int +zfs_secpolicy_config(zfs_cmd_t *zc, cred_t *cr) +{ + if (secpolicy_sys_config(cr, B_FALSE) != 0) + return (EPERM); + + return (0); +} + +/* + * 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(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)); + } +} + +/* + * Returns the nvlist as specified by the user in the zfs_cmd_t. + */ +static int +get_nvlist(uint64_t nvl, uint64_t size, nvlist_t **nvp) +{ + char *packed; + int error; + nvlist_t *list = NULL; + + /* + * Read in and unpack the user-supplied nvlist. + */ + if (size == 0) + return (EINVAL); + + packed = kmem_alloc(size, KM_SLEEP); + + if ((error = xcopyin((void *)(uintptr_t)nvl, packed, size)) != 0) { + kmem_free(packed, size); + return (error); + } + + if ((error = nvlist_unpack(packed, size, &list, 0)) != 0) { + kmem_free(packed, size); + return (error); + } + + kmem_free(packed, size); + + *nvp = list; + return (0); +} + +static int +put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl) +{ + char *packed = NULL; + size_t size; + int error; + + VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0); + + if (size > zc->zc_nvlist_dst_size) { + error = ENOMEM; + } 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, + size); + kmem_free(packed, size); + } + + zc->zc_nvlist_dst_size = size; + return (error); +} + +static int +zfs_ioc_pool_create(zfs_cmd_t *zc) +{ + int error; + nvlist_t *config, *props = NULL; + char *buf; + + if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, + &config)) + 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); + } + + buf = history_str_get(zc); + + error = spa_create(zc->zc_name, config, props, buf); + + if (buf != NULL) + history_str_free(buf); + + nvlist_free(config); + + if (props) + nvlist_free(props); + + return (error); +} + +static int +zfs_ioc_pool_destroy(zfs_cmd_t *zc) +{ + 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, *props = NULL; + uint64_t guid; + + 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 + 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) +{ + int error; + zfs_log_history(zc); + error = spa_export(zc->zc_name, NULL); + return (error); +} + +static int +zfs_ioc_pool_configs(zfs_cmd_t *zc) +{ + nvlist_t *configs; + int error; + + if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL) + return (EEXIST); + + error = put_nvlist(zc, configs); + + nvlist_free(configs); + + return (error); +} + +static int +zfs_ioc_pool_stats(zfs_cmd_t *zc) +{ + nvlist_t *config; + int error; + int ret = 0; + + error = spa_get_stats(zc->zc_name, &config, zc->zc_value, + sizeof (zc->zc_value)); + + if (config != NULL) { + ret = put_nvlist(zc, config); + nvlist_free(config); + + /* + * The config may be present even if 'error' is non-zero. + * In this case we return success, and preserve the real errno + * in 'zc_cookie'. + */ + zc->zc_cookie = error; + } else { + ret = error; + } + + return (ret); +} + +/* + * Try to import the given pool, returning pool stats as appropriate so that + * user land knows which devices are available and overall pool health. + */ +static int +zfs_ioc_pool_tryimport(zfs_cmd_t *zc) +{ + nvlist_t *tryconfig, *config; + int error; + + if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, + &tryconfig)) != 0) + return (error); + + config = spa_tryimport(tryconfig); + + nvlist_free(tryconfig); + + if (config == NULL) + return (EINVAL); + + error = put_nvlist(zc, config); + nvlist_free(config); + + return (error); +} + +static int +zfs_ioc_pool_scrub(zfs_cmd_t *zc) +{ + spa_t *spa; + int error; + + if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) + return (error); + + mutex_enter(&spa_namespace_lock); + error = spa_scrub(spa, zc->zc_cookie, B_FALSE); + mutex_exit(&spa_namespace_lock); + + spa_close(spa, FTAG); + + return (error); +} + +static int +zfs_ioc_pool_freeze(zfs_cmd_t *zc) +{ + spa_t *spa; + int error; + + error = spa_open(zc->zc_name, &spa, FTAG); + if (error == 0) { + spa_freeze(spa); + spa_close(spa, FTAG); + } + return (error); +} + +static int +zfs_ioc_pool_upgrade(zfs_cmd_t *zc) +{ + spa_t *spa; + int error; + + if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) + return (error); + + 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); +} + +static int +zfs_ioc_pool_get_history(zfs_cmd_t *zc) +{ + spa_t *spa; + char *hist_buf; + uint64_t size; + int error; + + if ((size = zc->zc_history_len) == 0) + return (EINVAL); + + if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) + return (error); + + if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) { + spa_close(spa, FTAG); + return (ENOTSUP); + } + + 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, + zc->zc_history_len); + } + + spa_close(spa, FTAG); + kmem_free(hist_buf, size); + return (error); +} + +static int +zfs_ioc_dsobj_to_dsname(zfs_cmd_t *zc) +{ + int error; + + if (error = dsl_dsobj_to_dsname(zc->zc_name, zc->zc_obj, zc->zc_value)) + return (error); + + return (0); +} + +static int +zfs_ioc_obj_to_path(zfs_cmd_t *zc) +{ + objset_t *osp; + int error; + + if ((error = dmu_objset_open(zc->zc_name, DMU_OST_ZFS, + DS_MODE_NONE | 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); + + return (error); +} + +static int +zfs_ioc_vdev_add(zfs_cmd_t *zc) +{ + spa_t *spa; + int error; + 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. + * + * 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_bootfs != 0 && nl2cache == 0 && nspares == 0) { + spa_close(spa, FTAG); + return (EDOM); + } + + if (error == 0) { + error = spa_vdev_add(spa, config); + nvlist_free(config); + } + spa_close(spa, FTAG); + return (error); +} + +static int +zfs_ioc_vdev_remove(zfs_cmd_t *zc) +{ + spa_t *spa; + int error; + + error = spa_open(zc->zc_name, &spa, FTAG); + if (error != 0) + return (error); + error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE); + spa_close(spa, FTAG); + return (error); +} + +static int +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); + switch (zc->zc_cookie) { + case VDEV_STATE_ONLINE: + error = vdev_online(spa, zc->zc_guid, zc->zc_obj, &newstate); + break; + + case VDEV_STATE_OFFLINE: + error = vdev_offline(spa, zc->zc_guid, zc->zc_obj); + break; + + 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); +} + +static int +zfs_ioc_vdev_attach(zfs_cmd_t *zc) +{ + spa_t *spa; + int replacing = zc->zc_cookie; + nvlist_t *config; + int error; + + if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) + return (error); + + 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); + } + + spa_close(spa, FTAG); + return (error); +} + +static int +zfs_ioc_vdev_detach(zfs_cmd_t *zc) +{ + spa_t *spa; + int error; + + if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) + return (error); + + error = spa_vdev_detach(spa, zc->zc_guid, B_FALSE); + + spa_close(spa, FTAG); + return (error); +} + +static int +zfs_ioc_vdev_setpath(zfs_cmd_t *zc) +{ + spa_t *spa; + char *path = zc->zc_value; + uint64_t guid = zc->zc_guid; + int error; + + error = spa_open(zc->zc_name, &spa, FTAG); + if (error != 0) + return (error); + + error = spa_vdev_setpath(spa, guid, path); + spa_close(spa, FTAG); + return (error); +} + +static int +zfs_os_open_retry(char *name, objset_t **os) +{ + int error; + +retry: + error = dmu_objset_open(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; + } + } + 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 + * zc_value alternate root + */ +static int +zfs_ioc_objset_stats(zfs_cmd_t *zc) +{ + objset_t *os = NULL; + int error; + nvlist_t *nv; + + if ((error = zfs_os_open_retry(zc->zc_name, &os)) != 0) + 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) { + 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 + * inconsistent. So this is a bit of a workaround... + */ + 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); + return (error); +} + +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 = zfs_os_open_retry(zc->zc_name, &os)) != 0) + 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_STANDARD + * open, because it could be inconsistent. + */ + if (zc->zc_nvlist_dst != NULL && + !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 + * zc_value alternate root + */ +static int +zfs_ioc_dataset_list_next(zfs_cmd_t *zc) +{ + objset_t *os; + int error; + char *p; + + if ((error = zfs_os_open_retry(zc->zc_name, &os)) != 0) { + if (error == ENOENT) + error = ESRCH; + return (error); + } + + p = strrchr(zc->zc_name, '/'); + if (p == NULL || p[1] != '\0') + (void) strlcat(zc->zc_name, "/", sizeof (zc->zc_name)); + p = zc->zc_name + strlen(zc->zc_name); + + do { + error = dmu_dir_list_next(os, + sizeof (zc->zc_name) - (p - zc->zc_name), p, + NULL, &zc->zc_cookie); + if (error == ENOENT) + error = ESRCH; + } while (error == 0 && !INGLOBALZONE(curproc) && + !zone_dataset_visible(zc->zc_name, NULL)); + + /* + * If it's a hidden dataset (ie. with a '$' in its name), don't + * try to get stats for it. Userland will skip over it. + */ + 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 + * zc_value alternate root + */ +static int +zfs_ioc_snapshot_list_next(zfs_cmd_t *zc) +{ + objset_t *os; + int error; + + if ((error = zfs_os_open_retry(zc->zc_name, &os)) != 0) { + if (error == ENOENT) + error = ESRCH; + return (error); + } + + /* + * A dataset name of maximum length cannot have any snapshots, + * so exit immediately. + */ + if (strlcat(zc->zc_name, "@", sizeof (zc->zc_name)) >= MAXNAMELEN) { + dmu_objset_close(os); + return (ESRCH); + } + + 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, NULL); + if (error == ENOENT) + error = ESRCH; + + if (error == 0) + error = zfs_ioc_objset_stats(zc); /* fill in the stats */ + + /* if we failed, undo the @ that we tacked on to zc_name */ + if (error != 0) + *strchr(zc->zc_name, '@') = '\0'; + + dmu_objset_close(os); + return (error); +} + +int +zfs_set_prop_nvlist(const char *name, nvlist_t *nvl) +{ + nvpair_t *elem; + int error; + uint64_t intval; + char *strval; + + /* + * First validate permission to set all of the properties + */ + 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) { + /* + * If this is a user-defined property, it must be a + * string, and there is no further validation to do. + */ + if (!zfs_prop_user(propname) || + nvpair_type(elem) != DATA_TYPE_STRING) + return (EINVAL); + + 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 that this value is valid for this pool version + */ + switch (prop) { + case ZFS_PROP_COMPRESSION: + /* + * If the user specified gzip compression, make sure + * the SPA supports it. We ignore any errors here since + * 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 (zfs_check_version(name, + SPA_VERSION_GZIP_COMPRESSION)) + return (ENOTSUP); + } + break; + + case ZFS_PROP_COPIES: + if (zfs_check_version(name, SPA_VERSION_DITTO_BLOCKS)) + return (ENOTSUP); + break; + + case ZFS_PROP_SHARESMB: + if (zpl_check_version(name, ZPL_VERSION_FUID)) + return (ENOTSUP); + break; + } + if ((error = zfs_secpolicy_setprop(name, prop, CRED())) != 0) + return (error); + } + + 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) + 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; + + case ZFS_PROP_RESERVATION: + if ((error = nvpair_value_uint64(elem, &intval)) != 0 || + (error = dsl_dir_set_reservation(name, + intval)) != 0) + return (error); + break; + + case ZFS_PROP_REFRESERVATION: + if ((error = nvpair_value_uint64(elem, &intval)) != 0 || + (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) + 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) + return (EINVAL); + VERIFY(nvpair_value_string(elem, &strval) == 0); + if ((error = dsl_prop_set(name, + nvpair_name(elem), 1, strlen(strval) + 1, + strval)) != 0) + return (error); + } else if (nvpair_type(elem) == DATA_TYPE_UINT64) { + const char *unused; + + VERIFY(nvpair_value_uint64(elem, &intval) == 0); + + switch (zfs_prop_get_type(prop)) { + case PROP_TYPE_NUMBER: + break; + case PROP_TYPE_STRING: + return (EINVAL); + 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"); + break; + } + + if ((error = dsl_prop_set(name, propname, + 8, 1, &intval)) != 0) + return (error); + } else { + return (EINVAL); + } + break; + } + } + + return (0); +} + +/* + * inputs: + * zc_name name of filesystem + * zc_value name of property to inherit + * zc_nvlist_src{_size} nvlist of properties to apply + * + * outputs: none + */ +static int +zfs_ioc_set_prop(zfs_cmd_t *zc) +{ + nvlist_t *nvl; + int error; + + if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, + &nvl)) != 0) + return (error); + + error = zfs_set_prop_nvlist(zc->zc_name, 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 *props; + spa_t *spa; + int error; + + 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(props); + return (error); + } + + error = spa_prop_set(spa, props); + + nvlist_free(props); + spa_close(spa, FTAG); + + return (error); +} + +static int +zfs_ioc_pool_get_props(zfs_cmd_t *zc) +{ + spa_t *spa; + int error; + nvlist_t *nvp = NULL; + + if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) + return (error); + + error = spa_prop_get(spa, &nvp); + + if (error == 0 && zc->zc_nvlist_dst != NULL) + error = put_nvlist(zc, nvp); + else + error = EFAULT; + + spa_close(spa, FTAG); + + if (nvp) + nvlist_free(nvp); + return (error); +} + +static int +zfs_ioc_iscsi_perm_check(zfs_cmd_t *zc) +{ + 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); +} + +/* + * 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, ddi_driver_major(zfs_dip))); +} + +/* + * inputs: + * zc_name name of volume + * + * outputs: none + */ +static int +zfs_ioc_remove_minor(zfs_cmd_t *zc) +{ + return (zvol_remove_minor(zc->zc_name)); +} + +/* + * Search the vfs list for a specified resource. Returns a pointer to it + * or NULL if no suitable entry is found. The caller of this routine + * is responsible for releasing the returned vfs pointer. + */ +static vfs_t * +zfs_get_vfs(const char *resource) +{ + struct vfs *vfsp; + struct vfs *vfs_found = NULL; + + vfs_list_read_lock(); + vfsp = rootvfs; + do { + if (strcmp(refstr_value(vfsp->vfs_resource), resource) == 0) { + VFS_HOLD(vfsp); + vfs_found = vfsp; + break; + } + vfsp = vfsp->vfs_next; + } while (vfsp != rootvfs); + vfs_list_unlock(); + return (vfs_found); +} + +/* ARGSUSED */ +static void +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 + * dataset name of dataset we are creating + * + * outputs: + * zplprops values for the zplprops we attach to the master node object + * + * 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(const char *dataset, nvlist_t *createprops, + nvlist_t *zplprops, uint64_t zplver, boolean_t *is_ci) +{ + objset_t *os; + char parentname[MAXNAMELEN]; + char *cp; + uint64_t sense = ZFS_PROP_UNDEFINED; + uint64_t norm = ZFS_PROP_UNDEFINED; + uint64_t u8 = ZFS_PROP_UNDEFINED; + int error = 0; + + ASSERT(zplprops != NULL); + + (void) strlcpy(parentname, dataset, sizeof (parentname)); + cp = strrchr(parentname, '/'); + ASSERT(cp != NULL); + cp[0] = '\0'; + + /* + * Pull out creator prop choices, if any. + */ + if (createprops) { + (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 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. We only need check the + * ZPL version because we've already checked by now that the + * SPA version is compatible with the selected ZPL version. + */ + if (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); + + /* + * Open parent object set so we can inherit zplprop values if + * necessary. + */ + if ((error = zfs_os_open_retry(parentname, &os)) != 0) + return (error); + + 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); + + dmu_objset_close(os); + return (0); +} + +/* + * 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_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) { + + case DMU_OST_ZFS: + cbfunc = zfs_create_cb; + break; + + case DMU_OST_ZVOL: + cbfunc = zvol_create_cb; + break; + + default: + cbfunc = NULL; + break; + } + if (strchr(zc->zc_name, '@') || + strchr(zc->zc_name, '%')) + return (EINVAL); + + if (zc->zc_nvlist_src != NULL && + (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, + &nvprops)) != 0) + return (error); + + zct.zct_zplprops = NULL; + zct.zct_props = nvprops; + + if (zc->zc_value[0] != '\0') { + /* + * We're creating a clone of an existing snapshot. + */ + zc->zc_value[sizeof (zc->zc_value) - 1] = '\0'; + if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0) { + nvlist_free(nvprops); + return (EINVAL); + } + + error = dmu_objset_open(zc->zc_value, type, + DS_MODE_STANDARD | 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) { + dmu_objset_close(clone); + nvlist_free(nvprops); + return (error); + } + dmu_objset_close(clone); + } else { + boolean_t is_insensitive = B_FALSE; + + if (cbfunc == NULL) { + nvlist_free(nvprops); + return (EINVAL); + } + + if (type == DMU_OST_ZVOL) { + uint64_t volsize, volblocksize; + + if (nvprops == NULL || + nvlist_lookup_uint64(nvprops, + zfs_prop_to_name(ZFS_PROP_VOLSIZE), + &volsize) != 0) { + nvlist_free(nvprops); + return (EINVAL); + } + + if ((error = nvlist_lookup_uint64(nvprops, + zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), + &volblocksize)) != 0 && error != ENOENT) { + nvlist_free(nvprops); + return (EINVAL); + } + + if (error != 0) + volblocksize = zfs_prop_default_numeric( + ZFS_PROP_VOLBLOCKSIZE); + + if ((error = zvol_check_volblocksize( + volblocksize)) != 0 || + (error = zvol_check_volsize(volsize, + volblocksize)) != 0) { + nvlist_free(nvprops); + return (error); + } + } else if (type == DMU_OST_ZFS) { + uint64_t version; + int error; + + /* + * Default ZPL version to non-FUID capable if the + * pool is not upgraded to support FUIDs. + */ + if (zfs_check_version(zc->zc_name, SPA_VERSION_FUID)) + version = ZPL_VERSION_FUID - 1; + else + version = ZPL_VERSION; + + /* + * Potentially override default ZPL version based + * on creator's request. + */ + (void) nvlist_lookup_uint64(nvprops, + zfs_prop_to_name(ZFS_PROP_VERSION), &version); + + /* + * Make sure version we ended up with is kosher + */ + if ((version < ZPL_VERSION_INITIAL || + version > ZPL_VERSION) || + (version >= ZPL_VERSION_FUID && + zfs_check_version(zc->zc_name, SPA_VERSION_FUID))) { + nvlist_free(nvprops); + return (ENOTSUP); + } + + /* + * 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, version, &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, nvprops)) != 0) + (void) dmu_objset_destroy(zc->zc_name); + } + nvlist_free(nvprops); + return (error); +} + +/* + * 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) +{ + if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) + return (EINVAL); + return (dmu_objset_snapshot(zc->zc_name, + zc->zc_value, zc->zc_cookie)); +} + +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 (snapname) { + (void) strcat(name, "@"); + (void) strcat(name, snapname); + vfsp = zfs_get_vfs(name); + cp = strchr(name, '@'); + *cp = '\0'; + } else if (strchr(name, '@')) { + vfsp = zfs_get_vfs(name); + } + + if (vfsp) { + /* + * Always force the unmount for snapshots. + */ + int flag = MS_FORCE; + int err; + + if ((err = vn_vfswlock(vfsp->vfs_vnodecovered)) != 0) { + VFS_RELE(vfsp); + return (err); + } + VFS_RELE(vfsp); + if ((err = dounmount(vfsp, flag, kcred)) != 0) + return (err); + } + 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) +{ + int err; + + if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) + return (EINVAL); + err = dmu_objset_find(zc->zc_name, + zfs_unmount_snap, zc->zc_value, DS_FIND_CHILDREN); + if (err) + return (err); + 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) +{ + if (strchr(zc->zc_name, '@') && zc->zc_objset_type == DMU_OST_ZFS) { + int err = zfs_unmount_snap(zc->zc_name, NULL); + if (err) + return (err); + } + + 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) +{ + 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_STANDARD, &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() closes 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) +{ + 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 || + strchr(zc->zc_value, '%')) + return (EINVAL); + + /* + * Unmount snapshot unless we're doing a recursive rename, + * in which case the dataset code figures out which snapshots + * to unmount. + */ + if (!recursive && strchr(zc->zc_name, '@') != NULL && + zc->zc_objset_type == DMU_OST_ZFS) { + int err = zfs_unmount_snap(zc->zc_name, NULL); + if (err) + return (err); + } + + return (dmu_objset_rename(zc->zc_name, zc->zc_value, recursive)); +} + +/* + * 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_recv(zfs_cmd_t *zc) +{ + 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; + 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, '%')) + return (EINVAL); + + (void) strcpy(tofs, zc->zc_value); + tosnap = strchr(tofs, '@'); + *tosnap = '\0'; + tosnap++; + + if (zc->zc_nvlist_src != NULL && + (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, + &props)) != 0) + return (error); + + fd = zc->zc_cookie; + fp = getf(fd); + if (fp == NULL) { + nvlist_free(props); + return (EBADF); + } + + /* + * 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(tofs, DMU_OST_ZFS, + DS_MODE_STANDARD | DS_MODE_READONLY, &os); + if (!error) { + 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 (!mutex_tryenter(&zfsvfs->z_online_recv_lock)) { + VFS_RELE(zfsvfs->z_vfs); + dmu_objset_close(os); + nvlist_free(props); + releasef(fd); + return (EBUSY); + } + } else { + mutex_exit(&os->os->os_user_ptr_lock); + } + dmu_objset_close(os); + } + + if (zc->zc_string[0]) { + error = dmu_objset_open(zc->zc_string, DMU_OST_ANY, + DS_MODE_STANDARD | DS_MODE_READONLY, &origin); + if (error) { + if (zfsvfs != NULL) { + mutex_exit(&zfsvfs->z_online_recv_lock); + VFS_RELE(zfsvfs->z_vfs); + } + nvlist_free(props); + releasef(fd); + return (error); + } + } + + error = dmu_recv_begin(tofs, tosnap, &zc->zc_begin_record, + force, origin, zfsvfs != NULL, &drc); + if (origin) + dmu_objset_close(origin); + if (error) { + if (zfsvfs != NULL) { + mutex_exit(&zfsvfs->z_online_recv_lock); + VFS_RELE(zfsvfs->z_vfs); + } + nvlist_free(props); + releasef(fd); + return (error); + } + + /* + * If properties are supplied, they are to completely replace + * the existing ones; "inherit" any existing properties. + */ + if (props) { + objset_t *os; + nvlist_t *nv = NULL; + + error = dmu_objset_open(tofs, DMU_OST_ANY, + DS_MODE_STANDARD | DS_MODE_READONLY | DS_MODE_INCONSISTENT, + &os); + if (error == 0) { + error = dsl_prop_get_all(os, &nv); + dmu_objset_close(os); + } + if (error == 0) { + nvpair_t *elem; + zfs_cmd_t *zc2; + zc2 = kmem_alloc(sizeof (zfs_cmd_t), KM_SLEEP); + + (void) strcpy(zc2->zc_name, tofs); + for (elem = nvlist_next_nvpair(nv, NULL); elem; + elem = nvlist_next_nvpair(nv, elem)) { + (void) strcpy(zc2->zc_value, nvpair_name(elem)); + if (zfs_secpolicy_inherit(zc2, CRED()) == 0) + (void) zfs_ioc_inherit_prop(zc2); + } + kmem_free(zc2, sizeof (zfs_cmd_t)); + } + if (nv) + nvlist_free(nv); + } + + /* + * Set properties. Note, we ignore errors. Would be better to + * do best-effort in zfs_set_prop_nvlist, too. + */ + (void) zfs_set_prop_nvlist(tofs, props); + nvlist_free(props); + + off = fp->f_offset; + error = dmu_recv_stream(&drc, fp->f_vnode, &off); + + if (error == 0) { + if (zfsvfs != NULL) { + char osname[MAXNAMELEN]; + int mode; + + 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 { + error = dmu_recv_end(&drc); + } + } + if (zfsvfs != NULL) { + mutex_exit(&zfsvfs->z_online_recv_lock); + VFS_RELE(zfsvfs->z_vfs); + } + + zc->zc_cookie = off - fp->f_offset; + if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) + fp->f_offset = off; + + releasef(fd); + 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_send(zfs_cmd_t *zc) +{ + objset_t *fromsnap = NULL; + objset_t *tosnap; + 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); + if (error) + return (error); + + if (zc->zc_value[0] != '\0') { + char buf[MAXPATHLEN]; + char *cp; + + (void) strncpy(buf, zc->zc_name, sizeof (buf)); + cp = strchr(buf, '@'); + if (cp) + *(cp+1) = 0; + (void) strncat(buf, zc->zc_value, sizeof (buf)); + error = dmu_objset_open(buf, DMU_OST_ANY, + DS_MODE_STANDARD | DS_MODE_READONLY, &fromsnap); + if (error) { + dmu_objset_close(tosnap); + return (error); + } + } + + fp = getf(zc->zc_cookie); + if (fp == NULL) { + dmu_objset_close(tosnap); + if (fromsnap) + dmu_objset_close(fromsnap); + return (EBADF); + } + + off = fp->f_offset; + error = dmu_sendbackup(tosnap, fromsnap, zc->zc_obj, fp->f_vnode, &off); + + if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) + fp->f_offset = off; + releasef(zc->zc_cookie); + if (fromsnap) + dmu_objset_close(fromsnap); + dmu_objset_close(tosnap); + return (error); +} + +static int +zfs_ioc_inject_fault(zfs_cmd_t *zc) +{ + int id, error; + + error = zio_inject_fault(zc->zc_name, (int)zc->zc_guid, &id, + &zc->zc_inject_record); + + if (error == 0) + zc->zc_guid = (uint64_t)id; + + return (error); +} + +static int +zfs_ioc_clear_fault(zfs_cmd_t *zc) +{ + return (zio_clear_fault((int)zc->zc_guid)); +} + +static int +zfs_ioc_inject_list_next(zfs_cmd_t *zc) +{ + int id = (int)zc->zc_guid; + int error; + + error = zio_inject_list_next(&id, zc->zc_name, sizeof (zc->zc_name), + &zc->zc_inject_record); + + zc->zc_guid = id; + + return (error); +} + +static int +zfs_ioc_error_log(zfs_cmd_t *zc) +{ + spa_t *spa; + int error; + size_t count = (size_t)zc->zc_nvlist_dst_size; + + if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) + return (error); + + error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_nvlist_dst, + &count); + if (error == 0) + zc->zc_nvlist_dst_size = count; + else + zc->zc_nvlist_dst_size = spa_get_errlog_size(spa); + + spa_close(spa, FTAG); + + return (error); +} + +static int +zfs_ioc_clear(zfs_cmd_t *zc) +{ + spa_t *spa; + vdev_t *vd; + uint64_t txg; + int error; + + if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) + return (error); + + /* + * Try to resume any I/Os which may have been suspended + * as a result of a complete pool failure. + */ + if (!list_is_empty(&spa->spa_zio_list)) { + if (zio_vdev_resume_io(spa) != 0) { + spa_close(spa, FTAG); + return (EIO); + } + } + + txg = spa_vdev_enter(spa); + + if (zc->zc_guid == 0) { + vd = NULL; + } else if ((vd = spa_lookup_by_guid(spa, zc->zc_guid)) == NULL) { + spa_aux_vdev_t *sav; + int i; + + /* + * Check if this is an l2cache device. + */ + ASSERT(spa != NULL); + sav = &spa->spa_l2cache; + for (i = 0; i < sav->sav_count; i++) { + if (sav->sav_vdevs[i]->vdev_guid == zc->zc_guid) { + vd = sav->sav_vdevs[i]; + break; + } + } + + if (vd == NULL) { + (void) spa_vdev_exit(spa, NULL, txg, ENODEV); + spa_close(spa, FTAG); + return (ENODEV); + } + } + + vdev_clear(spa, vd, B_TRUE); + + (void) spa_vdev_exit(spa, NULL, txg, 0); + + 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) +{ + char *cp; + + /* + * We don't need to unmount *all* the origin fs's snapshots, but + * it's easier. + */ + cp = strchr(zc->zc_value, '@'); + if (cp) + *cp = '\0'; + (void) dmu_objset_find(zc->zc_value, + zfs_unmount_snap, NULL, DS_FIND_SNAPSHOTS); + return (dsl_dataset_promote(zc->zc_name)); +} + +/* + * 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; +kmutex_t zfs_share_lock; + +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); +} + +static int +zfs_ioc_share(zfs_cmd_t *zc) +{ + 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); + +} + +/* + * 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 zfs_ioc_vec_t zfs_ioc_vec[] = { + { 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 }, +}; + +static int +zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) +{ + zfs_cmd_t *zc; + uint_t vec; + int error, rc; + + if (getminor(dev) != 0) + return (zvol_ioctl(dev, cmd, arg, flag, cr, rvalp)); + + vec = cmd - ZFS_IOC; + ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip)); + + if (vec >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0])) + return (EINVAL); + + zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP); + + error = xcopyin((void *)arg, zc, sizeof (zfs_cmd_t)); + + if (error == 0) + error = zfs_ioc_vec[vec].zvec_secpolicy(zc, cr); + + /* + * Ensure that all pool/dataset names are valid before we pass down to + * the lower layers. + */ + if (error == 0) { + zc->zc_name[sizeof (zc->zc_name) - 1] = '\0'; + switch (zfs_ioc_vec[vec].zvec_namecheck) { + case POOL_NAME: + if (pool_namecheck(zc->zc_name, NULL, NULL) != 0) + error = EINVAL; + break; + + case DATASET_NAME: + if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0) + error = EINVAL; + break; + + case NO_NAME: + break; + } + } + + if (error == 0) + error = zfs_ioc_vec[vec].zvec_func(zc); + + rc = xcopyout(zc, (void *)arg, sizeof (zfs_cmd_t)); + if (error == 0) { + error = rc; + if (zfs_ioc_vec[vec].zvec_his_log == B_TRUE) + zfs_log_history(zc); + } + + kmem_free(zc, sizeof (zfs_cmd_t)); + return (error); +} + +static int +zfs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + if (cmd != DDI_ATTACH) + return (DDI_FAILURE); + + if (ddi_create_minor_node(dip, "zfs", S_IFCHR, 0, + DDI_PSEUDO, 0) == DDI_FAILURE) + return (DDI_FAILURE); + + zfs_dip = dip; + + ddi_report_dev(dip); + + return (DDI_SUCCESS); +} + +static int +zfs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + if (spa_busy() || zfs_busy() || zvol_busy()) + return (DDI_FAILURE); + + if (cmd != DDI_DETACH) + return (DDI_FAILURE); + + zfs_dip = NULL; + + ddi_prop_remove_all(dip); + ddi_remove_minor_node(dip, NULL); + + return (DDI_SUCCESS); +} + +/*ARGSUSED*/ +static int +zfs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) +{ + switch (infocmd) { + case DDI_INFO_DEVT2DEVINFO: + *result = zfs_dip; + return (DDI_SUCCESS); + + case DDI_INFO_DEVT2INSTANCE: + *result = (void *)0; + return (DDI_SUCCESS); + } + + return (DDI_FAILURE); +} + +/* + * OK, so this is a little weird. + * + * /dev/zfs is the control node, i.e. minor 0. + * /dev/zvol/[r]dsk/pool/dataset are the zvols, minor > 0. + * + * /dev/zfs has basically nothing to do except serve up ioctls, + * so most of the standard driver entry points are in zvol.c. + */ +static struct cb_ops zfs_cb_ops = { + zvol_open, /* open */ + zvol_close, /* close */ + zvol_strategy, /* strategy */ + nodev, /* print */ + zvol_dump, /* dump */ + zvol_read, /* read */ + zvol_write, /* write */ + zfsdev_ioctl, /* ioctl */ + nodev, /* devmap */ + nodev, /* mmap */ + nodev, /* segmap */ + nochpoll, /* poll */ + ddi_prop_op, /* prop_op */ + NULL, /* streamtab */ + D_NEW | D_MP | D_64BIT, /* Driver compatibility flag */ + CB_REV, /* version */ + nodev, /* async read */ + nodev, /* async write */ +}; + +static struct dev_ops zfs_dev_ops = { + DEVO_REV, /* version */ + 0, /* refcnt */ + zfs_info, /* info */ + nulldev, /* identify */ + nulldev, /* probe */ + zfs_attach, /* attach */ + zfs_detach, /* detach */ + nodev, /* reset */ + &zfs_cb_ops, /* driver operations */ + NULL /* no bus operations */ +}; + +static struct modldrv zfs_modldrv = { + &mod_driverops, "ZFS storage pool version " SPA_VERSION_STRING, + &zfs_dev_ops +}; + +static struct modlinkage modlinkage = { + MODREV_1, + (void *)&zfs_modlfs, + (void *)&zfs_modldrv, + NULL +}; + + +uint_t zfs_fsyncer_key; +extern uint_t rrw_tsd_key; + +int +_init(void) +{ + int error; + + spa_init(FREAD | FWRITE); + zfs_init(); + zvol_init(); + + if ((error = mod_install(&modlinkage)) != 0) { + zvol_fini(); + zfs_fini(); + spa_fini(); + return (error); + } + + tsd_create(&zfs_fsyncer_key, NULL); + tsd_create(&rrw_tsd_key, NULL); + + error = ldi_ident_from_mod(&modlinkage, &zfs_li); + ASSERT(error == 0); + mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL); + + return (0); +} + +int +_fini(void) +{ + int error; + + if (spa_busy() || zfs_busy() || zvol_busy() || zio_injection_enabled) + return (EBUSY); + + if ((error = mod_remove(&modlinkage)) != 0) + return (error); + + zvol_fini(); + zfs_fini(); + spa_fini(); + if (zfs_nfsshare_inited) + (void) ddi_modclose(nfs_mod); + if (zfs_smbshare_inited) + (void) ddi_modclose(smbsrv_mod); + if (zfs_nfsshare_inited || zfs_smbshare_inited) + (void) ddi_modclose(sharefs_mod); + + tsd_destroy(&zfs_fsyncer_key); + ldi_ident_release(zfs_li); + zfs_li = NULL; + mutex_destroy(&zfs_share_lock); + + return (error); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} diff --git a/zfs/lib/libdmu-ctl/zfs_log.c b/zfs/lib/libdmu-ctl/zfs_log.c new file mode 100644 index 0000000..3643858 --- /dev/null +++ b/zfs/lib/libdmu-ctl/zfs_log.c @@ -0,0 +1,693 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "@(#)zfs_log.c 1.13 08/04/09 SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * All the functions in this file are used to construct the log entries + * 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_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, 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; + + /* + * 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; + 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; + + /* + * 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; + zp->z_last_itx = seq; +} + +/* + * zfs_log_remove() handles both TX_REMOVE and TX_RMDIR transactions. + */ +void +zfs_log_remove(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype, + znode_t *dzp, char *name) +{ + itx_t *itx; + uint64_t seq; + lr_remove_t *lr; + size_t namesize = strlen(name) + 1; + + if (zilog == NULL) + return; + + itx = zil_itx_create(txtype, sizeof (*lr) + namesize); + lr = (lr_remove_t *)&itx->itx_lr; + lr->lr_doid = dzp->z_id; + bcopy(name, (char *)(lr + 1), namesize); + + seq = zil_itx_assign(zilog, itx, tx); + dzp->z_last_itx = seq; +} + +/* + * zfs_log_link() handles TX_LINK transactions. + */ +void +zfs_log_link(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype, + znode_t *dzp, znode_t *zp, char *name) +{ + itx_t *itx; + uint64_t seq; + lr_link_t *lr; + size_t namesize = strlen(name) + 1; + + if (zilog == NULL) + return; + + itx = zil_itx_create(txtype, sizeof (*lr) + namesize); + lr = (lr_link_t *)&itx->itx_lr; + lr->lr_doid = dzp->z_id; + lr->lr_link_obj = zp->z_id; + bcopy(name, (char *)(lr + 1), namesize); + + seq = zil_itx_assign(zilog, itx, tx); + dzp->z_last_itx = seq; + zp->z_last_itx = seq; +} + +/* + * zfs_log_symlink() handles TX_SYMLINK transactions. + */ +void +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; + lr_create_t *lr; + size_t namesize = strlen(name) + 1; + size_t linksize = strlen(link) + 1; + + if (zilog == NULL) + return; + + itx = zil_itx_create(txtype, sizeof (*lr) + namesize + linksize); + 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; + 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]; + bcopy(name, (char *)(lr + 1), namesize); + bcopy(link, (char *)(lr + 1) + namesize, linksize); + + seq = zil_itx_assign(zilog, itx, tx); + dzp->z_last_itx = seq; + zp->z_last_itx = seq; +} + +/* + * zfs_log_rename() handles TX_RENAME transactions. + */ +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) +{ + itx_t *itx; + uint64_t seq; + lr_rename_t *lr; + size_t snamesize = strlen(sname) + 1; + size_t dnamesize = strlen(dname) + 1; + + if (zilog == NULL) + return; + + itx = zil_itx_create(txtype, sizeof (*lr) + snamesize + dnamesize); + lr = (lr_rename_t *)&itx->itx_lr; + lr->lr_sdoid = sdzp->z_id; + lr->lr_tdoid = tdzp->z_id; + bcopy(sname, (char *)(lr + 1), snamesize); + bcopy(dname, (char *)(lr + 1) + snamesize, dnamesize); + + seq = zil_itx_assign(zilog, itx, tx); + sdzp->z_last_itx = seq; + tdzp->z_last_itx = seq; + szp->z_last_itx = seq; +} + +/* + * zfs_log_write() handles TX_WRITE transactions. + */ +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 resid, int ioflag) +{ + itx_wr_state_t write_state; + boolean_t slogging; + uintptr_t fsync_cnt; + + if (zilog == NULL || zp->z_unlinked) + return; + + /* + * Writes are handled in three different ways: + * + * WR_INDIRECT: + * If the write is greater than zfs_immediate_write_sz and there are + * no separate logs in this pool then later *if* we need to log the + * write then dmu_sync() is used to immediately write the block and + * its block pointer is put in the log record. + * WR_COPIED: + * If we know we'll immediately be committing the + * 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. + */ + slogging = spa_has_slogs(zilog->zl_spa); + if (resid > zfs_immediate_write_sz && !slogging) + write_state = WR_INDIRECT; + else if (ioflag & (FSYNC | FDSYNC)) + write_state = WR_COPIED; + else + write_state = WR_NEED_COPY; + + 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 there are slogs and the write would overflow the largest + * block, then because we don't want to use the main pool + * to dmu_sync, we have to split the write. + */ + if (slogging && 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; + 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; + + 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; + } +} + +/* + * zfs_log_truncate() handles TX_TRUNCATE transactions. + */ +void +zfs_log_truncate(zilog_t *zilog, dmu_tx_t *tx, int txtype, + znode_t *zp, uint64_t off, uint64_t len) +{ + itx_t *itx; + uint64_t seq; + lr_truncate_t *lr; + + if (zilog == NULL || zp->z_unlinked) + return; + + itx = zil_itx_create(txtype, sizeof (*lr)); + lr = (lr_truncate_t *)&itx->itx_lr; + lr->lr_foid = zp->z_id; + lr->lr_offset = off; + lr->lr_length = len; + + itx->itx_sync = (zp->z_sync_cnt != 0); + seq = zil_itx_assign(zilog, itx, tx); + zp->z_last_itx = seq; +} + +/* + * zfs_log_setattr() handles TX_SETATTR transactions. + */ +void +zfs_log_setattr(zilog_t *zilog, dmu_tx_t *tx, int txtype, + 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; + xvattr_t *xvap = (xvattr_t *)vap; + size_t recsize = sizeof (lr_setattr_t); + void *start; + + + if (zilog == NULL || zp->z_unlinked) + return; + + /* + * 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; + 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); + zp->z_last_itx = seq; +} + +/* + * zfs_log_acl() handles TX_ACL transactions. + */ +void +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; + + txtype = (zp->z_zfsvfs->z_version == ZPL_VERSION_INITIAL) ? + TX_ACL_V0 : TX_ACL; + + if (txtype == TX_ACL) + lrsize = sizeof (*lr); + else + lrsize = sizeof (*lrv0); + + if (zilog == NULL || zp->z_unlinked) + return; + + txsize = lrsize + + ((txtype == TX_ACL) ? ZIL_ACE_LENGTH(aclbytes) : aclbytes) + + (fuidp ? fuidp->z_domain_str_sz : 0) + + sizeof (uint64) * (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; + 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); + zp->z_last_itx = seq; +} diff --git a/zfs/lib/libdmu-ctl/zfs_replay.c b/zfs/lib/libdmu-ctl/zfs_replay.c new file mode 100644 index 0000000..ca9990d --- /dev/null +++ b/zfs/lib/libdmu-ctl/zfs_replay.c @@ -0,0 +1,876 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL 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 "@(#)zfs_replay.c 1.7 08/01/14 SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Functions to replay ZFS intent log (ZIL) records + * The functions are called through a function vector (zfs_replay_vector) + * which is indexed by the transaction type. + */ + +static void +zfs_init_vattr(vattr_t *vap, uint64_t mask, uint64_t mode, + uint64_t uid, uint64_t gid, uint64_t rdev, uint64_t nodeid) +{ + bzero(vap, sizeof (*vap)); + vap->va_mask = (uint_t)mask; + vap->va_type = IFTOVT(mode); + vap->va_mode = mode & MODEMASK; + 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; +} + +/* ARGSUSED */ +static int +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); + } + + error = VOP_CREATE(ZTOV(dzp), name, &xva.xva_vattr, + 0, 0, &vp, kcred, vflg, NULL, &vsec); + 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); + } + error = VOP_MKDIR(ZTOV(dzp), name, &xva.xva_vattr, + &vp, kcred, NULL, vflg, &vsec); + 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 = NULL; /* location determined later */ + char *link; /* symlink content follows name */ + znode_t *dzp; + vnode_t *vp = NULL; + 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; + int error; + + 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); + + 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 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); + } + + 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: + if (name == NULL) + name = (char *)start; + + error = VOP_CREATE(ZTOV(dzp), name, &xva.xva_vattr, + 0, 0, &vp, kcred, vflg, NULL, NULL); + 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: + if (name == NULL) + name = (char *)(lr + 1); + + error = VOP_MKDIR(ZTOV(dzp), name, &xva.xva_vattr, + &vp, kcred, NULL, vflg, NULL); + break; + case TX_MKXATTR: + 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), name, &xva.xva_vattr, + link, kcred, NULL, vflg); + break; + default: + error = ENOTSUP; + } + +out: + if (error == 0 && vp != NULL) + VN_RELE(vp); + + VN_RELE(ZTOV(dzp)); + + if (zfsvfs->z_fuid_replay) + zfs_fuid_info_free(zfsvfs->z_fuid_replay); + zfsvfs->z_fuid_replay = NULL; + return (error); +} + +static int +zfs_replay_remove(zfsvfs_t *zfsvfs, lr_remove_t *lr, boolean_t byteswap) +{ + char *name = (char *)(lr + 1); /* name follows lr_remove_t */ + znode_t *dzp; + int error; + int vflg = 0; + + if (byteswap) + byteswap_uint64_array(lr, sizeof (*lr)); + + if ((error = zfs_zget(zfsvfs, lr->lr_doid, &dzp)) != 0) + return (error); + + if (lr->lr_common.lrc_txtype & TX_CI) + vflg |= FIGNORECASE; + + switch ((int)lr->lr_common.lrc_txtype) { + case TX_REMOVE: + error = VOP_REMOVE(ZTOV(dzp), name, kcred, NULL, vflg); + break; + case TX_RMDIR: + error = VOP_RMDIR(ZTOV(dzp), name, NULL, kcred, NULL, vflg); + break; + default: + error = ENOTSUP; + } + + VN_RELE(ZTOV(dzp)); + + return (error); +} + +static int +zfs_replay_link(zfsvfs_t *zfsvfs, lr_link_t *lr, boolean_t byteswap) +{ + char *name = (char *)(lr + 1); /* name follows lr_link_t */ + znode_t *dzp, *zp; + int error; + int vflg = 0; + + if (byteswap) + byteswap_uint64_array(lr, sizeof (*lr)); + + if ((error = zfs_zget(zfsvfs, lr->lr_doid, &dzp)) != 0) + return (error); + + if ((error = zfs_zget(zfsvfs, lr->lr_link_obj, &zp)) != 0) { + VN_RELE(ZTOV(dzp)); + return (error); + } + + if (lr->lr_common.lrc_txtype & TX_CI) + vflg |= FIGNORECASE; + + error = VOP_LINK(ZTOV(dzp), ZTOV(zp), name, kcred, NULL, vflg); + + VN_RELE(ZTOV(zp)); + VN_RELE(ZTOV(dzp)); + + return (error); +} + +static int +zfs_replay_rename(zfsvfs_t *zfsvfs, lr_rename_t *lr, boolean_t byteswap) +{ + char *sname = (char *)(lr + 1); /* sname and tname follow lr_rename_t */ + char *tname = sname + strlen(sname) + 1; + znode_t *sdzp, *tdzp; + int error; + int vflg = 0; + + if (byteswap) + byteswap_uint64_array(lr, sizeof (*lr)); + + if ((error = zfs_zget(zfsvfs, lr->lr_sdoid, &sdzp)) != 0) + return (error); + + if ((error = zfs_zget(zfsvfs, lr->lr_tdoid, &tdzp)) != 0) { + VN_RELE(ZTOV(sdzp)); + return (error); + } + + if (lr->lr_common.lrc_txtype & TX_CI) + vflg |= FIGNORECASE; + + error = VOP_RENAME(ZTOV(sdzp), sname, ZTOV(tdzp), tname, kcred, + NULL, vflg); + + VN_RELE(ZTOV(tdzp)); + VN_RELE(ZTOV(sdzp)); + + return (error); +} + +static int +zfs_replay_write(zfsvfs_t *zfsvfs, lr_write_t *lr, boolean_t byteswap) +{ + char *data = (char *)(lr + 1); /* data follows lr_write_t */ + znode_t *zp; + int error; + ssize_t resid; + + if (byteswap) + byteswap_uint64_array(lr, sizeof (*lr)); + + if ((error = zfs_zget(zfsvfs, lr->lr_foid, &zp)) != 0) { + /* + * As we can log writes out of order, it's possible the + * file has been removed. In this case just drop the write + * and return success. + */ + if (error == ENOENT) + error = 0; + return (error); + } + + error = vn_rdwr(UIO_WRITE, ZTOV(zp), data, lr->lr_length, + lr->lr_offset, UIO_SYSSPACE, 0, RLIM64_INFINITY, kcred, &resid); + + VN_RELE(ZTOV(zp)); + + return (error); +} + +static int +zfs_replay_truncate(zfsvfs_t *zfsvfs, lr_truncate_t *lr, boolean_t byteswap) +{ + znode_t *zp; + flock64_t fl; + int error; + + if (byteswap) + byteswap_uint64_array(lr, sizeof (*lr)); + + if ((error = zfs_zget(zfsvfs, lr->lr_foid, &zp)) != 0) { + /* + * As we can log truncates out of order, it's possible the + * file has been removed. In this case just drop the truncate + * and return success. + */ + if (error == ENOENT) + error = 0; + return (error); + } + + bzero(&fl, sizeof (fl)); + fl.l_type = F_WRLCK; + fl.l_whence = 0; + fl.l_start = lr->lr_offset; + fl.l_len = lr->lr_length; + + error = VOP_SPACE(ZTOV(zp), F_FREESP, &fl, FWRITE | FOFFMAX, + lr->lr_offset, kcred, NULL); + + VN_RELE(ZTOV(zp)); + + return (error); +} + +static int +zfs_replay_setattr(zfsvfs_t *zfsvfs, lr_setattr_t *lr, boolean_t byteswap) +{ + znode_t *zp; + xvattr_t xva; + vattr_t *vap = &xva.xva_vattr; + int error; + void *start; + + 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 + * file has been removed. In this case just drop the setattr + * and return success. + */ + if (error == ENOENT) + error = 0; + return (error); + } + + zfs_init_vattr(vap, lr->lr_mask, lr->lr_mode, + lr->lr_uid, lr->lr_gid, 0, lr->lr_foid); + + 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); + + error = VOP_SETATTR(ZTOV(zp), vap, 0, kcred, NULL); + + zfs_fuid_info_free(zfsvfs->z_fuid_replay); + zfsvfs->z_fuid_replay = NULL; + VN_RELE(ZTOV(zp)); + + return (error); +} + +static int +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 */ + 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_aclentp = ace; + + error = VOP_SETSECATTR(ZTOV(zp), &vsa, 0, kcred, NULL); + + 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_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) { + /* + * 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_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); + + if (zfsvfs->z_fuid_replay) + zfs_fuid_info_free(zfsvfs->z_fuid_replay); + + zfsvfs->z_fuid_replay = NULL; + VN_RELE(ZTOV(zp)); + + return (error); +} + +/* + * Callback vectors for replaying records + */ +zil_replay_func_t *zfs_replay_vector[TX_MAX_TYPE] = { + zfs_replay_error, /* 0 no such transaction type */ + zfs_replay_create, /* TX_CREATE */ + zfs_replay_create, /* TX_MKDIR */ + zfs_replay_create, /* TX_MKXATTR */ + zfs_replay_create, /* TX_SYMLINK */ + zfs_replay_remove, /* TX_REMOVE */ + zfs_replay_remove, /* TX_RMDIR */ + zfs_replay_link, /* TX_LINK */ + zfs_replay_rename, /* TX_RENAME */ + 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/zfs/lib/libdmu-ctl/zfs_rlock.c b/zfs/lib/libdmu-ctl/zfs_rlock.c new file mode 100644 index 0000000..44ec73b --- /dev/null +++ b/zfs/lib/libdmu-ctl/zfs_rlock.c @@ -0,0 +1,602 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: 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 "@(#)zfs_rlock.c 1.4 07/08/08 SMI" + +/* + * This file contains the code to implement file range locking in + * ZFS, although there isn't much specific to ZFS (all that comes to mind + * support for growing the blocksize). + * + * Interface + * --------- + * Defined in zfs_rlock.h but essentially: + * rl = zfs_range_lock(zp, off, len, lock_type); + * zfs_range_unlock(rl); + * zfs_range_reduce(rl, off, len); + * + * AVL tree + * -------- + * An AVL tree is used to maintain the state of the existing ranges + * that are locked for exclusive (writer) or shared (reader) use. + * The starting range offset is used for searching and sorting the tree. + * + * Common case + * ----------- + * The (hopefully) usual case is of no overlaps or contention for + * locks. On entry to zfs_lock_range() a rl_t is allocated; the tree + * searched that finds no overlap, and *this* rl_t is placed in the tree. + * + * Overlaps/Reference counting/Proxy locks + * --------------------------------------- + * The avl code only allows one node at a particular offset. Also it's very + * inefficient to search through all previous entries looking for overlaps + * (because the very 1st in the ordered list might be at offset 0 but + * cover the whole file). + * So this implementation uses reference counts and proxy range locks. + * Firstly, only reader locks use reference counts and proxy locks, + * because writer locks are exclusive. + * When a reader lock overlaps with another then a proxy lock is created + * for that range and replaces the original lock. If the overlap + * is exact then the reference count of the proxy is simply incremented. + * Otherwise, the proxy lock is split into smaller lock ranges and + * new proxy locks created for non overlapping ranges. + * The reference counts are adjusted accordingly. + * Meanwhile, the orginal lock is kept around (this is the callers handle) + * and its offset and length are used when releasing the lock. + * + * Thread coordination + * ------------------- + * In order to make wakeups efficient and to ensure multiple continuous + * readers on a range don't starve a writer for the same range lock, + * two condition variables are allocated in each rl_t. + * If a writer (or reader) can't get a range it initialises the writer + * (or reader) cv; sets a flag saying there's a writer (or reader) waiting; + * and waits on that cv. When a thread unlocks that range it wakes up all + * writers then all readers before destroying the lock. + * + * Append mode writes + * ------------------ + * Append mode writes need to lock a range at the end of a file. + * The offset of the end of the file is determined under the + * range locking mutex, and the lock type converted from RL_APPEND to + * RL_WRITER and the range locked. + * + * Grow block handling + * ------------------- + * ZFS supports multiple block sizes currently upto 128K. The smallest + * block size is used for the file which is grown as needed. During this + * growth all other writers and readers must be excluded. + * So if the block size needs to be grown then the whole file is + * exclusively locked, then later the caller will reduce the lock + * range to just the range to be written using zfs_reduce_range. + */ + +#include + +/* + * Check if a write lock can be grabbed, or wait and recheck until available. + */ +static void +zfs_range_lock_writer(znode_t *zp, rl_t *new) +{ + avl_tree_t *tree = &zp->z_range_avl; + rl_t *rl; + avl_index_t where; + uint64_t end_size; + uint64_t off = new->r_off; + uint64_t len = new->r_len; + + for (;;) { + /* + * Range locking is also used by zvol and uses a + * dummied up znode. However, for zvol, we don't need to + * append or grow blocksize, and besides we don't have + * a z_phys or z_zfsvfs - so skip that processing. + * + * Yes, this is ugly, and would be solved by not handling + * grow or append in range lock code. If that was done then + * we could make the range locking code generically available + * to other non-zfs consumers. + */ + if (zp->z_vnode) { /* caller is ZPL */ + /* + * If in append mode pick up the current end of file. + * This is done under z_range_lock to avoid races. + */ + if (new->r_type == RL_APPEND) + new->r_off = zp->z_phys->zp_size; + + /* + * If we need to grow the block size then grab the whole + * file range. This is also done under z_range_lock to + * avoid races. + */ + end_size = MAX(zp->z_phys->zp_size, new->r_off + len); + if (end_size > zp->z_blksz && (!ISP2(zp->z_blksz) || + zp->z_blksz < zp->z_zfsvfs->z_max_blksz)) { + new->r_off = 0; + new->r_len = UINT64_MAX; + } + } + + /* + * First check for the usual case of no locks + */ + if (avl_numnodes(tree) == 0) { + new->r_type = RL_WRITER; /* convert to writer */ + avl_add(tree, new); + return; + } + + /* + * Look for any locks in the range. + */ + rl = avl_find(tree, new, &where); + if (rl) + goto wait; /* already locked at same offset */ + + rl = (rl_t *)avl_nearest(tree, where, AVL_AFTER); + if (rl && (rl->r_off < new->r_off + new->r_len)) + goto wait; + + rl = (rl_t *)avl_nearest(tree, where, AVL_BEFORE); + if (rl && rl->r_off + rl->r_len > new->r_off) + goto wait; + + new->r_type = RL_WRITER; /* convert possible RL_APPEND */ + avl_insert(tree, new, where); + return; +wait: + if (!rl->r_write_wanted) { + cv_init(&rl->r_wr_cv, NULL, CV_DEFAULT, NULL); + rl->r_write_wanted = B_TRUE; + } + cv_wait(&rl->r_wr_cv, &zp->z_range_lock); + + /* reset to original */ + new->r_off = off; + new->r_len = len; + } +} + +/* + * If this is an original (non-proxy) lock then replace it by + * a proxy and return the proxy. + */ +static rl_t * +zfs_range_proxify(avl_tree_t *tree, rl_t *rl) +{ + rl_t *proxy; + + if (rl->r_proxy) + return (rl); /* already a proxy */ + + ASSERT3U(rl->r_cnt, ==, 1); + ASSERT(rl->r_write_wanted == B_FALSE); + ASSERT(rl->r_read_wanted == B_FALSE); + avl_remove(tree, rl); + rl->r_cnt = 0; + + /* create a proxy range lock */ + proxy = kmem_alloc(sizeof (rl_t), KM_SLEEP); + proxy->r_off = rl->r_off; + proxy->r_len = rl->r_len; + proxy->r_cnt = 1; + proxy->r_type = RL_READER; + proxy->r_proxy = B_TRUE; + proxy->r_write_wanted = B_FALSE; + proxy->r_read_wanted = B_FALSE; + avl_add(tree, proxy); + + return (proxy); +} + +/* + * Split the range lock at the supplied offset + * returning the *front* proxy. + */ +static rl_t * +zfs_range_split(avl_tree_t *tree, rl_t *rl, uint64_t off) +{ + rl_t *front, *rear; + + ASSERT3U(rl->r_len, >, 1); + ASSERT3U(off, >, rl->r_off); + ASSERT3U(off, <, rl->r_off + rl->r_len); + ASSERT(rl->r_write_wanted == B_FALSE); + ASSERT(rl->r_read_wanted == B_FALSE); + + /* create the rear proxy range lock */ + rear = kmem_alloc(sizeof (rl_t), KM_SLEEP); + rear->r_off = off; + rear->r_len = rl->r_off + rl->r_len - off; + rear->r_cnt = rl->r_cnt; + rear->r_type = RL_READER; + rear->r_proxy = B_TRUE; + rear->r_write_wanted = B_FALSE; + rear->r_read_wanted = B_FALSE; + + front = zfs_range_proxify(tree, rl); + front->r_len = off - rl->r_off; + + avl_insert_here(tree, rear, front, AVL_AFTER); + return (front); +} + +/* + * Create and add a new proxy range lock for the supplied range. + */ +static void +zfs_range_new_proxy(avl_tree_t *tree, uint64_t off, uint64_t len) +{ + rl_t *rl; + + ASSERT(len); + rl = kmem_alloc(sizeof (rl_t), KM_SLEEP); + rl->r_off = off; + rl->r_len = len; + rl->r_cnt = 1; + rl->r_type = RL_READER; + rl->r_proxy = B_TRUE; + rl->r_write_wanted = B_FALSE; + rl->r_read_wanted = B_FALSE; + avl_add(tree, rl); +} + +static void +zfs_range_add_reader(avl_tree_t *tree, rl_t *new, rl_t *prev, avl_index_t where) +{ + rl_t *next; + uint64_t off = new->r_off; + uint64_t len = new->r_len; + + /* + * prev arrives either: + * - pointing to an entry at the same offset + * - pointing to the entry with the closest previous offset whose + * range may overlap with the new range + * - null, if there were no ranges starting before the new one + */ + if (prev) { + if (prev->r_off + prev->r_len <= off) { + prev = NULL; + } else if (prev->r_off != off) { + /* + * convert to proxy if needed then + * split this entry and bump ref count + */ + prev = zfs_range_split(tree, prev, off); + prev = AVL_NEXT(tree, prev); /* move to rear range */ + } + } + ASSERT((prev == NULL) || (prev->r_off == off)); + + if (prev) + next = prev; + else + next = (rl_t *)avl_nearest(tree, where, AVL_AFTER); + + if (next == NULL || off + len <= next->r_off) { + /* no overlaps, use the original new rl_t in the tree */ + avl_insert(tree, new, where); + return; + } + + if (off < next->r_off) { + /* Add a proxy for initial range before the overlap */ + zfs_range_new_proxy(tree, off, next->r_off - off); + } + + new->r_cnt = 0; /* will use proxies in tree */ + /* + * We now search forward through the ranges, until we go past the end + * of the new range. For each entry we make it a proxy if it + * isn't already, then bump its reference count. If there's any + * gaps between the ranges then we create a new proxy range. + */ + for (prev = NULL; next; prev = next, next = AVL_NEXT(tree, next)) { + if (off + len <= next->r_off) + break; + if (prev && prev->r_off + prev->r_len < next->r_off) { + /* there's a gap */ + ASSERT3U(next->r_off, >, prev->r_off + prev->r_len); + zfs_range_new_proxy(tree, prev->r_off + prev->r_len, + next->r_off - (prev->r_off + prev->r_len)); + } + if (off + len == next->r_off + next->r_len) { + /* exact overlap with end */ + next = zfs_range_proxify(tree, next); + next->r_cnt++; + return; + } + if (off + len < next->r_off + next->r_len) { + /* new range ends in the middle of this block */ + next = zfs_range_split(tree, next, off + len); + next->r_cnt++; + return; + } + ASSERT3U(off + len, >, next->r_off + next->r_len); + next = zfs_range_proxify(tree, next); + next->r_cnt++; + } + + /* Add the remaining end range. */ + zfs_range_new_proxy(tree, prev->r_off + prev->r_len, + (off + len) - (prev->r_off + prev->r_len)); +} + +/* + * Check if a reader lock can be grabbed, or wait and recheck until available. + */ +static void +zfs_range_lock_reader(znode_t *zp, rl_t *new) +{ + avl_tree_t *tree = &zp->z_range_avl; + rl_t *prev, *next; + avl_index_t where; + uint64_t off = new->r_off; + uint64_t len = new->r_len; + + /* + * Look for any writer locks in the range. + */ +retry: + prev = avl_find(tree, new, &where); + if (prev == NULL) + prev = (rl_t *)avl_nearest(tree, where, AVL_BEFORE); + + /* + * Check the previous range for a writer lock overlap. + */ + if (prev && (off < prev->r_off + prev->r_len)) { + if ((prev->r_type == RL_WRITER) || (prev->r_write_wanted)) { + if (!prev->r_read_wanted) { + cv_init(&prev->r_rd_cv, NULL, CV_DEFAULT, NULL); + prev->r_read_wanted = B_TRUE; + } + cv_wait(&prev->r_rd_cv, &zp->z_range_lock); + goto retry; + } + if (off + len < prev->r_off + prev->r_len) + goto got_lock; + } + + /* + * Search through the following ranges to see if there's + * write lock any overlap. + */ + if (prev) + next = AVL_NEXT(tree, prev); + else + next = (rl_t *)avl_nearest(tree, where, AVL_AFTER); + for (; next; next = AVL_NEXT(tree, next)) { + if (off + len <= next->r_off) + goto got_lock; + if ((next->r_type == RL_WRITER) || (next->r_write_wanted)) { + if (!next->r_read_wanted) { + cv_init(&next->r_rd_cv, NULL, CV_DEFAULT, NULL); + next->r_read_wanted = B_TRUE; + } + cv_wait(&next->r_rd_cv, &zp->z_range_lock); + goto retry; + } + if (off + len <= next->r_off + next->r_len) + goto got_lock; + } + +got_lock: + /* + * Add the read lock, which may involve splitting existing + * locks and bumping ref counts (r_cnt). + */ + zfs_range_add_reader(tree, new, prev, where); +} + +/* + * Lock a range (offset, length) as either shared (RL_READER) + * or exclusive (RL_WRITER). Returns the range lock structure + * for later unlocking or reduce range (if entire file + * previously locked as RL_WRITER). + */ +rl_t * +zfs_range_lock(znode_t *zp, uint64_t off, uint64_t len, rl_type_t type) +{ + rl_t *new; + + ASSERT(type == RL_READER || type == RL_WRITER || type == RL_APPEND); + + new = kmem_alloc(sizeof (rl_t), KM_SLEEP); + new->r_zp = zp; + new->r_off = off; + new->r_len = len; + new->r_cnt = 1; /* assume it's going to be in the tree */ + new->r_type = type; + new->r_proxy = B_FALSE; + new->r_write_wanted = B_FALSE; + new->r_read_wanted = B_FALSE; + + mutex_enter(&zp->z_range_lock); + if (type == RL_READER) { + /* + * First check for the usual case of no locks + */ + if (avl_numnodes(&zp->z_range_avl) == 0) + avl_add(&zp->z_range_avl, new); + else + zfs_range_lock_reader(zp, new); + } else + zfs_range_lock_writer(zp, new); /* RL_WRITER or RL_APPEND */ + mutex_exit(&zp->z_range_lock); + return (new); +} + +/* + * Unlock a reader lock + */ +static void +zfs_range_unlock_reader(znode_t *zp, rl_t *remove) +{ + avl_tree_t *tree = &zp->z_range_avl; + rl_t *rl, *next; + uint64_t len; + + /* + * The common case is when the remove entry is in the tree + * (cnt == 1) meaning there's been no other reader locks overlapping + * with this one. Otherwise the remove entry will have been + * removed from the tree and replaced by proxies (one or + * more ranges mapping to the entire range). + */ + if (remove->r_cnt == 1) { + avl_remove(tree, remove); + if (remove->r_write_wanted) { + cv_broadcast(&remove->r_wr_cv); + 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); + ASSERT3U(remove->r_read_wanted, ==, 0); + /* + * Find start proxy representing this reader lock, + * then decrement ref count on all proxies + * that make up this range, freeing them as needed. + */ + rl = avl_find(tree, remove, NULL); + ASSERT(rl); + ASSERT(rl->r_cnt); + ASSERT(rl->r_type == RL_READER); + for (len = remove->r_len; len != 0; rl = next) { + len -= rl->r_len; + if (len) { + next = AVL_NEXT(tree, rl); + ASSERT(next); + ASSERT(rl->r_off + rl->r_len == next->r_off); + ASSERT(next->r_cnt); + ASSERT(next->r_type == RL_READER); + } + rl->r_cnt--; + if (rl->r_cnt == 0) { + avl_remove(tree, rl); + if (rl->r_write_wanted) { + cv_broadcast(&rl->r_wr_cv); + 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)); + } + } + } + kmem_free(remove, sizeof (rl_t)); +} + +/* + * Unlock range and destroy range lock structure. + */ +void +zfs_range_unlock(rl_t *rl) +{ + znode_t *zp = rl->r_zp; + + ASSERT(rl->r_type == RL_WRITER || rl->r_type == RL_READER); + ASSERT(rl->r_cnt == 1 || rl->r_cnt == 0); + ASSERT(!rl->r_proxy); + + mutex_enter(&zp->z_range_lock); + if (rl->r_type == RL_WRITER) { + /* writer locks can't be shared or split */ + avl_remove(&zp->z_range_avl, rl); + mutex_exit(&zp->z_range_lock); + if (rl->r_write_wanted) { + cv_broadcast(&rl->r_wr_cv); + 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)); + } else { + /* + * lock may be shared, let zfs_range_unlock_reader() + * release the lock and free the rl_t + */ + zfs_range_unlock_reader(zp, rl); + mutex_exit(&zp->z_range_lock); + } +} + +/* + * Reduce range locked as RL_WRITER from whole file to specified range. + * Asserts the whole file is exclusivly locked and so there's only one + * entry in the tree. + */ +void +zfs_range_reduce(rl_t *rl, uint64_t off, uint64_t len) +{ + znode_t *zp = rl->r_zp; + + /* Ensure there are no other locks */ + ASSERT(avl_numnodes(&zp->z_range_avl) == 1); + ASSERT(rl->r_off == 0); + ASSERT(rl->r_type == RL_WRITER); + ASSERT(!rl->r_proxy); + ASSERT3U(rl->r_len, ==, UINT64_MAX); + ASSERT3U(rl->r_cnt, ==, 1); + + mutex_enter(&zp->z_range_lock); + rl->r_off = off; + rl->r_len = len; + mutex_exit(&zp->z_range_lock); + if (rl->r_write_wanted) + cv_broadcast(&rl->r_wr_cv); + if (rl->r_read_wanted) + cv_broadcast(&rl->r_rd_cv); +} + +/* + * AVL comparison function used to order range locks + * Locks are ordered on the start offset of the range. + */ +int +zfs_range_compare(const void *arg1, const void *arg2) +{ + const rl_t *rl1 = arg1; + const rl_t *rl2 = arg2; + + if (rl1->r_off > rl2->r_off) + return (1); + if (rl1->r_off < rl2->r_off) + return (-1); + return (0); +} diff --git a/zfs/lib/libdmu-ctl/zfs_vfsops.c b/zfs/lib/libdmu-ctl/zfs_vfsops.c new file mode 100644 index 0000000..39c8ce4 --- /dev/null +++ b/zfs/lib/libdmu-ctl/zfs_vfsops.c @@ -0,0 +1,1671 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL 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 "@(#)zfs_vfsops.c 1.41 08/04/11 SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fs/fs_subr.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int zfsfstype; +vfsops_t *zfs_vfsops = NULL; +static major_t zfs_major; +static minor_t zfs_minor; +static kmutex_t zfs_dev_mtx; + +static int zfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr); +static int zfs_umount(vfs_t *vfsp, int fflag, cred_t *cr); +static int zfs_mountroot(vfs_t *vfsp, enum whymountroot); +static int zfs_root(vfs_t *vfsp, vnode_t **vpp); +static int zfs_statvfs(vfs_t *vfsp, struct statvfs64 *statp); +static int zfs_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp); +static void zfs_freevfs(vfs_t *vfsp); + +static const fs_operation_def_t zfs_vfsops_template[] = { + VFSNAME_MOUNT, { .vfs_mount = zfs_mount }, + VFSNAME_MOUNTROOT, { .vfs_mountroot = zfs_mountroot }, + VFSNAME_UNMOUNT, { .vfs_unmount = zfs_umount }, + VFSNAME_ROOT, { .vfs_root = zfs_root }, + VFSNAME_STATVFS, { .vfs_statvfs = zfs_statvfs }, + VFSNAME_SYNC, { .vfs_sync = zfs_sync }, + VFSNAME_VGET, { .vfs_vget = zfs_vget }, + VFSNAME_FREEVFS, { .vfs_freevfs = zfs_freevfs }, + NULL, NULL +}; + +static const fs_operation_def_t zfs_vfsops_eio_template[] = { + VFSNAME_FREEVFS, { .vfs_freevfs = zfs_freevfs }, + NULL, NULL +}; + +/* + * We need to keep a count of active fs's. + * This is necessary to prevent our module + * from being unloaded after a umount -f + */ +static uint32_t zfs_active_fs_count = 0; + +static char *noatime_cancel[] = { MNTOPT_ATIME, NULL }; +static char *atime_cancel[] = { MNTOPT_NOATIME, NULL }; +static char *noxattr_cancel[] = { MNTOPT_XATTR, NULL }; +static char *xattr_cancel[] = { MNTOPT_NOXATTR, NULL }; + +/* + * MO_DEFAULT is not used since the default value is determined + * by the equivalent property. + */ +static mntopt_t mntopts[] = { + { MNTOPT_NOXATTR, noxattr_cancel, NULL, 0, NULL }, + { MNTOPT_XATTR, xattr_cancel, NULL, 0, NULL }, + { MNTOPT_NOATIME, noatime_cancel, NULL, 0, NULL }, + { MNTOPT_ATIME, atime_cancel, NULL, 0, NULL } +}; + +static mntopts_t zfs_mntopts = { + sizeof (mntopts) / sizeof (mntopt_t), + mntopts +}; + +/*ARGSUSED*/ +int +zfs_sync(vfs_t *vfsp, short flag, cred_t *cr) +{ + /* + * Data integrity is job one. We don't want a compromised kernel + * writing to the storage pool, so we never sync during panic. + */ + if (panicstr) + return (0); + + /* + * SYNC_ATTR is used by fsflush() to force old filesystems like UFS + * to sync metadata, which they would otherwise cache indefinitely. + * Semantically, the only requirement is that the sync be initiated. + * The DMU syncs out txgs frequently, so there's nothing to do. + */ + if (flag & SYNC_ATTR) + return (0); + + if (vfsp != NULL) { + /* + * Sync a specific filesystem. + */ + zfsvfs_t *zfsvfs = vfsp->vfs_data; + + ZFS_ENTER(zfsvfs); + if (zfsvfs->z_log != NULL) + zil_commit(zfsvfs->z_log, UINT64_MAX, 0); + else + txg_wait_synced(dmu_objset_pool(zfsvfs->z_os), 0); + ZFS_EXIT(zfsvfs); + } else { + /* + * Sync all ZFS filesystems. This is what happens when you + * run sync(1M). Unlike other filesystems, ZFS honors the + * request by waiting for all pools to commit all dirty data. + */ + spa_sync_allpools(); + } + + return (0); +} + +static int +zfs_create_unique_device(dev_t *dev) +{ + major_t new_major; + + do { + ASSERT3U(zfs_minor, <=, MAXMIN32); + minor_t start = zfs_minor; + do { + mutex_enter(&zfs_dev_mtx); + if (zfs_minor >= MAXMIN32) { + /* + * If we're still using the real major + * keep out of /dev/zfs and /dev/zvol minor + * number space. If we're using a getudev()'ed + * major number, we can use all of its minors. + */ + if (zfs_major == ddi_name_to_major(ZFS_DRIVER)) + zfs_minor = ZFS_MIN_MINOR; + else + zfs_minor = 0; + } else { + zfs_minor++; + } + *dev = makedevice(zfs_major, zfs_minor); + mutex_exit(&zfs_dev_mtx); + } while (vfs_devismounted(*dev) && zfs_minor != start); + if (zfs_minor == start) { + /* + * We are using all ~262,000 minor numbers for the + * current major number. Create a new major number. + */ + if ((new_major = getudev()) == (major_t)-1) { + cmn_err(CE_WARN, + "zfs_mount: Can't get unique major " + "device number."); + return (-1); + } + mutex_enter(&zfs_dev_mtx); + zfs_major = new_major; + zfs_minor = 0; + + mutex_exit(&zfs_dev_mtx); + } else { + break; + } + /* CONSTANTCONDITION */ + } while (1); + + return (0); +} + +static void +atime_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + + if (newval == TRUE) { + zfsvfs->z_atime = TRUE; + vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NOATIME); + vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_ATIME, NULL, 0); + } else { + zfsvfs->z_atime = FALSE; + vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_ATIME); + vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NOATIME, NULL, 0); + } +} + +static void +xattr_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + + if (newval == TRUE) { + /* XXX locking on vfs_flag? */ + zfsvfs->z_vfs->vfs_flag |= VFS_XATTR; + vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NOXATTR); + vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_XATTR, NULL, 0); + } else { + /* XXX locking on vfs_flag? */ + zfsvfs->z_vfs->vfs_flag &= ~VFS_XATTR; + vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_XATTR); + vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NOXATTR, NULL, 0); + } +} + +static void +blksz_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + + if (newval < SPA_MINBLOCKSIZE || + newval > SPA_MAXBLOCKSIZE || !ISP2(newval)) + newval = SPA_MAXBLOCKSIZE; + + zfsvfs->z_max_blksz = newval; + zfsvfs->z_vfs->vfs_bsize = newval; +} + +static void +readonly_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + + if (newval) { + /* XXX locking on vfs_flag? */ + zfsvfs->z_vfs->vfs_flag |= VFS_RDONLY; + vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_RW); + vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_RO, NULL, 0); + } else { + /* XXX locking on vfs_flag? */ + zfsvfs->z_vfs->vfs_flag &= ~VFS_RDONLY; + vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_RO); + vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_RW, NULL, 0); + } +} + +static void +devices_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + + if (newval == FALSE) { + zfsvfs->z_vfs->vfs_flag |= VFS_NODEVICES; + vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_DEVICES); + vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NODEVICES, NULL, 0); + } else { + zfsvfs->z_vfs->vfs_flag &= ~VFS_NODEVICES; + vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NODEVICES); + vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_DEVICES, NULL, 0); + } +} + +static void +setuid_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + + if (newval == FALSE) { + zfsvfs->z_vfs->vfs_flag |= VFS_NOSETUID; + vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_SETUID); + vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NOSETUID, NULL, 0); + } else { + zfsvfs->z_vfs->vfs_flag &= ~VFS_NOSETUID; + vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NOSETUID); + vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_SETUID, NULL, 0); + } +} + +static void +exec_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + + if (newval == FALSE) { + zfsvfs->z_vfs->vfs_flag |= VFS_NOEXEC; + vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_EXEC); + vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NOEXEC, NULL, 0); + } else { + zfsvfs->z_vfs->vfs_flag &= ~VFS_NOEXEC; + vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NOEXEC); + vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_EXEC, NULL, 0); + } +} + +/* + * 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) +{ + zfsvfs_t *zfsvfs = arg; + + zfsvfs->z_show_ctldir = newval; +} + +static void +vscan_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + + zfsvfs->z_vscan = newval; +} + +static void +acl_mode_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + + zfsvfs->z_acl_mode = newval; +} + +static void +acl_inherit_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + + zfsvfs->z_acl_inherit = newval; +} + +static int +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 = B_FALSE; + int setuid, do_setuid = B_FALSE; + int exec, do_exec = B_FALSE; + int devices, do_devices = B_FALSE; + int xattr, do_xattr = B_FALSE; + int atime, do_atime = B_FALSE; + int error = 0; + + ASSERT(vfsp); + zfsvfs = vfsp->vfs_data; + ASSERT(zfsvfs); + os = zfsvfs->z_os; + + /* + * The act of registering our callbacks will destroy any mount + * options we may have. In order to enable temporary overrides + * of mount options, we stash away the current values and + * restore them after we register the callbacks. + */ + if (vfs_optionisset(vfsp, MNTOPT_RO, NULL)) { + readonly = B_TRUE; + do_readonly = B_TRUE; + } else if (vfs_optionisset(vfsp, MNTOPT_RW, NULL)) { + readonly = B_FALSE; + do_readonly = B_TRUE; + } + if (vfs_optionisset(vfsp, MNTOPT_NOSUID, NULL)) { + devices = B_FALSE; + setuid = B_FALSE; + do_devices = B_TRUE; + do_setuid = B_TRUE; + } else { + if (vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL)) { + devices = B_FALSE; + do_devices = B_TRUE; + } else if (vfs_optionisset(vfsp, MNTOPT_DEVICES, NULL)) { + devices = B_TRUE; + do_devices = B_TRUE; + } + + if (vfs_optionisset(vfsp, MNTOPT_NOSETUID, NULL)) { + setuid = B_FALSE; + do_setuid = B_TRUE; + } else if (vfs_optionisset(vfsp, MNTOPT_SETUID, NULL)) { + setuid = B_TRUE; + do_setuid = B_TRUE; + } + } + if (vfs_optionisset(vfsp, MNTOPT_NOEXEC, NULL)) { + exec = B_FALSE; + do_exec = B_TRUE; + } else if (vfs_optionisset(vfsp, MNTOPT_EXEC, NULL)) { + exec = B_TRUE; + do_exec = B_TRUE; + } + if (vfs_optionisset(vfsp, MNTOPT_NOXATTR, NULL)) { + xattr = B_FALSE; + do_xattr = B_TRUE; + } else if (vfs_optionisset(vfsp, MNTOPT_XATTR, NULL)) { + 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. + * + * It would probably be fine to just check for i/o error from + * the first prop_register(), but I guess I like to go + * overboard... + */ + ds = dmu_objset_ds(os); + error = dsl_prop_register(ds, "atime", atime_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + "xattr", xattr_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + "recordsize", blksz_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + "readonly", readonly_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + "devices", devices_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + "setuid", setuid_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + "exec", exec_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + "snapdir", snapdir_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + "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; + + /* + * Invoke our callbacks to restore temporary mount options. + */ + if (do_readonly) + readonly_changed_cb(zfsvfs, readonly); + if (do_setuid) + setuid_changed_cb(zfsvfs, setuid); + if (do_exec) + exec_changed_cb(zfsvfs, exec); + if (do_devices) + devices_changed_cb(zfsvfs, devices); + if (do_xattr) + xattr_changed_cb(zfsvfs, xattr); + if (do_atime) + atime_changed_cb(zfsvfs, atime); + + nbmand_changed_cb(zfsvfs, nbmand); + + return (0); + +unregister: + /* + * We may attempt to unregister some callbacks that are not + * registered, but this is OK; it will simply return ENOMSG, + * which we will ignore. + */ + (void) dsl_prop_unregister(ds, "atime", atime_changed_cb, zfsvfs); + (void) dsl_prop_unregister(ds, "xattr", xattr_changed_cb, zfsvfs); + (void) dsl_prop_unregister(ds, "recordsize", blksz_changed_cb, zfsvfs); + (void) dsl_prop_unregister(ds, "readonly", readonly_changed_cb, zfsvfs); + (void) dsl_prop_unregister(ds, "devices", devices_changed_cb, zfsvfs); + (void) dsl_prop_unregister(ds, "setuid", setuid_changed_cb, zfsvfs); + (void) dsl_prop_unregister(ds, "exec", exec_changed_cb, zfsvfs); + (void) dsl_prop_unregister(ds, "snapdir", snapdir_changed_cb, zfsvfs); + (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 +zfsvfs_setup(zfsvfs_t *zfsvfs, boolean_t mounting) +{ + uint_t readonly; + 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) { + /* + * During replay we remove the read only flag to + * allow replays to succeed. + */ + readonly = zfsvfs->z_vfs->vfs_flag & VFS_RDONLY; + if (readonly != 0) + zfsvfs->z_vfs->vfs_flag &= ~VFS_RDONLY; + else + zfs_unlinked_drain(zfsvfs); + + /* + * Parse and replay the intent log. + * + * Because of ziltest, this must be done after + * zfs_unlinked_drain(). (Further note: ziltest doesn't + * use readonly mounts, where zfs_unlinked_drain() isn't + * called.) This is because ziltest causes spa_sync() + * to think it's committed, but actually it is not, so + * the intent log contains many txg's worth of changes. + * + * In particular, if object N is in the unlinked set in + * the last txg to actually sync, then it could be + * actually freed in a later txg and then reallocated in + * a yet later txg. This would write a "create object + * N" record to the intent log. Normally, this would be + * fine because the spa_sync() would have written out + * the fact that object N is free, before we could write + * the "create object N" intent log record. + * + * But when we are in ziltest mode, we advance the "open + * txg" without actually spa_sync()-ing the changes to + * disk. So we would see that object N is still + * allocated and in the unlinked set, and there is an + * intent log record saying to allocate it. + */ + zil_replay(zfsvfs->z_os, zfsvfs, &zfsvfs->z_assign, + zfs_replay_vector); + + 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) +{ + dev_t mount_dev; + uint64_t recordsize, readonly; + int error = 0; + int mode; + zfsvfs_t *zfsvfs; + znode_t *zp = NULL; + + ASSERT(vfsp); + ASSERT(osname); + + /* + * Initialize the zfs-specific filesystem structure. + * Should probably make this a kmem cache, shuffle fields, + * and just bzero up to z_hold_mtx[]. + */ + zfsvfs = kmem_zalloc(sizeof (zfsvfs_t), KM_SLEEP); + zfsvfs->z_vfs = vfsp; + zfsvfs->z_parent = zfsvfs; + zfsvfs->z_assign = TXG_NOWAIT; + zfsvfs->z_max_blksz = SPA_MAXBLOCKSIZE; + 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)); + 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); + + /* Initialize the generic filesystem structure. */ + vfsp->vfs_bcount = 0; + vfsp->vfs_data = NULL; + + if (zfs_create_unique_device(&mount_dev) == -1) { + error = ENODEV; + goto out; + } + ASSERT(vfs_devismounted(mount_dev) == 0); + + if (error = dsl_prop_get_integer(osname, "recordsize", &recordsize, + NULL)) + goto out; + + vfsp->vfs_dev = mount_dev; + vfsp->vfs_fstype = zfsfstype; + vfsp->vfs_bsize = recordsize; + vfsp->vfs_flag |= VFS_NOTRUNC; + vfsp->vfs_data = zfsvfs; + + if (error = dsl_prop_get_integer(osname, "readonly", &readonly, NULL)) + goto out; + + if (readonly) + mode = DS_MODE_PRIMARY | DS_MODE_READONLY; + else + mode = DS_MODE_PRIMARY; + + error = dmu_objset_open(osname, DMU_OST_ZFS, mode, &zfsvfs->z_os); + if (error == EROFS) { + mode = DS_MODE_PRIMARY | DS_MODE_READONLY; + error = dmu_objset_open(osname, DMU_OST_ZFS, mode, + &zfsvfs->z_os); + } + + if (error) + goto out; + + if (error = zfs_init_fs(zfsvfs, &zp, cr)) + goto out; + + /* The call to zfs_init_fs leaves the vnode held, release it here. */ + VN_RELE(ZTOV(zp)); + + /* + * 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_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 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", &pval, NULL)) + goto out; + xattr_changed_cb(zfsvfs, pval); + zfsvfs->z_issnap = B_TRUE; + } else { + error = zfsvfs_setup(zfsvfs, B_TRUE); + } + + if (!zfsvfs->z_issnap) + zfsctl_create(zfsvfs); +out: + if (error) { + if (zfsvfs->z_os) + dmu_objset_close(zfsvfs->z_os); + zfs_freezfsvfs(zfsvfs); + } else { + atomic_add_32(&zfs_active_fs_count, 1); + } + + return (error); +} + +void +zfs_unregister_callbacks(zfsvfs_t *zfsvfs) +{ + objset_t *os = zfsvfs->z_os; + struct dsl_dataset *ds; + + /* + * Unregister properties. + */ + if (!dmu_objset_is_snapshot(os)) { + ds = dmu_objset_ds(os); + VERIFY(dsl_prop_unregister(ds, "atime", atime_changed_cb, + zfsvfs) == 0); + + VERIFY(dsl_prop_unregister(ds, "xattr", xattr_changed_cb, + zfsvfs) == 0); + + VERIFY(dsl_prop_unregister(ds, "recordsize", blksz_changed_cb, + zfsvfs) == 0); + + VERIFY(dsl_prop_unregister(ds, "readonly", readonly_changed_cb, + zfsvfs) == 0); + + VERIFY(dsl_prop_unregister(ds, "devices", devices_changed_cb, + zfsvfs) == 0); + + VERIFY(dsl_prop_unregister(ds, "setuid", setuid_changed_cb, + zfsvfs) == 0); + + VERIFY(dsl_prop_unregister(ds, "exec", exec_changed_cb, + zfsvfs) == 0); + + VERIFY(dsl_prop_unregister(ds, "snapdir", snapdir_changed_cb, + zfsvfs) == 0); + + VERIFY(dsl_prop_unregister(ds, "aclmode", acl_mode_changed_cb, + zfsvfs) == 0); + + VERIFY(dsl_prop_unregister(ds, "aclinherit", + acl_inherit_changed_cb, zfsvfs) == 0); + + VERIFY(dsl_prop_unregister(ds, "vscan", + vscan_changed_cb, zfsvfs) == 0); + } +} + +/* + * Convert a decimal digit string to a uint64_t integer. + */ +static int +str_to_uint64(char *str, uint64_t *objnum) +{ + uint64_t num = 0; + + while (*str) { + if (*str < '0' || *str > '9') + return (EINVAL); + + num = num*10 + *str++ - '0'; + } + + *objnum = num; + return (0); +} + +/* + * The boot path passed from the boot loader is in the form of + * "rootpool-name/root-filesystem-object-number'. Convert this + * string to a dataset name: "rootpool-name/root-filesystem-name". + */ +static int +zfs_parse_bootfs(char *bpath, char *outpath) +{ + char *slashp; + uint64_t objnum; + int error; + + if (*bpath == 0 || *bpath == '/') + return (EINVAL); + + slashp = strchr(bpath, '/'); + + /* if no '/', just return the pool name */ + if (slashp == NULL) { + (void) strcpy(outpath, bpath); + return (0); + } + + if (error = str_to_uint64(slashp+1, &objnum)) + return (error); + + *slashp = '\0'; + error = dsl_dsobj_to_dsname(bpath, objnum, outpath); + *slashp = '/'; + + return (error); +} + +static int +zfs_mountroot(vfs_t *vfsp, enum whymountroot why) +{ + int error = 0; + static int zfsrootdone = 0; + zfsvfs_t *zfsvfs = NULL; + znode_t *zp = NULL; + vnode_t *vp = NULL; + char *zfs_bootfs; + + ASSERT(vfsp); + + /* + * The filesystem that we mount as root is defined in the + * boot property "zfs-bootfs" with a format of + * "poolname/root-dataset-objnum". + */ + if (why == ROOT_INIT) { + if (zfsrootdone++) + return (EBUSY); + /* + * the process of doing a spa_load will require the + * clock to be set before we could (for example) do + * something better by looking at the timestamp on + * an uberblock, so just set it to -1. + */ + clkset(-1); + + if ((zfs_bootfs = spa_get_bootfs()) == NULL) { + cmn_err(CE_NOTE, "\nspa_get_bootfs: can not get " + "bootfs name \n"); + return (EINVAL); + } + + if (error = spa_import_rootpool(rootfs.bo_name)) { + spa_free_bootfs(zfs_bootfs); + cmn_err(CE_NOTE, "\nspa_import_rootpool: error %d\n", + error); + return (error); + } + + if (error = zfs_parse_bootfs(zfs_bootfs, rootfs.bo_name)) { + spa_free_bootfs(zfs_bootfs); + cmn_err(CE_NOTE, "\nzfs_parse_bootfs: error %d\n", + error); + return (error); + } + + spa_free_bootfs(zfs_bootfs); + + if (error = vfs_lock(vfsp)) + return (error); + + if (error = zfs_domount(vfsp, rootfs.bo_name, CRED())) { + cmn_err(CE_NOTE, "\nzfs_domount: error %d\n", error); + goto out; + } + + zfsvfs = (zfsvfs_t *)vfsp->vfs_data; + ASSERT(zfsvfs); + if (error = zfs_zget(zfsvfs, zfsvfs->z_root, &zp)) { + cmn_err(CE_NOTE, "\nzfs_zget: error %d\n", error); + goto out; + } + + vp = ZTOV(zp); + mutex_enter(&vp->v_lock); + vp->v_flag |= VROOT; + mutex_exit(&vp->v_lock); + rootvp = vp; + + /* + * The zfs_zget call above returns with a hold on vp, we release + * it here. + */ + VN_RELE(vp); + + vfs_add((struct vnode *)0, vfsp, + (vfsp->vfs_flag & VFS_RDONLY) ? MS_RDONLY : 0); +out: + vfs_unlock(vfsp); + return (error); + } else if (why == ROOT_REMOUNT) { + readonly_changed_cb(vfsp->vfs_data, B_FALSE); + vfsp->vfs_flag |= VFS_REMOUNT; + + /* refresh mount options */ + zfs_unregister_callbacks(vfsp->vfs_data); + return (zfs_register_callbacks(vfsp)); + + } else if (why == ROOT_UNMOUNT) { + zfs_unregister_callbacks((zfsvfs_t *)vfsp->vfs_data); + (void) zfs_sync(vfsp, 0, 0); + return (0); + } + + /* + * if "why" is equal to anything else other than ROOT_INIT, + * ROOT_REMOUNT, or ROOT_UNMOUNT, we do not support it. + */ + return (ENOTSUP); +} + +/*ARGSUSED*/ +static int +zfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) +{ + char *osname; + pathname_t spn; + int error = 0; + uio_seg_t fromspace = (uap->flags & MS_SYSSPACE) ? + UIO_SYSSPACE : UIO_USERSPACE; + int canwrite; + + if (mvp->v_type != VDIR) + return (ENOTDIR); + + mutex_enter(&mvp->v_lock); + if ((uap->flags & MS_REMOUNT) == 0 && + (uap->flags & MS_OVERLAY) == 0 && + (mvp->v_count != 1 || (mvp->v_flag & VROOT))) { + mutex_exit(&mvp->v_lock); + return (EBUSY); + } + mutex_exit(&mvp->v_lock); + + /* + * ZFS does not support passing unparsed data in via MS_DATA. + * Users should use the MS_OPTIONSTR interface; this means + * that all option parsing is already done and the options struct + * can be interrogated. + */ + if ((uap->flags & MS_DATA) && uap->datalen > 0) + return (EINVAL); + + /* + * Get the objset name (the "special" mount argument). + */ + if (error = pn_get(uap->spec, fromspace, &spn)) + return (error); + + osname = spn.pn_path; + + /* + * 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, 0, cr, NULL)) { + goto out; + } + + if (secpolicy_vnode_owner(cr, vattr.va_uid) != 0 && + VOP_ACCESS(mvp, VWRITE, 0, cr, NULL) != 0) { + error = EPERM; + goto out; + } + + 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(curproc) && + (!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 (uap->flags & MS_REMOUNT) { + /* refresh mount options */ + zfs_unregister_callbacks(vfsp->vfs_data); + error = zfs_register_callbacks(vfsp); + goto out; + } + + error = zfs_domount(vfsp, osname, cr); + +out: + pn_free(&spn); + return (error); +} + +static int +zfs_statvfs(vfs_t *vfsp, struct statvfs64 *statp) +{ + zfsvfs_t *zfsvfs = vfsp->vfs_data; + dev32_t d32; + uint64_t refdbytes, availbytes, usedobjs, availobjs; + + ZFS_ENTER(zfsvfs); + + dmu_objset_space(zfsvfs->z_os, + &refdbytes, &availbytes, &usedobjs, &availobjs); + + /* + * The underlying storage pool actually uses multiple block sizes. + * We report the fragsize as the smallest block size we support, + * and we report our blocksize as the filesystem's maximum blocksize. + */ + statp->f_frsize = 1UL << SPA_MINBLOCKSHIFT; + statp->f_bsize = zfsvfs->z_max_blksz; + + /* + * The following report "total" blocks of various kinds in the + * file system, but reported in terms of f_frsize - the + * "fragment" size. + */ + + statp->f_blocks = (refdbytes + availbytes) >> SPA_MINBLOCKSHIFT; + statp->f_bfree = availbytes >> SPA_MINBLOCKSHIFT; + statp->f_bavail = statp->f_bfree; /* no root reservation */ + + /* + * statvfs() should really be called statufs(), because it assumes + * static metadata. ZFS doesn't preallocate files, so the best + * we can do is report the max that could possibly fit in f_files, + * and that minus the number actually used in f_ffree. + * For f_ffree, report the smaller of the number of object available + * and the number of blocks (each object will take at least a block). + */ + statp->f_ffree = MIN(availobjs, statp->f_bfree); + statp->f_favail = statp->f_ffree; /* no "root reservation" */ + statp->f_files = statp->f_ffree + usedobjs; + + (void) cmpldev(&d32, vfsp->vfs_dev); + statp->f_fsid = d32; + + /* + * We're a zfs filesystem. + */ + (void) strcpy(statp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name); + + statp->f_flag = vf_to_stf(vfsp->vfs_flag); + + statp->f_namemax = ZFS_MAXNAMELEN; + + /* + * We have all of 32 characters to stuff a string here. + * Is there anything useful we could/should provide? + */ + bzero(statp->f_fstr, sizeof (statp->f_fstr)); + + ZFS_EXIT(zfsvfs); + return (0); +} + +static int +zfs_root(vfs_t *vfsp, vnode_t **vpp) +{ + zfsvfs_t *zfsvfs = vfsp->vfs_data; + znode_t *rootzp; + int error; + + ZFS_ENTER(zfsvfs); + + error = zfs_zget(zfsvfs, zfsvfs->z_root, &rootzp); + if (error == 0) + *vpp = ZTOV(rootzp); + + ZFS_EXIT(zfsvfs); + 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, cred_t *cr) +{ + zfsvfs_t *zfsvfs = vfsp->vfs_data; + objset_t *os; + int ret; + + 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 + * dataset itself. + */ + if (zfsvfs->z_ctldir != NULL && + (ret = zfsctl_umount_snapshots(vfsp, fflag, cr)) != 0) { + return (ret); + } + + 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); + } + } + + vfsp->vfs_flag |= VFS_UNMOUNTED; + + 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); + + /* + * Finally close the objset + */ + dmu_objset_close(os); + } + + /* + * We can now safely destroy the '.zfs' directory node. + */ + if (zfsvfs->z_ctldir != NULL) + zfsctl_destroy(zfsvfs); + + return (0); +} + +static int +zfs_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp) +{ + zfsvfs_t *zfsvfs = vfsp->vfs_data; + znode_t *zp; + uint64_t object = 0; + uint64_t fid_gen = 0; + uint64_t gen_mask; + uint64_t zp_gen; + int i, err; + + *vpp = NULL; + + ZFS_ENTER(zfsvfs); + + if (fidp->fid_len == LONG_FID_LEN) { + zfid_long_t *zlfid = (zfid_long_t *)fidp; + uint64_t objsetid = 0; + uint64_t setgen = 0; + + for (i = 0; i < sizeof (zlfid->zf_setid); i++) + objsetid |= ((uint64_t)zlfid->zf_setid[i]) << (8 * i); + + for (i = 0; i < sizeof (zlfid->zf_setgen); i++) + setgen |= ((uint64_t)zlfid->zf_setgen[i]) << (8 * i); + + ZFS_EXIT(zfsvfs); + + err = zfsctl_lookup_objset(vfsp, objsetid, &zfsvfs); + if (err) + return (EINVAL); + ZFS_ENTER(zfsvfs); + } + + if (fidp->fid_len == SHORT_FID_LEN || fidp->fid_len == LONG_FID_LEN) { + zfid_short_t *zfid = (zfid_short_t *)fidp; + + for (i = 0; i < sizeof (zfid->zf_object); i++) + object |= ((uint64_t)zfid->zf_object[i]) << (8 * i); + + for (i = 0; i < sizeof (zfid->zf_gen); i++) + fid_gen |= ((uint64_t)zfid->zf_gen[i]) << (8 * i); + } else { + ZFS_EXIT(zfsvfs); + return (EINVAL); + } + + /* A zero fid_gen means we are in the .zfs control directories */ + if (fid_gen == 0 && + (object == ZFSCTL_INO_ROOT || object == ZFSCTL_INO_SNAPDIR)) { + *vpp = zfsvfs->z_ctldir; + ASSERT(*vpp != NULL); + if (object == ZFSCTL_INO_SNAPDIR) { + VERIFY(zfsctl_root_lookup(*vpp, "snapshot", vpp, NULL, + 0, NULL, NULL, NULL, NULL, NULL) == 0); + } else { + VN_HOLD(*vpp); + } + ZFS_EXIT(zfsvfs); + return (0); + } + + gen_mask = -1ULL >> (64 - 8 * i); + + dprintf("getting %llu [%u mask %llx]\n", object, fid_gen, gen_mask); + if (err = zfs_zget(zfsvfs, object, &zp)) { + ZFS_EXIT(zfsvfs); + return (err); + } + zp_gen = zp->z_phys->zp_gen & gen_mask; + if (zp_gen == 0) + zp_gen = 1; + if (zp->z_unlinked || zp_gen != fid_gen) { + dprintf("znode gen (%u) != fid gen (%u)\n", zp_gen, fid_gen); + VN_RELE(ZTOV(zp)); + ZFS_EXIT(zfsvfs); + return (EINVAL); + } + + *vpp = ZTOV(zp); + ZFS_EXIT(zfsvfs); + return (0); +} + +/* + * 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) +{ + int error; + + if ((error = zfsvfs_teardown(zfsvfs, B_FALSE)) != 0) + return (error); + + *mode = zfsvfs->z_os->os_mode; + dmu_objset_name(zfsvfs->z_os, name); + dmu_objset_close(zfsvfs->z_os); + + return (0); +} + +/* + * Reopen zfsvfs_t::z_os and release VOPs. + */ +int +zfs_resume_fs(zfsvfs_t *zfsvfs, const char *osname, int mode) +{ + int err; + + 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); + + } + + /* 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, CRED()); + } + return (err); +} + +static void +zfs_freevfs(vfs_t *vfsp) +{ + zfsvfs_t *zfsvfs = vfsp->vfs_data; + int i; + + for (i = 0; i != ZFS_OBJ_MTX_SZ; i++) + mutex_destroy(&zfsvfs->z_hold_mtx[i]); + + zfs_fuid_destroy(zfsvfs); + zfs_freezfsvfs(zfsvfs); + + atomic_add_32(&zfs_active_fs_count, -1); +} + +/* + * VFS_INIT() initialization. Note that there is no VFS_FINI(), + * so we can't safely do any non-idempotent initialization here. + * Leave that to zfs_init() and zfs_fini(), which are called + * from the module's _init() and _fini() entry points. + */ +/*ARGSUSED*/ +static int +zfs_vfsinit(int fstype, char *name) +{ + int error; + + zfsfstype = fstype; + + /* + * Setup vfsops and vnodeops tables. + */ + error = vfs_setfsops(fstype, zfs_vfsops_template, &zfs_vfsops); + if (error != 0) { + cmn_err(CE_WARN, "zfs: bad vfs ops template"); + } + + error = zfs_create_op_tables(); + if (error) { + zfs_remove_op_tables(); + cmn_err(CE_WARN, "zfs: bad vnode ops template"); + (void) vfs_freevfsops_by_type(zfsfstype); + return (error); + } + + mutex_init(&zfs_dev_mtx, NULL, MUTEX_DEFAULT, NULL); + + /* + * Unique major number for all zfs mounts. + * If we run out of 32-bit minors, we'll getudev() another major. + */ + zfs_major = ddi_name_to_major(ZFS_DRIVER); + zfs_minor = ZFS_MIN_MINOR; + + return (0); +} + +void +zfs_init(void) +{ + /* + * Initialize .zfs directory structures + */ + zfsctl_init(); + + /* + * Initialize znode cache, vnode ops, etc... + */ + zfs_znode_init(); +} + +void +zfs_fini(void) +{ + zfsctl_fini(); + zfs_znode_fini(); +} + +int +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_PRIMARY, &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; + + /* + * 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); + + 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); +} + +static vfsdef_t vfw = { + VFSDEF_VERSION, + MNTTYPE_ZFS, + zfs_vfsinit, + VSW_HASPROTO|VSW_CANRWRO|VSW_CANREMOUNT|VSW_VOLATILEDEV|VSW_STATS| + VSW_XID, + &zfs_mntopts +}; + +struct modlfs zfs_modlfs = { + &mod_fsops, "ZFS filesystem version " SPA_VERSION_STRING, &vfw +}; diff --git a/zfs/lib/libdmu-ctl/zfs_vnops.c b/zfs/lib/libdmu-ctl/zfs_vnops.c new file mode 100644 index 0000000..3f36328 --- /dev/null +++ b/zfs/lib/libdmu-ctl/zfs_vnops.c @@ -0,0 +1,4558 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL 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. + */ + +/* Portions Copyright 2007 Jeremy Teo */ + +#pragma ident "@(#)zfs_vnops.c 1.73 08/04/27 SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fs/fs_subr.h" +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Programming rules. + * + * 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 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. 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: + * First, if it's the last reference, the vnode/znode + * can be freed, so the zp may point to freed memory. Second, the last + * reference will call zfs_zinactive(), which may induce a lot of work -- + * pushing cached pages (which acquires range locks) and syncing out + * cached atime changes. Third, zfs_zinactive() may require a new tx, + * which could deadlock the system if you were already holding one. + * + * (3) All range locks must be grabbed before calling dmu_tx_assign(), + * as they can span dmu_tx_assign() calls. + * + * (4) Always pass zfsvfs->z_assign as the second argument to dmu_tx_assign(). + * In normal operation, this will be TXG_NOWAIT. During ZIL replay, + * it will be a specific txg. Either way, dmu_tx_assign() never blocks. + * This is critical because we don't want to block while holding locks. + * Note, in particular, that if a lock is sometimes acquired before + * the tx assigns, and sometimes after (e.g. z_lock), then failing to + * use a non-blocking assign can deadlock the system. The scenario: + * + * Thread A has grabbed a lock before calling dmu_tx_assign(). + * Thread B is in an already-assigned tx, and blocks for this lock. + * Thread A calls dmu_tx_assign(TXG_WAIT) and blocks in txg_wait_open() + * forever, because the previous txg can't quiesce until B's tx commits. + * + * If dmu_tx_assign() returns ERESTART and zfsvfs->z_assign is TXG_NOWAIT, + * then drop all locks, call dmu_tx_wait(), and try again. + * + * (5) If the operation succeeded, generate the intent log entry for it + * before dropping locks. This ensures that the ordering of events + * in the intent log matches the order in which they actually occurred. + * + * (6) At the end of each vnode op, the DMU tx must always commit, + * regardless of whether there were any errors. + * + * (7) After dropping all locks, invoke zil_commit(zilog, seq, foid) + * to ensure that synchronous semantics are provided when necessary. + * + * In general, this is how things should be ordered in each vnode op: + * + * ZFS_ENTER(zfsvfs); // exit if unmounted + * top: + * zfs_dirent_lock(&dl, ...) // lock directory entry (may VN_HOLD()) + * rw_enter(...); // grab any other locks you need + * tx = dmu_tx_create(...); // get DMU tx + * dmu_tx_hold_*(); // hold each object you might modify + * error = dmu_tx_assign(tx, zfsvfs->z_assign); // try to assign + * if (error) { + * rw_exit(...); // drop locks + * zfs_dirent_unlock(dl); // unlock directory entry + * VN_RELE(...); // release held vnodes + * if (error == ERESTART && zfsvfs->z_assign == TXG_NOWAIT) { + * dmu_tx_wait(tx); + * dmu_tx_abort(tx); + * goto top; + * } + * dmu_tx_abort(tx); // abort DMU tx + * ZFS_EXIT(zfsvfs); // finished in zfs + * return (error); // really out of space + * } + * error = do_real_work(); // do whatever this VOP does + * if (error == 0) + * zfs_log_*(...); // on success, make ZIL entry + * dmu_tx_commit(tx); // commit DMU tx -- error or not + * rw_exit(...); // drop locks + * zfs_dirent_unlock(dl); // unlock directory entry + * VN_RELE(...); // release held vnodes + * zil_commit(zilog, seq, foid); // synchronous when necessary + * ZFS_EXIT(zfsvfs); // finished in zfs + * return (error); // done, report error + */ + +/* ARGSUSED */ +static int +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, + caller_context_t *ct) +{ + znode_t *zp = VTOZ(vp); + + /* Decrement the synchronous opens in the znode */ + if ((flag & (FSYNC | FDSYNC)) && (count == 1)) + atomic_dec_32(&zp->z_sync_cnt); + + /* + * Clean up any locks held by this process on the vp. + */ + 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); +} + +/* + * Lseek support for finding holes (cmd == _FIO_SEEK_HOLE) and + * data (cmd == _FIO_SEEK_DATA). "off" is an in/out parameter. + */ +static int +zfs_holey(vnode_t *vp, int cmd, offset_t *off) +{ + znode_t *zp = VTOZ(vp); + uint64_t noff = (uint64_t)*off; /* new offset */ + uint64_t file_sz; + int error; + boolean_t hole; + + file_sz = zp->z_phys->zp_size; + if (noff >= file_sz) { + return (ENXIO); + } + + if (cmd == _FIO_SEEK_HOLE) + hole = B_TRUE; + else + hole = B_FALSE; + + error = dmu_offset_next(zp->z_zfsvfs->z_os, zp->z_id, hole, &noff); + + /* end of file? */ + if ((error == ESRCH) || (noff > file_sz)) { + /* + * Handle the virtual hole at the end of file. + */ + if (hole) { + *off = file_sz; + return (0); + } + return (ENXIO); + } + + if (noff < *off) + return (error); + *off = noff; + return (error); +} + +/* ARGSUSED */ +static int +zfs_ioctl(vnode_t *vp, int com, intptr_t data, int flag, cred_t *cred, + int *rvalp, caller_context_t *ct) +{ + offset_t off; + int error; + zfsvfs_t *zfsvfs; + znode_t *zp; + + switch (com) { + case _FIOFFS: + return (zfs_sync(vp->v_vfsp, 0, cred)); + + /* + * The following two ioctls are used by bfu. Faking out, + * necessary to avoid bfu errors. + */ + case _FIOGDIO: + case _FIOSDIO: + return (0); + + case _FIO_SEEK_DATA: + case _FIO_SEEK_HOLE: + if (ddi_copyin((void *)data, &off, sizeof (off), flag)) + return (EFAULT); + + 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); + ZFS_EXIT(zfsvfs); + if (error) + return (error); + if (ddi_copyout(&off, (void *)data, sizeof (off), flag)) + return (EFAULT); + return (0); + } + return (ENOTTY); +} + +/* + * When a file is memory mapped, we must keep the IO data synchronized + * between the DMU cache and the memory mapped pages. What this means: + * + * On Write: If we find a memory mapped page, we write to *both* + * the page and the dmu buffer. + * + * NOTE: We will always "break up" the IO into PAGESIZE uiomoves when + * the file is memory mapped. + */ +static int +mappedwrite(vnode_t *vp, int nbytes, uio_t *uio, dmu_tx_t *tx) +{ + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + int64_t start, off; + int len = nbytes; + int error = 0; + + start = uio->uio_loffset; + off = start & PAGEOFFSET; + for (start &= PAGEMASK; len > 0; start += PAGESIZE) { + page_t *pp; + uint64_t bytes = MIN(PAGESIZE - off, len); + uint64_t woff = uio->uio_loffset; + + /* + * We don't want a new page to "appear" in the middle of + * the file update (because it may not get the write + * update data), so we grab a lock to block + * zfs_getpage(). + */ + rw_enter(&zp->z_map_lock, RW_WRITER); + if (pp = page_lookup(vp, start, SE_SHARED)) { + caddr_t va; + + rw_exit(&zp->z_map_lock); + va = ppmapin(pp, PROT_READ | PROT_WRITE, (caddr_t)-1L); + error = uiomove(va+off, bytes, UIO_WRITE, uio); + if (error == 0) { + dmu_write(zfsvfs->z_os, zp->z_id, + woff, bytes, va+off, tx); + } + ppmapout(va); + page_unlock(pp); + } else { + error = dmu_write_uio(zfsvfs->z_os, zp->z_id, + uio, bytes, tx); + rw_exit(&zp->z_map_lock); + } + len -= bytes; + off = 0; + if (error) + break; + } + return (error); +} + +/* + * When a file is memory mapped, we must keep the IO data synchronized + * between the DMU cache and the memory mapped pages. What this means: + * + * On Read: We "read" preferentially from memory mapped pages, + * else we default from the dmu buffer. + * + * NOTE: We will always "break up" the IO into PAGESIZE uiomoves when + * the file is memory mapped. + */ +static int +mappedread(vnode_t *vp, int nbytes, uio_t *uio) +{ + znode_t *zp = VTOZ(vp); + objset_t *os = zp->z_zfsvfs->z_os; + int64_t start, off; + int len = nbytes; + int error = 0; + + start = uio->uio_loffset; + off = start & PAGEOFFSET; + for (start &= PAGEMASK; len > 0; start += PAGESIZE) { + page_t *pp; + uint64_t bytes = MIN(PAGESIZE - off, len); + + if (pp = page_lookup(vp, start, SE_SHARED)) { + caddr_t va; + + va = ppmapin(pp, PROT_READ, (caddr_t)-1L); + error = uiomove(va + off, bytes, UIO_READ, uio); + ppmapout(va); + page_unlock(pp); + } else { + error = dmu_read_uio(os, zp->z_id, uio, bytes); + } + len -= bytes; + off = 0; + if (error) + break; + } + return (error); +} + +offset_t zfs_read_chunk_size = 1024 * 1024; /* Tunable */ + +/* + * Read bytes from specified file into supplied buffer. + * + * IN: vp - vnode of file to be read from. + * uio - structure supplying read location, range info, + * 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. + * + * RETURN: 0 if success + * error code if failure + * + * Side Effects: + * vp - atime updated if byte count > 0 + */ +/* ARGSUSED */ +static int +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; + 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 + */ + if (uio->uio_loffset < (offset_t)0) { + ZFS_EXIT(zfsvfs); + return (EINVAL); + } + + /* + * Fasttrack empty reads + */ + if (uio->uio_resid == 0) { + ZFS_EXIT(zfsvfs); + return (0); + } + + /* + * Check for mandatory locks + */ + if (MANDMODE((mode_t)zp->z_phys->zp_mode)) { + if (error = chklock(vp, FREAD, + uio->uio_loffset, uio->uio_resid, uio->uio_fmode, ct)) { + ZFS_EXIT(zfsvfs); + return (error); + } + } + + /* + * If we're in FRSYNC mode, sync out this znode before reading it. + */ + if (ioflag & FRSYNC) + zil_commit(zfsvfs->z_log, zp->z_last_itx, zp->z_id); + + /* + * Lock the range against changes. + */ + rl = zfs_range_lock(zp, uio->uio_loffset, uio->uio_resid, RL_READER); + + /* + * If we are reading past end-of-file we can skip + * to the end; but we might still need to set atime. + */ + if (uio->uio_loffset >= zp->z_phys->zp_size) { + error = 0; + goto out; + } + + ASSERT(uio->uio_loffset < zp->z_phys->zp_size); + n = MIN(uio->uio_resid, zp->z_phys->zp_size - uio->uio_loffset); + + while (n > 0) { + nbytes = MIN(n, zfs_read_chunk_size - + P2PHASE(uio->uio_loffset, zfs_read_chunk_size)); + + if (vn_has_cached_data(vp)) + error = mappedread(vp, nbytes, uio); + else + error = dmu_read_uio(os, zp->z_id, uio, nbytes); + if (error) + break; + + n -= nbytes; + } + +out: + zfs_range_unlock(rl); + + ZFS_ACCESSTIME_STAMP(zfsvfs, zp); + ZFS_EXIT(zfsvfs); + return (error); +} + +/* + * Fault in the pages of the first n bytes specified by the uio structure. + * 1 byte in each page is touched and the uio struct is unmodified. + * Any error will exit this routine as this is only a best + * attempt to get the pages resident. This is a copy of ufs_trans_touch(). + */ +static void +zfs_prefault_write(ssize_t n, struct uio *uio) +{ + struct iovec *iov; + ulong_t cnt, incr; + caddr_t p; + uint8_t tmp; + + iov = uio->uio_iov; + + while (n) { + cnt = MIN(iov->iov_len, n); + if (cnt == 0) { + /* empty iov entry */ + iov++; + continue; + } + n -= cnt; + /* + * touch each page in this segment. + */ + p = iov->iov_base; + while (cnt) { + switch (uio->uio_segflg) { + case UIO_USERSPACE: + case UIO_USERISPACE: + if (fuword8(p, &tmp)) + return; + break; + case UIO_SYSSPACE: + if (kcopy(p, &tmp, 1)) + return; + break; + } + incr = MIN(cnt, PAGESIZE); + p += incr; + cnt -= incr; + } + /* + * touch the last byte in case it straddles a page. + */ + p--; + switch (uio->uio_segflg) { + case UIO_USERSPACE: + case UIO_USERISPACE: + if (fuword8(p, &tmp)) + return; + break; + case UIO_SYSSPACE: + if (kcopy(p, &tmp, 1)) + return; + break; + } + iov++; + } +} + +/* + * Write the bytes to a file. + * + * IN: vp - vnode of file to be written to. + * uio - structure supplying write location, range info, + * and data buffer. + * ioflag - FAPPEND flag set if in append mode. + * cr - credentials of caller. + * ct - caller context (NFS/CIFS fem monitor only) + * + * OUT: uio - updated offset and range. + * + * RETURN: 0 if success + * error code if failure + * + * Timestamps: + * vp - ctime|mtime updated if byte count > 0 + */ +/* ARGSUSED */ +static int +zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct) +{ + znode_t *zp = VTOZ(vp); + rlim64_t limit = uio->uio_llimit; + ssize_t start_resid = uio->uio_resid; + ssize_t tx_bytes; + uint64_t end_size; + dmu_tx_t *tx; + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + zilog_t *zilog; + offset_t woff; + ssize_t n, nbytes; + rl_t *rl; + int max_blksz = zfsvfs->z_max_blksz; + uint64_t pflags = zp->z_phys->zp_flags; + int error; + + /* + * If immutable or not appending then return EPERM + */ + if ((pflags & (ZFS_IMMUTABLE | ZFS_READONLY)) || + ((pflags & ZFS_APPENDONLY) && !(ioflag & FAPPEND) && + (uio->uio_loffset < zp->z_phys->zp_size))) + return (EPERM); + + /* + * Fasttrack empty write + */ + n = start_resid; + if (n == 0) + return (0); + + if (limit == RLIM64_INFINITY || limit > MAXOFFSET_T) + limit = MAXOFFSET_T; + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(zp); + zilog = zfsvfs->z_log; + + /* + * Pre-fault the pages to ensure slow (eg NFS) pages + * don't hold up txg. + */ + zfs_prefault_write(n, uio); + + /* + * If in append mode, set the io offset pointer to eof. + */ + if (ioflag & FAPPEND) { + /* + * Range lock for a file append: + * The value for the start of range will be determined by + * zfs_range_lock() (to guarantee append semantics). + * If this write will cause the block size to increase, + * zfs_range_lock() will lock the entire file, so we must + * later reduce the range after we grow the block size. + */ + rl = zfs_range_lock(zp, 0, n, RL_APPEND); + if (rl->r_len == UINT64_MAX) { + /* overlocked, zp_size can't change */ + woff = uio->uio_loffset = zp->z_phys->zp_size; + } else { + woff = uio->uio_loffset = rl->r_off; + } + } else { + woff = uio->uio_loffset; + /* + * Validate file offset + */ + if (woff < 0) { + ZFS_EXIT(zfsvfs); + return (EINVAL); + } + + /* + * If we need to grow the block size then zfs_range_lock() + * will lock a wider range than we request here. + * Later after growing the block size we reduce the range. + */ + rl = zfs_range_lock(zp, woff, n, RL_WRITER); + } + + if (woff >= limit) { + zfs_range_unlock(rl); + ZFS_EXIT(zfsvfs); + return (EFBIG); + } + + if ((woff + n) > limit || woff > (limit - n)) + n = limit - woff; + + /* + * Check for mandatory locks + */ + if (MANDMODE((mode_t)zp->z_phys->zp_mode) && + (error = chklock(vp, FWRITE, woff, n, uio->uio_fmode, ct)) != 0) { + zfs_range_unlock(rl); + ZFS_EXIT(zfsvfs); + return (error); + } + end_size = MAX(zp->z_phys->zp_size, woff + n); + + /* + * Write the file in reasonable size chunks. Each chunk is written + * in a separate transaction; this keeps the intent log records small + * and allows us to do more fine-grained space accounting. + */ + while (n > 0) { + /* + * Start a transaction. + */ + woff = uio->uio_loffset; + tx = dmu_tx_create(zfsvfs->z_os); + dmu_tx_hold_bonus(tx, zp->z_id); + dmu_tx_hold_write(tx, zp->z_id, woff, MIN(n, max_blksz)); + 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); + continue; + } + dmu_tx_abort(tx); + break; + } + + /* + * If zfs_range_lock() over-locked we grow the blocksize + * and then reduce the lock range. This will only happen + * on the first iteration since zfs_range_reduce() will + * shrink down r_len to the appropriate size. + */ + if (rl->r_len == UINT64_MAX) { + uint64_t new_blksz; + + if (zp->z_blksz > max_blksz) { + ASSERT(!ISP2(zp->z_blksz)); + new_blksz = MIN(end_size, SPA_MAXBLOCKSIZE); + } else { + new_blksz = MIN(end_size, max_blksz); + } + zfs_grow_blocksize(zp, new_blksz, tx); + zfs_range_reduce(rl, woff, n); + } + + /* + * XXX - should we really limit each write to z_max_blksz? + * Perhaps we should use SPA_MAXBLOCKSIZE chunks? + */ + nbytes = MIN(n, max_blksz - P2PHASE(woff, max_blksz)); + rw_enter(&zp->z_map_lock, RW_READER); + + tx_bytes = uio->uio_resid; + if (vn_has_cached_data(vp)) { + rw_exit(&zp->z_map_lock); + error = mappedwrite(vp, nbytes, uio, tx); + } else { + error = dmu_write_uio(zfsvfs->z_os, zp->z_id, + uio, nbytes, tx); + rw_exit(&zp->z_map_lock); + } + tx_bytes -= uio->uio_resid; + + /* + * If we made no progress, we're done. If we made even + * partial progress, update the znode and ZIL accordingly. + */ + if (tx_bytes == 0) { + dmu_tx_commit(tx); + ASSERT(error != 0); + break; + } + + /* + * Clear Set-UID/Set-GID bits on successful write if not + * privileged and at least one of the excute bits is set. + * + * 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, + (zp->z_phys->zp_mode & S_ISUID) != 0 && + zp->z_phys->zp_uid == 0) != 0) { + zp->z_phys->zp_mode &= ~(S_ISUID | S_ISGID); + } + mutex_exit(&zp->z_acl_lock); + + /* + * Update time stamp. NOTE: This marks the bonus buffer as + * dirty, so we don't have to do it again for zp_size. + */ + zfs_time_stamper(zp, CONTENT_MODIFIED, tx); + + /* + * Update the file size (zp_size) if it has changed; + * account for possible concurrent updates. + */ + while ((end_size = zp->z_phys->zp_size) < uio->uio_loffset) + (void) atomic_cas_64(&zp->z_phys->zp_size, end_size, + uio->uio_loffset); + zfs_log_write(zilog, tx, TX_WRITE, zp, woff, tx_bytes, ioflag); + dmu_tx_commit(tx); + + if (error != 0) + break; + ASSERT(tx_bytes == nbytes); + n -= nbytes; + } + + zfs_range_unlock(rl); + + /* + * If we're in replay mode, or we made no progress, return error. + * Otherwise, it's at least a partial write, so it's successful. + */ + if (zfsvfs->z_assign >= TXG_INITIAL || uio->uio_resid == start_resid) { + ZFS_EXIT(zfsvfs); + return (error); + } + + if (ioflag & (FSYNC | FDSYNC)) + zil_commit(zilog, zp->z_last_itx, zp->z_id); + + ZFS_EXIT(zfsvfs); + return (0); +} + +void +zfs_get_done(dmu_buf_t *db, void *vzgd) +{ + zgd_t *zgd = (zgd_t *)vzgd; + rl_t *rl = zgd->zgd_rl; + vnode_t *vp = ZTOV(rl->r_zp); + + dmu_buf_rele(db, vzgd); + zfs_range_unlock(rl); + VN_RELE(vp); + zil_add_block(zgd->zgd_zilog, zgd->zgd_bp); + kmem_free(zgd, sizeof (zgd_t)); +} + +/* + * Get data to generate a TX_WRITE intent log record. + */ +int +zfs_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio) +{ + zfsvfs_t *zfsvfs = arg; + objset_t *os = zfsvfs->z_os; + znode_t *zp; + uint64_t off = lr->lr_offset; + dmu_buf_t *db; + rl_t *rl; + zgd_t *zgd; + int dlen = lr->lr_length; /* length of user data */ + int error = 0; + + ASSERT(zio); + ASSERT(dlen != 0); + + /* + * Nothing to do if the file has been removed + */ + if (zfs_zget(zfsvfs, lr->lr_foid, &zp) != 0) + return (ENOENT); + if (zp->z_unlinked) { + VN_RELE(ZTOV(zp)); + return (ENOENT); + } + + /* + * Write records come in two flavors: immediate and indirect. + * For small writes it's cheaper to store the data with the + * log record (immediate); for large writes it's cheaper to + * sync the data and get a pointer to it (indirect) so that + * we don't have to write the data twice. + */ + if (buf != NULL) { /* immediate write */ + rl = zfs_range_lock(zp, off, dlen, RL_READER); + /* test for truncation needs to be done while range locked */ + if (off >= zp->z_phys->zp_size) { + error = ENOENT; + goto out; + } + VERIFY(0 == dmu_read(os, lr->lr_foid, off, dlen, buf)); + } else { /* indirect write */ + uint64_t boff; /* block starting offset */ + + /* + * Have to lock the whole block to ensure when it's + * written out and it's checksum is being calculated + * that no one can change the data. We need to re-check + * blocksize after we get the lock in case it's changed! + */ + for (;;) { + if (ISP2(zp->z_blksz)) { + boff = P2ALIGN_TYPED(off, zp->z_blksz, + uint64_t); + } else { + boff = 0; + } + dlen = zp->z_blksz; + rl = zfs_range_lock(zp, boff, dlen, RL_READER); + if (zp->z_blksz == dlen) + break; + zfs_range_unlock(rl); + } + /* test for truncation needs to be done while range locked */ + if (off >= zp->z_phys->zp_size) { + error = ENOENT; + goto out; + } + zgd = (zgd_t *)kmem_alloc(sizeof (zgd_t), KM_SLEEP); + zgd->zgd_rl = rl; + zgd->zgd_zilog = zfsvfs->z_log; + zgd->zgd_bp = &lr->lr_blkptr; + VERIFY(0 == dmu_buf_hold(os, lr->lr_foid, boff, zgd, &db)); + ASSERT(boff == db->db_offset); + lr->lr_blkoff = off - boff; + error = dmu_sync(zio, db, &lr->lr_blkptr, + lr->lr_common.lrc_txg, zfs_get_done, zgd); + 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 + * we can release this dbuf. We will finish everything + * up in the zfs_get_done() callback. + */ + if (error == EINPROGRESS) + return (0); + dmu_buf_rele(db, zgd); + kmem_free(zgd, sizeof (zgd_t)); + } +out: + zfs_range_unlock(rl); + VN_RELE(ZTOV(zp)); + return (error); +} + +/*ARGSUSED*/ +static int +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); + 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); +} + +/* + * Lookup an entry in a directory, or an extended attribute directory. + * If it exists, return a held vnode reference for it. + * + * IN: dvp - vnode of directory to search. + * nm - name of entry to lookup. + * pnp - full pathname to lookup [UNUSED]. + * 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. + * + * RETURN: 0 if success + * error code if failure + * + * Timestamps: + * NA + */ +/* ARGSUSED */ +static int +zfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp, + int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct, + int *direntflags, pathname_t *realpnp) +{ + znode_t *zdp = VTOZ(dvp); + zfsvfs_t *zfsvfs = zdp->z_zfsvfs; + int error; + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(zdp); + + *vpp = NULL; + + if (flags & LOOKUP_XATTR) { + /* + * If the xattr property is off, refuse the lookup request. + */ + if (!(zfsvfs->z_vfs->vfs_flag & VFS_XATTR)) { + ZFS_EXIT(zfsvfs); + return (EINVAL); + } + + /* + * We don't allow recursive attributes.. + * Maybe someday we will. + */ + if (zdp->z_phys->zp_flags & ZFS_XATTR) { + ZFS_EXIT(zfsvfs); + return (EINVAL); + } + + if (error = zfs_get_xattrdir(VTOZ(dvp), vpp, cr, flags)) { + ZFS_EXIT(zfsvfs); + return (error); + } + + /* + * Do we have permission to get into attribute directory? + */ + + if (error = zfs_zaccess(VTOZ(*vpp), ACE_EXECUTE, 0, + B_FALSE, cr)) { + VN_RELE(*vpp); + *vpp = NULL; + } + + ZFS_EXIT(zfsvfs); + return (error); + } + + if (dvp->v_type != VDIR) { + ZFS_EXIT(zfsvfs); + return (ENOTDIR); + } + + /* + * Check accessibility of directory. + */ + + if (error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr)) { + ZFS_EXIT(zfsvfs); + return (error); + } + + 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 + */ + if (IS_DEVVP(*vpp)) { + vnode_t *svp; + + svp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, cr); + VN_RELE(*vpp); + if (svp == NULL) + error = ENOSYS; + else + *vpp = svp; + } + } + + ZFS_EXIT(zfsvfs); + return (error); +} + +/* + * Attempt to create a new entry in a directory. If the entry + * already exists, truncate the file if permissible, else return + * an error. Return the vp of the created or trunc'd file. + * + * IN: dvp - vnode of directory to put new file entry in. + * name - name of new file entry. + * vap - attributes of new file. + * excl - flag indicating exclusive or non-exclusive mode. + * 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. + * + * RETURN: 0 if success + * error code if failure + * + * Timestamps: + * 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, vcexcl_t excl, + int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct, + vsecattr_t *vsecp) +{ + znode_t *zp, *dzp = VTOZ(dvp); + zfsvfs_t *zfsvfs = dzp->z_zfsvfs; + zilog_t *zilog; + objset_t *os; + zfs_dirlock_t *dl; + dmu_tx_t *tx; + int error; + zfs_acl_t *aclp = NULL; + zfs_fuid_info_t *fuidp = NULL; + + /* + * 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; + + if ((vap->va_mode & VSVTX) && secpolicy_vnode_stky_modify(cr)) + vap->va_mode &= ~VSVTX; + + if (*name == '\0') { + /* + * Null component name refers to the directory itself. + */ + VN_HOLD(dvp); + zp = dzp; + dl = NULL; + error = 0; + } else { + /* possible VN_HOLD(zp) */ + 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); + } + } + + 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, 0, B_FALSE, cr)) { + goto out; + } + + /* + * We only support the creation of regular files in + * extended attribute directories. + */ + if ((dzp->z_phys->zp_flags & ZFS_XATTR) && + (vap->va_type != VREG)) { + error = EINVAL; + goto out; + } + + 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) || 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); + if (error == ERESTART && + zfsvfs->z_assign == TXG_NOWAIT) { + dmu_tx_wait(tx); + dmu_tx_abort(tx); + goto top; + } + dmu_tx_abort(tx); + ZFS_EXIT(zfsvfs); + if (aclp) + zfs_acl_free(aclp); + return (error); + } + zfs_mknode(dzp, vap, tx, cr, 0, &zp, 0, aclp, &fuidp); + (void) zfs_link_create(dl, zp, tx, ZNEW); + 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. + */ + /* + * Can't truncate an existing file if in exclusive mode. + */ + if (excl == EXCL) { + error = EEXIST; + goto out; + } + /* + * Can't open a directory for writing. + */ + if ((ZTOV(zp)->v_type == VDIR) && (mode & S_IWRITE)) { + error = EISDIR; + goto out; + } + /* + * Verify requested access to file. + */ + if (mode && (error = zfs_zaccess_rwx(zp, mode, aflags, cr))) { + goto out; + } + + mutex_enter(&dzp->z_lock); + dzp->z_seq++; + mutex_exit(&dzp->z_lock); + + /* + * Truncate regular files if requested. + */ + if ((ZTOV(zp)->v_type == VREG) && + (vap->va_mask & AT_SIZE) && (vap->va_size == 0)) { + 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); + } + } + } +out: + + if (dl) + zfs_dirent_unlock(dl); + + if (error) { + if (zp) + VN_RELE(ZTOV(zp)); + } else { + *vpp = ZTOV(zp); + /* + * If vnode is for a device return a specfs vnode instead. + */ + if (IS_DEVVP(*vpp)) { + struct vnode *svp; + + svp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, cr); + VN_RELE(*vpp); + if (svp == NULL) { + error = ENOSYS; + } + *vpp = svp; + } + } + if (aclp) + zfs_acl_free(aclp); + + ZFS_EXIT(zfsvfs); + return (error); +} + +/* + * Remove an entry from a directory. + * + * 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 + * + * Timestamps: + * dvp - ctime|mtime + * vp - ctime (if nlink > 0) + */ +/*ARGSUSED*/ +static int +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; + 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; + 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, zflg, + NULL, realnmp)) { + if (realnmp) + pn_free(realnmp); + ZFS_EXIT(zfsvfs); + return (error); + } + + vp = ZTOV(zp); + + if (error = zfs_zaccess_delete(dzp, zp, cr)) { + goto out; + } + + /* + * Need to use rmdir for removing directories. + */ + if (vp->v_type == VDIR) { + error = EPERM; + goto out; + } + + vnevent_remove(vp, dvp, name, ct); + + if (realnmp) + dnlc_remove(dvp, realnmp->pn_buf); + else + dnlc_remove(dvp, name); + + mutex_enter(&vp->v_lock); + may_delete_now = vp->v_count == 1 && !vn_has_cached_data(vp); + mutex_exit(&vp->v_lock); + + /* + * We may delete the znode now, or we may put it in the unlinked set; + * it depends on whether we're the last link, and on whether there are + * other holds on the vnode. So we dmu_tx_hold() the right things to + * allow for either case. + */ + 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); + + /* are there any extended attributes? */ + if ((xattr_obj = zp->z_phys->zp_xattr) != 0) { + /* XXX - do we need this if we are deleting? */ + dmu_tx_hold_bonus(tx, xattr_obj); + } + + /* are there any additional acls */ + if ((acl_obj = zp->z_phys->zp_acl.z_acl_extern_obj) != 0 && + may_delete_now) + dmu_tx_hold_free(tx, acl_obj, 0, DMU_OBJECT_END); + + /* charge as an update -- would be nice not to charge at all */ + dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL); + + error = dmu_tx_assign(tx, zfsvfs->z_assign); + if (error) { + zfs_dirent_unlock(dl); + VN_RELE(vp); + if (error == ERESTART && zfsvfs->z_assign == TXG_NOWAIT) { + dmu_tx_wait(tx); + dmu_tx_abort(tx); + goto top; + } + if (realnmp) + pn_free(realnmp); + dmu_tx_abort(tx); + ZFS_EXIT(zfsvfs); + return (error); + } + + /* + * Remove the directory entry. + */ + error = zfs_link_destroy(dl, zp, tx, zflg, &unlinked); + + if (error) { + dmu_tx_commit(tx); + goto out; + } + + if (unlinked) { + mutex_enter(&vp->v_lock); + delete_now = may_delete_now && + 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; + mutex_exit(&vp->v_lock); + } + + if (delete_now) { + if (zp->z_phys->zp_xattr) { + error = zfs_zget(zfsvfs, zp->z_phys->zp_xattr, &xzp); + ASSERT3U(error, ==, 0); + ASSERT3U(xzp->z_phys->zp_links, ==, 2); + dmu_buf_will_dirty(xzp->z_dbuf, tx); + mutex_enter(&xzp->z_lock); + xzp->z_unlinked = 1; + xzp->z_phys->zp_links = 0; + mutex_exit(&xzp->z_lock); + zfs_unlinked_add(xzp, tx); + zp->z_phys->zp_xattr = 0; /* probably unnecessary */ + } + mutex_enter(&zp->z_lock); + mutex_enter(&vp->v_lock); + vp->v_count--; + ASSERT3U(vp->v_count, ==, 0); + mutex_exit(&vp->v_lock); + mutex_exit(&zp->z_lock); + zfs_znode_delete(zp, tx); + } else if (unlinked) { + zfs_unlinked_add(zp, tx); + } + + 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 */ + VN_RELE(ZTOV(xzp)); + } + + ZFS_EXIT(zfsvfs); + return (error); +} + +/* + * Create a new directory and insert it into dvp using the name + * provided. Return a pointer to the inserted directory. + * + * IN: dvp - vnode of directory to add subdir to. + * 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. + * + * RETURN: 0 if success + * error code if failure + * + * Timestamps: + * 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, + caller_context_t *ct, int flags, vsecattr_t *vsecp) +{ + znode_t *zp, *dzp = VTOZ(dvp); + zfsvfs_t *zfsvfs = dzp->z_zfsvfs; + zilog_t *zilog; + zfs_dirlock_t *dl; + 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); + } + + 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. + */ +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, 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 ((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); + if (error) { + zfs_dirent_unlock(dl); + if (error == ERESTART && zfsvfs->z_assign == TXG_NOWAIT) { + dmu_tx_wait(tx); + dmu_tx_abort(tx); + goto top; + } + dmu_tx_abort(tx); + ZFS_EXIT(zfsvfs); + if (aclp) + zfs_acl_free(aclp); + return (error); + } + + /* + * Create new node. + */ + zfs_mknode(dzp, vap, tx, cr, 0, &zp, 0, aclp, &fuidp); + + if (aclp) + zfs_acl_free(aclp); + + /* + * Now put new name in parent dir. + */ + (void) zfs_link_create(dl, zp, tx, ZNEW); + + *vpp = ZTOV(zp); + + 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); + + ZFS_EXIT(zfsvfs); + return (0); +} + +/* + * Remove a directory subdir entry. If the current working + * directory is the same as the subdir to be removed, the + * remove will fail. + * + * IN: dvp - vnode of directory to remove from. + * 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 + * + * Timestamps: + * dvp - ctime|mtime updated + */ +/*ARGSUSED*/ +static int +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; + 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, zflg, + NULL, NULL)) { + ZFS_EXIT(zfsvfs); + return (error); + } + + vp = ZTOV(zp); + + if (error = zfs_zaccess_delete(dzp, zp, cr)) { + goto out; + } + + if (vp->v_type != VDIR) { + error = ENOTDIR; + goto out; + } + + if (vp == cwd) { + error = EINVAL; + goto out; + } + + vnevent_rmdir(vp, dvp, name, ct); + + /* + * Grab a lock on the directory to make sure that noone is + * trying to add (or lookup) entries while we are removing it. + */ + rw_enter(&zp->z_name_lock, RW_WRITER); + + /* + * Grab a lock on the parent pointer to make sure we play well + * with the treewalk and directory rename code. + */ + rw_enter(&zp->z_parent_lock, RW_WRITER); + + 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); + dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL); + error = dmu_tx_assign(tx, zfsvfs->z_assign); + if (error) { + rw_exit(&zp->z_parent_lock); + rw_exit(&zp->z_name_lock); + zfs_dirent_unlock(dl); + VN_RELE(vp); + if (error == ERESTART && zfsvfs->z_assign == TXG_NOWAIT) { + dmu_tx_wait(tx); + dmu_tx_abort(tx); + goto top; + } + dmu_tx_abort(tx); + ZFS_EXIT(zfsvfs); + return (error); + } + + error = zfs_link_destroy(dl, zp, tx, zflg, NULL); + + 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); + + rw_exit(&zp->z_parent_lock); + rw_exit(&zp->z_name_lock); +out: + zfs_dirent_unlock(dl); + + VN_RELE(vp); + + ZFS_EXIT(zfsvfs); + return (error); +} + +/* + * Read as many directory entries as will fit into the provided + * buffer from the given directory cursor position (specified in + * the uio structure. + * + * IN: vp - vnode of directory to read. + * 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. + * + * RETURN: 0 if success + * error code if failure + * + * Timestamps: + * vp - atime updated + * + * Note that the low 4 bits of the cookie returned by zap is always zero. + * This allows us to use the low range for "special" directory entries: + * We use 0 for '.', and 1 for '..'. If this is the root of the filesystem, + * we use the offset 2 for the '.zfs' directory. + */ +/* ARGSUSED */ +static int +zfs_readdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp, + caller_context_t *ct, int flags) +{ + znode_t *zp = VTOZ(vp); + iovec_t *iovp; + edirent_t *eodp; + dirent64_t *odp; + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + objset_t *os; + caddr_t outbuf; + size_t bufsize; + zap_cursor_t zc; + zap_attribute_t zap; + uint_t bytes_wanted; + uint64_t offset; /* must be unsigned; checks for < 1 */ + int local_eof; + int outcount; + int error; + uint8_t prefetch; + boolean_t check_sysattrs; + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(zp); + + /* + * If we are not given an eof variable, + * use a local one. + */ + if (eofp == NULL) + eofp = &local_eof; + + /* + * Check for valid iov_len. + */ + if (uio->uio_iov->iov_len <= 0) { + ZFS_EXIT(zfsvfs); + return (EINVAL); + } + + /* + * Quit if directory has been removed (posix) + */ + if ((*eofp = zp->z_unlinked) != 0) { + ZFS_EXIT(zfsvfs); + return (0); + } + + error = 0; + os = zfsvfs->z_os; + offset = uio->uio_loffset; + prefetch = zp->z_zn_prefetch; + + /* + * Initialize the iterator cursor. + */ + if (offset <= 3) { + /* + * Start iteration from the beginning of the directory. + */ + zap_cursor_init(&zc, os, zp->z_id); + } else { + /* + * The offset is a serialized cursor. + */ + zap_cursor_init_serialized(&zc, os, zp->z_id, offset); + } + + /* + * Get space to change directory entries into fs independent format. + */ + iovp = uio->uio_iov; + bytes_wanted = iovp->iov_len; + if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1) { + bufsize = bytes_wanted; + outbuf = kmem_alloc(bufsize, KM_SLEEP); + odp = (struct dirent64 *)outbuf; + } else { + bufsize = bytes_wanted; + odp = (struct dirent64 *)iovp->iov_base; + } + eodp = (struct edirent *)odp; + + /* + * If this VFS supports system attributes; 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. + */ + check_sysattrs = vfs_has_feature(vp->v_vfsp, VFSFT_XVATTR) && + (vp->v_flag & V_XATTRDIR) && zfsvfs->z_norm && + (flags & V_RDDIR_ENTFLAGS); + + /* + * Transform to file-system independent format + */ + outcount = 0; + 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; + } else if (offset == 1) { + (void) strcpy(zap.za_name, ".."); + zap.za_normalization_conflict = 0; + objnum = zp->z_phys->zp_parent; + } 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; + } else { + /* + * Grab next entry. + */ + if (error = zap_cursor_retrieve(&zc, &zap)) { + if ((*eofp = (error == ENOENT)) != 0) + break; + else + goto update; + } + + if (zap.za_integer_length != 8 || + zap.za_num_integers != 1) { + cmn_err(CE_WARN, "zap_readdir: bad directory " + "entry, obj = %lld, offset = %lld\n", + (u_longlong_t)zp->z_id, + (u_longlong_t)offset); + error = ENXIO; + goto update; + } + + objnum = ZFS_DIRENT_OBJ(zap.za_first_integer); + /* + * MacOS X can extract the object type here such as: + * uint8_t type = ZFS_DIRENT_TYPE(zap.za_first_integer); + */ + + if (check_sysattrs && !zap.za_normalization_conflict) { + zap.za_normalization_conflict = + xattr_sysattr_casechk(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? + */ + if (outcount + reclen > bufsize) { + /* + * Did we manage to fit anything in the buffer? + */ + if (!outcount) { + error = EINVAL; + goto update; + } + break; + } + 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; + /* NOTE: d_off is the offset for the *next* entry */ + next = &(odp->d_off); + (void) strncpy(odp->d_name, zap.za_name, + DIRENT64_NAMELEN(reclen)); + odp = (dirent64_t *)((intptr_t)odp + reclen); + } + outcount += reclen; + + ASSERT(outcount <= bufsize); + + /* Prefetch znode */ + if (prefetch) + dmu_prefetch(os, objnum, 0, 0); + + /* + * Move to the next entry, fill in the previous offset. + */ + if (offset > 2 || (offset == 2 && !zfs_show_ctldir(zp))) { + zap_cursor_advance(&zc); + offset = zap_cursor_serialize(&zc); + } else { + offset += 1; + } + *next = offset; + } + zp->z_zn_prefetch = B_FALSE; /* a lookup will re-enable pre-fetching */ + + if (uio->uio_segflg == UIO_SYSSPACE && uio->uio_iovcnt == 1) { + iovp->iov_base += outcount; + iovp->iov_len -= outcount; + uio->uio_resid -= outcount; + } else if (error = uiomove(outbuf, (long)outcount, UIO_READ, uio)) { + /* + * Reset the pointer. + */ + offset = uio->uio_loffset; + } + +update: + zap_cursor_fini(&zc); + if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1) + kmem_free(outbuf, bufsize); + + if (error == ENOENT) + error = 0; + + ZFS_ACCESSTIME_STAMP(zfsvfs, zp); + + uio->uio_loffset = offset; + ZFS_EXIT(zfsvfs); + return (error); +} + +ulong_t zfs_fsync_sync_cnt = 4; + +static int +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; + + /* + * Regardless of whether this is required for standards conformance, + * this is the logical behavior when fsync() is called on a file with + * dirty pages. We use B_ASYNC since the ZIL transactions are already + * going to be pushed out as part of the zil_commit(). + */ + if (vn_has_cached_data(vp) && !(syncflag & FNODSYNC) && + (vp->v_type == VREG) && !(IS_SWAPVP(vp))) + (void) VOP_PUTPAGE(vp, (offset_t)0, (size_t)0, B_ASYNC, cr, ct); + + (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. + * 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. + * + * RETURN: 0 (always succeeds) + */ +/* ARGSUSED */ +static int +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; + int error = 0; + 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. + */ + + vap->va_type = vp->v_type; + vap->va_mode = pzp->zp_mode & MODEMASK; + 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; + 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_rdev = vp->v_rdev; + vap->va_seq = zp->z_seq; + + /* + * Add in any requested optional attributes and the create time. + * Also set the corresponding bits in the returned attribute bitmap. + */ + 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); + + mutex_exit(&zp->z_lock); + + dmu_object_size_from_db(zp->z_dbuf, &vap->va_blksize, &vap->va_nblocks); + + if (zp->z_blksz == 0) { + /* + * Block size hasn't been set; suggest maximal I/O transfers. + */ + vap->va_blksize = zfsvfs->z_max_blksz; + } + + ZFS_EXIT(zfsvfs); + return (0); +} + +/* + * Set the file attributes to the values contained in the + * vattr structure. + * + * 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 + * + * Timestamps: + * vp - ctime updated, mtime updated if size changed. + */ +/* ARGSUSED */ +static int +zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) +{ + znode_t *zp = VTOZ(vp); + znode_phys_t *pzp; + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + zilog_t *zilog; + dmu_tx_t *tx; + vattr_t oldva; + uint_t mask = vap->va_mask; + uint_t saved_mask; + int trim_mask = 0; + uint64_t new_mode; + 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); + + if (mask & AT_NOSET) + return (EINVAL); + + 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) { + ZFS_EXIT(zfsvfs); + return (EINVAL); + } + + /* + * 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; + + if (zfsvfs->z_vfs->vfs_flag & VFS_RDONLY) { + ZFS_EXIT(zfsvfs); + return (EROFS); + } + + /* + * First validate permissions + */ + + if (mask & AT_SIZE) { + err = zfs_zaccess(zp, ACE_WRITE_DATA, 0, skipaclchk, cr); + if (err) { + ZFS_EXIT(zfsvfs); + return (err); + } + /* + * XXX - Note, we are not providing any open + * mode flags here (like FNDELAY), so we may + * 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); + if (err) { + ZFS_EXIT(zfsvfs); + return (err); + } + } + + 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)); + int take_owner; + int take_group; + + /* + * NOTE: even if a new mode is being set, + * we may clear S_ISUID/S_ISGID bits. + */ + + if (!(mask & AT_MODE)) + vap->va_mode = pzp->zp_mode; + + /* + * Take ownership or chgrp to group we are a member of + */ + + take_owner = (mask & AT_UID) && (vap->va_uid == crgetuid(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 + * take_group must both be set in order to allow taking + * ownership. + * + * Otherwise, send the check through secpolicy_vnode_setattr() + * + */ + + if (((idmask == (AT_UID|AT_GID)) && take_owner && take_group) || + ((idmask == AT_UID) && take_owner) || + ((idmask == AT_GID) && take_group)) { + if (zfs_zaccess(zp, ACE_WRITE_OWNER, 0, + skipaclchk, cr) == 0) { + /* + * Remove setuid/setgid for non-privileged users + */ + secpolicy_setid_clear(vap, cr); + trim_mask = (mask & (AT_UID|AT_GID)); + } else { + need_policy = TRUE; + } + } else { + need_policy = TRUE; + } + } + + mutex_enter(&zp->z_lock); + oldva.va_mode = pzp->zp_mode; + 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(zp, ACE_WRITE_ACL, 0, skipaclchk, cr) == 0) { + err = secpolicy_setid_setsticky_clear(vp, vap, + &oldva, cr); + if (err) { + ZFS_EXIT(zfsvfs); + return (err); + } + trim_mask |= AT_MODE; + } else { + need_policy = TRUE; + } + } + + if (need_policy) { + /* + * If trim_mask is set then take ownership + * has been granted or write_acl is present and user + * has the ability to modify mode. In that case remove + * UID|GID and or MODE from mask so that + * secpolicy_vnode_setattr() doesn't revoke it. + */ + + 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_unix, zp); + if (err) { + ZFS_EXIT(zfsvfs); + return (err); + } + + if (trim_mask) + vap->va_mask |= saved_mask; + } + + /* + * secpolicy_vnode_setattr, or take ownership may have + * changed va_mask + */ + mask = vap->va_mask; + + 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 (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, aclp->z_acl_bytes); + } + } + + 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); + } + + err = dmu_tx_assign(tx, zfsvfs->z_assign); + 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); + goto top; + } + dmu_tx_abort(tx); + ZFS_EXIT(zfsvfs); + return (err); + } + + dmu_buf_will_dirty(zp->z_dbuf, tx); + + /* + * Set each attribute requested. + * We group settings according to the locks they need to acquire. + * + * Note: you cannot set ctime directly, although it will be + * updated as a side-effect of calling this function. + */ + + mutex_enter(&zp->z_lock); + + if (mask & AT_MODE) { + 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) { + pzp->zp_uid = zfs_fuid_create(zfsvfs, + vap->va_uid, cr, ZFS_OWNER, tx, &fuidp); + if (attrzp) { + attrzp->z_phys->zp_uid = zfs_fuid_create(zfsvfs, + vap->va_uid, cr, ZFS_OWNER, tx, &fuidp); + } + } + + if (mask & AT_GID) { + pzp->zp_gid = zfs_fuid_create(zfsvfs, vap->va_gid, + cr, ZFS_GROUP, tx, &fuidp); + if (attrzp) + 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); + + if (mask & AT_ATIME) + ZFS_TIME_ENCODE(&vap->va_atime, pzp->zp_atime); + + if (mask & AT_MTIME) + ZFS_TIME_ENCODE(&vap->va_mtime, pzp->zp_mtime); + + 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, fuidp); + + if (fuidp) + zfs_fuid_info_free(fuidp); + mutex_exit(&zp->z_lock); + + if (attrzp) + VN_RELE(ZTOV(attrzp)); + + dmu_tx_commit(tx); + + ZFS_EXIT(zfsvfs); + return (err); +} + +typedef struct zfs_zlock { + krwlock_t *zl_rwlock; /* lock we acquired */ + znode_t *zl_znode; /* znode we held */ + struct zfs_zlock *zl_next; /* next in list */ +} zfs_zlock_t; + +/* + * Drop locks and release vnodes that were held by zfs_rename_lock(). + */ +static void +zfs_rename_unlock(zfs_zlock_t **zlpp) +{ + zfs_zlock_t *zl; + + while ((zl = *zlpp) != NULL) { + if (zl->zl_znode != NULL) + VN_RELE(ZTOV(zl->zl_znode)); + rw_exit(zl->zl_rwlock); + *zlpp = zl->zl_next; + kmem_free(zl, sizeof (*zl)); + } +} + +/* + * Search back through the directory tree, using the ".." entries. + * Lock each directory in the chain to prevent concurrent renames. + * Fail any attempt to move a directory into one of its own descendants. + * XXX - z_parent_lock can overlap with map or grow locks + */ +static int +zfs_rename_lock(znode_t *szp, znode_t *tdzp, znode_t *sdzp, zfs_zlock_t **zlpp) +{ + zfs_zlock_t *zl; + znode_t *zp = tdzp; + uint64_t rootid = zp->z_zfsvfs->z_root; + uint64_t *oidp = &zp->z_id; + krwlock_t *rwlp = &szp->z_parent_lock; + krw_t rw = RW_WRITER; + + /* + * First pass write-locks szp and compares to zp->z_id. + * Later passes read-lock zp and compare to zp->z_parent. + */ + do { + if (!rw_tryenter(rwlp, rw)) { + /* + * Another thread is renaming in this path. + * Note that if we are a WRITER, we don't have any + * parent_locks held yet. + */ + if (rw == RW_READER && zp->z_id > szp->z_id) { + /* + * Drop our locks and restart + */ + zfs_rename_unlock(&zl); + *zlpp = NULL; + zp = tdzp; + oidp = &zp->z_id; + rwlp = &szp->z_parent_lock; + rw = RW_WRITER; + continue; + } else { + /* + * Wait for other thread to drop its locks + */ + rw_enter(rwlp, rw); + } + } + + zl = kmem_alloc(sizeof (*zl), KM_SLEEP); + zl->zl_rwlock = rwlp; + zl->zl_znode = NULL; + zl->zl_next = *zlpp; + *zlpp = zl; + + if (*oidp == szp->z_id) /* We're a descendant of szp */ + return (EINVAL); + + if (*oidp == rootid) /* We've hit the top */ + return (0); + + if (rw == RW_READER) { /* i.e. not the first pass */ + int error = zfs_zget(zp->z_zfsvfs, *oidp, &zp); + if (error) + return (error); + zl->zl_znode = zp; + } + oidp = &zp->z_phys->zp_parent; + rwlp = &zp->z_parent_lock; + rw = RW_READER; + + } while (zp->z_id != sdzp->z_id); + + return (0); +} + +/* + * Move an entry from the provided source directory to the target + * directory. Change the entry name as indicated. + * + * IN: sdvp - Source directory containing the "old entry". + * snm - Old entry name. + * 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 + * + * Timestamps: + * sdvp,tdvp - ctime|mtime updated + */ +/*ARGSUSED*/ +static int +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; + vnode_t *realvp; + zfs_dirlock_t *sdl, *tdl; + dmu_tx_t *tx; + zfs_zlock_t *zl; + 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, ct) == 0) + tdvp = realvp; + + if (tdvp->v_vfsp != sdvp->v_vfsp) { + ZFS_EXIT(zfsvfs); + return (EXDEV); + } + + 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; + zl = NULL; + + /* + * This is to prevent the creation of links into attribute space + * by renaming a linked file into/outof an attribute directory. + * See the comment in zfs_link() for why this is considered bad. + */ + if ((tdzp->z_phys->zp_flags & ZFS_XATTR) != + (sdzp->z_phys->zp_flags & ZFS_XATTR)) { + ZFS_EXIT(zfsvfs); + return (EINVAL); + } + + /* + * Lock source and target directory entries. To prevent deadlock, + * a lock ordering must be defined. We lock the directory with + * the smallest object id first, or if it's a tie, the one with + * the lexically first name. + */ + if (sdzp->z_id < tdzp->z_id) { + cmp = -1; + } else if (sdzp->z_id > tdzp->z_id) { + cmp = 1; + } else { + /* + * 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 + * both refer to links to the same existing file, + * the rename() function shall return successfully + * and perform no other action." + */ + 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 | zflg, NULL, NULL); + terr = zfs_dirent_lock(&tdl, + tdzp, tnm, &tzp, ZRENAMING | zflg, NULL, NULL); + } else { + 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) { + /* + * Source entry invalid or not there. + */ + if (!terr) { + zfs_dirent_unlock(tdl); + if (tzp) + VN_RELE(ZTOV(tzp)); + } + if (strcmp(snm, "..") == 0) + serr = EINVAL; + ZFS_EXIT(zfsvfs); + return (serr); + } + if (terr) { + zfs_dirent_unlock(sdl); + VN_RELE(ZTOV(szp)); + if (strcmp(tnm, "..") == 0) + terr = EINVAL; + ZFS_EXIT(zfsvfs); + return (terr); + } + + /* + * Must have write access at the source to remove the old entry + * and write access at the target to create the new entry. + * Note that if target and source are the same, this can be + * done in a single check. + */ + + if (error = zfs_zaccess_rename(sdzp, szp, tdzp, tzp, cr)) + goto out; + + if (ZTOV(szp)->v_type == VDIR) { + /* + * Check to make sure rename is valid. + * Can't do a move like this: /usr/a/b to /usr/a/b/c/d + */ + if (error = zfs_rename_lock(szp, tdzp, sdzp, &zl)) + goto out; + } + + /* + * Does target exist? + */ + if (tzp) { + /* + * Source and target must be the same type. + */ + if (ZTOV(szp)->v_type == VDIR) { + if (ZTOV(tzp)->v_type != VDIR) { + error = ENOTDIR; + goto out; + } + } else { + if (ZTOV(tzp)->v_type == VDIR) { + error = EISDIR; + goto out; + } + } + /* + * POSIX dictates that when the source and target + * entries refer to the same file object, rename + * must do nothing and exit without error. + */ + if (szp->z_id == tzp->z_id) { + error = 0; + goto out; + } + } + + vnevent_rename_src(ZTOV(szp), sdvp, snm, ct); + if (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 */ + dmu_tx_hold_bonus(tx, sdzp->z_id); /* nlink changes */ + dmu_tx_hold_zap(tx, sdzp->z_id, FALSE, snm); + dmu_tx_hold_zap(tx, tdzp->z_id, TRUE, tnm); + if (sdzp != tdzp) + dmu_tx_hold_bonus(tx, tdzp->z_id); /* nlink changes */ + if (tzp) + dmu_tx_hold_bonus(tx, tzp->z_id); /* parent changes */ + dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL); + error = dmu_tx_assign(tx, zfsvfs->z_assign); + if (error) { + if (zl != NULL) + zfs_rename_unlock(&zl); + zfs_dirent_unlock(sdl); + zfs_dirent_unlock(tdl); + VN_RELE(ZTOV(szp)); + if (tzp) + VN_RELE(ZTOV(tzp)); + if (error == ERESTART && zfsvfs->z_assign == TXG_NOWAIT) { + dmu_tx_wait(tx); + dmu_tx_abort(tx); + goto top; + } + dmu_tx_abort(tx); + ZFS_EXIT(zfsvfs); + return (error); + } + + if (tzp) /* Attempt to remove the existing target */ + 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 | (flags & FIGNORECASE ? TX_CI : 0), + sdzp, sdl->dl_name, tdzp, tdl->dl_name, szp); + } + } + + dmu_tx_commit(tx); +out: + if (zl != NULL) + zfs_rename_unlock(&zl); + + zfs_dirent_unlock(sdl); + zfs_dirent_unlock(tdl); + + VN_RELE(ZTOV(szp)); + if (tzp) + VN_RELE(ZTOV(tzp)); + + ZFS_EXIT(zfsvfs); + return (error); +} + +/* + * Insert the indicated symbolic reference entry into the directory. + * + * IN: dvp - Directory to contain new symbolic link. + * link - Name for new symlink entry. + * 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 + * + * Timestamps: + * dvp - ctime|mtime updated + */ +/*ARGSUSED*/ +static int +zfs_symlink(vnode_t *dvp, char *name, vattr_t *vap, char *link, cred_t *cr, + caller_context_t *ct, int flags) +{ + znode_t *zp, *dzp = VTOZ(dvp); + zfs_dirlock_t *dl; + dmu_tx_t *tx; + zfsvfs_t *zfsvfs = dzp->z_zfsvfs; + zilog_t *zilog; + int len = strlen(link); + int error; + int zflg = ZNEW; + zfs_fuid_info_t *fuidp = NULL; + + 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, 0, B_FALSE, cr)) { + ZFS_EXIT(zfsvfs); + return (error); + } + + if (len > MAXPATHLEN) { + ZFS_EXIT(zfsvfs); + return (ENAMETOOLONG); + } + + /* + * Attempt to lock directory; fail if entry already exists. + */ + error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg, NULL, NULL); + if (error) { + ZFS_EXIT(zfsvfs); + return (error); + } + + tx = dmu_tx_create(zfsvfs->z_os); + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, MAX(1, len)); + 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) + 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); + if (error == ERESTART && zfsvfs->z_assign == TXG_NOWAIT) { + dmu_tx_wait(tx); + dmu_tx_abort(tx); + goto top; + } + dmu_tx_abort(tx); + ZFS_EXIT(zfsvfs); + return (error); + } + + dmu_buf_will_dirty(dzp->z_dbuf, tx); + + /* + * Create a new object for the symlink. + * Put the link content into bonus buffer if it will fit; + * otherwise, store it just like any other file data. + */ + if (sizeof (znode_phys_t) + len <= dmu_bonus_max()) { + 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, 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, + zp->z_id, 0, FTAG, &dbp)); + dmu_buf_will_dirty(dbp, tx); + + ASSERT3U(len, <=, dbp->db_size); + bcopy(link, dbp->db_data, len); + dmu_buf_rele(dbp, FTAG); + } + zp->z_phys->zp_size = len; + + /* + * Insert the new object into the directory. + */ + (void) zfs_link_create(dl, zp, tx, ZNEW); +out: + if (error == 0) { + uint64_t txtype = TX_SYMLINK; + if (flags & FIGNORECASE) + txtype |= TX_CI; + zfs_log_symlink(zilog, tx, txtype, dzp, zp, name, link); + } + if (fuidp) + zfs_fuid_info_free(fuidp); + + dmu_tx_commit(tx); + + zfs_dirent_unlock(dl); + + VN_RELE(ZTOV(zp)); + + ZFS_EXIT(zfsvfs); + return (error); +} + +/* + * Return, in the buffer contained in the provided uio structure, + * the symbolic path referred to by vp. + * + * 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. + * + * RETURN: 0 if success + * error code if failure + * + * Timestamps: + * vp - atime updated + */ +/* ARGSUSED */ +static int +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; + size_t bufsz; + 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) { + error = uiomove(zp->z_phys + 1, + MIN((size_t)bufsz, uio->uio_resid), UIO_READ, uio); + } else { + dmu_buf_t *dbp; + error = dmu_buf_hold(zfsvfs->z_os, zp->z_id, 0, FTAG, &dbp); + if (error) { + ZFS_EXIT(zfsvfs); + return (error); + } + error = uiomove(dbp->db_data, + MIN((size_t)bufsz, uio->uio_resid), UIO_READ, uio); + dmu_buf_rele(dbp, FTAG); + } + + ZFS_ACCESSTIME_STAMP(zfsvfs, zp); + ZFS_EXIT(zfsvfs); + return (error); +} + +/* + * Insert a new entry into directory tdvp referencing svp. + * + * IN: tdvp - Directory to contain new entry. + * 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 + * + * Timestamps: + * tdvp - ctime|mtime updated + * svp - ctime updated + */ +/* ARGSUSED */ +static int +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; + 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, 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 + * because of the potential security risk of creating links + * into "normal" file space in order to circumvent restrictions + * imposed in attribute space. + */ + if ((szp->z_phys->zp_flags & ZFS_XATTR) != + (dzp->z_phys->zp_flags & ZFS_XATTR)) { + ZFS_EXIT(zfsvfs); + return (EINVAL); + } + + /* + * POSIX dictates that we return EPERM here. + * Better choices include ENOTSUP or EISDIR. + */ + if (svp->v_type == VDIR) { + ZFS_EXIT(zfsvfs); + return (EPERM); + } + + owner = zfs_fuid_map_id(zfsvfs, szp->z_phys->zp_uid, cr, ZFS_OWNER); + if (owner != crgetuid(cr) && + secpolicy_basic_link(cr) != 0) { + ZFS_EXIT(zfsvfs); + return (EPERM); + } + + if (error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr)) { + ZFS_EXIT(zfsvfs); + return (error); + } + + /* + * Attempt to lock directory; fail if entry already exists. + */ + error = zfs_dirent_lock(&dl, dzp, name, &tzp, zf, NULL, NULL); + if (error) { + ZFS_EXIT(zfsvfs); + return (error); + } + + tx = dmu_tx_create(zfsvfs->z_os); + dmu_tx_hold_bonus(tx, szp->z_id); + dmu_tx_hold_zap(tx, dzp->z_id, TRUE, name); + error = dmu_tx_assign(tx, zfsvfs->z_assign); + if (error) { + zfs_dirent_unlock(dl); + if (error == ERESTART && zfsvfs->z_assign == TXG_NOWAIT) { + dmu_tx_wait(tx); + dmu_tx_abort(tx); + goto top; + } + dmu_tx_abort(tx); + ZFS_EXIT(zfsvfs); + return (error); + } + + error = zfs_link_create(dl, szp, tx, 0); + + 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); +} + +/* + * zfs_null_putapage() is used when the file system has been force + * unmounted. It just drops the pages. + */ +/* ARGSUSED */ +static int +zfs_null_putapage(vnode_t *vp, page_t *pp, u_offset_t *offp, + size_t *lenp, int flags, cred_t *cr) +{ + pvn_write_done(pp, B_INVAL|B_FORCE|B_ERROR); + return (0); +} + +/* + * Push a page out to disk, klustering if possible. + * + * IN: vp - file to push page to. + * pp - page to push. + * flags - additional flags. + * cr - credentials of caller. + * + * OUT: offp - start of range pushed. + * lenp - len of range pushed. + * + * RETURN: 0 if success + * error code if failure + * + * NOTE: callers must have locked the page to be pushed. On + * exit, the page (and all other pages in the kluster) must be + * unlocked. + */ +/* ARGSUSED */ +static int +zfs_putapage(vnode_t *vp, page_t *pp, u_offset_t *offp, + size_t *lenp, int flags, cred_t *cr) +{ + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + zilog_t *zilog = zfsvfs->z_log; + dmu_tx_t *tx; + rl_t *rl; + u_offset_t off, koff; + size_t len, klen; + uint64_t filesz; + int err; + + filesz = zp->z_phys->zp_size; + off = pp->p_offset; + len = PAGESIZE; + /* + * If our blocksize is bigger than the page size, try to kluster + * muiltiple pages so that we write a full block (thus avoiding + * a read-modify-write). + */ + if (off < filesz && zp->z_blksz > PAGESIZE) { + if (!ISP2(zp->z_blksz)) { + /* Only one block in the file. */ + klen = P2ROUNDUP((ulong_t)zp->z_blksz, PAGESIZE); + koff = 0; + } else { + klen = zp->z_blksz; + koff = P2ALIGN(off, (u_offset_t)klen); + } + ASSERT(koff <= filesz); + if (koff + klen > filesz) + klen = P2ROUNDUP(filesz - koff, (uint64_t)PAGESIZE); + pp = pvn_write_kluster(vp, pp, &off, &len, koff, klen, flags); + } + ASSERT3U(btop(len), ==, btopr(len)); +top: + rl = zfs_range_lock(zp, off, len, RL_WRITER); + /* + * Can't push pages past end-of-file. + */ + filesz = zp->z_phys->zp_size; + if (off >= filesz) { + /* ignore all pages */ + err = 0; + goto out; + } else if (off + len > filesz) { + int npages = btopr(filesz - off); + page_t *trunc; + + page_list_break(&pp, &trunc, npages); + /* ignore pages past end of file */ + if (trunc) + pvn_write_done(trunc, flags); + len = filesz - off; + } + + tx = dmu_tx_create(zfsvfs->z_os); + dmu_tx_hold_write(tx, zp->z_id, off, len); + dmu_tx_hold_bonus(tx, zp->z_id); + err = dmu_tx_assign(tx, zfsvfs->z_assign); + if (err != 0) { + if (err == ERESTART && zfsvfs->z_assign == TXG_NOWAIT) { + zfs_range_unlock(rl); + dmu_tx_wait(tx); + dmu_tx_abort(tx); + err = 0; + goto top; + } + dmu_tx_abort(tx); + goto out; + } + + if (zp->z_blksz <= PAGESIZE) { + caddr_t va = ppmapin(pp, PROT_READ, (caddr_t)-1); + ASSERT3U(len, <=, PAGESIZE); + dmu_write(zfsvfs->z_os, zp->z_id, off, len, va, tx); + ppmapout(va); + } else { + err = dmu_write_pages(zfsvfs->z_os, zp->z_id, off, len, pp, tx); + } + + if (err == 0) { + zfs_time_stamper(zp, CONTENT_MODIFIED, tx); + zfs_log_write(zilog, tx, TX_WRITE, zp, off, len, 0); + dmu_tx_commit(tx); + } + +out: + zfs_range_unlock(rl); + pvn_write_done(pp, (err ? B_ERROR : 0) | flags); + if (offp) + *offp = off; + if (lenp) + *lenp = len; + + return (err); +} + +/* + * Copy the portion of the file indicated from pages into the file. + * The pages are stored in a page list attached to the files vnode. + * + * IN: vp - vnode of file to push page data to. + * off - position in file to put data. + * len - amount of data to write. + * flags - flags to control the operation. + * cr - credentials of caller. + * ct - caller context. + * + * RETURN: 0 if success + * error code if failure + * + * Timestamps: + * vp - ctime|mtime updated + */ +/*ARGSUSED*/ +static int +zfs_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr, + caller_context_t *ct) +{ + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + page_t *pp; + size_t io_len; + u_offset_t io_off; + uint64_t filesz; + int error = 0; + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(zp); + + if (len == 0) { + /* + * Search the entire vp list for pages >= off. + */ + error = pvn_vplist_dirty(vp, (u_offset_t)off, zfs_putapage, + flags, cr); + goto out; + } + + filesz = zp->z_phys->zp_size; /* get consistent copy of zp_size */ + if (off > filesz) { + /* past end of file */ + ZFS_EXIT(zfsvfs); + return (0); + } + + len = MIN(len, filesz - off); + + for (io_off = off; io_off < off + len; io_off += io_len) { + if ((flags & B_INVAL) || ((flags & B_ASYNC) == 0)) { + pp = page_lookup(vp, io_off, + (flags & (B_INVAL | B_FREE)) ? SE_EXCL : SE_SHARED); + } else { + pp = page_lookup_nowait(vp, io_off, + (flags & B_FREE) ? SE_EXCL : SE_SHARED); + } + + if (pp != NULL && pvn_getdirty(pp, flags)) { + int err; + + /* + * Found a dirty page to push + */ + err = zfs_putapage(vp, pp, &io_off, &io_len, flags, cr); + if (err) + error = err; + } else { + io_len = PAGESIZE; + } + } +out: + if ((flags & B_ASYNC) == 0) + zil_commit(zfsvfs->z_log, UINT64_MAX, zp->z_id); + ZFS_EXIT(zfsvfs); + return (error); +} + +/*ARGSUSED*/ +void +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_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. + */ + if (vn_has_cached_data(vp)) { + (void) pvn_vplist_dirty(vp, 0, zfs_null_putapage, + B_INVAL, cr); + } + + mutex_enter(&zp->z_lock); + vp->v_count = 0; /* count arrives as 1 */ + mutex_exit(&zp->z_lock); + rw_exit(&zfsvfs->z_teardown_inactive_lock); + zfs_znode_free(zp); + return; + } + + /* + * Attempt to push any data in the page cache. If this fails + * we will get kicked out later in zfs_zinactive(). + */ + if (vn_has_cached_data(vp)) { + (void) pvn_vplist_dirty(vp, 0, zfs_putapage, B_INVAL|B_ASYNC, + cr); + } + + if (zp->z_atime_dirty && zp->z_unlinked == 0) { + dmu_tx_t *tx = dmu_tx_create(zfsvfs->z_os); + + dmu_tx_hold_bonus(tx, zp->z_id); + error = dmu_tx_assign(tx, TXG_WAIT); + if (error) { + dmu_tx_abort(tx); + } else { + dmu_buf_will_dirty(zp->z_dbuf, tx); + mutex_enter(&zp->z_lock); + zp->z_atime_dirty = 0; + mutex_exit(&zp->z_lock); + dmu_tx_commit(tx); + } + } + + zfs_zinactive(zp); + rw_exit(&zfsvfs->z_teardown_inactive_lock); +} + +/* + * Bounds-check the seek operation. + * + * IN: vp - vnode seeking within + * ooff - old file offset + * noffp - pointer to new file offset + * ct - caller context + * + * RETURN: 0 if success + * EINVAL if new offset invalid + */ +/* ARGSUSED */ +static int +zfs_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, + caller_context_t *ct) +{ + if (vp->v_type == VDIR) + return (0); + return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0); +} + +/* + * Pre-filter the generic locking function to trap attempts to place + * a mandatory lock on a memory mapped file. + */ +static int +zfs_frlock(vnode_t *vp, int cmd, flock64_t *bfp, int flag, offset_t offset, + flk_callback_t *flk_cbp, cred_t *cr, caller_context_t *ct) +{ + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + int error; + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(zp); + + /* + * We are following the UFS semantics with respect to mapcnt + * here: If we see that the file is mapped already, then we will + * return an error, but we don't worry about races between this + * function and zfs_map(). + */ + if (zp->z_mapcnt > 0 && MANDMODE((mode_t)zp->z_phys->zp_mode)) { + ZFS_EXIT(zfsvfs); + return (EAGAIN); + } + error = fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr, ct); + ZFS_EXIT(zfsvfs); + return (error); +} + +/* + * If we can't find a page in the cache, we will create a new page + * and fill it with file data. For efficiency, we may try to fill + * multiple pages at once (klustering). + */ +static int +zfs_fillpage(vnode_t *vp, u_offset_t off, struct seg *seg, + caddr_t addr, page_t *pl[], size_t plsz, enum seg_rw rw) +{ + znode_t *zp = VTOZ(vp); + page_t *pp, *cur_pp; + objset_t *os = zp->z_zfsvfs->z_os; + caddr_t va; + u_offset_t io_off, total; + uint64_t oid = zp->z_id; + size_t io_len; + uint64_t filesz; + int err; + + /* + * If we are only asking for a single page don't bother klustering. + */ + filesz = zp->z_phys->zp_size; /* get consistent copy of zp_size */ + if (off >= filesz) + return (EFAULT); + if (plsz == PAGESIZE || zp->z_blksz <= PAGESIZE) { + io_off = off; + io_len = PAGESIZE; + pp = page_create_va(vp, io_off, io_len, PG_WAIT, seg, addr); + } else { + /* + * Try to fill a kluster of pages (a blocks worth). + */ + size_t klen; + u_offset_t koff; + + if (!ISP2(zp->z_blksz)) { + /* Only one block in the file. */ + klen = P2ROUNDUP((ulong_t)zp->z_blksz, PAGESIZE); + koff = 0; + } else { + /* + * It would be ideal to align our offset to the + * blocksize but doing so has resulted in some + * strange application crashes. For now, we + * leave the offset as is and only adjust the + * length if we are off the end of the file. + */ + koff = off; + klen = plsz; + } + ASSERT(koff <= filesz); + if (koff + klen > filesz) + klen = P2ROUNDUP(filesz, (uint64_t)PAGESIZE) - koff; + ASSERT3U(off, >=, koff); + ASSERT3U(off, <, koff + klen); + pp = pvn_read_kluster(vp, off, seg, addr, &io_off, + &io_len, koff, klen, 0); + } + if (pp == NULL) { + /* + * Some other thread entered the page before us. + * Return to zfs_getpage to retry the lookup. + */ + *pl = NULL; + return (0); + } + + /* + * Fill the pages in the kluster. + */ + cur_pp = pp; + for (total = io_off + io_len; io_off < total; io_off += PAGESIZE) { + ASSERT3U(io_off, ==, cur_pp->p_offset); + va = ppmapin(cur_pp, PROT_READ | PROT_WRITE, (caddr_t)-1); + err = dmu_read(os, oid, io_off, PAGESIZE, va); + ppmapout(va); + if (err) { + /* On error, toss the entire kluster */ + pvn_read_done(pp, B_ERROR); + return (err); + } + cur_pp = cur_pp->p_next; + } +out: + /* + * Fill in the page list array from the kluster. If + * there are too many pages in the kluster, return + * as many pages as possible starting from the desired + * offset `off'. + * NOTE: the page list will always be null terminated. + */ + pvn_plist_init(pp, pl, plsz, off, io_len, rw); + + return (0); +} + +/* + * Return pointers to the pages for the file region [off, off + len] + * in the pl array. If plsz is greater than len, this function may + * also return page pointers from before or after the specified + * region (i.e. some region [off', off' + plsz]). These additional + * pages are only returned if they are already in the cache, or were + * created as part of a klustered read. + * + * IN: vp - vnode of file to get data from. + * off - position in file to get data from. + * len - amount of data to retrieve. + * plsz - length of provided page list. + * seg - segment to obtain pages for. + * addr - virtual address of fault. + * rw - mode of created pages. + * cr - credentials of caller. + * ct - caller context. + * + * OUT: protp - protection mode of created pages. + * pl - list of pages created. + * + * RETURN: 0 if success + * error code if failure + * + * Timestamps: + * vp - atime updated + */ +/* ARGSUSED */ +static int +zfs_getpage(vnode_t *vp, offset_t off, size_t len, uint_t *protp, + page_t *pl[], size_t plsz, struct seg *seg, caddr_t addr, + enum seg_rw rw, cred_t *cr, caller_context_t *ct) +{ + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + page_t *pp, **pl0 = pl; + int need_unlock = 0, err = 0; + offset_t orig_off; + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(zp); + + if (protp) + *protp = PROT_ALL; + + /* no faultahead (for now) */ + if (pl == NULL) { + ZFS_EXIT(zfsvfs); + return (0); + } + + /* can't fault past EOF */ + if (off >= zp->z_phys->zp_size) { + ZFS_EXIT(zfsvfs); + return (EFAULT); + } + orig_off = off; + + /* + * If we already own the lock, then we must be page faulting + * in the middle of a write to this file (i.e., we are writing + * to this file using data from a mapped region of the file). + */ + if (rw_owner(&zp->z_map_lock) != curthread) { + rw_enter(&zp->z_map_lock, RW_WRITER); + need_unlock = TRUE; + } + + /* + * Loop through the requested range [off, off + len] looking + * for pages. If we don't find a page, we will need to create + * a new page and fill it with data from the file. + */ + while (len > 0) { + if (plsz < PAGESIZE) + break; + if (pp = page_lookup(vp, off, SE_SHARED)) { + *pl++ = pp; + off += PAGESIZE; + addr += PAGESIZE; + len -= PAGESIZE; + plsz -= PAGESIZE; + } else { + err = zfs_fillpage(vp, off, seg, addr, pl, plsz, rw); + if (err) + goto out; + /* + * klustering may have changed our region + * to be block aligned. + */ + if (((pp = *pl) != 0) && (off != pp->p_offset)) { + int delta = off - pp->p_offset; + len += delta; + off -= delta; + addr -= delta; + } + while (*pl) { + pl++; + off += PAGESIZE; + addr += PAGESIZE; + plsz -= PAGESIZE; + if (len > PAGESIZE) + len -= PAGESIZE; + else + len = 0; + } + } + } + + /* + * Fill out the page array with any pages already in the cache. + */ + while (plsz > 0) { + pp = page_lookup_nowait(vp, off, SE_SHARED); + if (pp == NULL) + break; + *pl++ = pp; + off += PAGESIZE; + plsz -= PAGESIZE; + } + + ZFS_ACCESSTIME_STAMP(zfsvfs, zp); +out: + /* + * We can't grab the range lock for the page as reader which would + * stop truncation as this leads to deadlock. So we need to recheck + * the file size. + */ + if (orig_off >= zp->z_phys->zp_size) + err = EFAULT; + if (err) { + /* + * Release any pages we have previously locked. + */ + while (pl > pl0) + page_unlock(*--pl); + } + + *pl = NULL; + + if (need_unlock) + rw_exit(&zp->z_map_lock); + + ZFS_EXIT(zfsvfs); + return (err); +} + +/* + * Request a memory map for a section of a file. This code interacts + * with common code and the VM system as follows: + * + * common code calls mmap(), which ends up in smmap_common() + * + * this calls VOP_MAP(), which takes you into (say) zfs + * + * zfs_map() calls as_map(), passing segvn_create() as the callback + * + * segvn_create() creates the new segment and calls VOP_ADDMAP() + * + * zfs_addmap() updates z_mapcnt + */ +/*ARGSUSED*/ +static int +zfs_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, + size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr, + caller_context_t *ct) +{ + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + segvn_crargs_t vn_a; + int error; + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(zp); + + if ((prot & PROT_WRITE) && + (zp->z_phys->zp_flags & (ZFS_IMMUTABLE | ZFS_READONLY | + ZFS_APPENDONLY))) { + ZFS_EXIT(zfsvfs); + return (EPERM); + } + + if ((prot & (PROT_READ | PROT_EXEC)) && + (zp->z_phys->zp_flags & ZFS_AV_QUARANTINED)) { + ZFS_EXIT(zfsvfs); + return (EACCES); + } + + if (vp->v_flag & VNOMAP) { + ZFS_EXIT(zfsvfs); + return (ENOSYS); + } + + if (off < 0 || len > MAXOFFSET_T - off) { + ZFS_EXIT(zfsvfs); + return (ENXIO); + } + + if (vp->v_type != VREG) { + ZFS_EXIT(zfsvfs); + return (ENODEV); + } + + /* + * If file is locked, disallow mapping. + */ + if (MANDMODE((mode_t)zp->z_phys->zp_mode) && vn_has_flocks(vp)) { + ZFS_EXIT(zfsvfs); + return (EAGAIN); + } + + as_rangelock(as); + error = choose_addr(as, addrp, len, off, ADDR_VACALIGN, flags); + if (error != 0) { + as_rangeunlock(as); + ZFS_EXIT(zfsvfs); + return (error); + } + + vn_a.vp = vp; + vn_a.offset = (u_offset_t)off; + vn_a.type = flags & MAP_TYPE; + vn_a.prot = prot; + vn_a.maxprot = maxprot; + vn_a.cred = cr; + vn_a.amp = NULL; + vn_a.flags = flags & ~MAP_TYPE; + vn_a.szc = 0; + vn_a.lgrp_mem_policy_flags = 0; + + error = as_map(as, *addrp, len, segvn_create, &vn_a); + + as_rangeunlock(as); + ZFS_EXIT(zfsvfs); + return (error); +} + +/* ARGSUSED */ +static int +zfs_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, + size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr, + caller_context_t *ct) +{ + uint64_t pages = btopr(len); + + atomic_add_64(&VTOZ(vp)->z_mapcnt, pages); + return (0); +} + +/* + * The reason we push dirty pages as part of zfs_delmap() is so that we get a + * more accurate mtime for the associated file. Since we don't have a way of + * detecting when the data was actually modified, we have to resort to + * heuristics. If an explicit msync() is done, then we mark the mtime when the + * last page is pushed. The problem occurs when the msync() call is omitted, + * which by far the most common case: + * + * open() + * mmap() + * + * munmap() + * close() + *