summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config/spl-build.m416
-rw-r--r--include/linux/file_compat.h6
-rw-r--r--module/spl/spl-vnode.c136
3 files changed, 158 insertions, 0 deletions
diff --git a/config/spl-build.m4 b/config/spl-build.m4
index 0c7a03cf1..eb644a188 100644
--- a/config/spl-build.m4
+++ b/config/spl-build.m4
@@ -86,6 +86,7 @@ AC_DEFUN([SPL_AC_CONFIG_KERNEL], [
SPL_AC_SHRINK_ICACHE_MEMORY
SPL_AC_KERN_PATH_PARENT_HEADER
SPL_AC_KERN_PATH_PARENT_SYMBOL
+ SPL_AC_KERN_PATH_LOCKED
SPL_AC_2ARGS_ZLIB_DEFLATE_WORKSPACESIZE
SPL_AC_SHRINK_CONTROL_STRUCT
SPL_AC_RWSEM_SPINLOCK_IS_RAW
@@ -2188,6 +2189,21 @@ AC_DEFUN([SPL_AC_KERN_PATH_PARENT_SYMBOL],
])
dnl #
+dnl # 3.6 API compat,
+dnl # The kern_path_parent() function was replaced by the kern_path_locked()
+dnl # function to eliminate all struct nameidata usage outside fs/namei.c.
+dnl #
+AC_DEFUN([SPL_AC_KERN_PATH_LOCKED], [
+ SPL_CHECK_SYMBOL_HEADER(
+ [kern_path_locked],
+ [struct dentry \*kern_path_locked(const char \*, struct path \*)],
+ [include/linux/namei.h],
+ [AC_DEFINE(HAVE_KERN_PATH_LOCKED, 1,
+ [kern_path_locked() is available])],
+ [])
+])
+
+dnl #
dnl # 2.6.39 API compat,
dnl # The function zlib_deflate_workspacesize() now take 2 arguments.
dnl # This was done to avoid always having to allocate the maximum size
diff --git a/include/linux/file_compat.h b/include/linux/file_compat.h
index 2b5b7d225..27819d5e5 100644
--- a/include/linux/file_compat.h
+++ b/include/linux/file_compat.h
@@ -83,6 +83,12 @@ extern kern_path_parent_t kern_path_parent_fn;
# define spl_kern_path_parent(path, nd) path_lookup(path, LOOKUP_PARENT, nd)
#endif /* HAVE_KERN_PATH_PARENT_HEADER */
+#ifdef HAVE_KERN_PATH_LOCKED
+typedef struct dentry * (*kern_path_locked_t)(const char *, struct path *);
+extern kern_path_locked_t kern_path_locked_fn;
+# define spl_kern_path_locked(name, path) kern_path_locked_fn(name, path)
+#endif /* HAVE_KERN_PATH_LOCKED */
+
#ifndef HAVE_CLEAR_CLOSE_ON_EXEC
#define __clear_close_on_exec(fd, fdt) FD_CLR(fd, fdt->close_on_exec)
#endif
diff --git a/module/spl/spl-vnode.c b/module/spl/spl-vnode.c
index f5fc65d26..a0fed3201 100644
--- a/module/spl/spl-vnode.c
+++ b/module/spl/spl-vnode.c
@@ -50,6 +50,10 @@ EXPORT_SYMBOL(kern_path_parent_fn);
#endif /* HAVE_KERN_PATH_PARENT_SYMBOL */
#endif /* HAVE_KERN_PATH_PARENT_HEADER */
+#ifdef HAVE_KERN_PATH_LOCKED
+kern_path_locked_t kern_path_locked_fn = SYMBOL_POISON;
+#endif /* HAVE_KERN_PATH_LOCKED */
+
vtype_t
vn_mode_to_vtype(mode_t mode)
{
@@ -298,6 +302,128 @@ vn_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, void *ct)
}
EXPORT_SYMBOL(vn_seek);
+#ifdef HAVE_KERN_PATH_LOCKED
+/* Based on do_unlinkat() from linux/fs/namei.c */
+int
+vn_remove(const char *path, uio_seg_t seg, int flags)
+{
+ struct dentry *dentry;
+ struct path parent;
+ struct inode *inode = NULL;
+ int rc = 0;
+ SENTRY;
+
+ ASSERT(seg == UIO_SYSSPACE);
+ ASSERT(flags == RMFILE);
+
+ dentry = spl_kern_path_locked(path, &parent);
+ rc = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ if (parent.dentry->d_name.name[parent.dentry->d_name.len])
+ SGOTO(slashes, rc = 0);
+
+ inode = dentry->d_inode;
+ if (!inode)
+ SGOTO(slashes, rc = 0);
+
+ if (inode)
+ ihold(inode);
+
+ rc = vfs_unlink(parent.dentry->d_inode, dentry);
+exit1:
+ dput(dentry);
+ }
+
+ spl_inode_unlock(parent.dentry->d_inode);
+ if (inode)
+ iput(inode); /* truncate the inode here */
+
+ path_put(&parent);
+ SRETURN(-rc);
+
+slashes:
+ rc = !dentry->d_inode ? -ENOENT :
+ S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
+ SGOTO(exit1, rc);
+} /* vn_remove() */
+EXPORT_SYMBOL(vn_remove);
+
+/* Based on do_rename() from linux/fs/namei.c */
+int
+vn_rename(const char *oldname, const char *newname, int x1)
+{
+ struct dentry *old_dir, *new_dir;
+ struct dentry *old_dentry, *new_dentry;
+ struct dentry *trap;
+ struct path old_parent, new_parent;
+ int rc = 0;
+ SENTRY;
+
+ old_dentry = spl_kern_path_locked(oldname, &old_parent);
+ if (IS_ERR(old_dentry))
+ SGOTO(exit, rc = PTR_ERR(old_dentry));
+
+ spl_inode_unlock(old_parent.dentry->d_inode);
+
+ new_dentry = spl_kern_path_locked(newname, &new_parent);
+ if (IS_ERR(new_dentry))
+ SGOTO(exit2, rc = PTR_ERR(new_dentry));
+
+ spl_inode_unlock(new_parent.dentry->d_inode);
+
+ rc = -EXDEV;
+ if (old_parent.mnt != new_parent.mnt)
+ SGOTO(exit3, rc);
+
+ old_dir = old_parent.dentry;
+ new_dir = new_parent.dentry;
+ trap = lock_rename(new_dir, old_dir);
+
+ /* source should not be ancestor of target */
+ rc = -EINVAL;
+ if (old_dentry == trap)
+ SGOTO(exit4, rc);
+
+ /* target should not be an ancestor of source */
+ rc = -ENOTEMPTY;
+ if (new_dentry == trap)
+ SGOTO(exit4, rc);
+
+ /* source must exist */
+ rc = -ENOENT;
+ if (!old_dentry->d_inode)
+ SGOTO(exit4, rc);
+
+ /* unless the source is a directory trailing slashes give -ENOTDIR */
+ if (!S_ISDIR(old_dentry->d_inode->i_mode)) {
+ rc = -ENOTDIR;
+ if (old_dentry->d_name.name[old_dentry->d_name.len])
+ SGOTO(exit4, rc);
+ if (new_dentry->d_name.name[new_dentry->d_name.len])
+ SGOTO(exit4, rc);
+ }
+
+#ifdef HAVE_4ARGS_VFS_RENAME
+ rc = vfs_rename(old_dir->d_inode, old_dentry,
+ new_dir->d_inode, new_dentry);
+#else
+ rc = vfs_rename(old_dir->d_inode, old_dentry, oldnd.nd_mnt,
+ new_dir->d_inode, new_dentry, newnd.nd_mnt);
+#endif /* HAVE_4ARGS_VFS_RENAME */
+exit4:
+ unlock_rename(new_dir, old_dir);
+exit3:
+ dput(new_dentry);
+ path_put(&new_parent);
+exit2:
+ dput(old_dentry);
+ path_put(&old_parent);
+exit:
+ SRETURN(-rc);
+}
+EXPORT_SYMBOL(vn_rename);
+
+#else
static struct dentry *
vn_lookup_hash(struct nameidata *nd)
{
@@ -458,6 +584,7 @@ exit:
SRETURN(-rc);
}
EXPORT_SYMBOL(vn_rename);
+#endif /* HAVE_KERN_PATH_LOCKED */
int
vn_getattr(vnode_t *vp, vattr_t *vap, int flags, void *x3, void *x4)
@@ -862,6 +989,15 @@ int spl_vn_init_kallsyms_lookup(void)
#endif /* HAVE_KERN_PATH_PARENT_SYMBOL */
#endif /* HAVE_KERN_PATH_PARENT_HEADER */
+#ifdef HAVE_KERN_PATH_LOCKED
+ kern_path_locked_fn = (kern_path_locked_t)
+ spl_kallsyms_lookup_name("kern_path_locked");
+ if (!kern_path_locked_fn) {
+ printk(KERN_ERR "Error: Unknown symbol kern_path_locked\n");
+ return -EFAULT;
+ }
+#endif
+
return (0);
}