2009-03-30 03:00:13 +04:00
# include <linux/module.h>
# include <linux/sched.h>
# include <linux/fs.h>
# include <linux/path.h>
# include <linux/slab.h>
2009-03-30 03:50:06 +04:00
# include <linux/fs_struct.h>
2009-03-30 03:00:13 +04:00
/*
* Replace the fs - > { rootmnt , root } with { mnt , dentry } . Put the old values .
* It can block .
*/
void set_fs_root ( struct fs_struct * fs , struct path * path )
{
struct path old_root ;
2010-08-17 22:37:33 +04:00
spin_lock ( & fs - > lock ) ;
2009-03-30 03:00:13 +04:00
old_root = fs - > root ;
fs - > root = * path ;
path_get ( path ) ;
2010-08-17 22:37:33 +04:00
spin_unlock ( & fs - > lock ) ;
2009-03-30 03:00:13 +04:00
if ( old_root . dentry )
path_put ( & old_root ) ;
}
/*
* Replace the fs - > { pwdmnt , pwd } with { mnt , dentry } . Put the old values .
* It can block .
*/
void set_fs_pwd ( struct fs_struct * fs , struct path * path )
{
struct path old_pwd ;
2010-08-17 22:37:33 +04:00
spin_lock ( & fs - > lock ) ;
2009-03-30 03:00:13 +04:00
old_pwd = fs - > pwd ;
fs - > pwd = * path ;
path_get ( path ) ;
2010-08-17 22:37:33 +04:00
spin_unlock ( & fs - > lock ) ;
2009-03-30 03:00:13 +04:00
if ( old_pwd . dentry )
path_put ( & old_pwd ) ;
}
void chroot_fs_refs ( struct path * old_root , struct path * new_root )
{
struct task_struct * g , * p ;
struct fs_struct * fs ;
int count = 0 ;
read_lock ( & tasklist_lock ) ;
do_each_thread ( g , p ) {
task_lock ( p ) ;
fs = p - > fs ;
if ( fs ) {
2010-08-17 22:37:33 +04:00
spin_lock ( & fs - > lock ) ;
2009-03-30 03:00:13 +04:00
if ( fs - > root . dentry = = old_root - > dentry
& & fs - > root . mnt = = old_root - > mnt ) {
path_get ( new_root ) ;
fs - > root = * new_root ;
count + + ;
}
if ( fs - > pwd . dentry = = old_root - > dentry
& & fs - > pwd . mnt = = old_root - > mnt ) {
path_get ( new_root ) ;
fs - > pwd = * new_root ;
count + + ;
}
2010-08-17 22:37:33 +04:00
spin_unlock ( & fs - > lock ) ;
2009-03-30 03:00:13 +04:00
}
task_unlock ( p ) ;
} while_each_thread ( g , p ) ;
read_unlock ( & tasklist_lock ) ;
while ( count - - )
path_put ( old_root ) ;
}
2009-03-30 15:20:30 +04:00
void free_fs_struct ( struct fs_struct * fs )
2009-03-30 03:00:13 +04:00
{
2009-03-30 15:20:30 +04:00
path_put ( & fs - > root ) ;
path_put ( & fs - > pwd ) ;
kmem_cache_free ( fs_cachep , fs ) ;
2009-03-30 03:00:13 +04:00
}
void exit_fs ( struct task_struct * tsk )
{
2009-03-30 15:20:30 +04:00
struct fs_struct * fs = tsk - > fs ;
2009-03-30 03:00:13 +04:00
if ( fs ) {
2009-03-30 15:20:30 +04:00
int kill ;
2009-03-30 03:00:13 +04:00
task_lock ( tsk ) ;
2010-08-17 22:37:33 +04:00
spin_lock ( & fs - > lock ) ;
2009-03-30 03:00:13 +04:00
tsk - > fs = NULL ;
2009-03-30 15:20:30 +04:00
kill = ! - - fs - > users ;
2010-08-17 22:37:33 +04:00
spin_unlock ( & fs - > lock ) ;
2009-03-30 03:00:13 +04:00
task_unlock ( tsk ) ;
2009-03-30 15:20:30 +04:00
if ( kill )
free_fs_struct ( fs ) ;
2009-03-30 03:00:13 +04:00
}
}
struct fs_struct * copy_fs_struct ( struct fs_struct * old )
{
struct fs_struct * fs = kmem_cache_alloc ( fs_cachep , GFP_KERNEL ) ;
/* We don't need to lock fs - think why ;-) */
if ( fs ) {
2009-03-30 15:20:30 +04:00
fs - > users = 1 ;
fs - > in_exec = 0 ;
2010-08-17 22:37:33 +04:00
spin_lock_init ( & fs - > lock ) ;
2009-03-30 03:00:13 +04:00
fs - > umask = old - > umask ;
2010-08-10 13:41:36 +04:00
get_fs_root_and_pwd ( old , & fs - > root , & fs - > pwd ) ;
2009-03-30 03:00:13 +04:00
}
return fs ;
}
int unshare_fs_struct ( void )
{
2009-03-30 15:20:30 +04:00
struct fs_struct * fs = current - > fs ;
struct fs_struct * new_fs = copy_fs_struct ( fs ) ;
int kill ;
if ( ! new_fs )
2009-03-30 03:00:13 +04:00
return - ENOMEM ;
2009-03-30 15:20:30 +04:00
task_lock ( current ) ;
2010-08-17 22:37:33 +04:00
spin_lock ( & fs - > lock ) ;
2009-03-30 15:20:30 +04:00
kill = ! - - fs - > users ;
current - > fs = new_fs ;
2010-08-17 22:37:33 +04:00
spin_unlock ( & fs - > lock ) ;
2009-03-30 15:20:30 +04:00
task_unlock ( current ) ;
if ( kill )
free_fs_struct ( fs ) ;
2009-03-30 03:00:13 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( unshare_fs_struct ) ;
2009-03-30 03:08:22 +04:00
int current_umask ( void )
{
return current - > fs - > umask ;
}
EXPORT_SYMBOL ( current_umask ) ;
2009-03-30 03:00:13 +04:00
/* to be mentioned only in INIT_TASK */
struct fs_struct init_fs = {
2009-03-30 15:20:30 +04:00
. users = 1 ,
2010-08-17 22:37:33 +04:00
. lock = __SPIN_LOCK_UNLOCKED ( init_fs . lock ) ,
2009-03-30 03:00:13 +04:00
. umask = 0022 ,
} ;
void daemonize_fs_struct ( void )
{
2009-03-30 15:20:30 +04:00
struct fs_struct * fs = current - > fs ;
if ( fs ) {
int kill ;
task_lock ( current ) ;
2009-03-30 03:00:13 +04:00
2010-08-17 22:37:33 +04:00
spin_lock ( & init_fs . lock ) ;
2009-03-30 15:20:30 +04:00
init_fs . users + + ;
2010-08-17 22:37:33 +04:00
spin_unlock ( & init_fs . lock ) ;
2009-03-30 15:20:30 +04:00
2010-08-17 22:37:33 +04:00
spin_lock ( & fs - > lock ) ;
2009-03-30 15:20:30 +04:00
current - > fs = & init_fs ;
kill = ! - - fs - > users ;
2010-08-17 22:37:33 +04:00
spin_unlock ( & fs - > lock ) ;
2009-03-30 15:20:30 +04:00
task_unlock ( current ) ;
if ( kill )
free_fs_struct ( fs ) ;
}
2009-03-30 03:00:13 +04:00
}