]> git.proxmox.com Git - mirror_zfs.git/commitdiff
Add .zfs control directory
authorBrian Behlendorf <behlendorf1@llnl.gov>
Fri, 11 Nov 2011 07:15:53 +0000 (12:45 +0530)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Thu, 22 Mar 2012 20:03:47 +0000 (13:03 -0700)
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 .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>
Closes #173

79 files changed:
Makefile.in
cmd/Makefile.in
cmd/mount_zfs/Makefile.in
cmd/sas_switch_id/Makefile.in
cmd/zdb/Makefile.in
cmd/zfs/Makefile.in
cmd/zinject/Makefile.in
cmd/zpios/Makefile.in
cmd/zpool/Makefile.in
cmd/zpool_id/Makefile.in
cmd/zpool_layout/Makefile.in
cmd/ztest/Makefile.in
cmd/zvol_id/Makefile.in
config/kernel-automount.m4 [new file with mode: 0644]
config/kernel.m4
configure
dracut/90zfs/Makefile.in
dracut/Makefile.in
etc/Makefile.in
etc/init.d/Makefile.in
etc/zfs/Makefile.in
include/Makefile.in
include/linux/Makefile.in
include/linux/dcache_compat.h
include/sys/Makefile.am
include/sys/Makefile.in
include/sys/dmu.h
include/sys/fm/Makefile.in
include/sys/fm/fs/Makefile.in
include/sys/fs/Makefile.in
include/sys/zfs_ctldir.h [new file with mode: 0644]
include/sys/zfs_vfsops.h
include/sys/zfs_znode.h
include/sys/zpl.h
lib/Makefile.in
lib/libavl/Makefile.in
lib/libefi/Makefile.in
lib/libnvpair/Makefile.in
lib/libshare/Makefile.in
lib/libspl/Makefile.in
lib/libspl/asm-generic/Makefile.in
lib/libspl/asm-i386/Makefile.in
lib/libspl/asm-x86_64/Makefile.in
lib/libspl/include/Makefile.in
lib/libspl/include/ia32/Makefile.in
lib/libspl/include/ia32/sys/Makefile.in
lib/libspl/include/rpc/Makefile.in
lib/libspl/include/sys/Makefile.in
lib/libspl/include/sys/dktp/Makefile.in
lib/libspl/include/sys/sysevent/Makefile.in
lib/libspl/include/util/Makefile.in
lib/libunicode/Makefile.in
lib/libuutil/Makefile.in
lib/libzfs/Makefile.in
lib/libzpool/Makefile.am
lib/libzpool/Makefile.in
man/Makefile.in
man/man8/Makefile.in
module/zfs/Makefile.in
module/zfs/dmu_objset.c
module/zfs/dsl_dataset.c
module/zfs/zfs_ctldir.c [new file with mode: 0644]
module/zfs/zfs_dir.c
module/zfs/zfs_ioctl.c
module/zfs/zfs_vfsops.c
module/zfs/zfs_vnops.c
module/zfs/zfs_znode.c
module/zfs/zpl_ctldir.c [new file with mode: 0644]
module/zfs/zpl_export.c
module/zfs/zpl_inode.c
module/zfs/zpl_super.c
scripts/Makefile.in
scripts/zpios-profile/Makefile.in
scripts/zpios-test/Makefile.in
scripts/zpool-config/Makefile.in
scripts/zpool-layout/Makefile.in
udev/Makefile.in
udev/rules.d/Makefile.in
zfs_config.h.in

