2005-04-16 15:20:36 -07:00
/*
* Copyright ( c ) 2002 Red Hat , Inc . All rights reserved .
*
* This software may be freely redistributed under the terms of the
* GNU General Public License .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
* Authors : David Woodhouse < dwmw2 @ cambridge . redhat . com >
* David Howells < dhowells @ redhat . com >
*
*/
# 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 "internal.h"
struct afs_iget_data {
struct afs_fid fid ;
struct afs_volume * volume ; /* volume on which resides */
} ;
/*
* map the AFS file status to the inode member variables
*/
2007-04-26 15:57:07 -07:00
static int afs_inode_map_status ( struct afs_vnode * vnode , struct key * key )
2005-04-16 15:20:36 -07:00
{
struct inode * inode = AFS_VNODE_TO_I ( vnode ) ;
2007-04-26 15:59:35 -07:00
_debug ( " FS: ft=%d lk=%d sz=%llu ver=%Lu mod=%hu " ,
2005-04-16 15:20:36 -07:00
vnode - > status . type ,
vnode - > status . nlink ,
2007-04-26 16:06:22 -07:00
( unsigned long long ) vnode - > status . size ,
2007-04-26 15:55:03 -07:00
vnode - > status . data_version ,
2005-04-16 15:20:36 -07:00
vnode - > status . mode ) ;
switch ( vnode - > status . type ) {
case AFS_FTYPE_FILE :
inode - > i_mode = S_IFREG | vnode - > status . mode ;
inode - > i_op = & afs_file_inode_operations ;
2007-04-26 15:57:07 -07:00
inode - > i_fop = & afs_file_operations ;
2005-04-16 15:20:36 -07:00
break ;
case AFS_FTYPE_DIR :
inode - > i_mode = S_IFDIR | vnode - > status . mode ;
inode - > i_op = & afs_dir_inode_operations ;
inode - > i_fop = & afs_dir_file_operations ;
break ;
case AFS_FTYPE_SYMLINK :
inode - > i_mode = S_IFLNK | vnode - > status . mode ;
inode - > i_op = & page_symlink_inode_operations ;
break ;
default :
printk ( " kAFS: AFS vnode with undefined type \n " ) ;
return - EBADMSG ;
}
inode - > i_nlink = vnode - > status . nlink ;
inode - > i_uid = vnode - > status . owner ;
inode - > i_gid = 0 ;
inode - > i_size = vnode - > status . size ;
inode - > i_ctime . tv_sec = vnode - > status . mtime_server ;
inode - > i_ctime . tv_nsec = 0 ;
inode - > i_atime = inode - > i_mtime = inode - > i_ctime ;
inode - > i_blocks = 0 ;
inode - > i_version = vnode - > fid . unique ;
inode - > i_mapping - > a_ops = & afs_fs_aops ;
/* check to see whether a symbolic link is really a mountpoint */
if ( vnode - > status . type = = AFS_FTYPE_SYMLINK ) {
2007-04-26 15:57:07 -07:00
afs_mntpt_check_symlink ( vnode , key ) ;
2005-04-16 15:20:36 -07:00
2007-04-26 15:55:03 -07:00
if ( test_bit ( AFS_VNODE_MOUNTPOINT , & vnode - > flags ) ) {
2005-04-16 15:20:36 -07:00
inode - > i_mode = S_IFDIR | vnode - > status . mode ;
inode - > i_op = & afs_mntpt_inode_operations ;
inode - > i_fop = & afs_mntpt_file_operations ;
}
}
return 0 ;
2007-04-26 15:49:28 -07:00
}
2005-04-16 15:20:36 -07:00
/*
* iget5 ( ) comparator
*/
static int afs_iget5_test ( struct inode * inode , void * opaque )
{
struct afs_iget_data * data = opaque ;
return inode - > i_ino = = data - > fid . vnode & &
inode - > i_version = = data - > fid . unique ;
2007-04-26 15:49:28 -07:00
}
2005-04-16 15:20:36 -07:00
/*
* iget5 ( ) inode initialiser
*/
static int afs_iget5_set ( struct inode * inode , void * opaque )
{
struct afs_iget_data * data = opaque ;
struct afs_vnode * vnode = AFS_FS_I ( inode ) ;
inode - > i_ino = data - > fid . vnode ;
inode - > i_version = data - > fid . unique ;
vnode - > fid = data - > fid ;
vnode - > volume = data - > volume ;
return 0 ;
2007-04-26 15:49:28 -07:00
}
2005-04-16 15:20:36 -07:00
/*
* inode retrieval
*/
2007-04-26 15:59:35 -07:00
struct inode * afs_iget ( struct super_block * sb , struct key * key ,
struct afs_fid * fid , struct afs_file_status * status ,
struct afs_callback * cb )
2005-04-16 15:20:36 -07:00
{
struct afs_iget_data data = { . fid = * fid } ;
struct afs_super_info * as ;
struct afs_vnode * vnode ;
struct inode * inode ;
int ret ;
_enter ( " ,{%u,%u,%u},, " , fid - > vid , fid - > vnode , fid - > unique ) ;
as = sb - > s_fs_info ;
data . volume = as - > volume ;
inode = iget5_locked ( sb , fid - > vnode , afs_iget5_test , afs_iget5_set ,
& data ) ;
if ( ! inode ) {
_leave ( " = -ENOMEM " ) ;
2007-04-26 15:55:03 -07:00
return ERR_PTR ( - ENOMEM ) ;
2005-04-16 15:20:36 -07:00
}
2007-04-26 15:55:03 -07:00
_debug ( " GOT INODE %p { vl=%x vn=%x, u=%x } " ,
inode , fid - > vid , fid - > vnode , fid - > unique ) ;
2005-04-16 15:20:36 -07:00
vnode = AFS_FS_I ( inode ) ;
/* deal with an existing inode */
if ( ! ( inode - > i_state & I_NEW ) ) {
2007-04-26 15:55:03 -07:00
_leave ( " = %p " , inode ) ;
return inode ;
2005-04-16 15:20:36 -07:00
}
# ifdef AFS_CACHING_SUPPORT
/* set up caching before reading the status, as fetch-status reads the
* first page of symlinks to see if they ' re really mntpts */
cachefs_acquire_cookie ( vnode - > volume - > cache ,
NULL ,
vnode ,
& vnode - > cache ) ;
# endif
2007-04-26 15:59:35 -07:00
if ( ! status ) {
/* it's a remotely extant inode */
set_bit ( AFS_VNODE_CB_BROKEN , & vnode - > flags ) ;
ret = afs_vnode_fetch_status ( vnode , NULL , key ) ;
if ( ret < 0 )
goto bad_inode ;
} else {
/* it's an inode we just created */
memcpy ( & vnode - > status , status , sizeof ( vnode - > status ) ) ;
if ( ! cb ) {
/* it's a symlink we just created (the fileserver
* didn ' t give us a callback ) */
vnode - > cb_version = 0 ;
vnode - > cb_expiry = 0 ;
vnode - > cb_type = 0 ;
vnode - > cb_expires = get_seconds ( ) ;
} else {
vnode - > cb_version = cb - > version ;
vnode - > cb_expiry = cb - > expiry ;
vnode - > cb_type = cb - > type ;
vnode - > cb_expires = vnode - > cb_expiry + get_seconds ( ) ;
}
}
2007-04-26 15:57:07 -07:00
ret = afs_inode_map_status ( vnode , key ) ;
2007-04-26 15:55:03 -07:00
if ( ret < 0 )
2005-04-16 15:20:36 -07:00
goto bad_inode ;
/* success */
2007-04-26 15:59:35 -07:00
clear_bit ( AFS_VNODE_UNSET , & vnode - > flags ) ;
2007-04-26 15:55:03 -07:00
inode - > i_flags | = S_NOATIME ;
2005-04-16 15:20:36 -07:00
unlock_new_inode ( inode ) ;
2007-04-26 15:55:03 -07:00
_leave ( " = %p [CB { v=%u t=%u }] " , inode , vnode - > cb_version , vnode - > cb_type ) ;
return inode ;
2005-04-16 15:20:36 -07:00
/* failure */
2007-04-26 15:49:28 -07:00
bad_inode :
2005-04-16 15:20:36 -07:00
make_bad_inode ( inode ) ;
unlock_new_inode ( inode ) ;
iput ( inode ) ;
_leave ( " = %d [bad] " , ret ) ;
2007-04-26 15:55:03 -07:00
return ERR_PTR ( ret ) ;
2007-04-26 15:49:28 -07:00
}
2005-04-16 15:20:36 -07:00
2007-04-26 15:59:35 -07:00
/*
* validate a vnode / inode
* - there are several things we need to check
* - parent dir data changes ( rm , rmdir , rename , mkdir , create , link ,
* symlink )
* - parent dir metadata changed ( security changes )
* - dentry data changed ( write , truncate )
* - dentry metadata changed ( security changes )
*/
int afs_validate ( struct afs_vnode * vnode , struct key * key )
{
int ret ;
_enter ( " {v={%x:%u} fl=%lx},%x " ,
vnode - > fid . vid , vnode - > fid . vnode , vnode - > flags ,
key_serial ( key ) ) ;
if ( vnode - > cb_promised & &
! test_bit ( AFS_VNODE_CB_BROKEN , & vnode - > flags ) & &
! test_bit ( AFS_VNODE_MODIFIED , & vnode - > flags ) & &
! test_bit ( AFS_VNODE_ZAP_DATA , & vnode - > flags ) ) {
if ( vnode - > cb_expires < get_seconds ( ) + 10 ) {
_debug ( " callback expired " ) ;
set_bit ( AFS_VNODE_CB_BROKEN , & vnode - > flags ) ;
} else {
goto valid ;
}
}
if ( test_bit ( AFS_VNODE_DELETED , & vnode - > flags ) )
goto valid ;
mutex_lock ( & vnode - > validate_lock ) ;
/* if the promise has expired, we need to check the server again to get
* a new promise - note that if the ( parent ) directory ' s metadata was
* changed then the security may be different and we may no longer have
* access */
if ( ! vnode - > cb_promised | |
test_bit ( AFS_VNODE_CB_BROKEN , & vnode - > flags ) ) {
_debug ( " not promised " ) ;
ret = afs_vnode_fetch_status ( vnode , NULL , key ) ;
if ( ret < 0 )
goto error_unlock ;
_debug ( " new promise [fl=%lx] " , vnode - > flags ) ;
}
if ( test_bit ( AFS_VNODE_DELETED , & vnode - > flags ) ) {
_debug ( " file already deleted " ) ;
ret = - ESTALE ;
goto error_unlock ;
}
/* if the vnode's data version number changed then its contents are
* different */
if ( test_and_clear_bit ( AFS_VNODE_ZAP_DATA , & vnode - > flags ) ) {
_debug ( " zap data {%x:%d} " , vnode - > fid . vid , vnode - > fid . vnode ) ;
invalidate_remote_inode ( & vnode - > vfs_inode ) ;
}
clear_bit ( AFS_VNODE_MODIFIED , & vnode - > flags ) ;
mutex_unlock ( & vnode - > validate_lock ) ;
valid :
_leave ( " = 0 " ) ;
return 0 ;
error_unlock :
mutex_unlock ( & vnode - > validate_lock ) ;
_leave ( " = %d " , ret ) ;
return ret ;
}
2005-04-16 15:20:36 -07:00
/*
* read the attributes of an inode
*/
int afs_inode_getattr ( struct vfsmount * mnt , struct dentry * dentry ,
struct kstat * stat )
{
struct inode * inode ;
inode = dentry - > d_inode ;
_enter ( " { ino=%lu v=%lu } " , inode - > i_ino , inode - > i_version ) ;
generic_fillattr ( inode , stat ) ;
return 0 ;
2007-04-26 15:49:28 -07:00
}
2005-04-16 15:20:36 -07:00
/*
* clear an AFS inode
*/
void afs_clear_inode ( struct inode * inode )
{
2007-04-26 15:57:07 -07:00
struct afs_permits * permits ;
2005-04-16 15:20:36 -07:00
struct afs_vnode * vnode ;
vnode = AFS_FS_I ( inode ) ;
2007-04-26 15:59:35 -07:00
_enter ( " {%x:%d.%d} v=%u x=%u t=%u } " ,
vnode - > fid . vid ,
2005-04-16 15:20:36 -07:00
vnode - > fid . vnode ,
2007-04-26 15:59:35 -07:00
vnode - > fid . unique ,
2005-04-16 15:20:36 -07:00
vnode - > cb_version ,
vnode - > cb_expiry ,
2007-04-26 15:55:03 -07:00
vnode - > cb_type ) ;
2005-04-16 15:20:36 -07:00
2007-04-26 15:55:03 -07:00
_debug ( " CLEAR INODE %p " , inode ) ;
ASSERTCMP ( inode - > i_ino , = = , vnode - > fid . vnode ) ;
afs_give_up_callback ( vnode ) ;
if ( vnode - > server ) {
spin_lock ( & vnode - > server - > fs_lock ) ;
rb_erase ( & vnode - > server_rb , & vnode - > server - > fs_vnodes ) ;
spin_unlock ( & vnode - > server - > fs_lock ) ;
afs_put_server ( vnode - > server ) ;
vnode - > server = NULL ;
}
2005-04-16 15:20:36 -07:00
2007-04-26 15:55:03 -07:00
ASSERT ( ! vnode - > cb_promised ) ;
2005-04-16 15:20:36 -07:00
# ifdef AFS_CACHING_SUPPORT
cachefs_relinquish_cookie ( vnode - > cache , 0 ) ;
vnode - > cache = NULL ;
# endif
2007-04-26 15:57:07 -07:00
mutex_lock ( & vnode - > permits_lock ) ;
permits = vnode - > permits ;
rcu_assign_pointer ( vnode - > permits , NULL ) ;
mutex_unlock ( & vnode - > permits_lock ) ;
if ( permits )
call_rcu ( & permits - > rcu , afs_zap_permits ) ;
2005-04-16 15:20:36 -07:00
_leave ( " " ) ;
2007-04-26 15:49:28 -07:00
}