diff options
-rw-r--r-- | config/spl-build.m4 | 16 | ||||
-rw-r--r-- | include/linux/file_compat.h | 6 | ||||
-rw-r--r-- | module/spl/spl-vnode.c | 136 |
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); } |