Skip to content
Browse files

Add .zfs control directory

Add support for the .zfs control directory.  This was accomplished
by leveraging as much of the existing ZFS infrastructure as posible
and updating it for Linux as required.  The bulk of the core
functionality is now all there with the following limitations.

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

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

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

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

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

*) Accessing the .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
  • Loading branch information...
1 parent 49be0cc commit ebe7e575eae1e03b1faa545a424f008faeac589d @behlendorf committed
Showing with 2,037 additions and 101 deletions.
  1. +1 −0 Makefile.in
  2. +1 −0 cmd/Makefile.in
  3. +1 −0 cmd/mount_zfs/Makefile.in
  4. +1 −0 cmd/sas_switch_id/Makefile.in
  5. +1 −0 cmd/zdb/Makefile.in
  6. +1 −0 cmd/zfs/Makefile.in
  7. +1 −0 cmd/zinject/Makefile.in
  8. +1 −0 cmd/zpios/Makefile.in
  9. +1 −0 cmd/zpool/Makefile.in
  10. +1 −0 cmd/zpool_id/Makefile.in
  11. +1 −0 cmd/zpool_layout/Makefile.in
  12. +1 −0 cmd/ztest/Makefile.in
  13. +1 −0 cmd/zvol_id/Makefile.in
  14. +23 −0 config/kernel-automount.m4
  15. +1 −0 config/kernel.m4
  16. +136 −0 configure
  17. +1 −0 dracut/90zfs/Makefile.in
  18. +1 −0 dracut/Makefile.in
  19. +1 −0 etc/Makefile.in
  20. +1 −0 etc/init.d/Makefile.in
  21. +1 −0 etc/zfs/Makefile.in
  22. +1 −0 include/Makefile.in
  23. +1 −0 include/linux/Makefile.in
  24. +1 −0 include/linux/dcache_compat.h
  25. +1 −0 include/sys/Makefile.am
  26. +4 −0 include/sys/Makefile.in
  27. +1 −0 include/sys/dmu.h
  28. +1 −0 include/sys/fm/Makefile.in
  29. +1 −0 include/sys/fm/fs/Makefile.in
  30. +1 −0 include/sys/fs/Makefile.in
  31. +113 −0 include/sys/zfs_ctldir.h
  32. +3 −18 include/sys/zfs_vfsops.h
  33. +1 −0 include/sys/zfs_znode.h
  34. +18 −0 include/sys/zpl.h
  35. +1 −0 lib/Makefile.in
  36. +1 −0 lib/libavl/Makefile.in
  37. +1 −0 lib/libefi/Makefile.in
  38. +1 −0 lib/libnvpair/Makefile.in
  39. +1 −0 lib/libshare/Makefile.in
  40. +1 −0 lib/libspl/Makefile.in
  41. +1 −0 lib/libspl/asm-generic/Makefile.in
  42. +1 −0 lib/libspl/asm-i386/Makefile.in
  43. +1 −0 lib/libspl/asm-x86_64/Makefile.in
  44. +1 −0 lib/libspl/include/Makefile.in
  45. +1 −0 lib/libspl/include/ia32/Makefile.in
  46. +1 −0 lib/libspl/include/ia32/sys/Makefile.in
  47. +1 −0 lib/libspl/include/rpc/Makefile.in
  48. +1 −0 lib/libspl/include/sys/Makefile.in
  49. +1 −0 lib/libspl/include/sys/dktp/Makefile.in
  50. +1 −0 lib/libspl/include/sys/sysevent/Makefile.in
  51. +1 −0 lib/libspl/include/util/Makefile.in
  52. +1 −0 lib/libunicode/Makefile.in
  53. +1 −0 lib/libuutil/Makefile.in
  54. +1 −0 lib/libzfs/Makefile.in
  55. +2 −0 lib/libzpool/Makefile.am
  56. +3 −0 lib/libzpool/Makefile.in
  57. +1 −0 man/Makefile.in
  58. +1 −0 man/man8/Makefile.in
  59. +2 −0 module/zfs/Makefile.in
  60. +35 −0 module/zfs/dmu_objset.c
  61. +1 −2 module/zfs/dsl_dataset.c
  62. +984 −0 module/zfs/zfs_ctldir.c
  63. +4 −7 module/zfs/zfs_dir.c
  64. +38 −50 module/zfs/zfs_ioctl.c
  65. +37 −10 module/zfs/zfs_vfsops.c
  66. +6 −4 module/zfs/zfs_vnops.c
  67. +13 −1 module/zfs/zfs_znode.c
  68. +519 −0 module/zfs/zpl_ctldir.c
  69. +5 −1 module/zfs/zpl_export.c
  70. +14 −1 module/zfs/zpl_inode.c
  71. +17 −7 module/zfs/zpl_super.c
  72. +1 −0 scripts/Makefile.in
  73. +1 −0 scripts/zpios-profile/Makefile.in
  74. +1 −0 scripts/zpios-test/Makefile.in
  75. +1 −0 scripts/zpool-config/Makefile.in
  76. +1 −0 scripts/zpool-layout/Makefile.in
  77. +1 −0 udev/Makefile.in
  78. +1 −0 udev/rules.d/Makefile.in
  79. +3 −0 zfs_config.h.in
