summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Behlendorf <[email protected]>2009-10-01 16:06:15 -0700
committerBrian Behlendorf <[email protected]>2009-10-01 16:06:15 -0700
commit51a727e90f1dd11194737e64e85ca09d34be5d8b (patch)
tree06a0b78065a317c9e8f9536e28189b3032cfccf0
parent0e77fc118e2c105af0df5d1c7aa6627ed601bfa1 (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.m433
-rwxr-xr-xconfigure210
-rw-r--r--include/linux/module_compat.h35
-rw-r--r--include/sys/sysmacros.h2
-rw-r--r--include/sys/types.h1
-rw-r--r--include/sys/vnode.h3
-rw-r--r--module/spl/spl-generic.c26
-rw-r--r--module/spl/spl-vnode.c86
-rw-r--r--module/splat/splat-ctl.c10
-rw-r--r--spl_config.h.in6
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 #
diff --git a/configure b/configure
index 03b23036e..8c8736883 100755
--- a/configure
+++ b/configure
@@ -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