index bb9bc1d9966aea2edf49b31175157de02426a17c..077d318eb2554693a6ef871079b9fc504e44747b 100644 (file)
@@ -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 \
index a6ab6cca92118ad087805ff0336c06d3267a855d..76a5731a3005406f7767bcfbb4b53263d1c88720 100644 (file)
@@ -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 \
index 94d9d05b16e7d40a8a55263ad1f0341703e79a8c..ecc61d153132b8f244e3df2ec3d96f5f4bcba9b8 100644 (file)
@@ -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 \
index ba0535f5347e2af50b0b1a19d0657d66330c9e5f..375c65ec1a39e3024453ff894b32dab76e07c80a 100644 (file)
@@ -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 \
index 0029e5dfeb4904484be9f847ed537b88e0e2337e..ae4e2c9128a46d915f84e1bd5160c0edc52960e2 100644 (file)
@@ -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 \
index e0ba1e55d9c6cd0a902c2aaaa2921cf4f059b60c..4452a967fda7c87da104838b71df8246370c2f4e 100644 (file)
@@ -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 \
index 14f1b2a8a08ad81216113d734a0319a436db0b94..5e200d1dbfaccf7c2cb3a68475fabc9036307227 100644 (file)
@@ -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 \
index b74b113df81efbec1b8c71c7dba4f6c507ece648..c7507a17d4a7c7cdcf5af6a9b831e3900951227b 100644 (file)
@@ -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 \
index bb17ef5b48c2b20bd262f6288a1b9fc0b087ce5c..5775a4f29edf72e308a9cfbe44bd8d5b105a3853 100644 (file)
@@ -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 \
index 4b6f04c1e7c497128638b24be4ed8cfcdff6547f..3d51566cab75ffdab461586cf5dbb2cd359feeab 100644 (file)
@@ -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 \
index 95e802cce6018593b373e1d932489b99a78dcf62..077f1d25ee6dc315dc5f99a2e69520e2bfeea066 100644 (file)
@@ -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 \
index d1c8029a20c18ce17bb01c9906a37026b0462ad8..405e496eeea9ce1bccb90965a5f8f27cdbfaff62 100644 (file)
@@ -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 \
index 5d96d2b0782728f9cf9661c609dc83cbb04bd5b5..6f6688f7aeb620dae4153f92f0dbd2e0c81156af 100644 (file)
@@ -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 (file)
index 0000000..972b09b
--- /dev/null
@@ -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)
+       ])
+])
index 0adceadacb1fc5f0fde3f766ba3fc18df9e104ec..a6d544bb743821836a25c8545d36b410debc30bc 100644 (file)
@@ -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
index 14c1c4d5f4100db5626d77733e983e3b7f4b5a70..da28b4724e16782fb07b2f8fa785691d56861701 100755 (executable)
--- a/configure
+++ b/configure
 
 
 
+       { $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:]]' \
 
 
 
+       { $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:]]' \
index 470f204002185fc4db88965e38c7cc87ecf36566..e34f65ac9f7f7492265f07a9bbe0a56a50def36c 100644 (file)
@@ -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 \
index d65ef24ed6e53929057019bad44a5ba3d56abd71..b8cc6ae6171800902c08d838f4f572949c798545 100644 (file)
@@ -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 \
index c8c8821f72b4d231dd659be967078269823016c9..1187d0a2c840fa9ec8e3c2491cf2c93a7d44758b 100644 (file)
@@ -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 \
index 9ff3e2252a11553ed2f533d4da3415a84e645465..8eb130fa2137720bd5e3650710bb742a72ba2e06 100644 (file)
@@ -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 \
index 0382b0c7a1c81f64210a468629dd30975fb6f15b..73025a193d7f4aadb04e68ed2444af576a6995fb 100644 (file)
@@ -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 \
index ad01d2bd668064439d54840780ed507259c99d26..e1817127ad35d00f4a80edaac2756195a1f0e3a2 100644 (file)
@@ -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 \
index 5dfa3de4b9b939d4e51ddd38c92f54ec40649f0c..3d47867750b03ecc7a8b48b35f07ba01e2166b41 100644 (file)
@@ -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 \
index a624d4dd993e142569a6d9f31efe3cc0bedbed89..5f7d5e97f1543519a5088ee0159208afdeff9de9 100644 (file)
@@ -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 */
index 083e1217b50421c5a96bb099bc587736a5a26cbb..651e68b0c0ff76f4a1732345f541291e7270e749 100644 (file)
@@ -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 \
index a2afe1422ecda00bc9adaec1414a9a546b77551f..a1795768977e0c1fd491e55f94ec72936f8ddb74 100644 (file)
@@ -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 \
index da1aa30fc73ff7b9a98bed452e5a6620ff1ea7a2..3f9c711b95e2cf9c036b47214fe86ab448c310bc 100644 (file)
@@ -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,
index 9fe283acfef715e59c880fb07c3f820d8171ed9e..5ae85a3a035304a4785c7509d99748b2ad1d32b6 100644 (file)
@@ -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 \
index bc2db281fdc5490c7012701768c14247d12b9281..5976245d71645e1b08a889a0c59ae1ce5cf30e45 100644 (file)
@@ -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 \
index 00ed240c68f92cf73bdab8870bb95d72c111d6e1..61f3125f4b048ac6ab2e7c607c230991fc80a56c 100644 (file)
@@ -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 (file)
index 0000000..e598529
--- /dev/null
@@ -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 */
index 7b70f32ea5947a8f6d36c9b15696db31d61d0a9d..33ecf72fbb5b4df6f3fb156a691bfc26fc3e2154 100644 (file)
@@ -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);
index 6903ad4cc42039e0fa3a91bc43c58f8ce6b87853..8af5798ba8f18479407b40833c3a1ce435c2ea52 100644 (file)
@@ -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;
 
