2007-04-27 02:49:28 +04:00
/* mountpoint management
2005-04-17 02:20:36 +04:00
*
* Copyright ( C ) 2002 Red Hat , Inc . All Rights Reserved .
* Written by David Howells ( dhowells @ redhat . com )
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/fs.h>
# include <linux/pagemap.h>
# include <linux/mount.h>
# include <linux/namei.h>
2006-12-08 13:37:56 +03:00
# include <linux/mnt_namespace.h>
2005-04-17 02:20:36 +04:00
# include "internal.h"
static struct dentry * afs_mntpt_lookup ( struct inode * dir ,
struct dentry * dentry ,
struct nameidata * nd ) ;
static int afs_mntpt_open ( struct inode * inode , struct file * file ) ;
[PATCH] Fix up symlink function pointers
This fixes up the symlink functions for the calling convention change:
* afs, autofs4, befs, devfs, freevxfs, jffs2, jfs, ncpfs, procfs,
smbfs, sysvfs, ufs, xfs - prototype change for ->follow_link()
* befs, smbfs, xfs - same for ->put_link()
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-08-20 03:17:39 +04:00
static void * afs_mntpt_follow_link ( struct dentry * dentry , struct nameidata * nd ) ;
2007-04-27 02:55:03 +04:00
static void afs_mntpt_expiry_timed_out ( struct work_struct * work ) ;
2005-04-17 02:20:36 +04:00
2006-03-28 13:56:42 +04:00
const struct file_operations afs_mntpt_file_operations = {
2005-04-17 02:20:36 +04:00
. open = afs_mntpt_open ,
} ;
2007-02-12 11:55:38 +03:00
const struct inode_operations afs_mntpt_inode_operations = {
2005-04-17 02:20:36 +04:00
. lookup = afs_mntpt_lookup ,
. follow_link = afs_mntpt_follow_link ,
. readlink = page_readlink ,
. getattr = afs_inode_getattr ,
} ;
static LIST_HEAD ( afs_vfsmounts ) ;
2007-04-27 02:55:03 +04:00
static DECLARE_DELAYED_WORK ( afs_mntpt_expiry_timer , afs_mntpt_expiry_timed_out ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
unsigned long afs_mntpt_expiry_timeout = 10 * 60 ;
2005-04-17 02:20:36 +04:00
/*
* check a symbolic link to see whether it actually encodes a mountpoint
* - sets the AFS_VNODE_MOUNTPOINT flag on the vnode appropriately
*/
int afs_mntpt_check_symlink ( struct afs_vnode * vnode )
{
struct page * page ;
size_t size ;
char * buf ;
int ret ;
_enter ( " {%u,%u} " , vnode - > fid . vnode , vnode - > fid . unique ) ;
/* read the contents of the symlink into the pagecache */
2006-06-23 13:05:08 +04:00
page = read_mapping_page ( AFS_VNODE_TO_I ( vnode ) - > i_mapping , 0 , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( page ) ) {
ret = PTR_ERR ( page ) ;
goto out ;
}
ret = - EIO ;
wait_on_page_locked ( page ) ;
buf = kmap ( page ) ;
if ( ! PageUptodate ( page ) )
goto out_free ;
if ( PageError ( page ) )
goto out_free ;
/* examine the symlink's contents */
size = vnode - > status . size ;
2007-04-27 02:55:03 +04:00
_debug ( " symlink to %*.*s " , ( int ) size , ( int ) size , buf ) ;
2005-04-17 02:20:36 +04:00
if ( size > 2 & &
( buf [ 0 ] = = ' % ' | | buf [ 0 ] = = ' # ' ) & &
buf [ size - 1 ] = = ' . '
) {
_debug ( " symlink is a mountpoint " ) ;
spin_lock ( & vnode - > lock ) ;
2007-04-27 02:55:03 +04:00
set_bit ( AFS_VNODE_MOUNTPOINT , & vnode - > flags ) ;
2005-04-17 02:20:36 +04:00
spin_unlock ( & vnode - > lock ) ;
}
ret = 0 ;
2007-04-27 02:49:28 +04:00
out_free :
2005-04-17 02:20:36 +04:00
kunmap ( page ) ;
page_cache_release ( page ) ;
2007-04-27 02:49:28 +04:00
out :
2005-04-17 02:20:36 +04:00
_leave ( " = %d " , ret ) ;
return ret ;
2007-04-27 02:49:28 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* no valid lookup procedure on this sort of dir
*/
static struct dentry * afs_mntpt_lookup ( struct inode * dir ,
struct dentry * dentry ,
struct nameidata * nd )
{
2007-04-27 02:55:03 +04:00
_enter ( " %p,%p{%p{%s},%s} " ,
2005-04-17 02:20:36 +04:00
dir ,
dentry ,
dentry - > d_parent ,
dentry - > d_parent ?
dentry - > d_parent - > d_name . name : ( const unsigned char * ) " " ,
dentry - > d_name . name ) ;
return ERR_PTR ( - EREMOTE ) ;
2007-04-27 02:49:28 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* no valid open procedure on this sort of dir
*/
static int afs_mntpt_open ( struct inode * inode , struct file * file )
{
2007-04-27 02:55:03 +04:00
_enter ( " %p,%p{%p{%s},%s} " ,
2005-04-17 02:20:36 +04:00
inode , file ,
2006-12-08 13:36:50 +03:00
file - > f_path . dentry - > d_parent ,
file - > f_path . dentry - > d_parent ?
file - > f_path . dentry - > d_parent - > d_name . name :
2005-04-17 02:20:36 +04:00
( const unsigned char * ) " " ,
2006-12-08 13:36:50 +03:00
file - > f_path . dentry - > d_name . name ) ;
2005-04-17 02:20:36 +04:00
return - EREMOTE ;
2007-04-27 02:49:28 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* create a vfsmount to be automounted
*/
static struct vfsmount * afs_mntpt_do_automount ( struct dentry * mntpt )
{
struct afs_super_info * super ;
struct vfsmount * mnt ;
struct page * page = NULL ;
size_t size ;
char * buf , * devname = NULL , * options = NULL ;
int ret ;
2007-04-27 02:55:03 +04:00
_enter ( " {%s} " , mntpt - > d_name . name ) ;
2005-04-17 02:20:36 +04:00
BUG_ON ( ! mntpt - > d_inode ) ;
ret = - EINVAL ;
size = mntpt - > d_inode - > i_size ;
if ( size > PAGE_SIZE - 1 )
goto error ;
ret = - ENOMEM ;
devname = ( char * ) get_zeroed_page ( GFP_KERNEL ) ;
if ( ! devname )
goto error ;
options = ( char * ) get_zeroed_page ( GFP_KERNEL ) ;
if ( ! options )
goto error ;
/* read the contents of the AFS special symlink */
2006-06-23 13:05:08 +04:00
page = read_mapping_page ( mntpt - > d_inode - > i_mapping , 0 , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( page ) ) {
ret = PTR_ERR ( page ) ;
goto error ;
}
ret = - EIO ;
wait_on_page_locked ( page ) ;
if ( ! PageUptodate ( page ) | | PageError ( page ) )
goto error ;
buf = kmap ( page ) ;
memcpy ( devname , buf , size ) ;
kunmap ( page ) ;
page_cache_release ( page ) ;
page = NULL ;
/* work out what options we want */
super = AFS_FS_S ( mntpt - > d_sb ) ;
memcpy ( options , " cell= " , 5 ) ;
strcpy ( options + 5 , super - > volume - > cell - > name ) ;
if ( super - > volume - > type = = AFSVL_RWVOL )
strcat ( options , " ,rwpath " ) ;
/* try and do the mount */
2007-04-27 02:55:03 +04:00
_debug ( " --- attempting mount %s -o %s --- " , devname , options ) ;
2006-06-09 17:34:16 +04:00
mnt = vfs_kern_mount ( & afs_fs_type , 0 , devname , options ) ;
2007-04-27 02:55:03 +04:00
_debug ( " --- mount result %p --- " , mnt ) ;
2005-04-17 02:20:36 +04:00
free_page ( ( unsigned long ) devname ) ;
free_page ( ( unsigned long ) options ) ;
2007-04-27 02:55:03 +04:00
_leave ( " = %p " , mnt ) ;
2005-04-17 02:20:36 +04:00
return mnt ;
2007-04-27 02:49:28 +04:00
error :
2005-04-17 02:20:36 +04:00
if ( page )
page_cache_release ( page ) ;
if ( devname )
free_page ( ( unsigned long ) devname ) ;
if ( options )
free_page ( ( unsigned long ) options ) ;
2007-04-27 02:55:03 +04:00
_leave ( " = %d " , ret ) ;
2005-04-17 02:20:36 +04:00
return ERR_PTR ( ret ) ;
2007-04-27 02:49:28 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* follow a link from a mountpoint directory , thus causing it to be mounted
*/
[PATCH] Fix up symlink function pointers
This fixes up the symlink functions for the calling convention change:
* afs, autofs4, befs, devfs, freevxfs, jffs2, jfs, ncpfs, procfs,
smbfs, sysvfs, ufs, xfs - prototype change for ->follow_link()
* befs, smbfs, xfs - same for ->put_link()
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-08-20 03:17:39 +04:00
static void * afs_mntpt_follow_link ( struct dentry * dentry , struct nameidata * nd )
2005-04-17 02:20:36 +04:00
{
struct vfsmount * newmnt ;
int err ;
2007-04-27 02:55:03 +04:00
_enter ( " %p{%s},{%s:%p{%s}} " ,
2005-04-17 02:20:36 +04:00
dentry ,
dentry - > d_name . name ,
nd - > mnt - > mnt_devname ,
dentry ,
nd - > dentry - > d_name . name ) ;
2007-04-27 02:55:03 +04:00
dput ( nd - > dentry ) ;
nd - > dentry = dget ( dentry ) ;
newmnt = afs_mntpt_do_automount ( nd - > dentry ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( newmnt ) ) {
path_release ( nd ) ;
[PATCH] Fix up symlink function pointers
This fixes up the symlink functions for the calling convention change:
* afs, autofs4, befs, devfs, freevxfs, jffs2, jfs, ncpfs, procfs,
smbfs, sysvfs, ufs, xfs - prototype change for ->follow_link()
* befs, smbfs, xfs - same for ->put_link()
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-08-20 03:17:39 +04:00
return ( void * ) newmnt ;
2005-04-17 02:20:36 +04:00
}
2007-04-27 02:55:03 +04:00
mntget ( newmnt ) ;
err = do_add_mount ( newmnt , nd , MNT_SHRINKABLE , & afs_vfsmounts ) ;
switch ( err ) {
case 0 :
path_release ( nd ) ;
2005-04-17 02:20:36 +04:00
nd - > mnt = newmnt ;
2007-04-27 02:55:03 +04:00
nd - > dentry = dget ( newmnt - > mnt_root ) ;
schedule_delayed_work ( & afs_mntpt_expiry_timer ,
afs_mntpt_expiry_timeout * HZ ) ;
break ;
case - EBUSY :
/* someone else made a mount here whilst we were busy */
while ( d_mountpoint ( nd - > dentry ) & &
follow_down ( & nd - > mnt , & nd - > dentry ) )
;
err = 0 ;
default :
mntput ( newmnt ) ;
break ;
2005-04-17 02:20:36 +04:00
}
2007-04-27 02:55:03 +04:00
_leave ( " = %d " , err ) ;
[PATCH] Fix up symlink function pointers
This fixes up the symlink functions for the calling convention change:
* afs, autofs4, befs, devfs, freevxfs, jffs2, jfs, ncpfs, procfs,
smbfs, sysvfs, ufs, xfs - prototype change for ->follow_link()
* befs, smbfs, xfs - same for ->put_link()
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-08-20 03:17:39 +04:00
return ERR_PTR ( err ) ;
2007-04-27 02:49:28 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* handle mountpoint expiry timer going off
*/
2007-04-27 02:55:03 +04:00
static void afs_mntpt_expiry_timed_out ( struct work_struct * work )
2005-04-17 02:20:36 +04:00
{
2007-04-27 02:55:03 +04:00
_enter ( " " ) ;
if ( ! list_empty ( & afs_vfsmounts ) ) {
mark_mounts_for_expiry ( & afs_vfsmounts ) ;
schedule_delayed_work ( & afs_mntpt_expiry_timer ,
afs_mntpt_expiry_timeout * HZ ) ;
}
_leave ( " " ) ;
}
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
/*
* kill the AFS mountpoint timer if it ' s still running
*/
void afs_mntpt_kill_timer ( void )
{
_enter ( " " ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
ASSERT ( list_empty ( & afs_vfsmounts ) ) ;
cancel_delayed_work ( & afs_mntpt_expiry_timer ) ;
flush_scheduled_work ( ) ;
}
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
/*
* begin unmount by attempting to remove all automounted mountpoints we added
*/
void afs_umount_begin ( struct vfsmount * vfsmnt , int flags )
{
shrink_submounts ( vfsmnt , & afs_vfsmounts ) ;
2007-04-27 02:49:28 +04:00
}