]> git.proxmox.com Git - mirror_zfs-debian.git/commitdiff
Add: Add-.zfs-control-directory.patch
authorDarik Horn <dajhorn@vanadac.com>
Wed, 21 Mar 2012 23:27:13 +0000 (18:27 -0500)
committerDarik Horn <dajhorn@vanadac.com>
Wed, 21 Mar 2012 23:27:13 +0000 (18:27 -0500)
Add support for the .zfs control directory.  This was accomplished
by leveraging as much of the existing ZFS infrastructure as posible
and updating it for Linux as required.  The bulk of the core
functionality is now all there with the following limitations.

*) The .zfs/snapshot directory automount support requires a 2.6.37
   or newer kernel.  The exception is RHEL6.2 which has backported
   the d_automount patches.

*) Creating/destroying/renaming snapshots with mkdir/rmdir/mv
   in the .zfs/snapshot directory works as expected.  However,
   this functionality is only available to root until zfs
   delegations are finished.

      * mkdir - create a snapshot
      * rmdir - destroy a snapshot
      * mv    - rename a snapshot

The following issues are known defeciences, but we expect them to
be addressed by future commits.

*) Add automount support for kernels older the 2.6.37.  This should
   be possible using follow_link() which is what Linux did before.

*) Accessing the the .zfs/snapshot directory via NFS is not yet
   possible.  The majority of the ground work for this is complete.
   However, finishing this work will require resolving some lingering
   integration issues with the Linux NFS kernel server.

*) The .zfs/shares directory exists but no futher smb functionality
   has yet been implemented.

Contributions-by: Rohan Puri <rohan.puri15@gmail.com>
Contributiobs-by: Andrew Barnes <barnes333@gmail.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Issue #173

debian/patches/0001-Prevent-manual-builds-in-the-DKMS-source.patch
debian/patches/0002-Remove-the-COPYING-file.patch
debian/patches/0003-Remove-all-upstream-init.d-components.patch
debian/patches/0004-Remove-the-upstream-dracut-component.patch
debian/patches/0005-Remove-userland-dist-rules.patch
debian/patches/0006-Improve-the-with-spl-error-for-DKMS-systems.patch
debian/patches/0007-Integrate-ARC-more-tightly-with-Linux.patch
debian/patches/0008-Add-.zfs-control-directory.patch [new file with mode: 0644]
debian/patches/series

index 48a89b91fce3d6094a9f17213676cb9844ba5bbb..5800a7a78197acee46a143ff2bbc6d51f5b3ad98 100644 (file)
@@ -1,7 +1,7 @@
-From d93e090fd9490eed0563f0bfaef8be5a5b5cf90d Mon Sep 17 00:00:00 2001
+From e226a12f50759eb7269a3957581ed61fcf9e9cc4 Mon Sep 17 00:00:00 2001
 From: Darik Horn <dajhorn@vanadac.com>
 Date: Tue, 17 Jan 2012 19:45:07 -0600
-Subject: [PATCH 1/7] Prevent manual builds in the DKMS source.
+Subject: [PATCH 1/8] Prevent manual builds in the DKMS source.
 
 Print an instructive error messsage and exit if the `dkms.conf` file
 exists when the userland is configured. (ie: `--with-config=user`)
@@ -53,7 +53,7 @@ index 4fcef3d..16b8a76 100644
        ZFS_AC_CONFIG_USER_ARCH
        ZFS_AC_CONFIG_USER_IOCTL
 diff --git a/config/zfs-build.m4 b/config/zfs-build.m4