index cfc86794e78a818b65611f299e672cbedb7abdd3..aa4f41f9780d08316ae8c657dc16d012b60c43c9 100644 (file)
@@ -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 */
index 8d57296c6fb388533b0e81b6f2a7d7575ec362dd..f40801f661c29e77aecb0c04427057ed61b0123f 100644 (file)
@@ -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 \
index 8c2be9a28218f6b7c94ab2a068a9edb1bb62177e..5e31cc51aaf48eb08498f07354eb0d95d22bd0e1 100644 (file)
@@ -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 \
index 4f954417b16790e8b77f2eb0015c867a97c065ec..a2f425530da1bef609fc74993bbdd6d45f4e8e97 100644 (file)
@@ -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 \
index c736a500afa0e72d922f8d93a82a344ce9db6cd3..7732784837a288928cea014fba9f057962d246d7 100644 (file)
@@ -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 \
index 65133e1bfb0026ce5043dcfc4a88c1e673080b41..e1c1a2ce25020e87a0baf120bdd7498f5d0f3071 100644 (file)
@@ -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 \
index 15c6720747637ed8261188ca44be5d1701fc2e9f..2f94b5db616e0d0e4fc3db23da3d53eb7f264746 100644 (file)
@@ -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 \
index 6ad997114b5f0d3a11b53502baece5b767ad056a..66ec77b006b41b5d89e1a47bb1871ef1537e2061 100644 (file)
@@ -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 \
index e68762c879236a73b4f465dad6687774a3988b9d..4c9f37fd38b368eb250f7f1fb935b731e3414b6d 100644 (file)
@@ -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 \
index 6b7f4becad5edf25e2c3eeb516f507ccb3a61dc6..845e2ab90f85b0244a58ffcf3a1d0152284f8dd4 100644 (file)
@@ -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 \
index 480b998be342d369db92343611d65eae9186a653..d9f694485d0975de54327bdcf7e993f5bb57948b 100644 (file)
@@ -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 \
index 634415864f910b26ef83ea09f823ea3ec9c589a8..abb9a7b24c08d2c965f2777d879f18683d4ce4ba 100644 (file)
@@ -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 \
index c00dc4dce8e2b9e116da8d241ec36c6c6a3a21e3..a7e3f3cd97487d941840142d3bb283912b153992 100644 (file)
@@ -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 \
index 3513cb88e73c3c81cf11bca7680b00e4f60bf7c5..6c3174da9ba2b592c689d672b16a696e79fec50a 100644 (file)
@@ -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 \
index c02b3ea75cca2bfc0a5be96bea85978bb611c25d..b226c8d4b750ac898849f9bbee1cca2808b8f0b4 100644 (file)
@@ -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 \
index cadff828ce4b6a11cf3f9654a948a18be1d6cda5..aa63471b767f8927ece9f22ac0312d28c3be25a2 100644 (file)
@@ -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 \
index 3e5f21946bd5d523a6310a76c98097bf9f25e548..29861385d88bef807ef8a940f6cbbf868ac33d95 100644 (file)
@@ -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 \
index 3d49a92df0069ef793c2fc4cbbee5d3310a6ead2..bb71a90d644827fd32c415099adaffb5aeb035d5 100644 (file)
@@ -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 \
index d05c40a35091d6826e9835ab3276931b2eae6f24..0b99b01a96de2dac904324c076a8d8276b20ba29 100644 (file)
@@ -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 \
index 4aca16091a24a58917b4b461da07282692cc7057..5e87e01144e4dce4fc01caafb5a06f77a06840aa 100644 (file)
@@ -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 \
index 720cf07292ac2b30f8adc33d30773a6c39ce0877..5fc0ed98ed4913f9c5395f39fc50dc6ec407f41d 100644 (file)
@@ -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 \
index d445d340d786fcbc32acdedf6978011fea4499e9..cbe3acd34c60bafcc0e0f572b7ed206de3978295 100644 (file)
@@ -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 \
index 67130c315cdb91384e8efb37328f5b7dc01b13a6..d357fdd9e752a59380642b0fe491e8a4402c0097 100644 (file)
@@ -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 \
index 5a065b740007cf5ec913460827e6f31f512e6ece..fb691d05bfe638fc9f17a07cfdc62b164f77e278 100644 (file)
@@ -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 \
index 5b4ca6154d05121fd4e28212e122df4c04f90e79..c8f015d1092d1cb2c67b3742cfc94bf9c75103ea 100644 (file)
@@ -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 \
index b303168c8a66e58b84895296b3aaae1210f8eaa2..5ec75a03af9aa738266521d7c9f0d5d9e00c7551 100644 (file)
@@ -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
index 0703a946687f399f455c0d48c702c929e93b6820..1d0b4619fd0f20e612bf529cf8160bcbeae2825c 100644 (file)
@@ -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)
index 718c3ad52b27c6a68d46898fa4914358a991acbc..2deec8cf185415accf3f2213b23165862e45f41e 100644 (file)
@@ -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 (file)
index 0000000..01bf52f
--- /dev/null
@@ -0,0 +1,984 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL 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, uint64_t id,
+    const struct file_operations *fops, const struct inode_operations *ops)
+{
+       struct timespec now = current_fs_time(zsb->z_sb);
+       struct inode *ip;
+       znode_t *zp;
+
+       ip = new_inode(zsb->z_sb);
+       if (ip == NULL)
+               return (NULL);
+
+       zp = ITOZ(ip);
+       ASSERT3P(zp->z_dirlocks, ==, NULL);
+       ASSERT3P(zp->z_acl_cached, ==, NULL);
+       ASSERT3P(zp->z_xattr_cached, ==, NULL);
+       zp->z_id = id;
+       zp->z_unlinked = 0;
+       zp->z_atime_dirty = 0;
+       zp->z_zn_prefetch = 0;
+       zp->z_moved = 0;
+       zp->z_sa_hdl = NULL;
+       zp->z_blksz = 0;
+       zp->z_seq = 0;
+       zp->z_mapcnt = 0;
+       zp->z_gen = 0;
+       zp->z_size = 0;
+       zp->z_atime[0] = 0;
+       zp->z_atime[1] = 0;
+       zp->z_links = 0;
+       zp->z_pflags = 0;
+       zp->z_uid = 0;
+       zp->z_gid = 0;
+       zp->z_mode = 0;
+       zp->z_sync_cnt = 0;
+       zp->z_is_zvol = B_FALSE;
+       zp->z_is_mapped = B_FALSE;
+       zp->z_is_ctldir = B_TRUE;
+       zp->z_is_sa = B_FALSE;
+       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_atime = now;
+       ip->i_mtime = now;
+       ip->i_ctime = now;
+       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");
index 8f1a0c2cc8f1f229829be5c7509854afd250bf62..6cd9c8508a1ec0e1f29c4a8215c65c6fb2736de4 100644 (file)
@@ -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;
 
index 532f17aa16afa781c7f1f42ec15a688a0b0aa9b6..d2ad1af71c6ebfa50931c7398c30e69751eaa74e 100644 (file)
@@ -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);
 }
 
 /*
index fb319a547a93d9b305bb39df77979d5c5ed66e69..8f1c713c0534b3c76a55a736097a34146890a236 100644 (file)
@@ -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,9 +1190,6 @@ 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 */
        }
 
        /* Allocate a root inode for the filesystem. */
