diff options
author | Brian Behlendorf <[email protected]> | 2009-10-01 16:06:15 -0700 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2009-10-01 16:06:15 -0700 |
commit | 51a727e90f1dd11194737e64e85ca09d34be5d8b (patch) | |
tree | 06a0b78065a317c9e8f9536e28189b3032cfccf0 | |
parent | 0e77fc118e2c105af0df5d1c7aa6627ed601bfa1 (diff) |
Set cwd to '/' for the process executing insmod.
Ricardo has pointed out that under Solaris the cwd is set to '/'
during module load, while under Linux it is set to the callers cwd.
To handle this cleanly I've reworked the module *_init()/_exit()
macros so they call a *_setup()/_cleanup() function when any SPL
dependent module is loaded or unloaded. This gives us a chance to
perform any needed modification of the process, in this case changing
the cwd. It also handily provides a way to avoid creating wrapper
init()/exit() functions because the Solaris and Linux prototypes
differ slightly. All dependent modules should now call the spl
helper macros spl_module_{init,exit}() instead of the native linux
versions.
Unfortunately, it appears that under Linux there has been no consistent
API in the kernel to set the cwd in a module. Because of this I have
had to add more autoconf magic than I'd like. However, what I have
done is correct and has been tested on RHEL5, SLES11, FC11, and CHAOS
kernels.
In addition, I have change the rootdir type from a 'void *' to the
correct 'vnode_t *' type. And I've set rootdir to a non-NULL value.
-rw-r--r-- | config/spl-build.m4 | 33 | ||||
-rwxr-xr-x | configure | 210 | ||||
-rw-r--r-- | include/linux/module_compat.h | 35 | ||||
-rw-r--r-- | include/sys/sysmacros.h | 2 | ||||
-rw-r--r-- | include/sys/types.h | 1 | ||||
-rw-r--r-- | include/sys/vnode.h | 3 | ||||
-rw-r--r-- | module/spl/spl-generic.c | 26 | ||||
-rw-r--r-- | module/spl/spl-vnode.c | 86 | ||||
-rw-r--r-- | module/splat/splat-ctl.c | 10 | ||||
-rw-r--r-- | spl_config.h.in | 6 |
10 files changed, 404 insertions, 8 deletions
diff --git a/config/spl-build.m4 b/config/spl-build.m4 index 3293aa273..e6a626108 100644 --- a/config/spl-build.m4 +++ b/config/spl-build.m4 @@ -64,6 +64,8 @@ AC_DEFUN([SPL_AC_CONFIG_KERNEL], [ SPL_AC_ZONE_STAT_ITEM_INACTIVE SPL_AC_ZONE_STAT_ITEM_ACTIVE SPL_AC_GET_ZONE_COUNTS + SPL_AC_SET_FS_PWD + SPL_AC_2ARGS_SET_FS_PWD SPL_AC_2ARGS_VFS_UNLINK SPL_AC_4ARGS_VFS_RENAME SPL_AC_CRED_STRUCT @@ -1204,6 +1206,37 @@ AC_DEFUN([SPL_AC_GET_ZONE_COUNTS], [ ]) dnl # +dnl # Symbol available in RHEL kernels not in stock kernels. +dnl # +AC_DEFUN([SPL_AC_SET_FS_PWD], [ + SPL_CHECK_SYMBOL_EXPORT( + [set_fs_pwd], + [], + [AC_DEFINE(HAVE_SET_FS_PWD, 1, + [set_fs_pwd() is available])], + []) +]) + +dnl # +dnl # 2.6.25 API change, +dnl # Simplied API by replacing mnt+dentry args with a single path arg. +dnl # +AC_DEFUN([SPL_AC_2ARGS_SET_FS_PWD], + [AC_MSG_CHECKING([whether set_fs_pwd() wants 2 args]) + SPL_LINUX_TRY_COMPILE([ + #include <linux/sched.h> + ],[ + set_fs_pwd(NULL, NULL); + ],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_2ARGS_SET_FS_PWD, 1, + [set_fs_pwd() wants 2 args]) + ],[ + AC_MSG_RESULT(no) + ]) +]) + +dnl # dnl # SLES API change, never adopted in mainline, dnl # Third 'struct vfsmount *' argument removed. dnl # @@ -21715,6 +21715,111 @@ fi + + echo "$as_me:$LINENO: checking whether symbol set_fs_pwd is exported" >&5 +echo $ECHO_N "checking whether symbol set_fs_pwd is exported... $ECHO_C" >&6 + grep -q -E '[[:space:]]set_fs_pwd[[:space:]]' \ + $LINUX_OBJ/Module*.symvers 2>/dev/null + rc=$? + if test $rc -ne 0; then + export=0 + for file in ; do + grep -q -E "EXPORT_SYMBOL.*(set_fs_pwd)" \ + "$LINUX_OBJ/$file" 2>/dev/null + rc=$? + if test $rc -eq 0; then + export=1 + break; + fi + done + if test $export -eq 0; then + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + + else + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SET_FS_PWD 1 +_ACEOF + + fi + else + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SET_FS_PWD 1 +_ACEOF + + fi + + + echo "$as_me:$LINENO: checking whether set_fs_pwd() wants 2 args" >&5 +echo $ECHO_N "checking whether set_fs_pwd() wants 2 args... $ECHO_C" >&6 + + +cat >conftest.c <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + + + #include <linux/sched.h> + +int +main (void) +{ + + set_fs_pwd(NULL, NULL); + + ; + 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=$? + 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=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +cat >>confdefs.h <<\_ACEOF +#define HAVE_2ARGS_SET_FS_PWD 1 +_ACEOF + + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + + + +fi + + rm -Rf build + + + echo "$as_me:$LINENO: checking whether vfs_unlink() wants 2 args" >&5 echo $ECHO_N "checking whether vfs_unlink() wants 2 args... $ECHO_C" >&6 @@ -24758,6 +24863,111 @@ fi + + echo "$as_me:$LINENO: checking whether symbol set_fs_pwd is exported" >&5 +echo $ECHO_N "checking whether symbol set_fs_pwd is exported... $ECHO_C" >&6 + grep -q -E '[[:space:]]set_fs_pwd[[:space:]]' \ + $LINUX_OBJ/Module*.symvers 2>/dev/null + rc=$? + if test $rc -ne 0; then + export=0 + for file in ; do + grep -q -E "EXPORT_SYMBOL.*(set_fs_pwd)" \ + "$LINUX_OBJ/$file" 2>/dev/null + rc=$? + if test $rc -eq 0; then + export=1 + break; + fi + done + if test $export -eq 0; then + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + + else + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SET_FS_PWD 1 +_ACEOF + + fi + else + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SET_FS_PWD 1 +_ACEOF + + fi + + + echo "$as_me:$LINENO: checking whether set_fs_pwd() wants 2 args" >&5 +echo $ECHO_N "checking whether set_fs_pwd() wants 2 args... $ECHO_C" >&6 + + +cat >conftest.c <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + + + #include <linux/sched.h> + +int +main (void) +{ + + set_fs_pwd(NULL, NULL); + + ; + 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=$? + 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=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +cat >>confdefs.h <<\_ACEOF +#define HAVE_2ARGS_SET_FS_PWD 1 +_ACEOF + + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + + + +fi + + rm -Rf build + + + echo "$as_me:$LINENO: checking whether vfs_unlink() wants 2 args" >&5 echo $ECHO_N "checking whether vfs_unlink() wants 2 args... $ECHO_C" >&6 diff --git a/include/linux/module_compat.h b/include/linux/module_compat.h new file mode 100644 index 000000000..766cb58ea --- /dev/null +++ b/include/linux/module_compat.h @@ -0,0 +1,35 @@ +#ifndef _SPL_MODULE_COMPAT_H +#define _SPL_MODULE_COMPAT_H + +#include <linux/module.h> + +#define spl_module_init(init_fn) \ +static int \ +spl_##init_fn(void) \ +{ \ + int rc; \ + \ + spl_setup(); \ + rc = init_fn(); \ + \ + return rc; \ +} \ + \ +module_init(spl_##init_fn) + +#define spl_module_exit(exit_fn) \ +static void \ +spl_##exit_fn(void) \ +{ \ + int rc; \ + \ + rc = exit_fn(); \ + spl_cleanup(); \ + if (rc) \ + printk(KERN_ERR "SPL: Failure %d unloading " \ + "dependent module\n", rc); \ +} \ + \ +module_exit(spl_##exit_fn) + +#endif /* _SPL_MODULE_COMPAT_H */ diff --git a/include/sys/sysmacros.h b/include/sys/sysmacros.h index 67afbfeb0..e66d8d991 100644 --- a/include/sys/sysmacros.h +++ b/include/sys/sysmacros.h @@ -144,6 +144,8 @@ extern int p0; /* Missing misc functions */ extern int highbit(unsigned long i); extern uint32_t zone_get_hostid(void *zone); +extern void spl_setup(void); +extern void spl_cleanup(void); #define makedevice(maj,min) makedev(maj,min) diff --git a/include/sys/types.h b/include/sys/types.h index 252def7bd..89cf115c0 100644 --- a/include/sys/types.h +++ b/include/sys/types.h @@ -18,6 +18,7 @@ extern "C" { #include <linux/workqueue_compat.h> #include <linux/kallsyms_compat.h> #include <linux/mutex_compat.h> +#include <linux/module_compat.h> #ifndef HAVE_UINTPTR_T typedef unsigned long uintptr_t; diff --git a/include/sys/vnode.h b/include/sys/vnode.h index 7972f6548..d8a8df272 100644 --- a/include/sys/vnode.h +++ b/include/sys/vnode.h @@ -215,6 +215,7 @@ extern int vn_getattr(vnode_t *vp, vattr_t *vap, int flags, void *x3, void *x4); extern int vn_fsync(vnode_t *vp, int flags, void *x3, void *x4); extern file_t *vn_getf(int fd); extern void vn_releasef(int fd); +extern int vn_set_pwd(const char *filename); int vn_init(void); void vn_fini(void); @@ -241,7 +242,7 @@ vn_putpage(vnode_t *vp, offset_t off, ssize_t size, #define getf vn_getf #define releasef vn_releasef -extern void *rootdir; +extern vnode_t *rootdir; #ifdef __cplusplus } diff --git a/module/spl/spl-generic.c b/module/spl/spl-generic.c index 8bf97e0cf..dc0ac21d0 100644 --- a/module/spl/spl-generic.c +++ b/module/spl/spl-generic.c @@ -358,7 +358,8 @@ set_kallsyms_lookup_name(void) } #endif -static int __init spl_init(void) +static int +__init spl_init(void) { int rc = 0; @@ -421,7 +422,8 @@ out1: return rc; } -static void spl_fini(void) +static void +spl_fini(void) { ENTRY; @@ -436,6 +438,26 @@ static void spl_fini(void) debug_fini(); } +/* Called when a dependent module is loaded */ +void +spl_setup(void) +{ + /* + * At module load time the pwd is set to '/' on a Solaris system. + * On a Linux system will be set to whatever directory the caller + * was in when executing insmod/modprobe. + */ + vn_set_pwd("/"); +} +EXPORT_SYMBOL(spl_setup); + +/* Called when a dependent module is unloaded */ +void +spl_cleanup(void) +{ +} +EXPORT_SYMBOL(spl_cleanup); + module_init(spl_init); module_exit(spl_fini); diff --git a/module/spl/spl-vnode.c b/module/spl/spl-vnode.c index 08c339a5a..7d2080022 100644 --- a/module/spl/spl-vnode.c +++ b/module/spl/spl-vnode.c @@ -34,7 +34,7 @@ #define DEBUG_SUBSYSTEM S_VNODE -void *rootdir = NULL; +vnode_t *rootdir = (vnode_t *)0xabcd1234; EXPORT_SYMBOL(rootdir); static spl_kmem_cache_t *vn_cache; @@ -602,6 +602,90 @@ vn_releasef(int fd) } /* releasef() */ EXPORT_SYMBOL(releasef); +#ifndef HAVE_SET_FS_PWD +# ifdef HAVE_2ARGS_SET_FS_PWD +/* Used from 2.6.25 - 2.6.31+ */ +void +set_fs_pwd(struct fs_struct *fs, struct path *path) +{ + struct path old_pwd; + + write_lock(&fs->lock); + old_pwd = fs->pwd; + fs->pwd = *path; + path_get(path); + write_unlock(&fs->lock); + + if (old_pwd.dentry) + path_put(&old_pwd); +} +# else +/* Used from 2.6.11 - 2.6.24 */ +void +set_fs_pwd(struct fs_struct *fs, struct vfsmount *mnt, struct dentry *dentry) +{ + struct dentry *old_pwd; + struct vfsmount *old_pwdmnt; + + write_lock(&fs->lock); + old_pwd = fs->pwd; + old_pwdmnt = fs->pwdmnt; + fs->pwdmnt = mntget(mnt); + fs->pwd = dget(dentry); + write_unlock(&fs->lock); + + if (old_pwd) { + dput(old_pwd); + mntput(old_pwdmnt); + } +} +# endif /* HAVE_2ARGS_SET_FS_PWD */ +#endif /* HAVE_SET_FS_PWD */ + +int +vn_set_pwd(const char *filename) +{ +#ifdef HAVE_2ARGS_SET_FS_PWD + struct path path; + int rc; + ENTRY; + + rc = user_path_dir(filename, &path); + if (rc) + GOTO(out, rc); + + rc = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS); + if (rc) + GOTO(dput_and_out, rc); + + set_fs_pwd(current->fs, &path); + +dput_and_out: + path_put(&path); +#else + struct nameidata nd; + int rc; + ENTRY; + + rc = __user_walk(filename, + LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_CHDIR, &nd); + if (rc) + GOTO(out, rc); + + rc = vfs_permission(&nd, MAY_EXEC); + if (rc) + GOTO(dput_and_out, rc); + + set_fs_pwd(current->fs, nd.nd_mnt, nd.nd_dentry); + +dput_and_out: + vn_path_release(&nd); +#endif /* HAVE_2ARGS_SET_FS_PWD */ +out: + RETURN(-rc); +} /* vn_set_pwd() */ +EXPORT_SYMBOL(vn_set_pwd); + static int vn_cache_constructor(void *buf, void *cdrarg, int kmflags) { diff --git a/module/splat/splat-ctl.c b/module/splat/splat-ctl.c index 159103980..9e52ab4fb 100644 --- a/module/splat/splat-ctl.c +++ b/module/splat/splat-ctl.c @@ -608,7 +608,7 @@ static struct cdev splat_cdev = { .kobj = { .name = SPLAT_NAME, }, }; -static int __init +static int splat_init(void) { dev_t dev; @@ -667,7 +667,7 @@ error: return rc; } -static void +static int splat_fini(void) { dev_t dev = MKDEV(SPLAT_MAJOR, 0); @@ -695,10 +695,12 @@ splat_fini(void) ASSERT(list_empty(&splat_module_list)); printk(KERN_INFO "SPLAT: Unloaded Solaris Porting LAyer " "Tests v%s\n", SPL_META_VERSION); + + return 0; } -module_init(splat_init); -module_exit(splat_fini); +spl_module_init(splat_init); +spl_module_exit(splat_fini); MODULE_AUTHOR("Lawrence Livermore National Labs"); MODULE_DESCRIPTION("Solaris Porting LAyer Tests"); diff --git a/spl_config.h.in b/spl_config.h.in index bd42119cb..176da466b 100644 --- a/spl_config.h.in +++ b/spl_config.h.in @@ -12,6 +12,9 @@ /* register_sysctl_table() wants 2 args */ #undef HAVE_2ARGS_REGISTER_SYSCTL +/* set_fs_pwd() wants 2 args */ +#undef HAVE_2ARGS_SET_FS_PWD + /* vfs_unlink() wants 2 args */ #undef HAVE_2ARGS_VFS_UNLINK @@ -120,6 +123,9 @@ /* __put_task_struct() is available */ #undef HAVE_PUT_TASK_STRUCT +/* set_fs_pwd() is available */ +#undef HAVE_SET_FS_PWD + /* set_normalized_timespec() is available as export */ #undef HAVE_SET_NORMALIZED_TIMESPEC_EXPORT |