-index 823f332..32fc6ab 100644
+index c5983d3..be1edfa 100644
 --- a/config/zfs-build.m4
 +++ b/config/zfs-build.m4
 @@ -58,10 +58,10 @@ AC_DEFUN([ZFS_AC_CONFIG], [
index 2134ceba078ee592ef5397025248ce3067e82b00..8519d964c697af1106312c82f20e759767a52426 100644 (file)
@@ -1,7 +1,7 @@
-From f752df7e1e9be1346b7044ec70e0ec795e72ed0b Mon Sep 17 00:00:00 2001
+From 3fbf212d9233c60cafd2d6357dacd496e51930f3 Mon Sep 17 00:00:00 2001
 From: Darik Horn <dajhorn@vanadac.com>
 Date: Tue, 17 Jan 2012 22:18:47 -0600
-Subject: [PATCH 2/7] Remove the COPYING file.
+Subject: [PATCH 2/8] Remove the COPYING file.
 
 Resolve this lintian warning by removing the COPYING file:
 
index 86bde21da7e8f5ceb76d14fb078c5f7c648d57e5..3766ecc6daeecb355ef55e3b37e83008d1681ae3 100644 (file)
@@ -1,7 +1,7 @@
-From 1cd781902bbf986a16b1fbcf82c50de6fda4a7c0 Mon Sep 17 00:00:00 2001
+From 049b1b4d3fdc31008aa8f40fb21049b7b16f984a Mon Sep 17 00:00:00 2001
 From: Darik Horn <dajhorn@vanadac.com>
 Date: Tue, 17 Jan 2012 11:54:21 -0600
-Subject: [PATCH 3/7] Remove all upstream init.d components.
+Subject: [PATCH 3/8] Remove all upstream init.d components.
 
 The etc/init.d/ components are redundant because the debian/ overlay
 bundles init files according to Debian packaging policy.
index 42edb46a0e4b987052ef2e3cb7aa0a14efd7ca27..81a9b4df067651a2331f40be77c863b8b7ae7577 100644 (file)
@@ -1,7 +1,7 @@
-From 84858495e0923fbf94fb331fb05953a598c84959 Mon Sep 17 00:00:00 2001
+From 444cc1a251e06c40918178c670a17742deeb2ca7 Mon Sep 17 00:00:00 2001
 From: Darik Horn <dajhorn@vanadac.com>
 Date: Tue, 17 Jan 2012 20:27:32 -0600
-Subject: [PATCH 4/7] Remove the upstream dracut component.
+Subject: [PATCH 4/8] Remove the upstream dracut component.
 
 The dracut/ component broke deb systems when it first added to the
 upstream ZoL repository, had a near-zero download count when it was
index 70dd9039f4251b4d900c2d91d1056f7edfc5e31a..8136b065c0f77bdea9dda431d253d75ab1aeca86 100644 (file)
@@ -1,7 +1,7 @@
-From d72cecab4b0d15082796648314a33fad36c25a1e Mon Sep 17 00:00:00 2001
+From 31a84f1e6d3a050469a1170b80de1ea1e9d7a856 Mon Sep 17 00:00:00 2001
 From: Darik Horn <dajhorn@vanadac.com>
 Date: Tue, 17 Jan 2012 22:23:16 -0600
-Subject: [PATCH 5/7] Remove userland dist rules.
+Subject: [PATCH 5/8] Remove userland dist rules.
 
 The `make dist` rule is used to create the DKMS source package, which
 does not contain the userland. (eg: Dist --with-config=kernel only.)
index 5cf2b9002508dcdafd262a93789d96f70bc8a655..cdb4b3dfdc0f95552769df7693e6da4d537b36d6 100644 (file)
@@ -1,7 +1,7 @@
-From d50295867c2d105b0db793660afa51e09ac6f520 Mon Sep 17 00:00:00 2001
+From 1ee1fa02519991f3a98fd7ec548b395d12a9d16d Mon Sep 17 00:00:00 2001
 From: Darik Horn <dajhorn@vanadac.com>
 Date: Fri, 3 Feb 2012 14:49:49 -0600
-Subject: [PATCH 6/7] Improve the --with-spl error for DKMS systems.
+Subject: [PATCH 6/8] Improve the --with-spl error for DKMS systems.
 
 If the SPL module is unavailable, then the operator gets an error
 message that does not apply to installations that are mananged by
index 908f11170a2065b011a1850cf32d6fea68f83860..45d678c727b7087e6b29a8ad1cc191d428311bc0 100644 (file)
@@ -1,7 +1,7 @@
-From 6bce26a67ad5ea4e26bb8aaa69357ffd6806db48 Mon Sep 17 00:00:00 2001
+From 3fae6446c3a99e380199e54515b3969f3a864b5e Mon Sep 17 00:00:00 2001
 From: Brian Behlendorf <behlendorf1@llnl.gov>
 Date: Tue, 13 Mar 2012 14:29:16 -0700
-Subject: [PATCH 7/7] Integrate ARC more tightly with Linux
+Subject: [PATCH 7/8] Integrate ARC more tightly with Linux
 
 Under Solaris the ARC was designed to stay one step ahead of the
 VM subsystem.  It would attempt to recognize low memory situtions
diff --git a/debian/patches/0008-Add-.zfs-control-directory.patch b/debian/patches/0008-Add-.zfs-control-directory.patch
new file mode 100644 (file)
index 0000000..96a1e70
--- /dev/null
@@ -0,0 +1,3320 @@
+From 9dbfc5d36211b9bfd3e7a770567903ca14995743 Mon Sep 17 00:00:00 2001
+From: Brian Behlendorf <behlendorf1@llnl.gov>
+Date: Fri, 11 Nov 2011 12:45:53 +0530
+Subject: [PATCH 8/8] Add .zfs control directory
+
+Add support for the .zfs control directory.  This was accomplished
+by leveraging as much of the existing ZFS infrastructure as posible
+and updating it for Linux as required.  The bulk of the core
+functionality is now all there with the following limitations.
+
+*) The .zfs/snapshot directory automount support requires a 2.6.37
+   or newer kernel.  The exception is RHEL6.2 which has backported
+   the d_automount patches.
+
+*) Creating/destroying/renaming snapshots with mkdir/rmdir/mv
+   in the .zfs/snapshot directory works as expected.  However,
+   this functionality is only available to root until zfs
+   delegations are finished.
+
+      * mkdir - create a snapshot
+      * rmdir - destroy a snapshot
+      * mv    - rename a snapshot
+
+The following issues are known defeciences, but we expect them to
+be addressed by future commits.
+
+*) Add automount support for kernels older the 2.6.37.  This should
+   be possible using follow_link() which is what Linux did before.
+
+*) Accessing the the .zfs/snapshot directory via NFS is not yet
+   possible.  The majority of the ground work for this is complete.
+   However, finishing this work will require resolving some lingering
+   integration issues with the Linux NFS kernel server.
+
+*) The .zfs/shares directory exists but no futher smb functionality
+   has yet been implemented.
+
+Contributions-by: Rohan Puri <rohan.puri15@gmail.com>
+Contributiobs-by: Andrew Barnes <barnes333@gmail.com>
+Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
+Issue #173
+
+Conflicts:
+
+       dracut/90zfs/Makefile.in
+       dracut/Makefile.in
+       etc/init.d/Makefile.in
+---
+ Makefile.in                                 |    1 +
+ cmd/Makefile.in                             |    1 +
+ cmd/mount_zfs/Makefile.in                   |    1 +
+ cmd/sas_switch_id/Makefile.in               |    1 +
+ cmd/zdb/Makefile.in                         |    1 +
+ cmd/zfs/Makefile.in                         |    1 +
+ cmd/zinject/Makefile.in                     |    1 +
+ cmd/zpios/Makefile.in                       |    1 +
+ cmd/zpool/Makefile.in                       |    1 +
+ cmd/zpool_id/Makefile.in                    |    1 +
+ cmd/zpool_layout/Makefile.in                |    1 +
+ cmd/ztest/Makefile.in                       |    1 +
+ cmd/zvol_id/Makefile.in                     |    1 +
+ config/kernel-automount.m4                  |   23 +
+ config/kernel.m4                            |    1 +
+ configure                                   |  136 ++++
+ etc/Makefile.in                             |    1 +
+ etc/zfs/Makefile.in                         |    1 +
+ include/Makefile.in                         |    1 +
+ include/linux/Makefile.in                   |    1 +
+ include/linux/dcache_compat.h               |    1 +
+ include/sys/Makefile.am                     |    1 +
+ include/sys/Makefile.in                     |    4 +
+ include/sys/dmu.h                           |    1 +
+ include/sys/fm/Makefile.in                  |    1 +
+ include/sys/fm/fs/Makefile.in               |    1 +
+ include/sys/fs/Makefile.in                  |    1 +
+ include/sys/zfs_ctldir.h                    |  113 ++++
+ include/sys/zfs_vfsops.h                    |   21 +-
+ include/sys/zfs_znode.h                     |    1 +
+ include/sys/zpl.h                           |   18 +
+ lib/Makefile.in                             |    1 +
+ lib/libavl/Makefile.in                      |    1 +
+ lib/libefi/Makefile.in                      |    1 +
+ lib/libnvpair/Makefile.in                   |    1 +
+ lib/libshare/Makefile.in                    |    1 +
+ lib/libspl/Makefile.in                      |    1 +
+ lib/libspl/asm-generic/Makefile.in          |    1 +
+ lib/libspl/asm-i386/Makefile.in             |    1 +
+ lib/libspl/asm-x86_64/Makefile.in           |    1 +
+ lib/libspl/include/Makefile.in              |    1 +
+ lib/libspl/include/ia32/Makefile.in         |    1 +
+ lib/libspl/include/ia32/sys/Makefile.in     |    1 +
+ lib/libspl/include/rpc/Makefile.in          |    1 +
+ lib/libspl/include/sys/Makefile.in          |    1 +
+ lib/libspl/include/sys/dktp/Makefile.in     |    1 +
+ lib/libspl/include/sys/sysevent/Makefile.in |    1 +
+ lib/libspl/include/util/Makefile.in         |    1 +
+ lib/libunicode/Makefile.in                  |    1 +
+ lib/libuutil/Makefile.in                    |    1 +
+ lib/libzfs/Makefile.in                      |    1 +
+ lib/libzpool/Makefile.am                    |    2 +
+ lib/libzpool/Makefile.in                    |    3 +
+ man/Makefile.in                             |    1 +
+ man/man8/Makefile.in                        |    1 +
+ module/zfs/Makefile.in                      |    2 +
+ module/zfs/dmu_objset.c                     |   35 +
+ module/zfs/dsl_dataset.c                    |    3 +-
+ module/zfs/zfs_ctldir.c                     |  957 +++++++++++++++++++++++++++
+ module/zfs/zfs_dir.c                        |   11 +-
+ module/zfs/zfs_ioctl.c                      |   88 ++--
+ module/zfs/zfs_vfsops.c                     |   47 +-
+ module/zfs/zfs_vnops.c                      |   10 +-
+ module/zfs/zfs_znode.c                      |    9 +
+ module/zfs/zpl_ctldir.c                     |  519 +++++++++++++++
+ module/zfs/zpl_export.c                     |    6 +-
+ module/zfs/zpl_inode.c                      |   15 +-
+ module/zfs/zpl_super.c                      |   24 +-
+ scripts/Makefile.in                         |    1 +
+ scripts/zpios-profile/Makefile.in           |    1 +
+ scripts/zpios-test/Makefile.in              |    1 +
+ scripts/zpool-config/Makefile.in            |    1 +
+ scripts/zpool-layout/Makefile.in            |    1 +
+ udev/Makefile.in                            |    1 +
+ udev/rules.d/Makefile.in                    |    1 +
+ zfs_config.h.in                             |    3 +
+ 76 files changed, 2003 insertions(+), 100 deletions(-)
+ create mode 100644 config/kernel-automount.m4
+ create mode 100644 include/sys/zfs_ctldir.h
+ create mode 100644 module/zfs/zfs_ctldir.c
+ create mode 100644 module/zfs/zpl_ctldir.c
+
+diff --git a/Makefile.in b/Makefile.in
+index bb9bc1d..077d318 100644
+--- a/Makefile.in
++++ b/Makefile.in
+@@ -62,6 +62,7 @@ subdir = .
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/cmd/Makefile.in b/cmd/Makefile.in
+index a6ab6cc..76a5731 100644
+--- a/cmd/Makefile.in
++++ b/cmd/Makefile.in
+@@ -39,6 +39,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/cmd/mount_zfs/Makefile.in b/cmd/mount_zfs/Makefile.in
+index 94d9d05..ecc61d1 100644
+--- a/cmd/mount_zfs/Makefile.in
++++ b/cmd/mount_zfs/Makefile.in
+@@ -42,6 +42,7 @@ subdir = cmd/mount_zfs
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/cmd/sas_switch_id/Makefile.in b/cmd/sas_switch_id/Makefile.in
+index ba0535f..375c65e 100644
+--- a/cmd/sas_switch_id/Makefile.in
++++ b/cmd/sas_switch_id/Makefile.in
+@@ -41,6 +41,7 @@ DIST_COMMON = $(dist_udev_SCRIPTS) $(srcdir)/Makefile.am \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/cmd/zdb/Makefile.in b/cmd/zdb/Makefile.in
+index 0029e5d..ae4e2c9 100644
+--- a/cmd/zdb/Makefile.in
++++ b/cmd/zdb/Makefile.in
+@@ -42,6 +42,7 @@ subdir = cmd/zdb
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/cmd/zfs/Makefile.in b/cmd/zfs/Makefile.in
+index e0ba1e5..4452a96 100644
+--- a/cmd/zfs/Makefile.in
++++ b/cmd/zfs/Makefile.in
+@@ -42,6 +42,7 @@ subdir = cmd/zfs
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/cmd/zinject/Makefile.in b/cmd/zinject/Makefile.in
+index 14f1b2a..5e200d1 100644
+--- a/cmd/zinject/Makefile.in
++++ b/cmd/zinject/Makefile.in
+@@ -42,6 +42,7 @@ subdir = cmd/zinject
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/cmd/zpios/Makefile.in b/cmd/zpios/Makefile.in
+index b74b113..c7507a1 100644
+--- a/cmd/zpios/Makefile.in
++++ b/cmd/zpios/Makefile.in
+@@ -42,6 +42,7 @@ subdir = cmd/zpios
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/cmd/zpool/Makefile.in b/cmd/zpool/Makefile.in
+index bb17ef5..5775a4f 100644
+--- a/cmd/zpool/Makefile.in
++++ b/cmd/zpool/Makefile.in
+@@ -42,6 +42,7 @@ subdir = cmd/zpool
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/cmd/zpool_id/Makefile.in b/cmd/zpool_id/Makefile.in
+index 4b6f04c..3d51566 100644
+--- a/cmd/zpool_id/Makefile.in
++++ b/cmd/zpool_id/Makefile.in
+@@ -41,6 +41,7 @@ DIST_COMMON = $(dist_udev_SCRIPTS) $(srcdir)/Makefile.am \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/cmd/zpool_layout/Makefile.in b/cmd/zpool_layout/Makefile.in
+index 95e802c..077f1d2 100644
+--- a/cmd/zpool_layout/Makefile.in
++++ b/cmd/zpool_layout/Makefile.in
+@@ -41,6 +41,7 @@ DIST_COMMON = $(dist_bin_SCRIPTS) $(srcdir)/Makefile.am \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/cmd/ztest/Makefile.in b/cmd/ztest/Makefile.in
+index d1c8029..405e496 100644
+--- a/cmd/ztest/Makefile.in
++++ b/cmd/ztest/Makefile.in
+@@ -42,6 +42,7 @@ subdir = cmd/ztest
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/cmd/zvol_id/Makefile.in b/cmd/zvol_id/Makefile.in
+index 5d96d2b..6f6688f 100644
+--- a/cmd/zvol_id/Makefile.in
++++ b/cmd/zvol_id/Makefile.in
+@@ -42,6 +42,7 @@ subdir = cmd/zvol_id
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/config/kernel-automount.m4 b/config/kernel-automount.m4
+new file mode 100644
+index 0000000..972b09b
+--- /dev/null
++++ b/config/kernel-automount.m4
+@@ -0,0 +1,23 @@
++dnl #
++dnl # 2.6.37 API change
++dnl # The dops->d_automount() dentry operation was added as a clean
++dnl # solution to handling automounts.  Prior to this cifs/nfs clients
++dnl # which required automount support would abuse the follow_link()
++dnl # operation on directories for this purpose.
++dnl #
++AC_DEFUN([ZFS_AC_KERNEL_AUTOMOUNT], [
++      AC_MSG_CHECKING([whether dops->d_automount() exists])
++      ZFS_LINUX_TRY_COMPILE([
++              #include <linux/dcache.h>
++      ],[
++              struct vfsmount *(*d_automount) (struct path *) = NULL;
++              struct dentry_operations dops __attribute__ ((unused)) = {
++                      .d_automount = d_automount,
++              };
++      ],[
++              AC_MSG_RESULT(yes)
++              AC_DEFINE(HAVE_AUTOMOUNT, 1, [dops->automount() exists])
++      ],[
++              AC_MSG_RESULT(no)
++      ])
++])
+diff --git a/config/kernel.m4 b/config/kernel.m4
+index bf43a75..2cb2e8c 100644
+--- a/config/kernel.m4
++++ b/config/kernel.m4
+@@ -45,6 +45,7 @@ AC_DEFUN([ZFS_AC_CONFIG_KERNEL], [
+       ZFS_AC_KERNEL_NR_CACHED_OBJECTS
+       ZFS_AC_KERNEL_FREE_CACHED_OBJECTS
+       ZFS_AC_KERNEL_FALLOCATE
++      ZFS_AC_KERNEL_AUTOMOUNT
+       ZFS_AC_KERNEL_INSERT_INODE_LOCKED
+       ZFS_AC_KERNEL_D_OBTAIN_ALIAS
+       ZFS_AC_KERNEL_CHECK_DISK_SIZE_CHANGE
+diff --git a/configure b/configure
+index 14c1c4d..da28b47 100755
+--- a/configure
++++ b/configure
+@@ -15681,6 +15681,74 @@ fi
++      { $as_echo "$as_me:$LINENO: checking whether dops->d_automount() exists" >&5
++$as_echo_n "checking whether dops->d_automount() exists... " >&6; }
++
++
++cat confdefs.h - <<_ACEOF >conftest.c
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++
++
++              #include <linux/dcache.h>
++
++int
++main (void)
++{
++
++              struct vfsmount *(*d_automount) (struct path *) = NULL;
++              struct dentry_operations dops __attribute__ ((unused)) = {
++                      .d_automount = d_automount,
++              };
++
++  ;
++  return 0;
++}
++
++_ACEOF
++
++
++      rm -Rf build && mkdir -p build
++      echo "obj-m := conftest.o" >build/Makefile
++      if { ac_try='cp conftest.c build && make modules -C $LINUX_OBJ EXTRA_CFLAGS="-Werror-implicit-function-declaration $EXTRA_KCFLAGS" $ARCH_UM M=$PWD/build'
++  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
++  (eval $ac_try) 2>&5
++  ac_status=$?
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); }; } >/dev/null && { ac_try='test -s build/conftest.o'
++  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
++  (eval $ac_try) 2>&5
++  ac_status=$?
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); }; }; then
++
++              { $as_echo "$as_me:$LINENO: result: yes" >&5
++$as_echo "yes" >&6; }
++
++cat >>confdefs.h <<\_ACEOF
++#define HAVE_AUTOMOUNT 1
++_ACEOF
++
++
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++              { $as_echo "$as_me:$LINENO: result: no" >&5
++$as_echo "no" >&6; }
++
++
++
++fi
++
++      rm -Rf build
++
++
++
++
+       { $as_echo "$as_me:$LINENO: checking whether symbol insert_inode_locked is exported" >&5
+ $as_echo_n "checking whether symbol insert_inode_locked is exported... " >&6; }
+       grep -q -E '[[:space:]]insert_inode_locked[[:space:]]' \
+@@ -21568,6 +21636,74 @@ fi
++      { $as_echo "$as_me:$LINENO: checking whether dops->d_automount() exists" >&5
++$as_echo_n "checking whether dops->d_automount() exists... " >&6; }
++
++
++cat confdefs.h - <<_ACEOF >conftest.c
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++
++
++              #include <linux/dcache.h>
++
++int
++main (void)
++{
++
++              struct vfsmount *(*d_automount) (struct path *) = NULL;
++              struct dentry_operations dops __attribute__ ((unused)) = {
++                      .d_automount = d_automount,
++              };
++
++  ;
++  return 0;
++}
++
++_ACEOF
++
++
++      rm -Rf build && mkdir -p build
++      echo "obj-m := conftest.o" >build/Makefile
++      if { ac_try='cp conftest.c build && make modules -C $LINUX_OBJ EXTRA_CFLAGS="-Werror-implicit-function-declaration $EXTRA_KCFLAGS" $ARCH_UM M=$PWD/build'
++  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
++  (eval $ac_try) 2>&5
++  ac_status=$?
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); }; } >/dev/null && { ac_try='test -s build/conftest.o'
++  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
++  (eval $ac_try) 2>&5
++  ac_status=$?
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); }; }; then
++
++              { $as_echo "$as_me:$LINENO: result: yes" >&5
++$as_echo "yes" >&6; }
++
++cat >>confdefs.h <<\_ACEOF
++#define HAVE_AUTOMOUNT 1
++_ACEOF
++
++
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++              { $as_echo "$as_me:$LINENO: result: no" >&5
++$as_echo "no" >&6; }
++
++
++
++fi
++
++      rm -Rf build
++
++
++
++
+       { $as_echo "$as_me:$LINENO: checking whether symbol insert_inode_locked is exported" >&5
+ $as_echo_n "checking whether symbol insert_inode_locked is exported... " >&6; }
+       grep -q -E '[[:space:]]insert_inode_locked[[:space:]]' \
+diff --git a/etc/Makefile.in b/etc/Makefile.in
+index c8c8821..1187d0a 100644
+--- a/etc/Makefile.in
++++ b/etc/Makefile.in
+@@ -39,6 +39,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/etc/zfs/Makefile.in b/etc/zfs/Makefile.in
+index 0382b0c..73025a1 100644
+--- a/etc/zfs/Makefile.in
++++ b/etc/zfs/Makefile.in
+@@ -40,6 +40,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/include/Makefile.in b/include/Makefile.in
+index ad01d2b..e181712 100644
+--- a/include/Makefile.in
++++ b/include/Makefile.in
+@@ -41,6 +41,7 @@ DIST_COMMON = $(am__kernel_HEADERS_DIST) $(am__libzfs_HEADERS_DIST) \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/include/linux/Makefile.in b/include/linux/Makefile.in
+index 5dfa3de..3d47867 100644
+--- a/include/linux/Makefile.in
++++ b/include/linux/Makefile.in
+@@ -41,6 +41,7 @@ DIST_COMMON = $(am__kernel_HEADERS_DIST) $(libzfs_HEADERS) \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/include/linux/dcache_compat.h b/include/linux/dcache_compat.h
+index a624d4d..5f7d5e9 100644
+--- a/include/linux/dcache_compat.h
++++ b/include/linux/dcache_compat.h
+@@ -29,5 +29,6 @@
+ #include <linux/dcache.h>
+ #define dname(dentry) ((char *)((dentry)->d_name.name))
++#define dlen(dentry)  ((int)((dentry)->d_name.len))
+ #endif /* _ZFS_DCACHE_H */
+diff --git a/include/sys/Makefile.am b/include/sys/Makefile.am
+index 083e121..651e68b 100644
+--- a/include/sys/Makefile.am
++++ b/include/sys/Makefile.am
+@@ -55,6 +55,7 @@ COMMON_H = \
+       $(top_srcdir)/include/sys/zap_leaf.h \
+       $(top_srcdir)/include/sys/zfs_acl.h \
+       $(top_srcdir)/include/sys/zfs_context.h \
++      $(top_srcdir)/include/sys/zfs_ctldir.h \
+       $(top_srcdir)/include/sys/zfs_debug.h \
+       $(top_srcdir)/include/sys/zfs_dir.h \
+       $(top_srcdir)/include/sys/zfs_fuid.h \
+diff --git a/include/sys/Makefile.in b/include/sys/Makefile.in
+index a2afe14..a179576 100644
+--- a/include/sys/Makefile.in
++++ b/include/sys/Makefile.in
+@@ -41,6 +41,7 @@ DIST_COMMON = $(am__kernel_HEADERS_DIST) $(am__libzfs_HEADERS_DIST) \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+@@ -170,6 +171,7 @@ am__kernel_HEADERS_DIST = $(top_srcdir)/include/sys/arc.h \
+       $(top_srcdir)/include/sys/zap_leaf.h \
+       $(top_srcdir)/include/sys/zfs_acl.h \
+       $(top_srcdir)/include/sys/zfs_context.h \
++      $(top_srcdir)/include/sys/zfs_ctldir.h \
+       $(top_srcdir)/include/sys/zfs_debug.h \
+       $(top_srcdir)/include/sys/zfs_dir.h \
+       $(top_srcdir)/include/sys/zfs_fuid.h \
+@@ -266,6 +268,7 @@ am__libzfs_HEADERS_DIST = $(top_srcdir)/include/sys/arc.h \
+       $(top_srcdir)/include/sys/zap_leaf.h \
+       $(top_srcdir)/include/sys/zfs_acl.h \
+       $(top_srcdir)/include/sys/zfs_context.h \
++      $(top_srcdir)/include/sys/zfs_ctldir.h \
+       $(top_srcdir)/include/sys/zfs_debug.h \
+       $(top_srcdir)/include/sys/zfs_dir.h \
+       $(top_srcdir)/include/sys/zfs_fuid.h \
+@@ -550,6 +553,7 @@ COMMON_H = \
+       $(top_srcdir)/include/sys/zap_leaf.h \
+       $(top_srcdir)/include/sys/zfs_acl.h \
+       $(top_srcdir)/include/sys/zfs_context.h \
++      $(top_srcdir)/include/sys/zfs_ctldir.h \
+       $(top_srcdir)/include/sys/zfs_debug.h \
+       $(top_srcdir)/include/sys/zfs_dir.h \
+       $(top_srcdir)/include/sys/zfs_fuid.h \
+diff --git a/include/sys/dmu.h b/include/sys/dmu.h
+index da1aa30..3f9c711 100644
+--- a/include/sys/dmu.h
++++ b/include/sys/dmu.h
+@@ -643,6 +643,7 @@ extern uint64_t dmu_objset_syncprop(objset_t *os);
+ extern uint64_t dmu_objset_logbias(objset_t *os);
+ extern int dmu_snapshot_list_next(objset_t *os, int namelen, char *name,
+     uint64_t *id, uint64_t *offp, boolean_t *case_conflict);
++extern int dmu_snapshot_id(objset_t *os, const char *snapname, uint64_t *idp);
+ extern int dmu_snapshot_realname(objset_t *os, char *name, char *real,
+     int maxlen, boolean_t *conflict);
+ extern int dmu_dir_list_next(objset_t *os, int namelen, char *name,
+diff --git a/include/sys/fm/Makefile.in b/include/sys/fm/Makefile.in
+index 9fe283a..5ae85a3 100644
+--- a/include/sys/fm/Makefile.in
++++ b/include/sys/fm/Makefile.in
+@@ -41,6 +41,7 @@ DIST_COMMON = $(am__kernel_HEADERS_DIST) $(am__libzfs_HEADERS_DIST) \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/include/sys/fm/fs/Makefile.in b/include/sys/fm/fs/Makefile.in
+index bc2db28..5976245 100644
+--- a/include/sys/fm/fs/Makefile.in
++++ b/include/sys/fm/fs/Makefile.in
+@@ -41,6 +41,7 @@ DIST_COMMON = $(am__kernel_HEADERS_DIST) $(am__libzfs_HEADERS_DIST) \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/include/sys/fs/Makefile.in b/include/sys/fs/Makefile.in
+index 00ed240..61f3125 100644
+--- a/include/sys/fs/Makefile.in
++++ b/include/sys/fs/Makefile.in
+@@ -41,6 +41,7 @@ DIST_COMMON = $(am__kernel_HEADERS_DIST) $(am__libzfs_HEADERS_DIST) \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/include/sys/zfs_ctldir.h b/include/sys/zfs_ctldir.h
+new file mode 100644
+index 0000000..e598529
+--- /dev/null
++++ b/include/sys/zfs_ctldir.h
+@@ -0,0 +1,113 @@
++/*
++ * CDDL HEADER START
++ *
++ * The contents of this file are subject to the terms of the
++ * Common Development and Distribution License (the "License").
++ * You may not use this file except in compliance with the License.
++ *
++ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
++ * or http://www.opensolaris.org/os/licensing.
++ * See the License for the specific language governing permissions
++ * and limitations under the License.
++ *
++ * When distributing Covered Code, include this CDDL HEADER in each
++ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
++ * If applicable, add the following below this CDDL HEADER, with the
++ * fields enclosed by brackets "[]" replaced with your own identifying
++ * information: Portions Copyright [yyyy] [name of copyright owner]
++ *
++ * CDDL HEADER END
++ */
++/*
++ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
++ * Copyright (C) 2011 Lawrence Livermore National Security, LLC.
++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
++ * LLNL-CODE-403049.
++ * Rewritten for Linux by:
++ *   Rohan Puri <rohan.puri15@gmail.com>
++ *   Brian Behlendorf <behlendorf1@llnl.gov>
++ */
++
++#ifndef       _ZFS_CTLDIR_H
++#define       _ZFS_CTLDIR_H
++
++#include <sys/vnode.h>
++#include <sys/zfs_vfsops.h>
++#include <sys/zfs_znode.h>
++
++#define       ZFS_CTLDIR_NAME         ".zfs"
++#define       ZFS_SNAPDIR_NAME        "snapshot"
++#define       ZFS_SHAREDIR_NAME       "shares"
++
++#define       zfs_has_ctldir(zdp)     \
++      ((zdp)->z_id == ZTOZSB(zdp)->z_root && \
++      (ZTOZSB(zdp)->z_ctldir != NULL))
++#define       zfs_show_ctldir(zdp)    \
++      (zfs_has_ctldir(zdp) && \
++      (ZTOZSB(zdp)->z_show_ctldir))
++
++typedef struct {
++      char                    *se_name;
++      char                    *se_path;
++      struct inode            *se_inode;
++      struct delayed_work     se_work;
++      avl_node_t              se_node;
++} zfs_snapentry_t;
++
++/* zfsctl generic functions */
++extern int snapentry_compare(const void *a, const void *b);
++extern boolean_t zfsctl_is_node(struct inode *ip);
++extern boolean_t zfsctl_is_snapdir(struct inode *ip);
++extern void zfsctl_inode_inactive(struct inode *ip);
++extern void zfsctl_inode_destroy(struct inode *ip);
++extern int zfsctl_create(zfs_sb_t *zsb);
++extern void zfsctl_destroy(zfs_sb_t *zsb);
++extern struct inode *zfsctl_root(znode_t *zp);
++extern int zfsctl_fid(struct inode *ip, fid_t *fidp);
++
++/* zfsctl '.zfs' functions */
++extern int zfsctl_root_lookup(struct inode *dip, char *name,
++    struct inode **ipp, int flags, cred_t *cr, int *direntflags,
++    pathname_t *realpnp);
++
++/* zfsctl '.zfs/snapshot' functions */
++extern int zfsctl_snapdir_lookup(struct inode *dip, char *name,
++    struct inode **ipp, int flags, cred_t *cr, int *direntflags,
++    pathname_t *realpnp);
++extern int zfsctl_snapdir_rename(struct inode *sdip, char *sname,
++    struct inode *tdip, char *tname, cred_t *cr, int flags);
++extern int zfsctl_snapdir_remove(struct inode *dip, char *name, cred_t *cr,
++    int flags);
++extern int zfsctl_snapdir_mkdir(struct inode *dip, char *dirname, vattr_t *vap,
++    struct inode **ipp, cred_t *cr, int flags);
++extern void zfsctl_snapdir_inactive(struct inode *ip);
++extern int zfsctl_unmount_snapshot(zfs_sb_t *zsb, char *name, int flags);
++extern int zfsctl_unmount_snapshots(zfs_sb_t *zsb, int flags, int *count);
++extern int zfsctl_mount_snapshot(struct path *path, int flags);
++extern int zfsctl_lookup_objset(struct super_block *sb, uint64_t objsetid,
++    zfs_sb_t **zsb);
++
++/* zfsctl '.zfs/shares' functions */
++extern int zfsctl_shares_lookup(struct inode *dip, char *name,
++    struct inode **ipp, int flags, cred_t *cr, int *direntflags,
++    pathname_t *realpnp);
++
++/* zfsctl_init/fini functions */
++extern void zfsctl_init(void);
++extern void zfsctl_fini(void);
++
++/*
++ * These inodes numbers are reserved for the .zfs control directory.
++ * It is important that they be no larger that 48-bits because only
++ * 6 bytes are reserved in the NFS file handle for the object number.
++ * However, they should be as large as possible to avoid conflicts
++ * with the objects which are assigned monotonically by the dmu.
++ */
++#define       ZFSCTL_INO_ROOT         0x0000FFFFFFFFFFFF
++#define       ZFSCTL_INO_SHARES       0x0000FFFFFFFFFFFE
++#define       ZFSCTL_INO_SNAPDIR      0x0000FFFFFFFFFFFD
++#define       ZFSCTL_INO_SNAPDIRS     0x0000FFFFFFFFFFFC
++
++#define       ZFSCTL_EXPIRE_SNAPSHOT  300
++
++#endif        /* _ZFS_CTLDIR_H */
+diff --git a/include/sys/zfs_vfsops.h b/include/sys/zfs_vfsops.h
+index 7b70f32..33ecf72 100644
+--- a/include/sys/zfs_vfsops.h
++++ b/include/sys/zfs_vfsops.h
+@@ -71,6 +71,8 @@ typedef struct zfs_sb {
+       uint64_t        z_nr_znodes;    /* number of znodes in the fs */
+       kmutex_t        z_znodes_lock;  /* lock for z_all_znodes */
+       struct inode    *z_ctldir;      /* .zfs directory inode */
++      avl_tree_t      z_ctldir_snaps; /* .zfs/snapshot entries */
++      kmutex_t        z_ctldir_lock;  /* .zfs ctldir lock */
+       boolean_t       z_show_ctldir;  /* expose .zfs in the root dir */
+       boolean_t       z_issnap;       /* true if this is a snapshot */
+       boolean_t       z_vscan;        /* virus scan on/off */
+@@ -93,24 +95,6 @@ typedef struct zfs_sb {
+ #define       ZSB_XATTR       0x0001          /* Enable user xattrs */
+-
+-/*
+- * Minimal snapshot helpers, the bulk of the Linux snapshot implementation
+- * lives in the zpl_snap.c file which is part of the zpl source.
+- */
+-#define       ZFS_CTLDIR_NAME         ".zfs"
+-
+-#define       zfs_has_ctldir(zdp)     \
+-      ((zdp)->z_id == ZTOZSB(zdp)->z_root && \
+-      (ZTOZSB(zdp)->z_ctldir != NULL))
+-#define       zfs_show_ctldir(zdp)    \
+-      (zfs_has_ctldir(zdp) && \
+-      (ZTOZSB(zdp)->z_show_ctldir))
+-
+-#define       ZFSCTL_INO_ROOT         0x1
+-#define       ZFSCTL_INO_SNAPDIR      0x2
+-#define       ZFSCTL_INO_SHARES       0x3
+-
+ /*
+  * Allow a maximum number of links.  While ZFS does not internally limit
+  * this the inode->i_nlink member is defined as an unsigned int.  To be
+@@ -195,6 +179,7 @@ extern boolean_t zfs_is_readonly(zfs_sb_t *zsb);
+ extern int zfs_register_callbacks(zfs_sb_t *zsb);
+ extern void zfs_unregister_callbacks(zfs_sb_t *zsb);
+ extern int zfs_domount(struct super_block *sb, void *data, int silent);
++extern void zfs_preumount(struct super_block *sb);
+ extern int zfs_umount(struct super_block *sb);
+ extern int zfs_remount(struct super_block *sb, int *flags, char *data);
+ extern int zfs_root(zfs_sb_t *zsb, struct inode **ipp);
+diff --git a/include/sys/zfs_znode.h b/include/sys/zfs_znode.h
+index 6903ad4..8af5798 100644
+--- a/include/sys/zfs_znode.h
++++ b/include/sys/zfs_znode.h
+@@ -214,6 +214,7 @@ typedef struct znode {
+       boolean_t       z_is_sa;        /* are we native sa? */
+       boolean_t       z_is_zvol;      /* are we used by the zvol */
+       boolean_t       z_is_mapped;    /* are we mmap'ed */
++      boolean_t       z_is_ctldir;    /* are we .zfs entry */
+       struct inode    z_inode;        /* generic vfs inode */
+ } znode_t;
+diff --git a/include/sys/zpl.h b/include/sys/zpl.h
+index cfc8679..aa4f41f 100644
+--- a/include/sys/zpl.h
++++ b/include/sys/zpl.h
+@@ -33,6 +33,9 @@
+ #include <linux/falloc.h>
+ /* zpl_inode.c */
++extern void zpl_vap_init(vattr_t *vap, struct inode *dir,
++    struct dentry *dentry, mode_t mode, cred_t *cr);
++
+ extern const struct inode_operations zpl_inode_operations;
+ extern const struct inode_operations zpl_dir_inode_operations;
+ extern const struct inode_operations zpl_symlink_inode_operations;
+@@ -69,4 +72,19 @@ extern int zpl_xattr_security_init(struct inode *ip, struct inode *dip,
+ extern xattr_handler_t *zpl_xattr_handlers[];
++/* zpl_ctldir.c */
++extern const struct file_operations zpl_fops_root;
++extern const struct inode_operations zpl_ops_root;
++
++extern const struct file_operations zpl_fops_snapdir;
++extern const struct inode_operations zpl_ops_snapdir;
++#ifdef HAVE_AUTOMOUNT
++extern const struct dentry_operations zpl_dops_snapdirs;
++#else
++extern const struct inode_operations zpl_ops_snapdirs;
++#endif /* HAVE_AUTOMOUNT */
++
++extern const struct file_operations zpl_fops_shares;
++extern const struct inode_operations zpl_ops_shares;
++
+ #endif        /* _SYS_ZPL_H */
+diff --git a/lib/Makefile.in b/lib/Makefile.in
+index 8d57296..f40801f 100644
+--- a/lib/Makefile.in
++++ b/lib/Makefile.in
+@@ -39,6 +39,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/lib/libavl/Makefile.in b/lib/libavl/Makefile.in
+index 8c2be9a..5e31cc5 100644
+--- a/lib/libavl/Makefile.in
++++ b/lib/libavl/Makefile.in
+@@ -41,6 +41,7 @@ subdir = lib/libavl
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/lib/libefi/Makefile.in b/lib/libefi/Makefile.in
+index 4f95441..a2f4255 100644
+--- a/lib/libefi/Makefile.in
++++ b/lib/libefi/Makefile.in
+@@ -41,6 +41,7 @@ subdir = lib/libefi
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/lib/libnvpair/Makefile.in b/lib/libnvpair/Makefile.in
+index c736a50..7732784 100644
+--- a/lib/libnvpair/Makefile.in
++++ b/lib/libnvpair/Makefile.in
+@@ -41,6 +41,7 @@ subdir = lib/libnvpair
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/lib/libshare/Makefile.in b/lib/libshare/Makefile.in
+index 65133e1..e1c1a2c 100644
+--- a/lib/libshare/Makefile.in
++++ b/lib/libshare/Makefile.in
+@@ -41,6 +41,7 @@ subdir = lib/libshare
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/lib/libspl/Makefile.in b/lib/libspl/Makefile.in
+index 15c6720..2f94b5d 100644
+--- a/lib/libspl/Makefile.in
++++ b/lib/libspl/Makefile.in
+@@ -41,6 +41,7 @@ subdir = lib/libspl
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/lib/libspl/asm-generic/Makefile.in b/lib/libspl/asm-generic/Makefile.in
+index 6ad9971..66ec77b 100644
+--- a/lib/libspl/asm-generic/Makefile.in
++++ b/lib/libspl/asm-generic/Makefile.in
+@@ -40,6 +40,7 @@ subdir = lib/libspl/asm-generic
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/lib/libspl/asm-i386/Makefile.in b/lib/libspl/asm-i386/Makefile.in
+index e68762c..4c9f37f 100644
+--- a/lib/libspl/asm-i386/Makefile.in
++++ b/lib/libspl/asm-i386/Makefile.in
+@@ -41,6 +41,7 @@ DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/lib/libspl/asm-x86_64/Makefile.in b/lib/libspl/asm-x86_64/Makefile.in
+index 6b7f4be..845e2ab 100644
+--- a/lib/libspl/asm-x86_64/Makefile.in
++++ b/lib/libspl/asm-x86_64/Makefile.in
+@@ -41,6 +41,7 @@ DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/lib/libspl/include/Makefile.in b/lib/libspl/include/Makefile.in
+index 480b998..d9f6944 100644
+--- a/lib/libspl/include/Makefile.in
++++ b/lib/libspl/include/Makefile.in
+@@ -41,6 +41,7 @@ DIST_COMMON = $(libspl_HEADERS) $(srcdir)/Makefile.am \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/lib/libspl/include/ia32/Makefile.in b/lib/libspl/include/ia32/Makefile.in
+index 6344158..abb9a7b 100644
+--- a/lib/libspl/include/ia32/Makefile.in
++++ b/lib/libspl/include/ia32/Makefile.in
+@@ -39,6 +39,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/lib/libspl/include/ia32/sys/Makefile.in b/lib/libspl/include/ia32/sys/Makefile.in
+index c00dc4d..a7e3f3c 100644
+--- a/lib/libspl/include/ia32/sys/Makefile.in
++++ b/lib/libspl/include/ia32/sys/Makefile.in
+@@ -41,6 +41,7 @@ DIST_COMMON = $(libspl_HEADERS) $(srcdir)/Makefile.am \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/lib/libspl/include/rpc/Makefile.in b/lib/libspl/include/rpc/Makefile.in
+index 3513cb8..6c3174d 100644
+--- a/lib/libspl/include/rpc/Makefile.in
++++ b/lib/libspl/include/rpc/Makefile.in
+@@ -41,6 +41,7 @@ DIST_COMMON = $(libspl_HEADERS) $(srcdir)/Makefile.am \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/lib/libspl/include/sys/Makefile.in b/lib/libspl/include/sys/Makefile.in
+index c02b3ea..b226c8d 100644
+--- a/lib/libspl/include/sys/Makefile.in
++++ b/lib/libspl/include/sys/Makefile.in
+@@ -41,6 +41,7 @@ DIST_COMMON = $(libspl_HEADERS) $(srcdir)/Makefile.am \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/lib/libspl/include/sys/dktp/Makefile.in b/lib/libspl/include/sys/dktp/Makefile.in
+index cadff82..aa63471 100644
+--- a/lib/libspl/include/sys/dktp/Makefile.in
++++ b/lib/libspl/include/sys/dktp/Makefile.in
+@@ -41,6 +41,7 @@ DIST_COMMON = $(libspl_HEADERS) $(srcdir)/Makefile.am \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/lib/libspl/include/sys/sysevent/Makefile.in b/lib/libspl/include/sys/sysevent/Makefile.in
+index 3e5f219..2986138 100644
+--- a/lib/libspl/include/sys/sysevent/Makefile.in
++++ b/lib/libspl/include/sys/sysevent/Makefile.in
+@@ -41,6 +41,7 @@ DIST_COMMON = $(libspl_HEADERS) $(srcdir)/Makefile.am \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/lib/libspl/include/util/Makefile.in b/lib/libspl/include/util/Makefile.in
+index 3d49a92..bb71a90 100644
+--- a/lib/libspl/include/util/Makefile.in
++++ b/lib/libspl/include/util/Makefile.in
+@@ -41,6 +41,7 @@ DIST_COMMON = $(libspl_HEADERS) $(srcdir)/Makefile.am \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/lib/libunicode/Makefile.in b/lib/libunicode/Makefile.in
+index d05c40a..0b99b01 100644
+--- a/lib/libunicode/Makefile.in
++++ b/lib/libunicode/Makefile.in
+@@ -41,6 +41,7 @@ subdir = lib/libunicode
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/lib/libuutil/Makefile.in b/lib/libuutil/Makefile.in
+index 4aca160..5e87e01 100644
+--- a/lib/libuutil/Makefile.in
++++ b/lib/libuutil/Makefile.in
+@@ -41,6 +41,7 @@ subdir = lib/libuutil
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/lib/libzfs/Makefile.in b/lib/libzfs/Makefile.in
+index 720cf07..5fc0ed9 100644
+--- a/lib/libzfs/Makefile.in
++++ b/lib/libzfs/Makefile.in
+@@ -41,6 +41,7 @@ subdir = lib/libzfs
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/lib/libzpool/Makefile.am b/lib/libzpool/Makefile.am
+index d445d34..cbe3acd 100644
+--- a/lib/libzpool/Makefile.am
++++ b/lib/libzpool/Makefile.am
+@@ -97,6 +97,7 @@ libzpool_la_LDFLAGS = -pthread -version-info 1:1:0
+ EXTRA_DIST = \
+       $(top_srcdir)/module/zfs/vdev_disk.c \
+       $(top_srcdir)/module/zfs/zfs_acl.c \
++      $(top_srcdir)/module/zfs/zfs_ctldir.c \
+       $(top_srcdir)/module/zfs/zfs_dir.c \
+       $(top_srcdir)/module/zfs/zfs_ioctl.c \
+       $(top_srcdir)/module/zfs/zfs_log.c \
+@@ -105,6 +106,7 @@ EXTRA_DIST = \
+       $(top_srcdir)/module/zfs/zfs_rlock.c \
+       $(top_srcdir)/module/zfs/zfs_vfsops.c \
+       $(top_srcdir)/module/zfs/zfs_vnops.c \
++      $(top_srcdir)/module/zfs/zpl_ctldir.c \
+       $(top_srcdir)/module/zfs/zpl_export.c \
+       $(top_srcdir)/module/zfs/zpl_file.c \
+       $(top_srcdir)/module/zfs/zpl_inode.c \
+diff --git a/lib/libzpool/Makefile.in b/lib/libzpool/Makefile.in
+index 67130c3..d357fdd 100644
+--- a/lib/libzpool/Makefile.in
++++ b/lib/libzpool/Makefile.in
+@@ -41,6 +41,7 @@ subdir = lib/libzpool
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+@@ -455,6 +456,7 @@ libzpool_la_LDFLAGS = -pthread -version-info 1:1:0
+ EXTRA_DIST = \
+       $(top_srcdir)/module/zfs/vdev_disk.c \
+       $(top_srcdir)/module/zfs/zfs_acl.c \
++      $(top_srcdir)/module/zfs/zfs_ctldir.c \
+       $(top_srcdir)/module/zfs/zfs_dir.c \
+       $(top_srcdir)/module/zfs/zfs_ioctl.c \
+       $(top_srcdir)/module/zfs/zfs_log.c \
+@@ -463,6 +465,7 @@ EXTRA_DIST = \
+       $(top_srcdir)/module/zfs/zfs_rlock.c \
+       $(top_srcdir)/module/zfs/zfs_vfsops.c \
+       $(top_srcdir)/module/zfs/zfs_vnops.c \
++      $(top_srcdir)/module/zfs/zpl_ctldir.c \
+       $(top_srcdir)/module/zfs/zpl_export.c \
+       $(top_srcdir)/module/zfs/zpl_file.c \
+       $(top_srcdir)/module/zfs/zpl_inode.c \
+diff --git a/man/Makefile.in b/man/Makefile.in
+index 5a065b7..fb691d0 100644
+--- a/man/Makefile.in
++++ b/man/Makefile.in
+@@ -39,6 +39,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/man/man8/Makefile.in b/man/man8/Makefile.in
+index 5b4ca61..c8f015d 100644
+--- a/man/man8/Makefile.in
++++ b/man/man8/Makefile.in
+@@ -39,6 +39,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/module/zfs/Makefile.in b/module/zfs/Makefile.in
+index b303168..5ec75a0 100644
+--- a/module/zfs/Makefile.in
++++ b/module/zfs/Makefile.in
+@@ -64,6 +64,7 @@ $(MODULE)-objs += @top_srcdir@/module/zfs/zap_leaf.o
+ $(MODULE)-objs += @top_srcdir@/module/zfs/zap_micro.o
+ $(MODULE)-objs += @top_srcdir@/module/zfs/zfs_acl.o
+ $(MODULE)-objs += @top_srcdir@/module/zfs/zfs_byteswap.o
++$(MODULE)-objs += @top_srcdir@/module/zfs/zfs_ctldir.o
+ $(MODULE)-objs += @top_srcdir@/module/zfs/zfs_debug.o
+ $(MODULE)-objs += @top_srcdir@/module/zfs/zfs_dir.o
+ $(MODULE)-objs += @top_srcdir@/module/zfs/zfs_fm.o
+@@ -83,6 +84,7 @@ $(MODULE)-objs += @top_srcdir@/module/zfs/zio_checksum.o
+ $(MODULE)-objs += @top_srcdir@/module/zfs/zio_compress.o
+ $(MODULE)-objs += @top_srcdir@/module/zfs/zio_inject.o
+ $(MODULE)-objs += @top_srcdir@/module/zfs/zle.o
++$(MODULE)-objs += @top_srcdir@/module/zfs/zpl_ctldir.o
+ $(MODULE)-objs += @top_srcdir@/module/zfs/zpl_export.o
+ $(MODULE)-objs += @top_srcdir@/module/zfs/zpl_file.o
+ $(MODULE)-objs += @top_srcdir@/module/zfs/zpl_inode.o
+diff --git a/module/zfs/dmu_objset.c b/module/zfs/dmu_objset.c
+index 0703a94..1d0b461 100644
+--- a/module/zfs/dmu_objset.c
++++ b/module/zfs/dmu_objset.c
+@@ -1584,6 +1584,41 @@ dmu_snapshot_list_next(objset_t *os, int namelen, char *name,
+       return (0);
+ }
++/*
++ * Determine the objset id for a given snapshot name.
++ */
++int
++dmu_snapshot_id(objset_t *os, const char *snapname, uint64_t *idp)
++{
++      dsl_dataset_t *ds = os->os_dsl_dataset;
++      zap_cursor_t cursor;
++      zap_attribute_t attr;
++      int error;
++
++      if (ds->ds_phys->ds_snapnames_zapobj == 0)
++              return (ENOENT);
++
++      zap_cursor_init(&cursor, ds->ds_dir->dd_pool->dp_meta_objset,
++          ds->ds_phys->ds_snapnames_zapobj);
++
++      error = zap_cursor_move_to_key(&cursor, snapname, MT_EXACT);
++      if (error) {
++              zap_cursor_fini(&cursor);
++              return (error);
++      }
++
++      error = zap_cursor_retrieve(&cursor, &attr);
++      if (error) {
++              zap_cursor_fini(&cursor);
++              return (error);
++      }
++
++      *idp = attr.za_first_integer;
++      zap_cursor_fini(&cursor);
++
++      return (0);
++}
++
+ int
+ dmu_dir_list_next(objset_t *os, int namelen, char *name,
+     uint64_t *idp, uint64_t *offp)
+diff --git a/module/zfs/dsl_dataset.c b/module/zfs/dsl_dataset.c
+index 718c3ad..2deec8c 100644
+--- a/module/zfs/dsl_dataset.c
++++ b/module/zfs/dsl_dataset.c
+@@ -2373,8 +2373,7 @@ dsl_snapshot_rename_one(const char *name, void *arg)
+               return (err == ENOENT ? 0 : err);
+       }
+-/* XXX: Ignore for SPL version until mounting the FS is supported */
+-#if defined(_KERNEL) && !defined(HAVE_SPL)
++#ifdef _KERNEL
+       /*
+        * For all filesystems undergoing rename, we'll need to unmount it.
+        */
+diff --git a/module/zfs/zfs_ctldir.c b/module/zfs/zfs_ctldir.c
+new file mode 100644
+index 0000000..d04c88c
+--- /dev/null
++++ b/module/zfs/zfs_ctldir.c
+@@ -0,0 +1,957 @@
++/*
++ * CDDL HEADER START
++ *
++ * The contents of this file are subject to the terms of the
++ * Common Development and Distribution License (the "License").
++ * You may not use this file except in compliance with the License.
++ *
++ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
++ * or http://www.opensolaris.org/os/licensing.
++ * See the License for the specific language governing permissions
++ * and limitations under the License.
++ *
++ * When distributing Covered Code, include this CDDL HEADER in each
++ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
++ * If applicable, add the following below this CDDL HEADER, with the
++ * fields enclosed by brackets "[]" replaced with your own identifying
++ * information: Portions Copyright [yyyy] [name of copyright owner]
++ *
++ * CDDL HEADER END
++ */
++/*
++ *
++ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
++ * Copyright (C) 2011 Lawrence Livermore National Security, LLC.
++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
++ * LLNL-CODE-403049.
++ * Rewritten for Linux by:
++ *   Rohan Puri <rohan.puri15@gmail.com>
++ *   Brian Behlendorf <behlendorf1@llnl.gov>
++ */
++
++/*
++ * ZFS control directory (a.k.a. ".zfs")
++ *
++ * This directory provides a common location for all ZFS meta-objects.
++ * Currently, this is only the 'snapshot' and 'shares' directory, but this may
++ * expand in the future.  The elements are built dynamically, 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 inode.
++ *
++ * All mounts are handled automatically by an user mode helper which invokes
++ * the mount mount procedure.  Unmounts are handled by allowing the mount
++ * point to expire so the kernel may automatically unmount it.
++ *
++ * The '.zfs', '.zfs/snapshot', and all directories created under
++ * '.zfs/snapshot' (ie: '.zfs/snapshot/<snapname>') all share the same
++ * share the same zfs_sb_t as the head filesystem (what '.zfs' lives under).
++ *
++ * File systems mounted on top of the '.zfs/snapshot/<snapname>' paths
++ * (ie: snapshots) are complete ZFS filesystems and have their own unique
++ * zfs_sb_t.  However, the fsid reported by these mounts will be the same
++ * as that used by the parent zfs_sb_t to make NFS happy.
++ */
++
++#include <sys/types.h>
++#include <sys/param.h>
++#include <sys/time.h>
++#include <sys/systm.h>
++#include <sys/sysmacros.h>
++#include <sys/pathname.h>
++#include <sys/vfs.h>
++#include <sys/vfs_opreg.h>
++#include <sys/zfs_ctldir.h>
++#include <sys/zfs_ioctl.h>
++#include <sys/zfs_vfsops.h>
++#include <sys/zfs_vnops.h>
++#include <sys/stat.h>
++#include <sys/dmu.h>
++#include <sys/dsl_deleg.h>
++#include <sys/mount.h>
++#include <sys/zpl.h>
++#include "zfs_namecheck.h"
++
++/*
++ * Control Directory Tunables (.zfs)
++ */
++int zfs_expire_snapshot = ZFSCTL_EXPIRE_SNAPSHOT;
++
++static zfs_snapentry_t *
++zfsctl_sep_alloc(void)
++{
++      return kmem_zalloc(sizeof (zfs_snapentry_t), KM_SLEEP);
++}
++
++void
++zfsctl_sep_free(zfs_snapentry_t *sep)
++{
++      kmem_free(sep->se_name, MAXNAMELEN);
++      kmem_free(sep->se_path, PATH_MAX);
++      kmem_free(sep, sizeof (zfs_snapentry_t));
++}
++
++/*
++ * Attempt to expire an automounted snapshot, unmounts are attempted every
++ * 'zfs_expire_snapshot' seconds until they succeed.  The work request is
++ * responsible for rescheduling itself and freeing the zfs_expire_snapshot_t.
++ */
++static void
++zfsctl_expire_snapshot(void *data)
++{
++      zfs_snapentry_t *sep;
++      zfs_sb_t *zsb;
++      int error;
++
++      sep = spl_get_work_data(data, zfs_snapentry_t, se_work.work);
++      zsb = ITOZSB(sep->se_inode);
++
++      error = zfsctl_unmount_snapshot(zsb, sep->se_name, MNT_EXPIRE);
++      if (error == EBUSY)
++              schedule_delayed_work(&sep->se_work, zfs_expire_snapshot * HZ);
++}
++
++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);
++}
++
++boolean_t
++zfsctl_is_node(struct inode *ip)
++{
++      return (ITOZ(ip)->z_is_ctldir);
++}
++
++boolean_t
++zfsctl_is_snapdir(struct inode *ip)
++{
++      return (zfsctl_is_node(ip) && (ip->i_ino <= ZFSCTL_INO_SNAPDIRS));
++}
++
++/*
++ * Allocate a new inode with the passed id and ops.
++ */
++static struct inode *
++zfsctl_inode_alloc(zfs_sb_t *zsb, unsigned long id,
++    const struct file_operations *fops, const struct inode_operations *ops)
++{
++      struct inode *ip;
++      znode_t *zp;
++
++      ip = new_inode(zsb->z_sb);
++      if (ip == NULL)
++              return (NULL);
++
++      zp = ITOZ(ip);
++      memset(zp, 0, sizeof (znode_t) - sizeof (struct inode));
++        zp->z_is_ctldir = B_TRUE;
++      zp->z_id = id;
++      ip->i_ino = id;
++      ip->i_mode = (S_IFDIR | S_IRUGO | S_IXUGO);
++      ip->i_uid = 0;
++      ip->i_gid = 0;
++      ip->i_blkbits = SPA_MINBLOCKSHIFT;
++      ip->i_fop = fops;
++      ip->i_op = ops;
++
++      if (insert_inode_locked(ip)) {
++              unlock_new_inode(ip);
++              iput(ip);
++              return (NULL);
++      }
++
++      mutex_enter(&zsb->z_znodes_lock);
++      list_insert_tail(&zsb->z_all_znodes, zp);
++      membar_producer();
++      mutex_exit(&zsb->z_znodes_lock);
++
++      unlock_new_inode(ip);
++
++      return (ip);
++}
++
++/*
++ * Lookup the inode with given id, it will be allocated if needed.
++ */
++static struct inode *
++zfsctl_inode_lookup(zfs_sb_t *zsb, unsigned long id,
++    const struct file_operations *fops, const struct inode_operations *ops)
++{
++      struct inode *ip = NULL;
++
++      while (ip == NULL) {
++              ip = ilookup(zsb->z_sb, id);
++              if (ip)
++                      break;
++
++              /* May fail due to concurrent zfsctl_inode_alloc() */
++              ip = zfsctl_inode_alloc(zsb, id, fops, ops);
++      }
++
++      return (ip);
++}
++
++/*
++ * Free zfsctl inode specific structures, currently there are none.
++ */
++void
++zfsctl_inode_destroy(struct inode *ip)
++{
++      return;
++}
++
++/*
++ * An inode is being evicted from the cache.
++ */
++void
++zfsctl_inode_inactive(struct inode *ip)
++{
++      if (zfsctl_is_snapdir(ip))
++              zfsctl_snapdir_inactive(ip);
++}
++
++/*
++ * Create the '.zfs' directory.  This directory is cached as part of the VFS
++ * structure.  This results in a hold on the zfs_sb_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.  All other entities
++ * under the '.zfs' directory are created dynamically as needed.
++ */
++int
++zfsctl_create(zfs_sb_t *zsb)
++{
++      ASSERT(zsb->z_ctldir == NULL);
++
++      zsb->z_ctldir = zfsctl_inode_alloc(zsb, ZFSCTL_INO_ROOT,
++          &zpl_fops_root, &zpl_ops_root);
++      if (zsb->z_ctldir == NULL)
++              return (ENOENT);
++
++      return (0);
++}
++
++/*
++ * Destroy the '.zfs' directory.  Only called when the filesystem is unmounted.
++ */
++void
++zfsctl_destroy(zfs_sb_t *zsb)
++{
++      iput(zsb->z_ctldir);
++      zsb->z_ctldir = NULL;
++}
++
++/*
++ * Given a root znode, retrieve the associated .zfs directory.
++ * Add a hold to the vnode and return it.
++ */
++struct inode *
++zfsctl_root(znode_t *zp)
++{
++      ASSERT(zfs_has_ctldir(zp));
++      igrab(ZTOZSB(zp)->z_ctldir);
++      return (ZTOZSB(zp)->z_ctldir);
++}
++
++/*ARGSUSED*/
++int
++zfsctl_fid(struct inode *ip, fid_t *fidp)
++{
++      znode_t         *zp = ITOZ(ip);
++      zfs_sb_t        *zsb = ITOZSB(ip);
++      uint64_t        object = zp->z_id;
++      zfid_short_t    *zfid;
++      int             i;
++
++      ZFS_ENTER(zsb);
++
++      if (fidp->fid_len < SHORT_FID_LEN) {
++              fidp->fid_len = SHORT_FID_LEN;
++              ZFS_EXIT(zsb);
++              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(zsb);
++      return (0);
++}
++
++static int
++zfsctl_snapshot_zname(struct inode *ip, const char *name, int len, char *zname)
++{
++      objset_t *os = ITOZSB(ip)->z_os;
++
++      if (snapshot_namecheck(name, NULL, NULL) != 0)
++              return (EILSEQ);
++
++      dmu_objset_name(os, zname);
++      if ((strlen(zname) + 1 + strlen(name)) >= len)
++              return (ENAMETOOLONG);
++
++      (void) strcat(zname, "@");
++      (void) strcat(zname, name);
++
++      return (0);
++}
++
++static int
++zfsctl_snapshot_zpath(struct path *path, int len, char *zpath)
++{
++      char *path_buffer, *path_ptr;
++      int path_len, error = 0;
++
++      path_buffer = kmem_alloc(len, KM_SLEEP);
++
++      path_ptr = d_path(path, path_buffer, len);
++      if (IS_ERR(path_ptr)) {
++              error = -PTR_ERR(path_ptr);
++              goto out;
++      }
++
++      path_len = path_buffer + len - 1 - path_ptr;
++      if (path_len > len) {
++              error = EFAULT;
++              goto out;
++      }
++
++      memcpy(zpath, path_ptr, path_len);
++      zpath[path_len] = '\0';
++out:
++      kmem_free(path_buffer, len);
++
++      return (error);
++}
++
++/*
++ * Special case the handling of "..".
++ */
++/* ARGSUSED */
++int
++zfsctl_root_lookup(struct inode *dip, char *name, struct inode **ipp,
++    int flags, cred_t *cr, int *direntflags, pathname_t *realpnp)
++{
++      zfs_sb_t *zsb = ITOZSB(dip);
++      int error = 0;
++
++      ZFS_ENTER(zsb);
++
++      if (strcmp(name, "..") == 0) {
++              *ipp = dip->i_sb->s_root->d_inode;
++      } else if (strcmp(name, ZFS_SNAPDIR_NAME) == 0) {
++              *ipp = zfsctl_inode_lookup(zsb, ZFSCTL_INO_SNAPDIR,
++                  &zpl_fops_snapdir, &zpl_ops_snapdir);
++      } else if (strcmp(name, ZFS_SHAREDIR_NAME) == 0) {
++              *ipp = zfsctl_inode_lookup(zsb, ZFSCTL_INO_SHARES,
++                  &zpl_fops_shares, &zpl_ops_shares);
++      } else {
++              *ipp = NULL;
++      }
++
++      if (*ipp == NULL)
++              error = ENOENT;
++
++      ZFS_EXIT(zsb);
++
++      return (error);
++}
++
++/*
++ * Lookup entry point for the 'snapshot' directory.  Try to open the
++ * snapshot if it exist, creating the pseudo filesystem inode as necessary.
++ * Perform a mount of the associated dataset on top of the inode.
++ */
++/* ARGSUSED */
++int
++zfsctl_snapdir_lookup(struct inode *dip, char *name, struct inode **ipp,
++    int flags, cred_t *cr, int *direntflags, pathname_t *realpnp)
++{
++      zfs_sb_t *zsb = ITOZSB(dip);
++      uint64_t id;
++      int error;
++
++      ZFS_ENTER(zsb);
++
++      error = dmu_snapshot_id(zsb->z_os, name, &id);
++      if (error) {
++              ZFS_EXIT(zsb);
++              return (error);
++      }
++
++      *ipp = zfsctl_inode_lookup(zsb, ZFSCTL_INO_SNAPDIRS - id,
++          &simple_dir_operations, &simple_dir_inode_operations);
++      if (*ipp) {
++#ifdef HAVE_AUTOMOUNT
++              (*ipp)->i_flags |= S_AUTOMOUNT;
++#endif /* HAVE_AUTOMOUNT */
++      } else {
++              error = ENOENT;
++      }
++
++      ZFS_EXIT(zsb);
++
++      return (error);
++}
++
++static void
++zfsctl_rename_snap(zfs_sb_t *zsb, zfs_snapentry_t *sep, const char *name)
++{
++      avl_index_t where;
++
++      ASSERT(MUTEX_HELD(&zsb->z_ctldir_lock));
++      ASSERT(sep != NULL);
++
++      /*
++       * Change the name in the AVL tree.
++       */
++      avl_remove(&zsb->z_ctldir_snaps, sep);
++      (void) strcpy(sep->se_name, name);
++      VERIFY(avl_find(&zsb->z_ctldir_snaps, sep, &where) == NULL);
++      avl_insert(&zsb->z_ctldir_snaps, sep, where);
++}
++
++/*
++ * Renaming a directory under '.zfs/snapshot' will automatically trigger
++ * a rename of the snapshot to the new given name.  The rename is confined
++ * to the '.zfs/snapshot' directory snapshots cannot be moved elsewhere.
++ */
++/*ARGSUSED*/
++int
++zfsctl_snapdir_rename(struct inode *sdip, char *sname,
++    struct inode *tdip, char *tname, cred_t *cr, int flags)
++{
++      zfs_sb_t *zsb = ITOZSB(sdip);
++      zfs_snapentry_t search, *sep;
++      avl_index_t where;
++      char *to, *from, *real;
++      int error;
++
++      ZFS_ENTER(zsb);
++
++      to = kmem_alloc(MAXNAMELEN, KM_SLEEP);
++      from = kmem_alloc(MAXNAMELEN, KM_SLEEP);
++      real = kmem_alloc(MAXNAMELEN, KM_SLEEP);
++
++      if (zsb->z_case == ZFS_CASE_INSENSITIVE) {
++              error = dmu_snapshot_realname(zsb->z_os, sname, real,
++                  MAXNAMELEN, NULL);
++              if (error == 0) {
++                      sname = real;
++              } else if (error != ENOTSUP) {
++                      goto out;
++              }
++      }
++
++      error = zfsctl_snapshot_zname(sdip, sname, MAXNAMELEN, from);
++      if (!error)
++              error = zfsctl_snapshot_zname(tdip, tname, MAXNAMELEN, to);
++      if (!error)
++              error = zfs_secpolicy_rename_perms(from, to, cr);
++      if (error)
++              goto out;
++
++      /*
++       * Cannot move snapshots out of the snapdir.
++       */
++      if (sdip != tdip) {
++              error = EINVAL;
++              goto out;
++      }
++
++      /*
++       * No-op when names are identical.
++       */
++      if (strcmp(sname, tname) == 0) {
++              error = 0;
++              goto out;
++      }
++
++      mutex_enter(&zsb->z_ctldir_lock);
++
++      error = dmu_objset_rename(from, to, B_FALSE);
++      if (error)
++              goto out_unlock;
++
++      search.se_name = (char *)sname;
++      sep = avl_find(&zsb->z_ctldir_snaps, &search, &where);
++      if (sep)
++              zfsctl_rename_snap(zsb, sep, tname);
++
++out_unlock:
++      mutex_exit(&zsb->z_ctldir_lock);
++out:
++      kmem_free(from, MAXNAMELEN);
++      kmem_free(to, MAXNAMELEN);
++      kmem_free(real, MAXNAMELEN);
++
++      ZFS_EXIT(zsb);
++
++      return (error);
++}
++
++/*
++ * Removing a directory under '.zfs/snapshot' will automatically trigger
++ * the removal of the snapshot with the given name.
++ */
++/* ARGSUSED */
++int
++zfsctl_snapdir_remove(struct inode *dip, char *name, cred_t *cr, int flags)
++{
++      zfs_sb_t *zsb = ITOZSB(dip);
++      char *snapname, *real;
++      int error;
++
++      ZFS_ENTER(zsb);
++
++      snapname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
++      real = kmem_alloc(MAXNAMELEN, KM_SLEEP);
++
++      if (zsb->z_case == ZFS_CASE_INSENSITIVE) {
++              error = dmu_snapshot_realname(zsb->z_os, name, real,
++                  MAXNAMELEN, NULL);
++              if (error == 0) {
++                      name = real;
++              } else if (error != ENOTSUP) {
++                      goto out;
++              }
++      }
++
++      error = zfsctl_snapshot_zname(dip, name, MAXNAMELEN, snapname);
++      if (!error)
++              error = zfs_secpolicy_destroy_perms(snapname, cr);
++      if (error)
++              goto out;
++
++      error = zfsctl_unmount_snapshot(zsb, name, MNT_FORCE);
++      if ((error == 0) || (error == ENOENT))
++              error = dmu_objset_destroy(snapname, B_FALSE);
++out:
++      kmem_free(snapname, MAXNAMELEN);
++      kmem_free(real, MAXNAMELEN);
++
++      ZFS_EXIT(zsb);
++
++      return (error);
++}
++
++/*
++ * Creating a directory under '.zfs/snapshot' will automatically trigger
++ * the creation of a new snapshot with the given name.
++ */
++/* ARGSUSED */
++int
++zfsctl_snapdir_mkdir(struct inode *dip, char *dirname, vattr_t *vap,
++      struct inode **ipp, cred_t *cr, int flags)
++{
++      zfs_sb_t *zsb = ITOZSB(dip);
++      char *dsname;
++      int error;
++
++      dsname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
++
++      if (snapshot_namecheck(dirname, NULL, NULL) != 0) {
++              error = EILSEQ;
++              goto out;
++      }
++
++      dmu_objset_name(zsb->z_os, dsname);
++
++      error = zfs_secpolicy_snapshot_perms(dsname, cr);
++      if (error)
++              goto out;
++
++      if (error == 0) {
++              error = dmu_objset_snapshot(dsname, dirname,
++                  NULL, NULL, B_FALSE, B_FALSE, -1);
++              if (error)
++                      goto out;
++
++              error = zfsctl_snapdir_lookup(dip, dirname, ipp,
++                  0, cr, NULL, NULL);
++      }
++out:
++      kmem_free(dsname, MAXNAMELEN);
++
++      return (error);
++}
++
++/*
++ * When a .zfs/snapshot/<snapshot> inode is evicted they must be removed
++ * from the snapshot list.  This will normally happen as part of the auto
++ * unmount, however in the case of a manual snapshot unmount this will be
++ * the only notification we receive.
++ */
++void
++zfsctl_snapdir_inactive(struct inode *ip)
++{
++      zfs_sb_t *zsb = ITOZSB(ip);
++      zfs_snapentry_t *sep, *next;
++
++      mutex_enter(&zsb->z_ctldir_lock);
++
++      sep = avl_first(&zsb->z_ctldir_snaps);
++      while (sep != NULL) {
++              next = AVL_NEXT(&zsb->z_ctldir_snaps, sep);
++
++              if (sep->se_inode == ip) {
++                      avl_remove(&zsb->z_ctldir_snaps, sep);
++                      cancel_delayed_work_sync(&sep->se_work);
++                      zfsctl_sep_free(sep);
++                      break;
++              }
++              sep = next;
++      }
++
++      mutex_exit(&zsb->z_ctldir_lock);
++}
++
++/*
++ * Attempt to unmount a snapshot by making a call to user space.
++ * There is no assurance that this can or will succeed, is just a
++ * best effort.  In the case where it does fail, perhaps because
++ * it's in use, the unmount will fail harmlessly.
++ */
++#define SET_UNMOUNT_CMD \
++      "exec 0</dev/null " \
++      "     1>/dev/null " \
++      "     2>/dev/null; " \
++      "umount -t zfs -n %s%s"
++
++static int
++__zfsctl_unmount_snapshot(zfs_snapentry_t *sep, int flags)
++{
++      char *argv[] = { "/bin/sh", "-c", NULL, NULL };
++      char *envp[] = { NULL };
++      int error;
++
++      argv[2] = kmem_asprintf(SET_UNMOUNT_CMD,
++          flags & MNT_FORCE ? "-f " : "", sep->se_path);
++      error = call_usermodehelper(argv[0], argv, envp, 1);
++      strfree(argv[2]);
++
++      /*
++       * The umount system utility will return 256 on error.  We must
++       * assume this error is because the file system is busy so it is
++       * converted to the more sensible EBUSY.
++       */
++      if (error)
++              error = EBUSY;
++
++      /*
++       * This was the result of a manual unmount, cancel the delayed work
++       * to prevent zfsctl_expire_snapshot() from attempting a unmount.
++       */
++      if ((error == 0) && !(flags & MNT_EXPIRE))
++              cancel_delayed_work(&sep->se_work);
++
++      return (error);
++}
++
++int
++zfsctl_unmount_snapshot(zfs_sb_t *zsb, char *name, int flags)
++{
++      zfs_snapentry_t search;
++      zfs_snapentry_t *sep;
++      int error = 0;
++
++      mutex_enter(&zsb->z_ctldir_lock);
++
++      search.se_name = name;
++      sep = avl_find(&zsb->z_ctldir_snaps, &search, NULL);
++      if (sep) {
++              avl_remove(&zsb->z_ctldir_snaps, sep);
++              error = __zfsctl_unmount_snapshot(sep, flags);
++              if (error == EBUSY)
++                      avl_add(&zsb->z_ctldir_snaps, sep);
++              else
++                      zfsctl_sep_free(sep);
++      } else {
++              error = ENOENT;
++      }
++
++      mutex_exit(&zsb->z_ctldir_lock);
++      ASSERT3S(error, >=, 0);
++
++      return (error);
++}
++
++/*
++ * Traverse all mounted snapshots and attempt to unmount them.  This
++ * is best effort, on failure EEXIST is returned and count will be set
++ * to the number of file snapshots which could not be unmounted.
++ */
++int
++zfsctl_unmount_snapshots(zfs_sb_t *zsb, int flags, int *count)
++{
++      zfs_snapentry_t *sep, *next;
++      int error = 0;
++
++      *count = 0;
++
++      ASSERT(zsb->z_ctldir != NULL);
++      mutex_enter(&zsb->z_ctldir_lock);
++
++      sep = avl_first(&zsb->z_ctldir_snaps);
++      while (sep != NULL) {
++              next = AVL_NEXT(&zsb->z_ctldir_snaps, sep);
++              avl_remove(&zsb->z_ctldir_snaps, sep);
++              error = __zfsctl_unmount_snapshot(sep, flags);
++              if (error == EBUSY) {
++                      avl_add(&zsb->z_ctldir_snaps, sep);
++                      (*count)++;
++              } else {
++                      zfsctl_sep_free(sep);
++              }
++
++              sep = next;
++      }
++
++      mutex_exit(&zsb->z_ctldir_lock);
++
++      return ((*count > 0) ? EEXIST : 0);
++}
++
++#define SET_MOUNT_CMD \
++      "exec 0</dev/null " \
++      "     1>/dev/null " \
++      "     2>/dev/null; " \
++      "mount -t zfs -n %s %s"
++
++int
++zfsctl_mount_snapshot(struct path *path, int flags)
++{
++      struct dentry *dentry = path->dentry;
++      struct inode *ip = dentry->d_inode;
++      zfs_sb_t *zsb = ITOZSB(ip);
++      char *full_name, *full_path;
++      zfs_snapentry_t *sep;
++      zfs_snapentry_t search;
++      char *argv[] = { "/bin/sh", "-c", NULL, NULL };
++      char *envp[] = { NULL };
++      int error;
++
++      ZFS_ENTER(zsb);
++
++      full_name = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
++      full_path = kmem_zalloc(PATH_MAX, KM_SLEEP);
++
++      error = zfsctl_snapshot_zname(ip, dname(dentry), MAXNAMELEN, full_name);
++      if (error)
++              goto error;
++
++      error = zfsctl_snapshot_zpath(path, PATH_MAX, full_path);
++      if (error)
++              goto error;
++
++      /*
++       * Attempt to mount the snapshot from user space.  Normally this
++       * would be done using the vfs_kern_mount() function, however that
++       * function is marked GPL-only and cannot be used.  On error we
++       * careful to log the real error to the console and return EISDIR
++       * to safely abort the automount.  This should be very rare.
++       */
++      argv[2] = kmem_asprintf(SET_MOUNT_CMD, full_name, full_path);
++      error = call_usermodehelper(argv[0], argv, envp, 1);
++      strfree(argv[2]);
++      if (error) {
++              printk("ZFS: Unable to automount %s at %s: %d\n",
++                  full_name, full_path, error);
++              error = EISDIR;
++              goto error;
++      }
++
++      mutex_enter(&zsb->z_ctldir_lock);
++
++      /*
++       * Ensure a previous entry does not exist, if it does safely remove
++       * it any cancel the outstanding expiration.  This can occur when a
++       * snapshot is manually unmounted and then an automount is triggered.
++       */
++      search.se_name = full_name;
++      sep = avl_find(&zsb->z_ctldir_snaps, &search, NULL);
++      if (sep) {
++              avl_remove(&zsb->z_ctldir_snaps, sep);
++              cancel_delayed_work_sync(&sep->se_work);
++              zfsctl_sep_free(sep);
++      }
++
++      sep = zfsctl_sep_alloc();
++      sep->se_name = full_name;
++      sep->se_path = full_path;
++      sep->se_inode = ip;
++      avl_add(&zsb->z_ctldir_snaps, sep);
++
++        spl_init_delayed_work(&sep->se_work, zfsctl_expire_snapshot, sep);
++      schedule_delayed_work(&sep->se_work, zfs_expire_snapshot * HZ);
++
++      mutex_exit(&zsb->z_ctldir_lock);
++error:
++      if (error) {
++              kmem_free(full_name, MAXNAMELEN);
++              kmem_free(full_path, PATH_MAX);
++      }
++
++      ZFS_EXIT(zsb);
++
++      return (error);
++}
++
++/*
++ * Check if this super block has a matching objset id.
++ */
++static int
++zfsctl_test_super(struct super_block *sb, void *objsetidp)
++{
++      zfs_sb_t *zsb = sb->s_fs_info;
++      uint64_t objsetid = *(uint64_t *)objsetidp;
++
++      return (dmu_objset_id(zsb->z_os) == objsetid);
++}
++
++/*
++ * Prevent a new super block from being allocated if an existing one
++ * could not be located.  We only want to preform a lookup operation.
++ */
++static int
++zfsctl_set_super(struct super_block *sb, void *objsetidp)
++{
++      return (-EEXIST);
++}
++
++int
++zfsctl_lookup_objset(struct super_block *sb, uint64_t objsetid, zfs_sb_t **zsbp)
++{
++      zfs_sb_t *zsb = sb->s_fs_info;
++      struct super_block *sbp;
++      zfs_snapentry_t *sep;
++      uint64_t id;
++      int error;
++
++      ASSERT(zsb->z_ctldir != NULL);
++
++      mutex_enter(&zsb->z_ctldir_lock);
++
++      /*
++       * Verify that the snapshot is mounted.
++       */
++      sep = avl_first(&zsb->z_ctldir_snaps);
++      while (sep != NULL) {
++              error = dmu_snapshot_id(zsb->z_os, sep->se_name, &id);
++              if (error)
++                      goto out;
++
++              if (id == objsetid)
++                      break;
++
++              sep = AVL_NEXT(&zsb->z_ctldir_snaps, sep);
++      }
++
++      if (sep != NULL) {
++              /*
++               * Lookup the mounted root rather than the covered mount
++               * point.  This may fail if the snapshot has just been
++               * unmounted by an unrelated user space process.  This
++               * race cannot occur to an expired mount point because
++               * we hold the zsb->z_ctldir_lock to prevent the race.
++               */
++              sbp = sget(&zpl_fs_type, zfsctl_test_super,
++                  zfsctl_set_super, &id);
++              if (IS_ERR(sbp)) {
++                      error = -PTR_ERR(sbp);
++              } else {
++                      *zsbp = sbp->s_fs_info;
++                      deactivate_super(sbp);
++              }
++      } else {
++              error = EINVAL;
++      }
++out:
++      mutex_exit(&zsb->z_ctldir_lock);
++      ASSERT3S(error, >=, 0);
++
++      return (error);
++}
++
++/* ARGSUSED */
++int
++zfsctl_shares_lookup(struct inode *dip, char *name, struct inode **ipp,
++    int flags, cred_t *cr, int *direntflags, pathname_t *realpnp)
++{
++      zfs_sb_t *zsb = ITOZSB(dip);
++      struct inode *ip;
++      znode_t *dzp;
++      int error;
++
++      ZFS_ENTER(zsb);
++
++      if (zsb->z_shares_dir == 0) {
++              ZFS_EXIT(zsb);
++              return (-ENOTSUP);
++      }
++
++      error = zfs_zget(zsb, zsb->z_shares_dir, &dzp);
++      if (error) {
++              ZFS_EXIT(zsb);
++              return (error);
++      }
++
++      error = zfs_lookup(ZTOI(dzp), name, &ip, 0, cr, NULL, NULL);
++
++      iput(ZTOI(dzp));
++      ZFS_EXIT(zsb);
++
++      return (error);
++}
++
++
++/*
++ * Initialize the various pieces we'll need to create and manipulate .zfs
++ * directories.  Currently this is unused but available.
++ */
++void
++zfsctl_init(void)
++{
++}
++
++/*
++ * Cleanup the various pieces we needed for .zfs directories.  In particular
++ * ensure the expiry timer is canceled safely.
++ */
++void
++zfsctl_fini(void)
++{
++}
++
++module_param(zfs_expire_snapshot, int, 0644);
++MODULE_PARM_DESC(zfs_expire_snapshot, "Seconds to expire .zfs/snapshot");
+diff --git a/module/zfs/zfs_dir.c b/module/zfs/zfs_dir.c
+index 8f1a0c2..6cd9c85 100644
+--- a/module/zfs/zfs_dir.c
++++ b/module/zfs/zfs_dir.c
+@@ -50,6 +50,7 @@
+ #include <sys/zap.h>
+ #include <sys/dmu.h>
+ #include <sys/atomic.h>
++#include <sys/zfs_ctldir.h>
+ #include <sys/zfs_fuid.h>
+ #include <sys/sa.h>
+ #include <sys/zfs_sa.h>
+@@ -415,28 +416,24 @@ zfs_dirlook(znode_t *dzp, char *name, struct inode **ipp, int flags,
+               /*
+                * If we are a snapshot mounted under .zfs, return
+-               * the vp for the snapshot directory.
++               * the inode pointer for the snapshot directory.
+                */
+               if ((error = sa_lookup(dzp->z_sa_hdl,
+                   SA_ZPL_PARENT(zsb), &parent, sizeof (parent))) != 0)
+                       return (error);
+-#ifdef HAVE_SNAPSHOT
++
+               if (parent == dzp->z_id && zsb->z_parent != zsb) {
+                       error = zfsctl_root_lookup(zsb->z_parent->z_ctldir,
+-                          "snapshot", ipp, NULL, 0, NULL, kcred,
+-                          NULL, NULL, NULL);
++                          "snapshot", ipp, 0, kcred, NULL, NULL);
+                       return (error);
+               }
+-#endif /* HAVE_SNAPSHOT */
+               rw_enter(&dzp->z_parent_lock, RW_READER);
+               error = zfs_zget(zsb, parent, &zp);
+               if (error == 0)
+                       *ipp = ZTOI(zp);
+               rw_exit(&dzp->z_parent_lock);
+-#ifdef HAVE_SNAPSHOT
+       } else if (zfs_has_ctldir(dzp) && strcmp(name, ZFS_CTLDIR_NAME) == 0) {
+               *ipp = zfsctl_root(dzp);
+-#endif /* HAVE_SNAPSHOT */
+       } else {
+               int zf;
+diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c
+index 532f17a..d2ad1af 100644
+--- a/module/zfs/zfs_ioctl.c
++++ b/module/zfs/zfs_ioctl.c
+@@ -58,6 +58,7 @@
+ #include <sys/mount.h>
+ #include <sys/sdt.h>
+ #include <sys/fs/zfs.h>
++#include <sys/zfs_ctldir.h>
+ #include <sys/zfs_dir.h>
+ #include <sys/zfs_onexit.h>
+ #include <sys/zvol.h>
+@@ -2690,33 +2691,6 @@ zfs_ioc_get_fsacl(zfs_cmd_t *zc)
+       return (error);
+ }
+-#ifdef HAVE_SNAPSHOT
+-/*
+- * 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) {
+-                      mntget(vfsp);
+-                      vfs_found = vfsp;
+-                      break;
+-              }
+-              vfsp = vfsp->vfs_next;
+-      } while (vfsp != rootvfs);
+-      vfs_list_unlock();
+-      return (vfs_found);
+-}
+-#endif /* HAVE_SNAPSHOT */
+-
+ /* ARGSUSED */
+ static void
+ zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx)
+@@ -3067,38 +3041,52 @@ out:
+       return (error);
+ }
++/*
++ * inputs:
++ * name               dataset name, or when 'arg == NULL' the full snapshot name
++ * arg                short snapshot name (i.e. part after the '@')
++ */
+ int
+ zfs_unmount_snap(const char *name, void *arg)
+ {
+-#ifdef HAVE_SNAPSHOT
+-      vfs_t *vfsp = NULL;
++      zfs_sb_t *zsb = NULL;
++      char *dsname;
++      char *snapname;
++      char *fullname;
++      char *ptr;
++      int error;
+       if (arg) {
+-              char *snapname = arg;
+-              char *fullname = kmem_asprintf("%s@%s", name, snapname);
+-              vfsp = zfs_get_vfs(fullname);
+-              strfree(fullname);
+-      } else if (strchr(name, '@')) {
+-              vfsp = zfs_get_vfs(name);
++              dsname = strdup(name);
++              snapname = strdup(arg);
++      } else {
++              ptr = strchr(name, '@');
++              if (ptr) {
++                      dsname = strdup(name);
++                      dsname[ptr - name] = '\0';
++                      snapname = strdup(ptr + 1);
++              } else {
++                      return (0);
++              }
+       }
+-      if (vfsp) {
+-              /*
+-               * Always force the unmount for snapshots.
+-               */
+-              int flag = MS_FORCE;
+-              int err;
++      fullname = kmem_asprintf("%s@%s", dsname, snapname);
+-              if ((err = vn_vfswlock(vfsp->vfs_vnodecovered)) != 0) {
+-                      mntput(vfsp);
+-                      return (err);
+-              }
+-              mntput(vfsp);
+-              if ((err = dounmount(vfsp, flag, kcred)) != 0)
+-                      return (err);
++      error = zfs_sb_hold(dsname, FTAG, &zsb, B_FALSE);
++      if (error == 0) {
++              error = zfsctl_unmount_snapshot(zsb, fullname, MNT_FORCE);
++              zfs_sb_rele(zsb, FTAG);
++
++              /* Allow ENOENT for consistency with upstream */
++              if (error == ENOENT)
++                      error = 0;
+       }
+-#endif /* HAVE_SNAPSHOT */
+-      return (0);
++
++      strfree(dsname);
++      strfree(snapname);
++      strfree(fullname);
++
++      return (error);
+ }
+ /*
+diff --git a/module/zfs/zfs_vfsops.c b/module/zfs/zfs_vfsops.c
+index fb319a5..f895f5c 100644
+--- a/module/zfs/zfs_vfsops.c
++++ b/module/zfs/zfs_vfsops.c
+@@ -56,6 +56,7 @@
+ #include <sys/modctl.h>
+ #include <sys/refstr.h>
+ #include <sys/zfs_ioctl.h>
++#include <sys/zfs_ctldir.h>
+ #include <sys/zfs_fuid.h>
+ #include <sys/bootconf.h>
+ #include <sys/sunddi.h>
+@@ -710,6 +711,10 @@ zfs_sb_create(const char *osname, zfs_sb_t **zsbp)
+       for (i = 0; i != ZFS_OBJ_MTX_SZ; i++)
+               mutex_init(&zsb->z_hold_mtx[i], NULL, MUTEX_DEFAULT, NULL);
++      avl_create(&zsb->z_ctldir_snaps, snapentry_compare,
++          sizeof (zfs_snapentry_t), offsetof(zfs_snapentry_t, se_node));
++      mutex_init(&zsb->z_ctldir_lock, NULL, MUTEX_DEFAULT, NULL);
++
+       *zsbp = zsb;
+       return (0);
+@@ -819,6 +824,8 @@ zfs_sb_free(zfs_sb_t *zsb)
+       rw_destroy(&zsb->z_fuid_lock);
+       for (i = 0; i != ZFS_OBJ_MTX_SZ; i++)
+               mutex_destroy(&zsb->z_hold_mtx[i]);
++      mutex_destroy(&zsb->z_ctldir_lock);
++      avl_destroy(&zsb->z_ctldir_snaps);
+       kmem_free(zsb, sizeof (zfs_sb_t));
+ }
+ EXPORT_SYMBOL(zfs_sb_free);
+@@ -1183,11 +1190,11 @@ zfs_domount(struct super_block *sb, void *data, int silent)
+               mutex_exit(&zsb->z_os->os_user_ptr_lock);
+       } else {
+               error = zfs_sb_setup(zsb, B_TRUE);
+-#ifdef HAVE_SNAPSHOT
+-              (void) zfs_snap_create(zsb);
+-#endif /* HAVE_SNAPSHOT */
+       }
++      if (!zsb->z_issnap)
++              zfsctl_create(zsb);
++
+       /* Allocate a root inode for the filesystem. */
+       error = zfs_root(zsb, &root_inode);
+       if (error) {
+@@ -1212,6 +1219,27 @@ out:
+ }
+ EXPORT_SYMBOL(zfs_domount);
++/*
++ * Called when an unmount is requested and certain sanity checks have
++ * already passed.  At this point no dentries or inodes have been reclaimed
++ * from their respective caches.  We drop the extra reference on the .zfs
++ * control directory to allow everything to be reclaimed.  All snapshots
++ * must already have been unmounted to reach this point.
++ */
++void
++zfs_preumount(struct super_block *sb)
++{
++      zfs_sb_t *zsb = sb->s_fs_info;
++
++      if (zsb->z_ctldir != NULL)
++              zfsctl_destroy(zsb);
++}
++EXPORT_SYMBOL(zfs_preumount);
++
++/*
++ * Called once all other unmount released tear down has occurred.
++ * It is our responsibility to release any remaining infrastructure.
++ */
+ /*ARGSUSED*/
+ int
+ zfs_umount(struct super_block *sb)
+@@ -1288,11 +1316,10 @@ zfs_vget(struct super_block *sb, struct inode **ipp, fid_t *fidp)
+               ZFS_EXIT(zsb);
+-#ifdef HAVE_SNAPSHOT
+-              err = zfsctl_lookup_objset(vfsp, objsetid, &zsb);
++              err = zfsctl_lookup_objset(sb, objsetid, &zsb);
+               if (err)
+                       return (EINVAL);
+-#endif /* HAVE_SNAPSHOT */
++
+               ZFS_ENTER(zsb);
+       }
+@@ -1309,22 +1336,20 @@ zfs_vget(struct super_block *sb, struct inode **ipp, fid_t *fidp)
+               return (EINVAL);
+       }
+-#ifdef HAVE_SNAPSHOT
+       /* A zero fid_gen means we are in the .zfs control directories */
+       if (fid_gen == 0 &&
+           (object == ZFSCTL_INO_ROOT || object == ZFSCTL_INO_SNAPDIR)) {
+               *ipp = zsb->z_ctldir;
+               ASSERT(*ipp != NULL);
+               if (object == ZFSCTL_INO_SNAPDIR) {
+-                      VERIFY(zfsctl_root_lookup(*ipp, "snapshot", ipp, NULL,
+-                          0, NULL, NULL, NULL, NULL, NULL) == 0);
++                      VERIFY(zfsctl_root_lookup(*ipp, "snapshot", ipp,
++                          0, kcred, NULL, NULL) == 0);
+               } else {
+                       igrab(*ipp);
+               }
+               ZFS_EXIT(zsb);
+               return (0);
+       }
+-#endif /* HAVE_SNAPSHOT */
+       gen_mask = -1ULL >> (64 - 8 * i);
+@@ -1550,6 +1575,7 @@ EXPORT_SYMBOL(zfs_get_zplprop);
+ void
+ zfs_init(void)
+ {
++      zfsctl_init();
+       zfs_znode_init();
+       dmu_objset_register_type(DMU_OST_ZFS, zfs_space_delta_cb);
+       register_filesystem(&zpl_fs_type);
+@@ -1561,4 +1587,5 @@ zfs_fini(void)
+ {
+       unregister_filesystem(&zpl_fs_type);
+       zfs_znode_fini();
++      zfsctl_fini();
+ }
+diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c
+index 74b96b8..2da5fec 100644
+--- a/module/zfs/zfs_vnops.c
++++ b/module/zfs/zfs_vnops.c
+@@ -63,6 +63,7 @@
+ #include <sys/sid.h>
+ #include <sys/mode.h>
+ #include "fs/fs_subr.h"
++#include <sys/zfs_ctldir.h>
+ #include <sys/zfs_fuid.h>
+ #include <sys/zfs_sa.h>
+ #include <sys/zfs_vnops.h>
+@@ -2045,7 +2046,7 @@ zfs_readdir(struct inode *ip, void *dirent, filldir_t filldir,
+                       dmu_prefetch(os, objnum, 0, 0);
+               }
+-              if (*pos >= 2) {
++              if (*pos > 2 || (*pos == 2 && !zfs_show_ctldir(zp))) {
+                       zap_cursor_advance(&zc);
+                       *pos = zap_cursor_serialize(&zc);
+               } else {
+@@ -3876,9 +3877,10 @@ zfs_inactive(struct inode *ip)
+       zfs_sb_t *zsb = ITOZSB(ip);
+       int error;
+-#ifdef HAVE_SNAPSHOT
+-      /* Early return for snapshot inode? */
+-#endif /* HAVE_SNAPSHOT */
++      if (zfsctl_is_node(ip)) {
++              zfsctl_inode_inactive(ip);
++              return;
++      }
+       rw_enter(&zsb->z_teardown_inactive_lock, RW_READER);
+       if (zp->z_sa_hdl == NULL) {
+diff --git a/module/zfs/zfs_znode.c b/module/zfs/zfs_znode.c
+index 709ae74..629c851 100644
+--- a/module/zfs/zfs_znode.c
++++ b/module/zfs/zfs_znode.c
+@@ -52,6 +52,7 @@
+ #include <sys/zfs_rlock.h>
+ #include <sys/zfs_fuid.h>
+ #include <sys/zfs_vnops.h>
++#include <sys/zfs_ctldir.h>
+ #include <sys/dnode.h>
+ #include <sys/fs/zfs.h>
+ #include <sys/kidmap.h>
+@@ -267,6 +268,9 @@ zfs_inode_destroy(struct inode *ip)
+       znode_t *zp = ITOZ(ip);
+       zfs_sb_t *zsb = ZTOZSB(zp);
++      if (zfsctl_is_node(ip))
++              zfsctl_inode_destroy(ip);
++
+       mutex_enter(&zsb->z_znodes_lock);
+       list_remove(&zsb->z_all_znodes, zp);
+       zsb->z_nr_znodes--;
+@@ -363,6 +367,7 @@ zfs_znode_alloc(zfs_sb_t *zsb, dmu_buf_t *db, int blksz,
+       zp->z_seq = 0x7A4653;
+       zp->z_sync_cnt = 0;
+       zp->z_is_zvol = 0;
++      zp->z_is_ctldir = 0;
+       zfs_znode_sa_init(zsb, zp, db, obj_type, hdl);
+@@ -434,6 +439,10 @@ zfs_inode_update(znode_t *zp)
+       zsb = ZTOZSB(zp);
+       ip = ZTOI(zp);
++      /* Skip .zfs control nodes which do not exist on disk. */
++      if (zfsctl_is_node(ip))
++              return;
++
+       sa_lookup(zp->z_sa_hdl, SA_ZPL_ATIME(zsb), &atime, 16);
+       sa_lookup(zp->z_sa_hdl, SA_ZPL_MTIME(zsb), &mtime, 16);
+       sa_lookup(zp->z_sa_hdl, SA_ZPL_CTIME(zsb), &ctime, 16);
+diff --git a/module/zfs/zpl_ctldir.c b/module/zfs/zpl_ctldir.c
+new file mode 100644
+index 0000000..6c742c9
+--- /dev/null
++++ b/module/zfs/zpl_ctldir.c
+@@ -0,0 +1,519 @@
++/*
++ * CDDL HEADER START
++ *
++ * The contents of this file are subject to the terms of the
++ * Common Development and Distribution License (the "License").
++ * You may not use this file except in compliance with the License.
++ *
++ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
++ * or http://www.opensolaris.org/os/licensing.
++ * See the License for the specific language governing permissions
++ * and limitations under the License.
++ *
++ * When distributing Covered Code, include this CDDL HEADER in each
++ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
++ * If applicable, add the following below this CDDL HEADER, with the
++ * fields enclosed by brackets "[]" replaced with your own identifying
++ * information: Portions Copyright [yyyy] [name of copyright owner]
++ *
++ * CDDL HEADER END
++ */
++/*
++ * Copyright (C) 2011 Lawrence Livermore National Security, LLC.
++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
++ * LLNL-CODE-403049.
++ * Rewritten for Linux by:
++ *   Rohan Puri <rohan.puri15@gmail.com>
++ *   Brian Behlendorf <behlendorf1@llnl.gov>
++ */
++
++#include <sys/zfs_vfsops.h>
++#include <sys/zfs_vnops.h>
++#include <sys/zfs_znode.h>
++#include <sys/zfs_ctldir.h>
++#include <sys/zpl.h>
++
++/*
++ * Common open routine.  Disallow any write access.
++ */
++/* ARGSUSED */
++static int
++zpl_common_open(struct inode *ip, struct file *filp)
++{
++      if (filp->f_mode & FMODE_WRITE)
++              return (-EACCES);
++
++      return generic_file_open(ip, filp);
++}
++
++static int
++zpl_common_readdir(struct file *filp, void *dirent, filldir_t filldir)
++{
++      struct dentry *dentry = filp->f_path.dentry;
++      struct inode *ip = dentry->d_inode;
++      int error = 0;
++
++      switch (filp->f_pos) {
++      case 0:
++              error = filldir(dirent, ".", 1, 0, ip->i_ino, DT_DIR);
++              if (error)
++                      break;
++
++              filp->f_pos++;
++              /* fall-thru */
++      case 1:
++              error = filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR);
++              if (error)
++                      break;
++
++              filp->f_pos++;
++              /* fall-thru */
++      default:
++              break;
++      }
++
++      return (error);
++}
++
++/*
++ * Get root directory contents.
++ */
++static int
++zpl_root_readdir(struct file *filp, void *dirent, filldir_t filldir)
++{
++      struct dentry *dentry = filp->f_path.dentry;
++      struct inode *ip = dentry->d_inode;
++      zfs_sb_t *zsb = ITOZSB(ip);
++      int error = 0;
++
++      ZFS_ENTER(zsb);
++
++      switch (filp->f_pos) {
++      case 0:
++              error = filldir(dirent, ".", 1, 0, ip->i_ino, DT_DIR);
++              if (error)
++                      goto out;
++
++              filp->f_pos++;
++              /* fall-thru */
++      case 1:
++              error = filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR);
++              if (error)
++                      goto out;
++
++              filp->f_pos++;
++              /* fall-thru */
++      case 2:
++              error = filldir(dirent, ZFS_SNAPDIR_NAME,
++                  strlen(ZFS_SNAPDIR_NAME), 2, ZFSCTL_INO_SNAPDIR, DT_DIR);
++              if (error)
++                      goto out;
++
++              filp->f_pos++;
++              /* fall-thru */
++      case 3:
++              error = filldir(dirent, ZFS_SHAREDIR_NAME,
++                  strlen(ZFS_SHAREDIR_NAME), 3, ZFSCTL_INO_SHARES, DT_DIR);
++              if (error)
++                      goto out;
++
++              filp->f_pos++;
++              /* fall-thru */
++      }
++out:
++      ZFS_EXIT(zsb);
++
++      return (error);
++}
++
++/*
++ * Get root directory attributes.
++ */
++/* ARGSUSED */
++static int
++zpl_root_getattr(struct vfsmount *mnt, struct dentry *dentry,
++    struct kstat *stat)
++{
++      int error;
++
++      error = simple_getattr(mnt, dentry, stat);
++      stat->atime = CURRENT_TIME;
++
++      return (error);
++}
++
++static struct dentry *
++zpl_root_lookup(struct inode *dip, struct dentry *dentry, struct nameidata *nd)
++{
++      cred_t *cr = CRED();
++      struct inode *ip;
++      int error;
++
++      crhold(cr);
++      error = -zfsctl_root_lookup(dip, dname(dentry), &ip, 0, cr, NULL, NULL);
++      ASSERT3S(error, <=, 0);
++      crfree(cr);
++
++      if (error) {
++              if (error == -ENOENT)
++                      return d_splice_alias(NULL, dentry);
++              else
++                      return ERR_PTR(error);
++      }
++
++        return d_splice_alias(ip, dentry);
++}
++
++/*
++ * The '.zfs' control directory file and inode operations.
++ */
++const struct file_operations zpl_fops_root = {
++      .open           = zpl_common_open,
++      .llseek         = generic_file_llseek,
++      .read           = generic_read_dir,
++      .readdir        = zpl_root_readdir,
++};
++
++const struct inode_operations zpl_ops_root = {
++      .lookup         = zpl_root_lookup,
++      .getattr        = zpl_root_getattr,
++};
++
++static struct dentry *
++zpl_snapdir_lookup(struct inode *dip, struct dentry *dentry,
++    struct nameidata *nd)
++{
++      cred_t *cr = CRED();
++      struct inode *ip;
++      int error;
++
++      crhold(cr);
++      error = -zfsctl_snapdir_lookup(dip, dname(dentry), &ip,
++          0, cr, NULL, NULL);
++      ASSERT3S(error, <=, 0);
++      crfree(cr);
++
++      if (error) {
++              if (error == -ENOENT)
++                      return d_splice_alias(NULL, dentry);
++              else
++                      return ERR_PTR(error);
++      }
++
++      /*
++       * Auto mounting of snapshots is only supported for 2.6.37 and
++       * newer kernels.  Prior to this kernel the ops->follow_link()
++       * callback was used as a hack to trigger the mount.  The
++       * resulting vfsmount was then explicitly grafted in to the
++       * name space.  While it might be possible to add compatibility
++       * code to accomplish this it would require considerable care.
++       */
++#ifdef HAVE_AUTOMOUNT
++      dentry->d_op = &zpl_dops_snapdirs;
++#endif /* HAVE_AUTOMOUNT */
++
++      return d_splice_alias(ip, dentry);
++}
++
++/* ARGSUSED */
++static int
++zpl_snapdir_readdir(struct file *filp, void *dirent, filldir_t filldir)
++{
++      struct dentry *dentry = filp->f_path.dentry;
++      struct inode *dip = dentry->d_inode;
++      zfs_sb_t *zsb = ITOZSB(dip);
++      char snapname[MAXNAMELEN];
++      uint64_t id, cookie;
++      boolean_t case_conflict;
++      int error = 0;
++
++      ZFS_ENTER(zsb);
++
++      cookie = filp->f_pos;
++      switch (filp->f_pos) {
++      case 0:
++              error = filldir(dirent, ".", 1, 0, dip->i_ino, DT_DIR);
++              if (error)
++                      goto out;
++
++              filp->f_pos++;
++              /* fall-thru */
++      case 1:
++              error = filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR);
++              if (error)
++                      goto out;
++
++              filp->f_pos++;
++              /* fall-thru */
++      default:
++              while (error == 0) {
++                      error = -dmu_snapshot_list_next(zsb->z_os, MAXNAMELEN,
++                          snapname, &id, &cookie, &case_conflict);
++                      if (error)
++                              goto out;
++
++                      error = filldir(dirent, snapname, strlen(snapname),
++                          filp->f_pos, ZFSCTL_INO_SHARES - id, DT_DIR);
++                      if (error)
++                              goto out;
++
++                      filp->f_pos = cookie;
++              }
++      }
++out:
++      ZFS_EXIT(zsb);
++
++      if (error == -ENOENT)
++              return (0);
++
++      return (error);
++}
++
++int
++zpl_snapdir_rename(struct inode *sdip, struct dentry *sdentry,
++    struct inode *tdip, struct dentry *tdentry)
++{
++      cred_t *cr = CRED();
++      int error;
++
++      crhold(cr);
++      error = -zfsctl_snapdir_rename(sdip, dname(sdentry),
++          tdip, dname(tdentry), cr, 0);
++      ASSERT3S(error, <=, 0);
++      crfree(cr);
++
++      return (error);
++}
++
++static int
++zpl_snapdir_rmdir(struct inode *dip, struct dentry *dentry)
++{
++      cred_t *cr = CRED();
++      int error;
++
++      crhold(cr);
++      error = -zfsctl_snapdir_remove(dip, dname(dentry), cr, 0);
++      ASSERT3S(error, <=, 0);
++      crfree(cr);
++
++      return (error);
++}
++
++static int
++zpl_snapdir_mkdir(struct inode *dip, struct dentry *dentry, int mode)
++{
++      cred_t *cr = CRED();
++      vattr_t *vap;
++      struct inode *ip;
++      int error;
++
++      crhold(cr);
++      vap = kmem_zalloc(sizeof(vattr_t), KM_SLEEP);
++      zpl_vap_init(vap, dip, dentry, mode | S_IFDIR, cr);
++
++      error = -zfsctl_snapdir_mkdir(dip, dname(dentry), vap, &ip, cr, 0);
++      if (error == 0) {
++#ifdef HAVE_AUTOMOUNT
++              dentry->d_op = &zpl_dops_snapdirs;
++#endif /* HAVE_AUTOMOUNT */
++              d_instantiate(dentry, ip);
++      }
++
++      kmem_free(vap, sizeof(vattr_t));
++      ASSERT3S(error, <=, 0);
++      crfree(cr);
++
++      return (error);
++}
++
++#ifdef HAVE_AUTOMOUNT
++static struct vfsmount *
++zpl_snapdir_automount(struct path *path)
++{
++      struct dentry *dentry = path->dentry;
++      int error;
++
++      /*
++       * We must briefly disable automounts for this dentry because the
++       * user space mount utility will trigger another lookup on this
++       * directory.  That will result in zpl_snapdir_automount() being
++       * called repeatedly.  The DCACHE_NEED_AUTOMOUNT flag can be
++       * safely reset once the mount completes.
++       */
++      dentry->d_flags &= ~DCACHE_NEED_AUTOMOUNT;
++      error = -zfsctl_mount_snapshot(path, 0);
++      dentry->d_flags |= DCACHE_NEED_AUTOMOUNT;
++      if (error)
++              return ERR_PTR(error);
++
++      /*
++       * Rather than returning the new vfsmount for the snapshot we must
++       * return NULL to indicate a mount collision.  This is done because
++       * the user space mount calls do_add_mount() which adds the vfsmount
++       * to the name space.  If we returned the new mount here it would be
++       * added again to the vfsmount list resulting in list corruption.
++       */
++      return (NULL);
++}
++#endif /* HAVE_AUTOMOUNT */
++
++/*
++ * Get snapshot directory attributes.
++ */
++/* ARGSUSED */
++static int
++zpl_snapdir_getattr(struct vfsmount *mnt, struct dentry *dentry,
++    struct kstat *stat)
++{
++      zfs_sb_t *zsb = ITOZSB(dentry->d_inode);
++      int error;
++
++      ZFS_ENTER(zsb);
++      error = simple_getattr(mnt, dentry, stat);
++      stat->nlink = stat->size = avl_numnodes(&zsb->z_ctldir_snaps) + 2;
++      stat->ctime = stat->mtime = dmu_objset_snap_cmtime(zsb->z_os);
++      stat->atime = CURRENT_TIME;
++      ZFS_EXIT(zsb);
++
++      return (error);
++}
++
++/*
++ * The '.zfs/snapshot' directory file operations.  These mainly control
++ * generating the list of available snapshots when doing an 'ls' in the
++ * directory.  See zpl_snapdir_readdir().
++ */
++const struct file_operations zpl_fops_snapdir = {
++      .open           = zpl_common_open,
++      .llseek         = generic_file_llseek,
++      .read           = generic_read_dir,
++      .readdir        = zpl_snapdir_readdir,
++};
++
++/*
++ * The '.zfs/snapshot' directory inode operations.  These mainly control
++ * creating an inode for a snapshot directory and initializing the needed
++ * infrastructure to automount the snapshot.  See zpl_snapdir_lookup().
++ */
++const struct inode_operations zpl_ops_snapdir = {
++      .lookup         = zpl_snapdir_lookup,
++      .getattr        = zpl_snapdir_getattr,
++      .rename         = zpl_snapdir_rename,
++      .rmdir          = zpl_snapdir_rmdir,
++      .mkdir          = zpl_snapdir_mkdir,
++};
++
++#ifdef HAVE_AUTOMOUNT
++const struct dentry_operations zpl_dops_snapdirs = {
++      .d_automount    = zpl_snapdir_automount,
++};
++#endif /* HAVE_AUTOMOUNT */
++
++static struct dentry *
++zpl_shares_lookup(struct inode *dip, struct dentry *dentry,
++    struct nameidata *nd)
++{
++      cred_t *cr = CRED();
++      struct inode *ip = NULL;
++      int error;
++
++      crhold(cr);
++      error = -zfsctl_shares_lookup(dip, dname(dentry), &ip,
++          0, cr, NULL, NULL);
++      ASSERT3S(error, <=, 0);
++      crfree(cr);
++
++      if (error) {
++              if (error == -ENOENT)
++                      return d_splice_alias(NULL, dentry);
++              else
++                      return ERR_PTR(error);
++      }
++
++      return d_splice_alias(ip, dentry);
++}
++
++/* ARGSUSED */
++static int
++zpl_shares_readdir(struct file *filp, void *dirent, filldir_t filldir)
++{
++      cred_t *cr = CRED();
++      struct dentry *dentry = filp->f_path.dentry;
++      struct inode *ip = dentry->d_inode;
++      zfs_sb_t *zsb = ITOZSB(ip);
++      znode_t *dzp;
++      int error;
++
++      ZFS_ENTER(zsb);
++
++      if (zsb->z_shares_dir == 0) {
++              error = zpl_common_readdir(filp, dirent, filldir);
++              ZFS_EXIT(zsb);
++              return (error);
++      }
++
++      error = -zfs_zget(zsb, zsb->z_shares_dir, &dzp);
++      if (error) {
++              ZFS_EXIT(zsb);
++              return (error);
++      }
++
++      crhold(cr);
++      error = -zfs_readdir(ZTOI(dzp), dirent, filldir, &filp->f_pos, cr);
++      crfree(cr);
++
++      iput(ZTOI(dzp));
++      ZFS_EXIT(zsb);
++      ASSERT3S(error, <=, 0);
++
++      return (error);
++}
++
++/* ARGSUSED */
++static int
++zpl_shares_getattr(struct vfsmount *mnt, struct dentry *dentry,
++    struct kstat *stat)
++{
++      struct inode *ip = dentry->d_inode;
++      zfs_sb_t *zsb = ITOZSB(ip);
++      znode_t *dzp;
++      int error;
++
++      ZFS_ENTER(zsb);
++
++      if (zsb->z_shares_dir == 0) {
++              error = simple_getattr(mnt, dentry, stat);
++              stat->nlink = stat->size = 2;
++              stat->atime = CURRENT_TIME;
++              ZFS_EXIT(zsb);
++              return (error);
++      }
++
++      error = -zfs_zget(zsb, zsb->z_shares_dir, &dzp);
++      if (error == 0)
++              error = -zfs_getattr_fast(dentry->d_inode, stat);
++
++      iput(ZTOI(dzp));
++      ZFS_EXIT(zsb);
++      ASSERT3S(error, <=, 0);
++
++      return (error);
++}
++
++/*
++ * The '.zfs/shares' directory file operations.
++ */
++const struct file_operations zpl_fops_shares = {
++      .open           = zpl_common_open,
++      .llseek         = generic_file_llseek,
++      .read           = generic_read_dir,
++      .readdir        = zpl_shares_readdir,
++};
++
++/*
++ * The '.zfs/shares' directory inode operations.
++ */
++const struct inode_operations zpl_ops_shares = {
++      .lookup         = zpl_shares_lookup,
++      .getattr        = zpl_shares_getattr,
++};
+diff --git a/module/zfs/zpl_export.c b/module/zfs/zpl_export.c
+index 4fe9984..f82ee30 100644
+--- a/module/zfs/zpl_export.c
++++ b/module/zfs/zpl_export.c
+@@ -25,6 +25,7 @@
+ #include <sys/zfs_vnops.h>
+ #include <sys/zfs_znode.h>
++#include <sys/zfs_ctldir.h>
+ #include <sys/zpl.h>
+@@ -42,7 +43,10 @@ zpl_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len, int connectable)
+       fid->fid_len = len_bytes - offsetof(fid_t, fid_data);
+-      rc = zfs_fid(ip, fid);
++      if (zfsctl_is_node(ip))
++              rc = zfsctl_fid(ip, fid);
++      else
++              rc = zfs_fid(ip, fid);
+       len_bytes = offsetof(fid_t, fid_data) + fid->fid_len;
+       *max_len = roundup(len_bytes, sizeof (__u32)) / sizeof (__u32);
+diff --git a/module/zfs/zpl_inode.c b/module/zfs/zpl_inode.c
+index 9b55337..d9b918b 100644
+--- a/module/zfs/zpl_inode.c
++++ b/module/zfs/zpl_inode.c
+@@ -25,6 +25,7 @@
+ #include <sys/zfs_vfsops.h>
+ #include <sys/zfs_vnops.h>
++#include <sys/zfs_znode.h>
+ #include <sys/vfs.h>
+ #include <sys/zpl.h>
+@@ -51,7 +52,7 @@ zpl_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
+       return d_splice_alias(ip, dentry);
+ }
+-static void
++void
+ zpl_vap_init(vattr_t *vap, struct inode *dir, struct dentry *dentry,
+     mode_t mode, cred_t *cr)
+ {
+@@ -171,8 +172,20 @@ zpl_rmdir(struct inode * dir, struct dentry *dentry)
+ static int
+ zpl_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
+ {
++      boolean_t issnap = ITOZSB(dentry->d_inode)->z_issnap;
+       int error;
++      /*
++       * Ensure MNT_SHRINKABLE is set on snapshots to ensure they are
++       * unmounted automatically with the parent file system.  This
++       * is done on the first getattr because it's not easy to get the
++       * vfsmount structure at mount time.  This call path is explicitly
++       * marked unlikely to avoid any performance impact.  FWIW, ext4
++       * resorts to a similar trick for sysadmin convenience.
++       */
++      if (unlikely(issnap && !(mnt->mnt_flags & MNT_SHRINKABLE)))
++              mnt->mnt_flags |= MNT_SHRINKABLE;
++
+       error = -zfs_getattr_fast(dentry->d_inode, stat);
+       ASSERT3S(error, <=, 0);
+diff --git a/module/zfs/zpl_super.c b/module/zfs/zpl_super.c
+index 0e6e936..98d0a03 100644
+--- a/module/zfs/zpl_super.c
++++ b/module/zfs/zpl_super.c
+@@ -26,6 +26,7 @@
+ #include <sys/zfs_vfsops.h>
+ #include <sys/zfs_vnops.h>
+ #include <sys/zfs_znode.h>
++#include <sys/zfs_ctldir.h>
+ #include <sys/zpl.h>
+@@ -139,6 +140,20 @@ zpl_remount_fs(struct super_block *sb, int *flags, char *data)
+       return (error);
+ }
++static void
++zpl_umount_begin(struct super_block *sb)
++{
++      zfs_sb_t *zsb = sb->s_fs_info;
++      int count;
++
++      /*
++       * Best effort to unmount snapshots in .zfs/snapshot/.  Normally this
++       * isn't required because snapshots have the MNT_SHRINKABLE flag set.
++       */
++      if (zsb->z_ctldir)
++              (void) zfsctl_unmount_snapshots(zsb, MNT_FORCE, &count);
++}
++
+ /*
+  * The Linux VFS automatically handles the following flags:
+  * MNT_NOSUID, MNT_NODEV, MNT_NOEXEC, MNT_NOATIME, MNT_READONLY
+@@ -199,13 +214,7 @@ zpl_get_sb(struct file_system_type *fs_type, int flags,
+ static void
+ zpl_kill_sb(struct super_block *sb)
+ {
+-#ifdef HAVE_SNAPSHOT
+-      zfs_sb_t *zsb = sb->s_fs_info;
+-
+-      if (zsb && dmu_objset_is_snapshot(zsb->z_os))
+-              zfs_snap_destroy(zsb);
+-#endif /* HAVE_SNAPSHOT */
+-
++      zfs_preumount(sb);
+       kill_anon_super(sb);
+ }
+@@ -306,6 +315,7 @@ const struct super_operations zpl_super_operations = {
+       .sync_fs                = zpl_sync_fs,
+       .statfs                 = zpl_statfs,
+       .remount_fs             = zpl_remount_fs,
++      .umount_begin           = zpl_umount_begin,
+       .show_options           = zpl_show_options,
+       .show_stats             = NULL,
+ #ifdef HAVE_NR_CACHED_OBJECTS
+diff --git a/scripts/Makefile.in b/scripts/Makefile.in
+index 5c81ae6..c6a49f6 100644
+--- a/scripts/Makefile.in
++++ b/scripts/Makefile.in
+@@ -40,6 +40,7 @@ DIST_COMMON = $(dist_pkglibexec_SCRIPTS) $(srcdir)/Makefile.am \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/scripts/zpios-profile/Makefile.in b/scripts/zpios-profile/Makefile.in
+index 3ee1ec3..684530d 100644
+--- a/scripts/zpios-profile/Makefile.in
++++ b/scripts/zpios-profile/Makefile.in
+@@ -40,6 +40,7 @@ DIST_COMMON = $(dist_pkglibexec_SCRIPTS) $(srcdir)/Makefile.am \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/scripts/zpios-test/Makefile.in b/scripts/zpios-test/Makefile.in
+index ee88174..c9cb7c2 100644
+--- a/scripts/zpios-test/Makefile.in
++++ b/scripts/zpios-test/Makefile.in
+@@ -40,6 +40,7 @@ DIST_COMMON = $(dist_pkglibexec_SCRIPTS) $(srcdir)/Makefile.am \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/scripts/zpool-config/Makefile.in b/scripts/zpool-config/Makefile.in
+index eed1573..db4b29c 100644
+--- a/scripts/zpool-config/Makefile.in
++++ b/scripts/zpool-config/Makefile.in
+@@ -40,6 +40,7 @@ DIST_COMMON = $(dist_pkglibexec_SCRIPTS) $(srcdir)/Makefile.am \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/scripts/zpool-layout/Makefile.in b/scripts/zpool-layout/Makefile.in
+index 75593b8..036f48c 100644
+--- a/scripts/zpool-layout/Makefile.in
++++ b/scripts/zpool-layout/Makefile.in
+@@ -40,6 +40,7 @@ DIST_COMMON = $(dist_pkglibexec_SCRIPTS) $(srcdir)/Makefile.am \
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/udev/Makefile.in b/udev/Makefile.in
+index b4033bb..49ca53a 100644
+--- a/udev/Makefile.in
++++ b/udev/Makefile.in
+@@ -39,6 +39,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/udev/rules.d/Makefile.in b/udev/rules.d/Makefile.in
+index aba04ee..84693ad 100644
+--- a/udev/rules.d/Makefile.in
++++ b/udev/rules.d/Makefile.in
+@@ -40,6 +40,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ am__aclocal_m4_deps =  \
+       $(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
++      $(top_srcdir)/config/kernel-automount.m4 \
+       $(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
+       $(top_srcdir)/config/kernel-bdev-logical-size.m4 \
+       $(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
+diff --git a/zfs_config.h.in b/zfs_config.h.in
+index f51bd18..234e4e4 100644
+--- a/zfs_config.h.in
++++ b/zfs_config.h.in
+@@ -9,6 +9,9 @@
+ /* security_inode_init_security wants 6 args */
+ #undef HAVE_6ARGS_SECURITY_INODE_INIT_SECURITY
++/* dops->automount() exists */
++#undef HAVE_AUTOMOUNT
++
+ /* struct block_device_operations use bdevs */
+ #undef HAVE_BDEV_BLOCK_DEVICE_OPERATIONS
+-- 
+1.7.5.4
+
index 104e97a6cc5cdaeb6ce4b5cde2dd657ff9ca0f13..d708c9feaa64053bcb4c2dc299829e029e0cf6b5 100644 (file)
@@ -5,3 +5,5 @@
 0005-Remove-userland-dist-rules.patch
 0006-Improve-the-with-spl-error-for-DKMS-systems.patch
 0007-Integrate-ARC-more-tightly-with-Linux.patch
+0008-Add-.zfs-control-directory.patch
+volatile-version.patch