@@ -1202,6 +1206,9 @@ zfs_domount(struct super_block *sb, void *data, int silent)
                error = ENOMEM;
                goto out;
        }
+
+       if (!zsb->z_issnap)
+               zfsctl_create(zsb);
 out:
        if (error) {
                dmu_objset_disown(zsb->z_os, zsb);
@@ -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();
 }
index 74b96b8d747ff88f575f9817f544b4c68e914fae..2da5fec8672c454759bc05dd94497888f77a3be8 100644 (file)
@@ -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) {
index 709ae74f8f5cfd54a50322349c5b263bbcf7056d..3a6872f3e45e0288a890662db7a62fc11733955d 100644 (file)
@@ -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--;
@@ -353,6 +357,8 @@ zfs_znode_alloc(zfs_sb_t *zsb, dmu_buf_t *db, int blksz,
 
        zp = ITOZ(ip);
        ASSERT(zp->z_dirlocks == NULL);
+       ASSERT3P(zp->z_acl_cached, ==, NULL);
+       ASSERT3P(zp->z_xattr_cached, ==, NULL);
        zp->z_moved = 0;
        zp->z_sa_hdl = NULL;
        zp->z_unlinked = 0;
@@ -362,7 +368,9 @@ zfs_znode_alloc(zfs_sb_t *zsb, dmu_buf_t *db, int blksz,
        zp->z_blksz = blksz;
        zp->z_seq = 0x7A4653;
        zp->z_sync_cnt = 0;
-       zp->z_is_zvol = 0;
+       zp->z_is_zvol = B_FALSE;
+       zp->z_is_mapped = B_FALSE;
+       zp->z_is_ctldir = B_FALSE;
 
        zfs_znode_sa_init(zsb, zp, db, obj_type, hdl);
 
@@ -434,6 +442,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 (file)
index 0000000..6c742c9
--- /dev/null
@@ -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,
+};
index 4fe998437f6aadfaa339e52f406f3c26ab1619fe..f82ee30887fc3a627482b13af0029a9645711410 100644 (file)
@@ -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);
index 9b5533755985845b5d6cbf3f7c5204ee2965111a..d9b918b43d92d09bc6c60d2e57661be31672d67d 100644 (file)
@@ -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);
 
