aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Behlendorf <[email protected]>2016-01-14 13:25:10 -0500
committerBrian Behlendorf <[email protected]>2016-01-20 11:36:00 -0800
commitbeeed4596b192f879fbb13e656cc6458ccde1193 (patch)
tree83fbf5e22dc10bccbd9034e9b16c787cbe4e23b8
parentbc89ac8479c0b265c539b579fda2bef1997d423c (diff)
Linux 4.5 compat: get_link() / put_link()
The follow_link() interface was retired in favor of get_link(). In the process of phasing in get_link() the Linux kernel went through two different versions. The first of which depended on put_link() and the final version on a delayed done function. - Improved configure checks for .follow_link, .get_link, .put_link. - Interfaces checked from newest to oldest. - Strict checking for each possible known interface. - Configure fails when no known interface is available. - Both versions .get_link are detected and supported as well two previous versions of .follow_link. Signed-off-by: Brian Behlendorf <[email protected]> Signed-off-by: Tim Chase <[email protected]> Signed-off-by: Chunwei Chen <[email protected]> Issue #4228
-rw-r--r--config/kernel-follow-link-nameidata.m424
-rw-r--r--config/kernel-get-link.m4100
-rw-r--r--config/kernel-put-link-nameidata.m423
-rw-r--r--config/kernel-put-link.m460
-rw-r--r--config/kernel.m42
-rw-r--r--module/zfs/zpl_inode.c125
6 files changed, 253 insertions, 81 deletions
diff --git a/config/kernel-follow-link-nameidata.m4 b/config/kernel-follow-link-nameidata.m4
deleted file mode 100644
index 88c85accb..000000000
--- a/config/kernel-follow-link-nameidata.m4
+++ /dev/null
@@ -1,24 +0,0 @@
-dnl #
-dnl # 4.2 API change
-dnl # This kernel retired the nameidata structure which forced the
-dnl # restructuring of the follow_link() prototype and how it is called.
-dnl # We check for the new interface rather than detecting the old one.
-dnl #
-AC_DEFUN([ZFS_AC_KERNEL_FOLLOW_LINK], [
- AC_MSG_CHECKING([whether iops->follow_link() passes nameidata])
- ZFS_LINUX_TRY_COMPILE([
- #include <linux/fs.h>
- const char *follow_link(struct dentry *de, void **cookie)
- { return "symlink"; }
- static struct inode_operations iops __attribute__ ((unused)) = {
- .follow_link = follow_link,
- };
- ],[
- ],[
- AC_MSG_RESULT(no)
- ],[
- AC_MSG_RESULT(yes)
- AC_DEFINE(HAVE_FOLLOW_LINK_NAMEIDATA, 1,
- [iops->follow_link() nameidata])
- ])
-])
diff --git a/config/kernel-get-link.m4 b/config/kernel-get-link.m4
new file mode 100644
index 000000000..022c49c54
--- /dev/null
+++ b/config/kernel-get-link.m4
@@ -0,0 +1,100 @@
+dnl #
+dnl # Supported get_link() interfaces checked newest to oldest.
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_FOLLOW_LINK], [
+ dnl #
+ dnl # 4.2 API change
+ dnl # - This kernel retired the nameidata structure.
+ dnl #
+ AC_MSG_CHECKING([whether iops->follow_link() passes cookie])
+ ZFS_LINUX_TRY_COMPILE([
+ #include <linux/fs.h>
+ const char *follow_link(struct dentry *de,
+ void **cookie) { return "symlink"; }
+ static struct inode_operations
+ iops __attribute__ ((unused)) = {
+ .follow_link = follow_link,
+ };
+ ],[
+ ],[
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_FOLLOW_LINK_COOKIE, 1,
+ [iops->follow_link() cookie])
+ ],[
+ dnl #
+ dnl # 2.6.32 API
+ dnl #
+ AC_MSG_RESULT(no)
+ AC_MSG_CHECKING(
+ [whether iops->follow_link() passes nameidata])
+ ZFS_LINUX_TRY_COMPILE([
+ #include <linux/fs.h>
+ void *follow_link(struct dentry *de, struct
+ nameidata *nd) { return (void *)NULL; }
+ static struct inode_operations
+ iops __attribute__ ((unused)) = {
+ .follow_link = follow_link,
+ };
+ ],[
+ ],[
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_FOLLOW_LINK_NAMEIDATA, 1,
+ [iops->follow_link() nameidata])
+ ],[
+ AC_MSG_ERROR(no; please file a bug report)
+ ])
+ ])
+])
+
+AC_DEFUN([ZFS_AC_KERNEL_GET_LINK], [
+ dnl #
+ dnl # 4.5 API change
+ dnl # The get_link interface has added a delayed done call and
+ dnl # used it to retire the put_link() interface.
+ dnl #
+ AC_MSG_CHECKING([whether iops->get_link() passes delayed])
+ ZFS_LINUX_TRY_COMPILE([
+ #include <linux/fs.h>
+ const char *get_link(struct dentry *de, struct inode *ip,
+ struct delayed_call *done) { return "symlink"; }
+ static struct inode_operations
+ iops __attribute__ ((unused)) = {
+ .get_link = get_link,
+ };
+ ],[
+ ],[
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_GET_LINK_DELAYED, 1,
+ [iops->get_link() delayed])
+ ],[
+ dnl #
+ dnl # 4.5 API change
+ dnl # The follow_link() interface has been replaced by
+ dnl # get_link() which behaves the same as before except:
+ dnl # - An inode is passed as a separate argument
+ dnl # - When called in RCU mode a NULL dentry is passed.
+ dnl #
+ AC_MSG_RESULT(no)
+ AC_MSG_CHECKING([whether iops->get_link() passes cookie])
+ ZFS_LINUX_TRY_COMPILE([
+ #include <linux/fs.h>
+ const char *get_link(struct dentry *de, struct
+ inode *ip, void **cookie) { return "symlink"; }
+ static struct inode_operations
+ iops __attribute__ ((unused)) = {
+ .get_link = get_link,
+ };
+ ],[
+ ],[
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_GET_LINK_COOKIE, 1,
+ [iops->get_link() cookie])
+ ],[
+ dnl #
+ dnl # Check for the follow_link APIs.
+ dnl #
+ AC_MSG_RESULT(no)
+ ZFS_AC_KERNEL_FOLLOW_LINK
+ ])
+ ])
+])
diff --git a/config/kernel-put-link-nameidata.m4 b/config/kernel-put-link-nameidata.m4
deleted file mode 100644
index 0181ae515..000000000
--- a/config/kernel-put-link-nameidata.m4
+++ /dev/null
@@ -1,23 +0,0 @@
-dnl #
-dnl # 4.2 API change
-dnl # This kernel retired the nameidata structure which forced the
-dnl # restructuring of the put_link() prototype and how it is called.
-dnl # We check for the new interface rather than detecting the old one.
-dnl #
-AC_DEFUN([ZFS_AC_KERNEL_PUT_LINK], [
- AC_MSG_CHECKING([whether iops->put_link() passes nameidata])
- ZFS_LINUX_TRY_COMPILE([
- #include <linux/fs.h>
- void put_link(struct inode *ip, void *cookie) { return; }
- static struct inode_operations iops __attribute__ ((unused)) = {
- .put_link = put_link,
- };
- ],[
- ],[
- AC_MSG_RESULT(no)
- ],[
- AC_MSG_RESULT(yes)
- AC_DEFINE(HAVE_PUT_LINK_NAMEIDATA, 1,
- [iops->put_link() nameidata])
- ])
-])
diff --git a/config/kernel-put-link.m4 b/config/kernel-put-link.m4
new file mode 100644
index 000000000..a0bb36ef2
--- /dev/null
+++ b/config/kernel-put-link.m4
@@ -0,0 +1,60 @@
+dnl #
+dnl # Supported symlink APIs
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_PUT_LINK], [
+ dnl #
+ dnl # 4.5 API change
+ dnl # get_link() uses delayed done, there is no put_link() interface.
+ dnl #
+ ZFS_LINUX_TRY_COMPILE([
+ #if !defined(HAVE_GET_LINK_DELAYED)
+ #error "Expecting get_link() delayed done"
+ #endif
+ ],[
+ ],[
+ AC_DEFINE(HAVE_PUT_LINK_DELAYED, 1, [iops->put_link() delayed])
+ ],[
+ dnl #
+ dnl # 4.2 API change
+ dnl # This kernel retired the nameidata structure.
+ dnl #
+ AC_MSG_CHECKING([whether iops->put_link() passes cookie])
+ ZFS_LINUX_TRY_COMPILE([
+ #include <linux/fs.h>
+ void put_link(struct inode *ip, void *cookie)
+ { return; }
+ static struct inode_operations
+ iops __attribute__ ((unused)) = {
+ .put_link = put_link,
+ };
+ ],[
+ ],[
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_PUT_LINK_COOKIE, 1,
+ [iops->put_link() cookie])
+ ],[
+ dnl #
+ dnl # 2.6.32 API
+ dnl #
+ AC_MSG_RESULT(no)
+ AC_MSG_CHECKING(
+ [whether iops->put_link() passes nameidata])
+ ZFS_LINUX_TRY_COMPILE([
+ #include <linux/fs.h>
+ void put_link(struct dentry *de, struct
+ nameidata *nd, void *ptr) { return; }
+ static struct inode_operations
+ iops __attribute__ ((unused)) = {
+ .put_link = put_link,
+ };
+ ],[
+ ],[
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_PUT_LINK_NAMEIDATA, 1,
+ [iops->put_link() nameidata])
+ ],[
+ AC_MSG_ERROR(no; please file a bug report)
+ ])
+ ])
+ ])
+])
diff --git a/config/kernel.m4 b/config/kernel.m4
index 16fb017fe..f63a056e4 100644
--- a/config/kernel.m4
+++ b/config/kernel.m4
@@ -58,7 +58,7 @@ AC_DEFUN([ZFS_AC_CONFIG_KERNEL], [
ZFS_AC_KERNEL_MKDIR_UMODE_T
ZFS_AC_KERNEL_LOOKUP_NAMEIDATA
ZFS_AC_KERNEL_CREATE_NAMEIDATA
- ZFS_AC_KERNEL_FOLLOW_LINK
+ ZFS_AC_KERNEL_GET_LINK
ZFS_AC_KERNEL_PUT_LINK
ZFS_AC_KERNEL_TRUNCATE_RANGE
ZFS_AC_KERNEL_AUTOMOUNT
diff --git a/module/zfs/zpl_inode.c b/module/zfs/zpl_inode.c
index 69a5db4da..c4233959b 100644
--- a/module/zfs/zpl_inode.c
+++ b/module/zfs/zpl_inode.c
@@ -367,26 +367,42 @@ zpl_symlink(struct inode *dir, struct dentry *dentry, const char *name)
return (error);
}
-#ifdef HAVE_FOLLOW_LINK_NAMEIDATA
-static void *
-zpl_follow_link(struct dentry *dentry, struct nameidata *nd)
-#else
-const char *
-zpl_follow_link(struct dentry *dentry, void **symlink_cookie)
+#if defined(HAVE_PUT_LINK_COOKIE)
+static void
+zpl_put_link(struct inode *unused, void *cookie)
+{
+ kmem_free(cookie, MAXPATHLEN);
+}
+#elif defined(HAVE_PUT_LINK_NAMEIDATA)
+static void
+zpl_put_link(struct dentry *dentry, struct nameidata *nd, void *ptr)
+{
+ const char *link = nd_get_link(nd);
+
+ if (!IS_ERR(link))
+ kmem_free(link, MAXPATHLEN);
+}
+#elif defined(HAVE_PUT_LINK_DELAYED)
+static void
+zpl_put_link(void *ptr)
+{
+ kmem_free(ptr, MAXPATHLEN);
+}
#endif
+
+static int
+zpl_get_link_common(struct dentry *dentry, struct inode *ip, char **link)
{
+ fstrans_cookie_t cookie;
cred_t *cr = CRED();
- struct inode *ip = dentry->d_inode;
struct iovec iov;
uio_t uio;
- char *link;
int error;
- fstrans_cookie_t cookie;
crhold(cr);
-
+ *link = NULL;
iov.iov_len = MAXPATHLEN;
- iov.iov_base = link = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+ iov.iov_base = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
@@ -397,41 +413,78 @@ zpl_follow_link(struct dentry *dentry, void **symlink_cookie)
cookie = spl_fstrans_mark();
error = -zfs_readlink(ip, &uio, cr);
spl_fstrans_unmark(cookie);
-
- if (error)
- kmem_free(link, MAXPATHLEN);
-
crfree(cr);
-#ifdef HAVE_FOLLOW_LINK_NAMEIDATA
if (error)
- nd_set_link(nd, ERR_PTR(error));
+ kmem_free(iov.iov_base, MAXPATHLEN);
else
- nd_set_link(nd, link);
+ *link = iov.iov_base;
- return (NULL);
-#else
+ return (error);
+}
+
+#if defined(HAVE_GET_LINK_DELAYED)
+const char *
+zpl_get_link(struct dentry *dentry, struct inode *inode,
+ struct delayed_call *done)
+{
+ char *link = NULL;
+ int error;
+
+ if (!dentry)
+ return (ERR_PTR(-ECHILD));
+
+ error = zpl_get_link_common(dentry, inode, &link);
if (error)
return (ERR_PTR(error));
- else
- return (*symlink_cookie = link);
-#endif
+
+ set_delayed_call(done, zpl_put_link, link);
+
+ return (link);
}
+#elif defined(HAVE_GET_LINK_COOKIE)
+const char *
+zpl_get_link(struct dentry *dentry, struct inode *inode, void **cookie)
+{
+ char *link = NULL;
+ int error;
-#ifdef HAVE_PUT_LINK_NAMEIDATA
-static void
-zpl_put_link(struct dentry *dentry, struct nameidata *nd, void *ptr)
+ if (!dentry)
+ return (ERR_PTR(-ECHILD));
+
+ error = zpl_get_link_common(dentry, inode, &link);
+ if (error)
+ return (ERR_PTR(error));
+
+ return (*cookie = link);
+}
+#elif defined(HAVE_FOLLOW_LINK_COOKIE)
+const char *
+zpl_follow_link(struct dentry *dentry, void **cookie)
{
- const char *link = nd_get_link(nd);
+ char *link = NULL;
+ int error;
- if (!IS_ERR(link))
- kmem_free(link, MAXPATHLEN);
+ error = zpl_get_link_common(dentry, dentry->d_inode, &link);
+ if (error)
+ return (ERR_PTR(error));
+
+ return (*cookie = link);
}
-#else
-static void
-zpl_put_link(struct inode *unused, void *symlink_cookie)
+#elif defined(HAVE_FOLLOW_LINK_NAMEIDATA)
+static void *
+zpl_follow_link(struct dentry *dentry, struct nameidata *nd)
{
- kmem_free(symlink_cookie, MAXPATHLEN);
+ char *link = NULL;
+ int error;
+
+ error = zpl_get_link_common(dentry, dentry->d_inode, &link);
+ if (error)
+ nd_set_link(nd, ERR_PTR(error));
+ else
+ nd_set_link(nd, link);
+
+ return (NULL);
}
#endif
@@ -619,8 +672,14 @@ const struct inode_operations zpl_dir_inode_operations = {
const struct inode_operations zpl_symlink_inode_operations = {
.readlink = generic_readlink,
+#if defined(HAVE_GET_LINK_DELAYED) || defined(HAVE_GET_LINK_COOKIE)
+ .get_link = zpl_get_link,
+#elif defined(HAVE_FOLLOW_LINK_COOKIE) || defined(HAVE_FOLLOW_LINK_NAMEIDATA)
.follow_link = zpl_follow_link,
+#endif
+#if defined(HAVE_PUT_LINK_COOKIE) || defined(HAVE_PUT_LINK_NAMEIDATA)
.put_link = zpl_put_link,
+#endif
.setattr = zpl_setattr,
.getattr = zpl_getattr,
.setxattr = generic_setxattr,