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 /module/spl/spl-vnode.c | |
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.
Diffstat (limited to 'module/spl/spl-vnode.c')
-rw-r--r-- | module/spl/spl-vnode.c | 86 |
1 files changed, 85 insertions, 1 deletions
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) { |