index 0e6e9360ff45d20ce8330bfd5f8dca62638cc404..98d0a03127de36cf14df75cf22489a13423ee7c7 100644 (file)
@@ -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
index 5c81ae66d80d1be513df71c14e692f1b28b48eab..c6a49f6c1e955a6ea2b56689b66d61525ca57da5 100644 (file)
@@ -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 \
index 3ee1ec310e651241e0c61861e47b9b78d7959dd3..684530d427c9e9a6309e5e3fae24d3a35ca13221 100644 (file)
@@ -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 \
index ee881748ee5ca498a3e36e8fba5de6e89f1406f7..c9cb7c2ee230899349053496d8f3f5990e86f71b 100644 (file)
@@ -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 \
index eed157329560375fa5eee092d9f502b8ce35ce3b..db4b29c3bf75436bf6e6392232548ff8a56e5f6b 100644 (file)
@@ -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 \
index 75593b81d08228739f8f0b4992a1d58d41280686..036f48c3bcc5bfda6e8953ba12b0d5d4d94e28fd 100644 (file)
@@ -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 \
index b4033bbaba0ee9a1eaab1e3c40f420f72c5118f5..49ca53aa3ab23c0dca833d07b66324bd6e78f6de 100644 (file)
@@ -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 \
index aba04ee465fe761b99ea5783f83347562ce6e531..84693adba84ca275d5a4183d0dfe45246ed7ff43 100644 (file)
@@ -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 \
index f51bd1868c3a1b62ad9784743b648e36991c013e..234e4e44eeea61955e323a4375c3951533693eaf 100644 (file)
@@ -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