summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config/kernel-dentry-operations.m464
-rw-r--r--config/kernel.m43
-rw-r--r--include/linux/dcache_compat.h25
-rw-r--r--module/zfs/zpl_ctldir.c124
4 files changed, 158 insertions, 58 deletions
diff --git a/config/kernel-dentry-operations.m4 b/config/kernel-dentry-operations.m4
new file mode 100644
index 000000000..5685b7d6f
--- /dev/null
+++ b/config/kernel-dentry-operations.m4
@@ -0,0 +1,64 @@
+dnl #
+dnl # 3.6 API change
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_D_REVALIDATE_NAMEIDATA], [
+ AC_MSG_CHECKING([whether dops->d_revalidate() takes struct nameidata])
+ ZFS_LINUX_TRY_COMPILE([
+ #include <linux/dcache.h>
+ ],[
+ int (*revalidate) (struct dentry *, struct nameidata *) = NULL;
+ struct dentry_operations dops __attribute__ ((unused)) = {
+ .d_revalidate = revalidate,
+ };
+ ],[
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_D_REVALIDATE_NAMEIDATA, 1,
+ [dops->d_revalidate() operation takes nameidata])
+ ],[
+ AC_MSG_RESULT(no)
+ ])
+])
+
+dnl #
+dnl # 2.6.30 API change
+dnl # The 'struct dentry_operations' was constified in the dentry structure.
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_CONST_DENTRY_OPERATIONS], [
+ AC_MSG_CHECKING([whether dentry uses const struct dentry_operations])
+ ZFS_LINUX_TRY_COMPILE([
+ #include <linux/dcache.h>
+
+ const struct dentry_operations test_d_op = {
+ .d_revalidate = NULL,
+ };
+ ],[
+ struct dentry d __attribute__ ((unused));
+
+ d.d_op = &test_d_op;
+ ],[
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_CONST_DENTRY_OPERATIONS, 1,
+ [dentry uses const struct dentry_operations])
+ ],[
+ AC_MSG_RESULT(no)
+ ])
+])
+
+dnl #
+dnl # 2.6.38 API change
+dnl # Added d_set_d_op() helper function.
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_D_SET_D_OP],
+ [AC_MSG_CHECKING([whether d_set_d_op() is available])
+ ZFS_LINUX_TRY_COMPILE_SYMBOL([
+ #include <linux/dcache.h>
+ ], [
+ d_set_d_op(NULL, NULL);
+ ], [d_set_d_op], [fs/dcache.c], [
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_D_SET_D_OP, 1,
+ [d_set_d_op() is available])
+ ], [
+ AC_MSG_RESULT(no)
+ ])
+])
diff --git a/config/kernel.m4 b/config/kernel.m4
index aab3a167b..58a808011 100644
--- a/config/kernel.m4
+++ b/config/kernel.m4
@@ -62,6 +62,9 @@ AC_DEFUN([ZFS_AC_CONFIG_KERNEL], [
ZFS_AC_KERNEL_INSERT_INODE_LOCKED
ZFS_AC_KERNEL_D_MAKE_ROOT
ZFS_AC_KERNEL_D_OBTAIN_ALIAS
+ ZFS_AC_KERNEL_D_SET_D_OP
+ ZFS_AC_KERNEL_D_REVALIDATE_NAMEIDATA
+ ZFS_AC_KERNEL_CONST_DENTRY_OPERATIONS
ZFS_AC_KERNEL_CHECK_DISK_SIZE_CHANGE
ZFS_AC_KERNEL_TRUNCATE_SETSIZE
ZFS_AC_KERNEL_6ARGS_SECURITY_INODE_INIT_SECURITY
diff --git a/include/linux/dcache_compat.h b/include/linux/dcache_compat.h
index a42356276..271a0cbef 100644
--- a/include/linux/dcache_compat.h
+++ b/include/linux/dcache_compat.h
@@ -35,4 +35,29 @@
#define d_make_root(inode) d_alloc_root(inode)
#endif /* HAVE_D_MAKE_ROOT */
+/*
+ * 2.6.30 API change,
+ * The const keyword was added to the 'struct dentry_operations' in
+ * the dentry structure. To handle this we define an appropriate
+ * dentry_operations_t typedef which can be used.
+ */
+#ifdef HAVE_CONST_DENTRY_OPERATIONS
+typedef const struct dentry_operations dentry_operations_t;
+#else
+typedef struct dentry_operations dentry_operations_t;
+#endif
+
+/*
+ * 2.6.38 API change,
+ * Added d_set_d_op() helper function which sets some flags in
+ * dentry->d_flags based on which operations are defined.
+ */
+#ifndef HAVE_D_SET_D_OP
+static inline void
+d_set_d_op(struct dentry *dentry, dentry_operations_t *op)
+{
+ dentry->d_op = op;
+}
+#endif /* HAVE_D_SET_D_OP */
+
#endif /* _ZFS_DCACHE_H */
diff --git a/module/zfs/zpl_ctldir.c b/module/zfs/zpl_ctldir.c
index 7dfaf6ebc..2e5209f8c 100644
--- a/module/zfs/zpl_ctldir.c
+++ b/module/zfs/zpl_ctldir.c
@@ -183,6 +183,66 @@ const struct inode_operations zpl_ops_root = {
.getattr = zpl_root_getattr,
};
+#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 */
+
+/*
+ * Revalidate any dentry in the snapshot directory on lookup, since a snapshot
+ * having the same name have been created or destroyed since it was cached.
+ */
+static int
+#ifdef HAVE_D_REVALIDATE_NAMEIDATA
+zpl_snapdir_revalidate(struct dentry *dentry, struct nameidata *i)
+#else
+zpl_snapdir_revalidate(struct dentry *dentry, unsigned int flags)
+#endif
+{
+ return 0;
+}
+
+dentry_operations_t zpl_dops_snapdirs = {
+/*
+ * 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
+ .d_automount = zpl_snapdir_automount,
+#endif /* HAVE_AUTOMOUNT */
+ .d_revalidate = zpl_snapdir_revalidate,
+};
+
static struct dentry *
#ifdef HAVE_LOOKUP_NAMEIDATA
zpl_snapdir_lookup(struct inode *dip, struct dentry *dentry,
@@ -194,7 +254,7 @@ zpl_snapdir_lookup(struct inode *dip, struct dentry *dentry,
{
cred_t *cr = CRED();
- struct inode *ip;
+ struct inode *ip = NULL;
int error;
crhold(cr);
@@ -203,24 +263,11 @@ zpl_snapdir_lookup(struct inode *dip, struct dentry *dentry,
ASSERT3S(error, <=, 0);
crfree(cr);
- if (error) {
- if (error == -ENOENT)
- return d_splice_alias(NULL, dentry);
- else
- return ERR_PTR(error);
- }
+ if (error && error != -ENOENT)
+ 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 */
+ ASSERT(error == 0 || ip == NULL);
+ d_set_d_op(dentry, &zpl_dops_snapdirs);
return d_splice_alias(ip, dentry);
}
@@ -323,9 +370,7 @@ zpl_snapdir_mkdir(struct inode *dip, struct dentry *dentry, zpl_umode_t mode)
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_set_d_op(dentry, &zpl_dops_snapdirs);
d_instantiate(dentry, ip);
}
@@ -336,37 +381,6 @@ zpl_snapdir_mkdir(struct inode *dip, struct dentry *dentry, zpl_umode_t mode)
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.
*/
@@ -413,12 +427,6 @@ const struct inode_operations zpl_ops_snapdir = {
.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 *
#ifdef HAVE_LOOKUP_NAMEIDATA
zpl_shares_lookup(struct inode *dip, struct dentry *dentry,