summaryrefslogtreecommitdiffstats
path: root/module/spl
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 /module/spl
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.
Diffstat (limited to 'module/spl')
-rw-r--r--module/spl/spl-generic.c26
-rw-r--r--module/spl/spl-vnode.c86
2 files changed, 109 insertions, 3 deletions
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)
{