View
1 Makefile.in
@@ -62,6 +62,7 @@ subdir = .
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 cmd/Makefile.in
@@ -39,6 +39,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 cmd/mount_zfs/Makefile.in
@@ -42,6 +42,7 @@ subdir = cmd/mount_zfs
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 cmd/sas_switch_id/Makefile.in
@@ -41,6 +41,7 @@ DIST_COMMON = $(dist_udev_SCRIPTS) $(srcdir)/Makefile.am \
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 cmd/zdb/Makefile.in
@@ -42,6 +42,7 @@ subdir = cmd/zdb
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 cmd/zfs/Makefile.in
@@ -42,6 +42,7 @@ subdir = cmd/zfs
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 cmd/zinject/Makefile.in
@@ -42,6 +42,7 @@ subdir = cmd/zinject
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 cmd/zpios/Makefile.in
@@ -42,6 +42,7 @@ subdir = cmd/zpios
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 cmd/zpool/Makefile.in
@@ -42,6 +42,7 @@ subdir = cmd/zpool
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 cmd/zpool_id/Makefile.in
@@ -41,6 +41,7 @@ DIST_COMMON = $(dist_udev_SCRIPTS) $(srcdir)/Makefile.am \
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 cmd/zpool_layout/Makefile.in
@@ -41,6 +41,7 @@ DIST_COMMON = $(dist_bin_SCRIPTS) $(srcdir)/Makefile.am \
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 cmd/ztest/Makefile.in
@@ -42,6 +42,7 @@ subdir = cmd/ztest
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 cmd/zvol_id/Makefile.in
@@ -42,6 +42,7 @@ subdir = cmd/zvol_id
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
23 config/kernel-automount.m4
@@ -0,0 +1,23 @@
+dnl #
+dnl # 2.6.37 API change
+dnl # The dops->d_automount() dentry operation was added as a clean
+dnl # solution to handling automounts. Prior to this cifs/nfs clients
+dnl # which required automount support would abuse the follow_link()
+dnl # operation on directories for this purpose.
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_AUTOMOUNT], [
+ AC_MSG_CHECKING([whether dops->d_automount() exists])
+ ZFS_LINUX_TRY_COMPILE([
+ #include <linux/dcache.h>
+ ],[
+ struct vfsmount *(*d_automount) (struct path *) = NULL;
+ struct dentry_operations dops __attribute__ ((unused)) = {
+ .d_automount = d_automount,
+ };
+ ],[
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_AUTOMOUNT, 1, [dops->automount() exists])
+ ],[
+ AC_MSG_RESULT(no)
+ ])
+])
View
1 config/kernel.m4
@@ -45,6 +45,7 @@ AC_DEFUN([ZFS_AC_CONFIG_KERNEL], [
ZFS_AC_KERNEL_NR_CACHED_OBJECTS
ZFS_AC_KERNEL_FREE_CACHED_OBJECTS
ZFS_AC_KERNEL_FALLOCATE
+ ZFS_AC_KERNEL_AUTOMOUNT
ZFS_AC_KERNEL_INSERT_INODE_LOCKED
ZFS_AC_KERNEL_D_OBTAIN_ALIAS
ZFS_AC_KERNEL_CHECK_DISK_SIZE_CHANGE
View
136 configure
@@ -15681,6 +15681,74 @@ fi
+ { $as_echo "$as_me:$LINENO: checking whether dops->d_automount() exists" >&5
+$as_echo_n "checking whether dops->d_automount() exists... " >&6; }
+
+
+cat confdefs.h - <<_ACEOF >conftest.c
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+
+ #include <linux/dcache.h>
+
+int
+main (void)
+{
+
+ struct vfsmount *(*d_automount) (struct path *) = NULL;
+ struct dentry_operations dops __attribute__ ((unused)) = {
+ .d_automount = d_automount,
+ };
+
+ ;
+ return 0;
+}
+
+_ACEOF
+
+
+ rm -Rf build && mkdir -p build
+ echo "obj-m := conftest.o" >build/Makefile
+ if { ac_try='cp conftest.c build && make modules -C $LINUX_OBJ EXTRA_CFLAGS="-Werror-implicit-function-declaration $EXTRA_KCFLAGS" $ARCH_UM M=$PWD/build'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } >/dev/null && { ac_try='test -s build/conftest.o'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_AUTOMOUNT 1
+_ACEOF
+
+
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+
+
+
+fi
+
+ rm -Rf build
+
+
+
+
{ $as_echo "$as_me:$LINENO: checking whether symbol insert_inode_locked is exported" >&5
$as_echo_n "checking whether symbol insert_inode_locked is exported... " >&6; }
grep -q -E '[[:space:]]insert_inode_locked[[:space:]]' \
@@ -21568,6 +21636,74 @@ fi
+ { $as_echo "$as_me:$LINENO: checking whether dops->d_automount() exists" >&5
+$as_echo_n "checking whether dops->d_automount() exists... " >&6; }
+
+
+cat confdefs.h - <<_ACEOF >conftest.c
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+
+ #include <linux/dcache.h>
+
+int
+main (void)
+{
+
+ struct vfsmount *(*d_automount) (struct path *) = NULL;
+ struct dentry_operations dops __attribute__ ((unused)) = {
+ .d_automount = d_automount,
+ };
+
+ ;
+ return 0;
+}
+
+_ACEOF
+
+
+ rm -Rf build && mkdir -p build
+ echo "obj-m := conftest.o" >build/Makefile
+ if { ac_try='cp conftest.c build && make modules -C $LINUX_OBJ EXTRA_CFLAGS="-Werror-implicit-function-declaration $EXTRA_KCFLAGS" $ARCH_UM M=$PWD/build'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } >/dev/null && { ac_try='test -s build/conftest.o'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_AUTOMOUNT 1
+_ACEOF
+
+
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+
+
+
+fi
+
+ rm -Rf build
+
+
+
+
{ $as_echo "$as_me:$LINENO: checking whether symbol insert_inode_locked is exported" >&5
$as_echo_n "checking whether symbol insert_inode_locked is exported... " >&6; }
grep -q -E '[[:space:]]insert_inode_locked[[:space:]]' \
View
1 dracut/90zfs/Makefile.in
@@ -40,6 +40,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 dracut/Makefile.in
@@ -39,6 +39,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 etc/Makefile.in
@@ -39,6 +39,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 etc/init.d/Makefile.in
@@ -40,6 +40,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 etc/zfs/Makefile.in
@@ -40,6 +40,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 include/Makefile.in
@@ -41,6 +41,7 @@ DIST_COMMON = $(am__kernel_HEADERS_DIST) $(am__libzfs_HEADERS_DIST) \
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 include/linux/Makefile.in
@@ -41,6 +41,7 @@ DIST_COMMON = $(am__kernel_HEADERS_DIST) $(libzfs_HEADERS) \
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 include/linux/dcache_compat.h
@@ -29,5 +29,6 @@
#include <linux/dcache.h>
#define dname(dentry) ((char *)((dentry)->d_name.name))
+#define dlen(dentry) ((int)((dentry)->d_name.len))
#endif /* _ZFS_DCACHE_H */
View
1 include/sys/Makefile.am
@@ -55,6 +55,7 @@ COMMON_H = \
$(top_srcdir)/include/sys/zap_leaf.h \
$(top_srcdir)/include/sys/zfs_acl.h \
$(top_srcdir)/include/sys/zfs_context.h \
+ $(top_srcdir)/include/sys/zfs_ctldir.h \
$(top_srcdir)/include/sys/zfs_debug.h \
$(top_srcdir)/include/sys/zfs_dir.h \
$(top_srcdir)/include/sys/zfs_fuid.h \
View
4 include/sys/Makefile.in
@@ -41,6 +41,7 @@ DIST_COMMON = $(am__kernel_HEADERS_DIST) $(am__libzfs_HEADERS_DIST) \
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
@@ -170,6 +171,7 @@ am__kernel_HEADERS_DIST = $(top_srcdir)/include/sys/arc.h \
$(top_srcdir)/include/sys/zap_leaf.h \
$(top_srcdir)/include/sys/zfs_acl.h \
$(top_srcdir)/include/sys/zfs_context.h \
+ $(top_srcdir)/include/sys/zfs_ctldir.h \
$(top_srcdir)/include/sys/zfs_debug.h \
$(top_srcdir)/include/sys/zfs_dir.h \
$(top_srcdir)/include/sys/zfs_fuid.h \
@@ -266,6 +268,7 @@ am__libzfs_HEADERS_DIST = $(top_srcdir)/include/sys/arc.h \
$(top_srcdir)/include/sys/zap_leaf.h \
$(top_srcdir)/include/sys/zfs_acl.h \
$(top_srcdir)/include/sys/zfs_context.h \
+ $(top_srcdir)/include/sys/zfs_ctldir.h \
$(top_srcdir)/include/sys/zfs_debug.h \
$(top_srcdir)/include/sys/zfs_dir.h \
$(top_srcdir)/include/sys/zfs_fuid.h \
@@ -550,6 +553,7 @@ COMMON_H = \
$(top_srcdir)/include/sys/zap_leaf.h \
$(top_srcdir)/include/sys/zfs_acl.h \
$(top_srcdir)/include/sys/zfs_context.h \
+ $(top_srcdir)/include/sys/zfs_ctldir.h \
$(top_srcdir)/include/sys/zfs_debug.h \
$(top_srcdir)/include/sys/zfs_dir.h \
$(top_srcdir)/include/sys/zfs_fuid.h \
View
1 include/sys/dmu.h
@@ -643,6 +643,7 @@ extern uint64_t dmu_objset_syncprop(objset_t *os);
extern uint64_t dmu_objset_logbias(objset_t *os);
extern int dmu_snapshot_list_next(objset_t *os, int namelen, char *name,
uint64_t *id, uint64_t *offp, boolean_t *case_conflict);
+extern int dmu_snapshot_id(objset_t *os, const char *snapname, uint64_t *idp);
extern int dmu_snapshot_realname(objset_t *os, char *name, char *real,
int maxlen, boolean_t *conflict);
extern int dmu_dir_list_next(objset_t *os, int namelen, char *name,
View
1 include/sys/fm/Makefile.in
@@ -41,6 +41,7 @@ DIST_COMMON = $(am__kernel_HEADERS_DIST) $(am__libzfs_HEADERS_DIST) \
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 include/sys/fm/fs/Makefile.in
@@ -41,6 +41,7 @@ DIST_COMMON = $(am__kernel_HEADERS_DIST) $(am__libzfs_HEADERS_DIST) \
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 include/sys/fs/Makefile.in
@@ -41,6 +41,7 @@ DIST_COMMON = $(am__kernel_HEADERS_DIST) $(am__libzfs_HEADERS_DIST) \
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
113 include/sys/zfs_ctldir.h
@@ -0,0 +1,113 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (C) 2011 Lawrence Livermore National Security, LLC.
+ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
+ * LLNL-CODE-403049.
+ * Rewritten for Linux by:
+ * Rohan Puri <rohan.puri15@gmail.com>
+ * Brian Behlendorf <behlendorf1@llnl.gov>
+ */
+
+#ifndef _ZFS_CTLDIR_H
+#define _ZFS_CTLDIR_H
+
+#include <sys/vnode.h>
+#include <sys/zfs_vfsops.h>
+#include <sys/zfs_znode.h>
+
+#define ZFS_CTLDIR_NAME ".zfs"
+#define ZFS_SNAPDIR_NAME "snapshot"
+#define ZFS_SHAREDIR_NAME "shares"
+
+#define zfs_has_ctldir(zdp) \
+ ((zdp)->z_id == ZTOZSB(zdp)->z_root && \
+ (ZTOZSB(zdp)->z_ctldir != NULL))
+#define zfs_show_ctldir(zdp) \
+ (zfs_has_ctldir(zdp) && \
+ (ZTOZSB(zdp)->z_show_ctldir))
+
+typedef struct {
+ char *se_name;
+ char *se_path;
+ struct inode *se_inode;
+ struct delayed_work se_work;
+ avl_node_t se_node;
+} zfs_snapentry_t;
+
+/* zfsctl generic functions */
+extern int snapentry_compare(const void *a, const void *b);
+extern boolean_t zfsctl_is_node(struct inode *ip);
+extern boolean_t zfsctl_is_snapdir(struct inode *ip);
+extern void zfsctl_inode_inactive(struct inode *ip);
+extern void zfsctl_inode_destroy(struct inode *ip);
+extern int zfsctl_create(zfs_sb_t *zsb);
+extern void zfsctl_destroy(zfs_sb_t *zsb);
+extern struct inode *zfsctl_root(znode_t *zp);
+extern int zfsctl_fid(struct inode *ip, fid_t *fidp);
+
+/* zfsctl '.zfs' functions */
+extern int zfsctl_root_lookup(struct inode *dip, char *name,
+ struct inode **ipp, int flags, cred_t *cr, int *direntflags,
+ pathname_t *realpnp);
+
+/* zfsctl '.zfs/snapshot' functions */
+extern int zfsctl_snapdir_lookup(struct inode *dip, char *name,
+ struct inode **ipp, int flags, cred_t *cr, int *direntflags,
+ pathname_t *realpnp);
+extern int zfsctl_snapdir_rename(struct inode *sdip, char *sname,
+ struct inode *tdip, char *tname, cred_t *cr, int flags);
+extern int zfsctl_snapdir_remove(struct inode *dip, char *name, cred_t *cr,
+ int flags);
+extern int zfsctl_snapdir_mkdir(struct inode *dip, char *dirname, vattr_t *vap,
+ struct inode **ipp, cred_t *cr, int flags);
+extern void zfsctl_snapdir_inactive(struct inode *ip);
+extern int zfsctl_unmount_snapshot(zfs_sb_t *zsb, char *name, int flags);
+extern int zfsctl_unmount_snapshots(zfs_sb_t *zsb, int flags, int *count);
+extern int zfsctl_mount_snapshot(struct path *path, int flags);
+extern int zfsctl_lookup_objset(struct super_block *sb, uint64_t objsetid,
+ zfs_sb_t **zsb);
+
+/* zfsctl '.zfs/shares' functions */
+extern int zfsctl_shares_lookup(struct inode *dip, char *name,
+ struct inode **ipp, int flags, cred_t *cr, int *direntflags,
+ pathname_t *realpnp);
+
+/* zfsctl_init/fini functions */
+extern void zfsctl_init(void);
+extern void zfsctl_fini(void);
+
+/*
+ * These inodes numbers are reserved for the .zfs control directory.
+ * It is important that they be no larger that 48-bits because only
+ * 6 bytes are reserved in the NFS file handle for the object number.
+ * However, they should be as large as possible to avoid conflicts
+ * with the objects which are assigned monotonically by the dmu.
+ */
+#define ZFSCTL_INO_ROOT 0x0000FFFFFFFFFFFF
+#define ZFSCTL_INO_SHARES 0x0000FFFFFFFFFFFE
+#define ZFSCTL_INO_SNAPDIR 0x0000FFFFFFFFFFFD
+#define ZFSCTL_INO_SNAPDIRS 0x0000FFFFFFFFFFFC
+
+#define ZFSCTL_EXPIRE_SNAPSHOT 300
+
+#endif /* _ZFS_CTLDIR_H */
View
21 include/sys/zfs_vfsops.h
@@ -71,6 +71,8 @@ typedef struct zfs_sb {
uint64_t z_nr_znodes; /* number of znodes in the fs */
kmutex_t z_znodes_lock; /* lock for z_all_znodes */
struct inode *z_ctldir; /* .zfs directory inode */
+ avl_tree_t z_ctldir_snaps; /* .zfs/snapshot entries */
+ kmutex_t z_ctldir_lock; /* .zfs ctldir lock */
boolean_t z_show_ctldir; /* expose .zfs in the root dir */
boolean_t z_issnap; /* true if this is a snapshot */
boolean_t z_vscan; /* virus scan on/off */
@@ -93,24 +95,6 @@ typedef struct zfs_sb {
#define ZSB_XATTR 0x0001 /* Enable user xattrs */
-
-/*
- * Minimal snapshot helpers, the bulk of the Linux snapshot implementation
- * lives in the zpl_snap.c file which is part of the zpl source.
- */
-#define ZFS_CTLDIR_NAME ".zfs"
-
-#define zfs_has_ctldir(zdp) \
- ((zdp)->z_id == ZTOZSB(zdp)->z_root && \
- (ZTOZSB(zdp)->z_ctldir != NULL))
-#define zfs_show_ctldir(zdp) \
- (zfs_has_ctldir(zdp) && \
- (ZTOZSB(zdp)->z_show_ctldir))
-
-#define ZFSCTL_INO_ROOT 0x1
-#define ZFSCTL_INO_SNAPDIR 0x2
-#define ZFSCTL_INO_SHARES 0x3
-
/*
* Allow a maximum number of links. While ZFS does not internally limit
* this the inode->i_nlink member is defined as an unsigned int. To be
@@ -195,6 +179,7 @@ extern boolean_t zfs_is_readonly(zfs_sb_t *zsb);
extern int zfs_register_callbacks(zfs_sb_t *zsb);
extern void zfs_unregister_callbacks(zfs_sb_t *zsb);
extern int zfs_domount(struct super_block *sb, void *data, int silent);
+extern void zfs_preumount(struct super_block *sb);
extern int zfs_umount(struct super_block *sb);
extern int zfs_remount(struct super_block *sb, int *flags, char *data);
extern int zfs_root(zfs_sb_t *zsb, struct inode **ipp);
View
1 include/sys/zfs_znode.h
@@ -214,6 +214,7 @@ typedef struct znode {
boolean_t z_is_sa; /* are we native sa? */
boolean_t z_is_zvol; /* are we used by the zvol */
boolean_t z_is_mapped; /* are we mmap'ed */
+ boolean_t z_is_ctldir; /* are we .zfs entry */
struct inode z_inode; /* generic vfs inode */
} znode_t;
View
18 include/sys/zpl.h
@@ -33,6 +33,9 @@
#include <linux/falloc.h>
/* zpl_inode.c */
+extern void zpl_vap_init(vattr_t *vap, struct inode *dir,
+ struct dentry *dentry, mode_t mode, cred_t *cr);
+
extern const struct inode_operations zpl_inode_operations;
extern const struct inode_operations zpl_dir_inode_operations;
extern const struct inode_operations zpl_symlink_inode_operations;
@@ -69,4 +72,19 @@ extern int zpl_xattr_security_init(struct inode *ip, struct inode *dip,
extern xattr_handler_t *zpl_xattr_handlers[];
+/* zpl_ctldir.c */
+extern const struct file_operations zpl_fops_root;
+extern const struct inode_operations zpl_ops_root;
+
+extern const struct file_operations zpl_fops_snapdir;
+extern const struct inode_operations zpl_ops_snapdir;
+#ifdef HAVE_AUTOMOUNT
+extern const struct dentry_operations zpl_dops_snapdirs;
+#else
+extern const struct inode_operations zpl_ops_snapdirs;
+#endif /* HAVE_AUTOMOUNT */
+
+extern const struct file_operations zpl_fops_shares;
+extern const struct inode_operations zpl_ops_shares;
+
#endif /* _SYS_ZPL_H */
View
1 lib/Makefile.in
@@ -39,6 +39,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 lib/libavl/Makefile.in
@@ -41,6 +41,7 @@ subdir = lib/libavl
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 lib/libefi/Makefile.in
@@ -41,6 +41,7 @@ subdir = lib/libefi
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 lib/libnvpair/Makefile.in
@@ -41,6 +41,7 @@ subdir = lib/libnvpair
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 lib/libshare/Makefile.in
@@ -41,6 +41,7 @@ subdir = lib/libshare
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 lib/libspl/Makefile.in
@@ -41,6 +41,7 @@ subdir = lib/libspl
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 lib/libspl/asm-generic/Makefile.in
@@ -40,6 +40,7 @@ subdir = lib/libspl/asm-generic
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 lib/libspl/asm-i386/Makefile.in
@@ -41,6 +41,7 @@ DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 lib/libspl/asm-x86_64/Makefile.in
@@ -41,6 +41,7 @@ DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 lib/libspl/include/Makefile.in
@@ -41,6 +41,7 @@ DIST_COMMON = $(libspl_HEADERS) $(srcdir)/Makefile.am \
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 lib/libspl/include/ia32/Makefile.in
@@ -39,6 +39,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 lib/libspl/include/ia32/sys/Makefile.in
@@ -41,6 +41,7 @@ DIST_COMMON = $(libspl_HEADERS) $(srcdir)/Makefile.am \
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 lib/libspl/include/rpc/Makefile.in
@@ -41,6 +41,7 @@ DIST_COMMON = $(libspl_HEADERS) $(srcdir)/Makefile.am \
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 lib/libspl/include/sys/Makefile.in
@@ -41,6 +41,7 @@ DIST_COMMON = $(libspl_HEADERS) $(srcdir)/Makefile.am \
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 lib/libspl/include/sys/dktp/Makefile.in
@@ -41,6 +41,7 @@ DIST_COMMON = $(libspl_HEADERS) $(srcdir)/Makefile.am \
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 lib/libspl/include/sys/sysevent/Makefile.in
@@ -41,6 +41,7 @@ DIST_COMMON = $(libspl_HEADERS) $(srcdir)/Makefile.am \
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 lib/libspl/include/util/Makefile.in
@@ -41,6 +41,7 @@ DIST_COMMON = $(libspl_HEADERS) $(srcdir)/Makefile.am \
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 lib/libunicode/Makefile.in
@@ -41,6 +41,7 @@ subdir = lib/libunicode
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 lib/libuutil/Makefile.in
@@ -41,6 +41,7 @@ subdir = lib/libuutil
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 lib/libzfs/Makefile.in
@@ -41,6 +41,7 @@ subdir = lib/libzfs
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
2 lib/libzpool/Makefile.am
@@ -97,6 +97,7 @@ libzpool_la_LDFLAGS = -pthread -version-info 1:1:0
EXTRA_DIST = \
$(top_srcdir)/module/zfs/vdev_disk.c \
$(top_srcdir)/module/zfs/zfs_acl.c \
+ $(top_srcdir)/module/zfs/zfs_ctldir.c \
$(top_srcdir)/module/zfs/zfs_dir.c \
$(top_srcdir)/module/zfs/zfs_ioctl.c \
$(top_srcdir)/module/zfs/zfs_log.c \
@@ -105,6 +106,7 @@ EXTRA_DIST = \
$(top_srcdir)/module/zfs/zfs_rlock.c \
$(top_srcdir)/module/zfs/zfs_vfsops.c \
$(top_srcdir)/module/zfs/zfs_vnops.c \
+ $(top_srcdir)/module/zfs/zpl_ctldir.c \
$(top_srcdir)/module/zfs/zpl_export.c \
$(top_srcdir)/module/zfs/zpl_file.c \
$(top_srcdir)/module/zfs/zpl_inode.c \
View
3 lib/libzpool/Makefile.in
@@ -41,6 +41,7 @@ subdir = lib/libzpool
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
@@ -455,6 +456,7 @@ libzpool_la_LDFLAGS = -pthread -version-info 1:1:0
EXTRA_DIST = \
$(top_srcdir)/module/zfs/vdev_disk.c \
$(top_srcdir)/module/zfs/zfs_acl.c \
+ $(top_srcdir)/module/zfs/zfs_ctldir.c \
$(top_srcdir)/module/zfs/zfs_dir.c \
$(top_srcdir)/module/zfs/zfs_ioctl.c \
$(top_srcdir)/module/zfs/zfs_log.c \
@@ -463,6 +465,7 @@ EXTRA_DIST = \
$(top_srcdir)/module/zfs/zfs_rlock.c \
$(top_srcdir)/module/zfs/zfs_vfsops.c \
$(top_srcdir)/module/zfs/zfs_vnops.c \
+ $(top_srcdir)/module/zfs/zpl_ctldir.c \
$(top_srcdir)/module/zfs/zpl_export.c \
$(top_srcdir)/module/zfs/zpl_file.c \
$(top_srcdir)/module/zfs/zpl_inode.c \
View
1 man/Makefile.in
@@ -39,6 +39,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
1 man/man8/Makefile.in
@@ -39,6 +39,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = \
$(top_srcdir)/config/always-no-unused-but-set-variable.m4 \
+ $(top_srcdir)/config/kernel-automount.m4 \
$(top_srcdir)/config/kernel-bdev-block-device-operations.m4 \
$(top_srcdir)/config/kernel-bdev-logical-size.m4 \
$(top_srcdir)/config/kernel-bdi-setup-and-register.m4 \
View
2 module/zfs/Makefile.in
@@ -64,6 +64,7 @@ $(MODULE)-objs += @top_srcdir@/module/zfs/zap_leaf.o
$(MODULE)-objs += @top_srcdir@/module/zfs/zap_micro.o
$(MODULE)-objs += @top_srcdir@/module/zfs/zfs_acl.o
$(MODULE)-objs += @top_srcdir@/module/zfs/zfs_byteswap.o
+$(MODULE)-objs += @top_srcdir@/module/zfs/zfs_ctldir.o
$(MODULE)-objs += @top_srcdir@/module/zfs/zfs_debug.o
$(MODULE)-objs += @top_srcdir@/module/zfs/zfs_dir.o
$(MODULE)-objs += @top_srcdir@/module/zfs/zfs_fm.o
@@ -83,6 +84,7 @@ $(MODULE)-objs += @top_srcdir@/module/zfs/zio_checksum.o
$(MODULE)-objs += @top_srcdir@/module/zfs/zio_compress.o
$(MODULE)-objs += @top_srcdir@/module/zfs/zio_inject.o
$(MODULE)-objs += @top_srcdir@/module/zfs/zle.o
+$(MODULE)-objs += @top_srcdir@/module/zfs/zpl_ctldir.o
$(MODULE)-objs += @top_srcdir@/module/zfs/zpl_export.o
$(MODULE)-objs += @top_srcdir@/module/zfs/zpl_file.o
$(MODULE)-objs += @top_srcdir@/module/zfs/zpl_inode.o
View
35 module/zfs/dmu_objset.c
@@ -1584,6 +1584,41 @@ dmu_snapshot_list_next(objset_t *os, int namelen, char *name,
return (0);
}
+/*
+ * Determine the objset id for a given snapshot name.
+ */
+int
+dmu_snapshot_id(objset_t *os, const char *snapname, uint64_t *idp)
+{
+ dsl_dataset_t *ds = os->os_dsl_dataset;
+ zap_cursor_t cursor;
+ zap_attribute_t attr;
+ int error;
+
+ if (ds->ds_phys->ds_snapnames_zapobj == 0)
+ return (ENOENT);
+
+ zap_cursor_init(&cursor, ds->ds_dir->dd_pool->dp_meta_objset,
+ ds->ds_phys->ds_snapnames_zapobj);
+
+ error = zap_cursor_move_to_key(&cursor, snapname, MT_EXACT);
+ if (error) {
+ zap_cursor_fini(&cursor);
+ return (error);
+ }
+
+ error = zap_cursor_retrieve(&cursor, &attr);
+ if (error) {
+ zap_cursor_fini(&cursor);
+ return (error);
+ }
+
+ *idp = attr.za_first_integer;
+ zap_cursor_fini(&cursor);
+
+ return (0);
+}
+
int
dmu_dir_list_next(objset_t *os, int namelen, char *name,
uint64_t *idp, uint64_t *offp)
View
3 module/zfs/dsl_dataset.c
@@ -2373,8 +2373,7 @@ dsl_snapshot_rename_one(const char *name, void *arg)
return (err == ENOENT ? 0 : err);
}
-/* XXX: Ignore for SPL version until mounting the FS is supported */
-#if defined(_KERNEL) && !defined(HAVE_SPL)
+#ifdef _KERNEL
/*
* For all filesystems undergoing rename, we'll need to unmount it.
*/
View
984 module/zfs/zfs_ctldir.c
@@ -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");
View
11 module/zfs/zfs_dir.c
@@ -50,6 +50,7 @@
#include <sys/zap.h>
#include <sys/dmu.h>
#include <sys/atomic.h>
+#include <sys/zfs_ctldir.h>
#include <sys/zfs_fuid.h>
#include <sys/sa.h>
#include <sys/zfs_sa.h>
@@ -415,28 +416,24 @@ zfs_dirlook(znode_t *dzp, char *name, struct inode **ipp, int flags,
/*
* If we are a snapshot mounted under .zfs, return
- * the vp for the snapshot directory.
+ * the inode pointer for the snapshot directory.
*/
if ((error = sa_lookup(dzp->z_sa_hdl,
SA_ZPL_PARENT(zsb), &parent, sizeof (parent))) != 0)
return (error);
-#ifdef HAVE_SNAPSHOT
+
if (parent == dzp->z_id && zsb->z_parent != zsb) {
error = zfsctl_root_lookup(zsb->z_parent->z_ctldir,
- "snapshot", ipp, NULL, 0, NULL, kcred,