2005-09-10 00:04:19 +04:00
/*
* linux / fs / 9 p / vfs_inode . c
*
2005-09-10 00:04:26 +04:00
* This file contains vfs inode ops for the 9 P2000 protocol .
2005-09-10 00:04:19 +04:00
*
* Copyright ( C ) 2004 by Eric Van Hensbergen < ericvh @ gmail . com >
* Copyright ( C ) 2002 by Ron Minnich < rminnich @ lanl . gov >
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to :
* Free Software Foundation
* 51 Franklin Street , Fifth Floor
* Boston , MA 02111 - 1301 USA
*
*/
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/fs.h>
# include <linux/file.h>
# include <linux/pagemap.h>
# include <linux/stat.h>
# include <linux/string.h>
# include <linux/smp_lock.h>
# include <linux/inet.h>
# include <linux/namei.h>
# include <linux/idr.h>
# include "debug.h"
# include "v9fs.h"
# include "9p.h"
# include "v9fs_vfs.h"
# include "fid.h"
static struct inode_operations v9fs_dir_inode_operations ;
2005-09-10 00:04:27 +04:00
static struct inode_operations v9fs_dir_inode_operations_ext ;
2005-09-10 00:04:19 +04:00
static struct inode_operations v9fs_file_inode_operations ;
static struct inode_operations v9fs_symlink_inode_operations ;
/**
* unixmode2p9mode - convert unix mode bits to plan 9
* @ v9ses : v9fs session information
* @ mode : mode to convert
*
*/
2005-09-10 00:04:26 +04:00
static int unixmode2p9mode ( struct v9fs_session_info * v9ses , int mode )
2005-09-10 00:04:19 +04:00
{
int res ;
res = mode & 0777 ;
if ( S_ISDIR ( mode ) )
res | = V9FS_DMDIR ;
if ( v9ses - > extended ) {
if ( S_ISLNK ( mode ) )
res | = V9FS_DMSYMLINK ;
if ( v9ses - > nodev = = 0 ) {
if ( S_ISSOCK ( mode ) )
res | = V9FS_DMSOCKET ;
if ( S_ISFIFO ( mode ) )
res | = V9FS_DMNAMEDPIPE ;
if ( S_ISBLK ( mode ) )
res | = V9FS_DMDEVICE ;
if ( S_ISCHR ( mode ) )
res | = V9FS_DMDEVICE ;
}
if ( ( mode & S_ISUID ) = = S_ISUID )
res | = V9FS_DMSETUID ;
if ( ( mode & S_ISGID ) = = S_ISGID )
res | = V9FS_DMSETGID ;
if ( ( mode & V9FS_DMLINK ) )
res | = V9FS_DMLINK ;
}
return res ;
}
/**
* p9mode2unixmode - convert plan9 mode bits to unix mode bits
* @ v9ses : v9fs session information
* @ mode : mode to convert
*
*/
2005-09-10 00:04:26 +04:00
static int p9mode2unixmode ( struct v9fs_session_info * v9ses , int mode )
2005-09-10 00:04:19 +04:00
{
int res ;
res = mode & 0777 ;
if ( ( mode & V9FS_DMDIR ) = = V9FS_DMDIR )
res | = S_IFDIR ;
else if ( ( mode & V9FS_DMSYMLINK ) & & ( v9ses - > extended ) )
res | = S_IFLNK ;
else if ( ( mode & V9FS_DMSOCKET ) & & ( v9ses - > extended )
& & ( v9ses - > nodev = = 0 ) )
res | = S_IFSOCK ;
else if ( ( mode & V9FS_DMNAMEDPIPE ) & & ( v9ses - > extended )
& & ( v9ses - > nodev = = 0 ) )
res | = S_IFIFO ;
else if ( ( mode & V9FS_DMDEVICE ) & & ( v9ses - > extended )
& & ( v9ses - > nodev = = 0 ) )
res | = S_IFBLK ;
else
res | = S_IFREG ;
if ( v9ses - > extended ) {
if ( ( mode & V9FS_DMSETUID ) = = V9FS_DMSETUID )
res | = S_ISUID ;
if ( ( mode & V9FS_DMSETGID ) = = V9FS_DMSETGID )
res | = S_ISGID ;
}
return res ;
}
/**
2006-01-08 12:05:00 +03:00
* v9fs_blank_wstat - helper function to setup a 9 P stat structure
2005-09-10 00:04:19 +04:00
* @ v9ses : 9 P session info ( for determining extended mode )
2006-01-08 12:05:00 +03:00
* @ wstat : structure to initialize
2005-09-10 00:04:19 +04:00
*
*/
2005-09-10 00:04:26 +04:00
static void
2006-01-08 12:05:00 +03:00
v9fs_blank_wstat ( struct v9fs_wstat * wstat )
2005-09-10 00:04:19 +04:00
{
2006-01-08 12:05:00 +03:00
wstat - > type = ~ 0 ;
wstat - > dev = ~ 0 ;
wstat - > qid . type = ~ 0 ;
wstat - > qid . version = ~ 0 ;
* ( ( long long * ) & wstat - > qid . path ) = ~ 0 ;
wstat - > mode = ~ 0 ;
wstat - > atime = ~ 0 ;
wstat - > mtime = ~ 0 ;
wstat - > length = ~ 0 ;
wstat - > name = NULL ;
wstat - > uid = NULL ;
wstat - > gid = NULL ;
wstat - > muid = NULL ;
wstat - > n_uid = ~ 0 ;
wstat - > n_gid = ~ 0 ;
wstat - > n_muid = ~ 0 ;
wstat - > extension = NULL ;
2005-09-10 00:04:19 +04:00
}
/**
* v9fs_get_inode - helper function to setup an inode
* @ sb : superblock
* @ mode : mode to setup inode with
*
*/
struct inode * v9fs_get_inode ( struct super_block * sb , int mode )
{
struct inode * inode = NULL ;
2005-09-10 00:04:27 +04:00
struct v9fs_session_info * v9ses = sb - > s_fs_info ;
2005-09-10 00:04:19 +04:00
dprintk ( DEBUG_VFS , " super block: %p mode: %o \n " , sb , mode ) ;
inode = new_inode ( sb ) ;
if ( inode ) {
inode - > i_mode = mode ;
inode - > i_uid = current - > fsuid ;
inode - > i_gid = current - > fsgid ;
inode - > i_blksize = sb - > s_blocksize ;
inode - > i_blocks = 0 ;
inode - > i_rdev = 0 ;
inode - > i_atime = inode - > i_mtime = inode - > i_ctime = CURRENT_TIME ;
2006-01-19 04:43:02 +03:00
inode - > i_mapping - > a_ops = & v9fs_addr_operations ;
2005-09-10 00:04:19 +04:00
switch ( mode & S_IFMT ) {
case S_IFIFO :
case S_IFBLK :
case S_IFCHR :
case S_IFSOCK :
2005-09-10 00:04:27 +04:00
if ( ! v9ses - > extended ) {
dprintk ( DEBUG_ERROR , " special files without extended mode \n " ) ;
return ERR_PTR ( - EINVAL ) ;
}
2005-09-10 00:04:27 +04:00
init_special_inode ( inode , inode - > i_mode ,
inode - > i_rdev ) ;
break ;
2005-09-10 00:04:19 +04:00
case S_IFREG :
inode - > i_op = & v9fs_file_inode_operations ;
inode - > i_fop = & v9fs_file_operations ;
break ;
2005-09-10 00:04:27 +04:00
case S_IFLNK :
if ( ! v9ses - > extended ) {
dprintk ( DEBUG_ERROR , " extended modes used w/o 9P2000.u \n " ) ;
return ERR_PTR ( - EINVAL ) ;
}
inode - > i_op = & v9fs_symlink_inode_operations ;
break ;
2005-09-10 00:04:19 +04:00
case S_IFDIR :
inode - > i_nlink + + ;
2005-09-10 00:04:27 +04:00
if ( v9ses - > extended )
inode - > i_op = & v9fs_dir_inode_operations_ext ;
else
inode - > i_op = & v9fs_dir_inode_operations ;
2005-09-10 00:04:19 +04:00
inode - > i_fop = & v9fs_dir_operations ;
break ;
default :
dprintk ( DEBUG_ERROR , " BAD mode 0x%x S_IFMT 0x%x \n " ,
mode , mode & S_IFMT ) ;
return ERR_PTR ( - EINVAL ) ;
}
} else {
eprintk ( KERN_WARNING , " Problem allocating inode \n " ) ;
return ERR_PTR ( - ENOMEM ) ;
}
return inode ;
}
/**
* v9fs_create - helper function to create files and directories
* @ dir : directory inode file is being created in
* @ file_dentry : dentry file is being created in
* @ perm : permissions file is being created with
2005-09-10 00:04:26 +04:00
* @ open_mode : resulting open mode for file
2005-09-10 00:04:19 +04:00
*
*/
static int
v9fs_create ( struct inode * dir ,
struct dentry * file_dentry ,
unsigned int perm , unsigned int open_mode )
{
struct v9fs_session_info * v9ses = v9fs_inode2v9ses ( dir ) ;
struct super_block * sb = dir - > i_sb ;
struct v9fs_fid * dirfid =
2005-09-28 08:45:24 +04:00
v9fs_fid_lookup ( file_dentry - > d_parent ) ;
2005-09-10 00:04:19 +04:00
struct v9fs_fid * fid = NULL ;
struct inode * file_inode = NULL ;
struct v9fs_fcall * fcall = NULL ;
struct v9fs_qid qid ;
int dirfidnum = - 1 ;
long newfid = - 1 ;
int result = 0 ;
unsigned int iounit = 0 ;
2005-09-28 08:45:24 +04:00
int wfidno = - 1 ;
2006-01-08 12:04:58 +03:00
int err ;
2005-09-10 00:04:19 +04:00
perm = unixmode2p9mode ( v9ses , perm ) ;
dprintk ( DEBUG_VFS , " dir: %p dentry: %p perm: %o mode: %o \n " , dir ,
file_dentry , perm , open_mode ) ;
if ( ! dirfid )
return - EBADF ;
dirfidnum = dirfid - > fid ;
if ( dirfidnum < 0 ) {
dprintk ( DEBUG_ERROR , " No fid for the directory #%lu \n " ,
dir - > i_ino ) ;
return - EBADF ;
}
if ( file_dentry - > d_inode ) {
dprintk ( DEBUG_ERROR ,
" Odd. There is an inode for dir %lu, name :%s: \n " ,
dir - > i_ino , file_dentry - > d_name . name ) ;
return - EEXIST ;
}
newfid = v9fs_get_idpool ( & v9ses - > fidpool ) ;
if ( newfid < 0 ) {
eprintk ( KERN_WARNING , " no free fids available \n " ) ;
return - ENOSPC ;
}
result = v9fs_t_walk ( v9ses , dirfidnum , newfid , NULL , & fcall ) ;
if ( result < 0 ) {
2006-01-08 12:05:00 +03:00
PRINT_FCALL_ERROR ( " clone error " , fcall ) ;
2005-09-10 00:04:19 +04:00
v9fs_put_idpool ( newfid , & v9ses - > fidpool ) ;
2005-09-28 08:45:24 +04:00
newfid = - 1 ;
2005-09-10 00:04:19 +04:00
goto CleanUpFid ;
}
kfree ( fcall ) ;
2006-01-08 12:04:58 +03:00
fcall = NULL ;
2005-09-10 00:04:19 +04:00
result = v9fs_t_create ( v9ses , newfid , ( char * ) file_dentry - > d_name . name ,
perm , open_mode , & fcall ) ;
if ( result < 0 ) {
2006-01-08 12:05:00 +03:00
PRINT_FCALL_ERROR ( " create fails " , fcall ) ;
2005-09-10 00:04:19 +04:00
goto CleanUpFid ;
}
iounit = fcall - > params . rcreate . iounit ;
qid = fcall - > params . rcreate . qid ;
kfree ( fcall ) ;
2006-01-08 12:04:58 +03:00
fcall = NULL ;
2005-09-10 00:04:19 +04:00
2006-01-08 12:04:58 +03:00
if ( ! ( perm & V9FS_DMDIR ) ) {
fid = v9fs_fid_create ( file_dentry , v9ses , newfid , 1 ) ;
dprintk ( DEBUG_VFS , " fid %p %d \n " , fid , fid - > fidcreate ) ;
if ( ! fid ) {
result = - ENOMEM ;
goto CleanUpFid ;
}
2005-09-10 00:04:19 +04:00
2006-01-08 12:04:58 +03:00
fid - > qid = qid ;
fid - > iounit = iounit ;
} else {
err = v9fs_t_clunk ( v9ses , newfid ) ;
2006-01-08 12:04:59 +03:00
newfid = - 1 ;
2006-01-08 12:04:58 +03:00
if ( err < 0 )
dprintk ( DEBUG_ERROR , " clunk for mkdir failed: %d \n " , err ) ;
}
2005-09-28 08:45:24 +04:00
/* walk to the newly created file and put the fid in the dentry */
wfidno = v9fs_get_idpool ( & v9ses - > fidpool ) ;
2006-01-08 12:04:59 +03:00
if ( wfidno < 0 ) {
2005-09-28 08:45:24 +04:00
eprintk ( KERN_WARNING , " no free fids available \n " ) ;
return - ENOSPC ;
}
result = v9fs_t_walk ( v9ses , dirfidnum , wfidno ,
2006-01-08 12:04:58 +03:00
( char * ) file_dentry - > d_name . name , & fcall ) ;
2005-09-28 08:45:24 +04:00
if ( result < 0 ) {
2006-01-08 12:05:00 +03:00
PRINT_FCALL_ERROR ( " clone error " , fcall ) ;
2005-09-28 08:45:24 +04:00
v9fs_put_idpool ( wfidno , & v9ses - > fidpool ) ;
wfidno = - 1 ;
goto CleanUpFid ;
}
2006-01-08 12:04:58 +03:00
kfree ( fcall ) ;
fcall = NULL ;
2005-09-28 08:45:24 +04:00
if ( ! v9fs_fid_create ( file_dentry , v9ses , wfidno , 0 ) ) {
2006-01-08 12:04:58 +03:00
v9fs_put_idpool ( wfidno , & v9ses - > fidpool ) ;
2005-09-28 08:45:24 +04:00
goto CleanUpFid ;
}
2005-09-10 00:04:19 +04:00
if ( ( perm & V9FS_DMSYMLINK ) | | ( perm & V9FS_DMLINK ) | |
( perm & V9FS_DMNAMEDPIPE ) | | ( perm & V9FS_DMSOCKET ) | |
( perm & V9FS_DMDEVICE ) )
return 0 ;
2006-01-08 12:04:59 +03:00
result = v9fs_t_stat ( v9ses , wfidno , & fcall ) ;
2005-09-10 00:04:19 +04:00
if ( result < 0 ) {
2006-01-08 12:05:00 +03:00
PRINT_FCALL_ERROR ( " stat error " , fcall ) ;
2005-09-10 00:04:19 +04:00
goto CleanUpFid ;
}
2006-01-08 12:05:00 +03:00
file_inode = v9fs_get_inode ( sb ,
p9mode2unixmode ( v9ses , fcall - > params . rstat . stat . mode ) ) ;
2005-09-10 00:04:19 +04:00
if ( ( ! file_inode ) | | IS_ERR ( file_inode ) ) {
dprintk ( DEBUG_ERROR , " create inode failed \n " ) ;
result = - EBADF ;
goto CleanUpFid ;
}
2006-01-08 12:05:00 +03:00
v9fs_stat2inode ( & fcall - > params . rstat . stat , file_inode , sb ) ;
2005-09-10 00:04:19 +04:00
kfree ( fcall ) ;
2005-11-29 00:44:05 +03:00
fcall = NULL ;
file_dentry - > d_op = & v9fs_dentry_operations ;
2005-09-10 00:04:19 +04:00
d_instantiate ( file_dentry , file_inode ) ;
return 0 ;
CleanUpFid :
kfree ( fcall ) ;
2006-01-08 12:04:58 +03:00
fcall = NULL ;
2005-09-10 00:04:19 +04:00
2005-09-28 08:45:24 +04:00
if ( newfid > = 0 ) {
2006-01-08 12:04:58 +03:00
err = v9fs_t_clunk ( v9ses , newfid ) ;
if ( err < 0 )
dprintk ( DEBUG_ERROR , " clunk failed: %d \n " , err ) ;
2005-09-28 08:45:24 +04:00
}
if ( wfidno > = 0 ) {
2006-01-08 12:04:58 +03:00
err = v9fs_t_clunk ( v9ses , wfidno ) ;
if ( err < 0 )
dprintk ( DEBUG_ERROR , " clunk failed: %d \n " , err ) ;
2005-09-10 00:04:19 +04:00
}
return result ;
}
/**
* v9fs_remove - helper function to remove files and directories
2005-09-10 00:04:26 +04:00
* @ dir : directory inode that is being deleted
* @ file : dentry that is being deleted
* @ rmdir : removing a directory
2005-09-10 00:04:19 +04:00
*
*/
static int v9fs_remove ( struct inode * dir , struct dentry * file , int rmdir )
{
struct v9fs_fcall * fcall = NULL ;
struct super_block * sb = NULL ;
struct v9fs_session_info * v9ses = NULL ;
struct v9fs_fid * v9fid = NULL ;
struct inode * file_inode = NULL ;
int fid = - 1 ;
int result = 0 ;
dprintk ( DEBUG_VFS , " inode: %p dentry: %p rmdir: %d \n " , dir , file ,
rmdir ) ;
file_inode = file - > d_inode ;
sb = file_inode - > i_sb ;
v9ses = v9fs_inode2v9ses ( file_inode ) ;
2005-09-28 08:45:24 +04:00
v9fid = v9fs_fid_lookup ( file ) ;
2005-09-10 00:04:19 +04:00
if ( ! v9fid ) {
dprintk ( DEBUG_ERROR ,
" no v9fs_fid \n " ) ;
return - EBADF ;
}
fid = v9fid - > fid ;
if ( fid < 0 ) {
dprintk ( DEBUG_ERROR , " inode #%lu, no fid! \n " ,
file_inode - > i_ino ) ;
return - EBADF ;
}
result = v9fs_t_remove ( v9ses , fid , & fcall ) ;
2006-01-08 12:05:00 +03:00
if ( result < 0 ) {
PRINT_FCALL_ERROR ( " remove fails " , fcall ) ;
} else {
2005-09-10 00:04:19 +04:00
v9fs_put_idpool ( fid , & v9ses - > fidpool ) ;
v9fs_fid_destroy ( v9fid ) ;
}
kfree ( fcall ) ;
return result ;
}
/**
* v9fs_vfs_create - VFS hook to create files
* @ inode : directory inode that is being deleted
* @ dentry : dentry that is being deleted
* @ perm : create permissions
* @ nd : path information
*
*/
static int
v9fs_vfs_create ( struct inode * inode , struct dentry * dentry , int perm ,
struct nameidata * nd )
{
return v9fs_create ( inode , dentry , perm , O_RDWR ) ;
}
/**
* v9fs_vfs_mkdir - VFS mkdir hook to create a directory
2005-09-10 00:04:26 +04:00
* @ inode : inode that is being unlinked
2005-09-10 00:04:19 +04:00
* @ dentry : dentry that is being unlinked
* @ mode : mode for new directory
*
*/
static int v9fs_vfs_mkdir ( struct inode * inode , struct dentry * dentry , int mode )
{
return v9fs_create ( inode , dentry , mode | S_IFDIR , O_RDONLY ) ;
}
/**
* v9fs_vfs_lookup - VFS lookup hook to " walk " to a new inode
* @ dir : inode that is being walked from
* @ dentry : dentry that is being walked to ?
* @ nameidata : path data
*
*/
static struct dentry * v9fs_vfs_lookup ( struct inode * dir , struct dentry * dentry ,
struct nameidata * nameidata )
{
struct super_block * sb ;
struct v9fs_session_info * v9ses ;
struct v9fs_fid * dirfid ;
struct v9fs_fid * fid ;
struct inode * inode ;
struct v9fs_fcall * fcall = NULL ;
int dirfidnum = - 1 ;
int newfid = - 1 ;
int result = 0 ;
dprintk ( DEBUG_VFS , " dir: %p dentry: (%s) %p nameidata: %p \n " ,
dir , dentry - > d_iname , dentry , nameidata ) ;
sb = dir - > i_sb ;
v9ses = v9fs_inode2v9ses ( dir ) ;
2005-09-28 08:45:24 +04:00
dirfid = v9fs_fid_lookup ( dentry - > d_parent ) ;
2005-09-10 00:04:19 +04:00
if ( ! dirfid ) {
dprintk ( DEBUG_ERROR , " no dirfid \n " ) ;
return ERR_PTR ( - EINVAL ) ;
}
dirfidnum = dirfid - > fid ;
if ( dirfidnum < 0 ) {
dprintk ( DEBUG_ERROR , " no dirfid for inode %p, #%lu \n " ,
dir , dir - > i_ino ) ;
return ERR_PTR ( - EBADF ) ;
}
newfid = v9fs_get_idpool ( & v9ses - > fidpool ) ;
if ( newfid < 0 ) {
eprintk ( KERN_WARNING , " newfid fails! \n " ) ;
return ERR_PTR ( - ENOSPC ) ;
}
result =
v9fs_t_walk ( v9ses , dirfidnum , newfid , ( char * ) dentry - > d_name . name ,
NULL ) ;
if ( result < 0 ) {
v9fs_put_idpool ( newfid , & v9ses - > fidpool ) ;
if ( result = = - ENOENT ) {
d_add ( dentry , NULL ) ;
2005-09-28 08:45:24 +04:00
dprintk ( DEBUG_VFS ,
2005-09-10 00:04:19 +04:00
" Return negative dentry %p count %d \n " ,
dentry , atomic_read ( & dentry - > d_count ) ) ;
return NULL ;
}
dprintk ( DEBUG_ERROR , " walk error:%d \n " , result ) ;
goto FreeFcall ;
}
result = v9fs_t_stat ( v9ses , newfid , & fcall ) ;
if ( result < 0 ) {
dprintk ( DEBUG_ERROR , " stat error \n " ) ;
goto FreeFcall ;
}
2006-01-08 12:05:00 +03:00
inode = v9fs_get_inode ( sb , p9mode2unixmode ( v9ses ,
fcall - > params . rstat . stat . mode ) ) ;
2005-09-10 00:04:19 +04:00
if ( IS_ERR ( inode ) & & ( PTR_ERR ( inode ) = = - ENOSPC ) ) {
eprintk ( KERN_WARNING , " inode alloc failes, returns %ld \n " ,
PTR_ERR ( inode ) ) ;
result = - ENOSPC ;
goto FreeFcall ;
}
2006-01-08 12:05:00 +03:00
inode - > i_ino = v9fs_qid2ino ( & fcall - > params . rstat . stat . qid ) ;
2005-09-10 00:04:19 +04:00
2005-09-28 08:45:24 +04:00
fid = v9fs_fid_create ( dentry , v9ses , newfid , 0 ) ;
2005-09-10 00:04:19 +04:00
if ( fid = = NULL ) {
dprintk ( DEBUG_ERROR , " couldn't insert \n " ) ;
result = - ENOMEM ;
goto FreeFcall ;
}
2006-01-08 12:05:00 +03:00
fid - > qid = fcall - > params . rstat . stat . qid ;
2005-09-10 00:04:19 +04:00
dentry - > d_op = & v9fs_dentry_operations ;
2006-01-08 12:05:00 +03:00
v9fs_stat2inode ( & fcall - > params . rstat . stat , inode , inode - > i_sb ) ;
2005-09-10 00:04:19 +04:00
d_add ( dentry , inode ) ;
kfree ( fcall ) ;
return NULL ;
FreeFcall :
kfree ( fcall ) ;
return ERR_PTR ( result ) ;
}
/**
* v9fs_vfs_unlink - VFS unlink hook to delete an inode
* @ i : inode that is being unlinked
2005-09-10 00:04:26 +04:00
* @ d : dentry that is being unlinked
2005-09-10 00:04:19 +04:00
*
*/
static int v9fs_vfs_unlink ( struct inode * i , struct dentry * d )
{
return v9fs_remove ( i , d , 0 ) ;
}
/**
* v9fs_vfs_rmdir - VFS unlink hook to delete a directory
* @ i : inode that is being unlinked
2005-09-10 00:04:26 +04:00
* @ d : dentry that is being unlinked
2005-09-10 00:04:19 +04:00
*
*/
static int v9fs_vfs_rmdir ( struct inode * i , struct dentry * d )
{
return v9fs_remove ( i , d , 1 ) ;
}
/**
* v9fs_vfs_rename - VFS hook to rename an inode
* @ old_dir : old dir inode
* @ old_dentry : old dentry
* @ new_dir : new dir inode
* @ new_dentry : new dentry
*
*/
static int
v9fs_vfs_rename ( struct inode * old_dir , struct dentry * old_dentry ,
struct inode * new_dir , struct dentry * new_dentry )
{
struct inode * old_inode = old_dentry - > d_inode ;
struct v9fs_session_info * v9ses = v9fs_inode2v9ses ( old_inode ) ;
2005-09-28 08:45:24 +04:00
struct v9fs_fid * oldfid = v9fs_fid_lookup ( old_dentry ) ;
2005-09-10 00:04:19 +04:00
struct v9fs_fid * olddirfid =
2005-09-28 08:45:24 +04:00
v9fs_fid_lookup ( old_dentry - > d_parent ) ;
2005-09-10 00:04:19 +04:00
struct v9fs_fid * newdirfid =
2005-09-28 08:45:24 +04:00
v9fs_fid_lookup ( new_dentry - > d_parent ) ;
2006-01-08 12:05:00 +03:00
struct v9fs_wstat wstat ;
2005-09-10 00:04:19 +04:00
struct v9fs_fcall * fcall = NULL ;
int fid = - 1 ;
int olddirfidnum = - 1 ;
int newdirfidnum = - 1 ;
int retval = 0 ;
dprintk ( DEBUG_VFS , " \n " ) ;
if ( ( ! oldfid ) | | ( ! olddirfid ) | | ( ! newdirfid ) ) {
dprintk ( DEBUG_ERROR , " problem with arguments \n " ) ;
return - EBADF ;
}
/* 9P can only handle file rename in the same directory */
if ( memcmp ( & olddirfid - > qid , & newdirfid - > qid , sizeof ( newdirfid - > qid ) ) ) {
dprintk ( DEBUG_ERROR , " old dir and new dir are different \n " ) ;
retval = - EPERM ;
goto FreeFcallnBail ;
}
fid = oldfid - > fid ;
olddirfidnum = olddirfid - > fid ;
newdirfidnum = newdirfid - > fid ;
if ( fid < 0 ) {
dprintk ( DEBUG_ERROR , " no fid for old file #%lu \n " ,
old_inode - > i_ino ) ;
retval = - EBADF ;
goto FreeFcallnBail ;
}
2006-01-08 12:05:00 +03:00
v9fs_blank_wstat ( & wstat ) ;
wstat . muid = v9ses - > name ;
wstat . name = ( char * ) new_dentry - > d_name . name ;
2005-09-10 00:04:19 +04:00
2006-01-08 12:05:00 +03:00
retval = v9fs_t_wstat ( v9ses , fid , & wstat , & fcall ) ;
2005-09-10 00:04:19 +04:00
FreeFcallnBail :
if ( retval < 0 )
2006-01-08 12:05:00 +03:00
PRINT_FCALL_ERROR ( " wstat error " , fcall ) ;
2005-09-10 00:04:19 +04:00
kfree ( fcall ) ;
return retval ;
}
/**
2006-01-10 02:10:13 +03:00
* v9fs_vfs_getattr - retrieve file metadata
2005-09-10 00:04:19 +04:00
* @ mnt - mount information
* @ dentry - file to get attributes on
* @ stat - metadata structure to populate
*
*/
static int
v9fs_vfs_getattr ( struct vfsmount * mnt , struct dentry * dentry ,
struct kstat * stat )
{
struct v9fs_fcall * fcall = NULL ;
struct v9fs_session_info * v9ses = v9fs_inode2v9ses ( dentry - > d_inode ) ;
2005-09-28 08:45:24 +04:00
struct v9fs_fid * fid = v9fs_fid_lookup ( dentry ) ;
2005-09-10 00:04:19 +04:00
int err = - EPERM ;
dprintk ( DEBUG_VFS , " dentry: %p \n " , dentry ) ;
if ( ! fid ) {
dprintk ( DEBUG_ERROR ,
" couldn't find fid associated with dentry \n " ) ;
return - EBADF ;
}
err = v9fs_t_stat ( v9ses , fid - > fid , & fcall ) ;
if ( err < 0 )
dprintk ( DEBUG_ERROR , " stat error \n " ) ;
else {
2006-01-08 12:05:00 +03:00
v9fs_stat2inode ( & fcall - > params . rstat . stat , dentry - > d_inode ,
2005-09-10 00:04:19 +04:00
dentry - > d_inode - > i_sb ) ;
generic_fillattr ( dentry - > d_inode , stat ) ;
}
kfree ( fcall ) ;
return err ;
}
/**
* v9fs_vfs_setattr - set file metadata
* @ dentry : file whose metadata to set
* @ iattr : metadata assignment structure
*
*/
static int v9fs_vfs_setattr ( struct dentry * dentry , struct iattr * iattr )
{
struct v9fs_session_info * v9ses = v9fs_inode2v9ses ( dentry - > d_inode ) ;
2005-09-28 08:45:24 +04:00
struct v9fs_fid * fid = v9fs_fid_lookup ( dentry ) ;
2005-09-10 00:04:19 +04:00
struct v9fs_fcall * fcall = NULL ;
2006-01-08 12:05:00 +03:00
struct v9fs_wstat wstat ;
2005-09-10 00:04:19 +04:00
int res = - EPERM ;
dprintk ( DEBUG_VFS , " \n " ) ;
2005-09-10 00:04:26 +04:00
2005-09-10 00:04:19 +04:00
if ( ! fid ) {
dprintk ( DEBUG_ERROR ,
" Couldn't find fid associated with dentry \n " ) ;
return - EBADF ;
}
2006-01-08 12:05:00 +03:00
v9fs_blank_wstat ( & wstat ) ;
2005-09-10 00:04:19 +04:00
if ( iattr - > ia_valid & ATTR_MODE )
2006-01-08 12:05:00 +03:00
wstat . mode = unixmode2p9mode ( v9ses , iattr - > ia_mode ) ;
2005-09-10 00:04:19 +04:00
if ( iattr - > ia_valid & ATTR_MTIME )
2006-01-08 12:05:00 +03:00
wstat . mtime = iattr - > ia_mtime . tv_sec ;
2005-09-10 00:04:19 +04:00
if ( iattr - > ia_valid & ATTR_ATIME )
2006-01-08 12:05:00 +03:00
wstat . atime = iattr - > ia_atime . tv_sec ;
2005-09-10 00:04:19 +04:00
if ( iattr - > ia_valid & ATTR_SIZE )
2006-01-08 12:05:00 +03:00
wstat . length = iattr - > ia_size ;
2005-09-10 00:04:19 +04:00
if ( v9ses - > extended ) {
2006-01-08 12:05:00 +03:00
if ( iattr - > ia_valid & ATTR_UID )
wstat . n_uid = iattr - > ia_uid ;
2005-09-10 00:04:19 +04:00
2006-01-08 12:05:00 +03:00
if ( iattr - > ia_valid & ATTR_GID )
wstat . n_gid = iattr - > ia_gid ;
2005-09-10 00:04:19 +04:00
}
2006-01-08 12:05:00 +03:00
res = v9fs_t_wstat ( v9ses , fid - > fid , & wstat , & fcall ) ;
2005-09-10 00:04:19 +04:00
if ( res < 0 )
2006-01-08 12:05:00 +03:00
PRINT_FCALL_ERROR ( " wstat error " , fcall ) ;
2005-09-10 00:04:19 +04:00
kfree ( fcall ) ;
if ( res > = 0 )
res = inode_setattr ( dentry - > d_inode , iattr ) ;
return res ;
}
/**
2006-01-08 12:05:00 +03:00
* v9fs_stat2inode - populate an inode structure with mistat info
* @ stat : Plan 9 metadata ( mistat ) structure
2005-09-10 00:04:19 +04:00
* @ inode : inode to populate
* @ sb : superblock of filesystem
*
*/
void
2006-01-08 12:05:00 +03:00
v9fs_stat2inode ( struct v9fs_stat * stat , struct inode * inode ,
struct super_block * sb )
2005-09-10 00:04:19 +04:00
{
2006-01-08 12:05:02 +03:00
int n ;
2006-01-08 12:05:00 +03:00
char ext [ 32 ] ;
2005-09-10 00:04:19 +04:00
struct v9fs_session_info * v9ses = sb - > s_fs_info ;
inode - > i_nlink = 1 ;
2006-01-08 12:05:00 +03:00
inode - > i_atime . tv_sec = stat - > atime ;
inode - > i_mtime . tv_sec = stat - > mtime ;
inode - > i_ctime . tv_sec = stat - > mtime ;
2005-09-10 00:04:19 +04:00
2006-01-08 12:05:00 +03:00
inode - > i_uid = v9ses - > uid ;
inode - > i_gid = v9ses - > gid ;
2005-09-10 00:04:19 +04:00
if ( v9ses - > extended ) {
2006-01-08 12:05:00 +03:00
inode - > i_uid = stat - > n_uid ;
inode - > i_gid = stat - > n_gid ;
2005-09-10 00:04:19 +04:00
}
2006-01-08 12:05:00 +03:00
inode - > i_mode = p9mode2unixmode ( v9ses , stat - > mode ) ;
2005-09-10 00:04:19 +04:00
if ( ( S_ISBLK ( inode - > i_mode ) ) | | ( S_ISCHR ( inode - > i_mode ) ) ) {
char type = 0 ;
int major = - 1 ;
int minor = - 1 ;
2006-01-08 12:05:00 +03:00
2006-01-08 12:05:02 +03:00
n = stat - > extension . len ;
if ( n > sizeof ( ext ) - 1 )
n = sizeof ( ext ) - 1 ;
memmove ( ext , stat - > extension . str , n ) ;
ext [ n ] = 0 ;
2006-01-08 12:05:00 +03:00
sscanf ( ext , " %c %u %u " , & type , & major , & minor ) ;
2005-09-10 00:04:19 +04:00
switch ( type ) {
case ' c ' :
inode - > i_mode & = ~ S_IFBLK ;
inode - > i_mode | = S_IFCHR ;
break ;
case ' b ' :
break ;
default :
2006-01-08 12:05:00 +03:00
dprintk ( DEBUG_ERROR , " Unknown special type %c (%.*s) \n " ,
type , stat - > extension . len , stat - > extension . str ) ;
2005-09-10 00:04:19 +04:00
} ;
inode - > i_rdev = MKDEV ( major , minor ) ;
} else
inode - > i_rdev = 0 ;
2006-01-08 12:05:00 +03:00
inode - > i_size = stat - > length ;
2005-09-10 00:04:19 +04:00
inode - > i_blksize = sb - > s_blocksize ;
inode - > i_blocks =
( inode - > i_size + inode - > i_blksize - 1 ) > > sb - > s_blocksize_bits ;
}
/**
* v9fs_qid2ino - convert qid into inode number
* @ qid : qid to hash
*
* BUG : potential for inode number collisions ?
*/
ino_t v9fs_qid2ino ( struct v9fs_qid * qid )
{
u64 path = qid - > path + 2 ;
ino_t i = 0 ;
if ( sizeof ( ino_t ) = = sizeof ( path ) )
memcpy ( & i , & path , sizeof ( ino_t ) ) ;
else
i = ( ino_t ) ( path ^ ( path > > 32 ) ) ;
return i ;
}
/**
* v9fs_readlink - read a symlink ' s location ( internal version )
* @ dentry : dentry for symlink
2005-09-10 00:04:26 +04:00
* @ buffer : buffer to load symlink location into
2005-09-10 00:04:19 +04:00
* @ buflen : length of buffer
*
*/
static int v9fs_readlink ( struct dentry * dentry , char * buffer , int buflen )
{
int retval = - EPERM ;
struct v9fs_fcall * fcall = NULL ;
struct v9fs_session_info * v9ses = v9fs_inode2v9ses ( dentry - > d_inode ) ;
2005-09-28 08:45:24 +04:00
struct v9fs_fid * fid = v9fs_fid_lookup ( dentry ) ;
2005-09-10 00:04:19 +04:00
if ( ! fid ) {
dprintk ( DEBUG_ERROR , " could not resolve fid from dentry \n " ) ;
retval = - EBADF ;
goto FreeFcall ;
}
if ( ! v9ses - > extended ) {
retval = - EBADF ;
dprintk ( DEBUG_ERROR , " not extended \n " ) ;
goto FreeFcall ;
}
dprintk ( DEBUG_VFS , " %s \n " , dentry - > d_name . name ) ;
retval = v9fs_t_stat ( v9ses , fid - > fid , & fcall ) ;
if ( retval < 0 ) {
dprintk ( DEBUG_ERROR , " stat error \n " ) ;
goto FreeFcall ;
}
if ( ! fcall )
return - EIO ;
2006-01-08 12:05:00 +03:00
if ( ! ( fcall - > params . rstat . stat . mode & V9FS_DMSYMLINK ) ) {
2005-09-10 00:04:19 +04:00
retval = - EINVAL ;
goto FreeFcall ;
}
/* copy extension buffer into buffer */
2006-02-03 14:04:17 +03:00
if ( fcall - > params . rstat . stat . extension . len + 1 < buflen )
buflen = fcall - > params . rstat . stat . extension . len + 1 ;
2005-09-10 00:04:19 +04:00
2006-01-08 12:05:00 +03:00
memcpy ( buffer , fcall - > params . rstat . stat . extension . str , buflen - 1 ) ;
buffer [ buflen - 1 ] = 0 ;
2005-09-10 00:04:19 +04:00
retval = buflen ;
FreeFcall :
kfree ( fcall ) ;
return retval ;
}
/**
* v9fs_vfs_readlink - read a symlink ' s location
* @ dentry : dentry for symlink
* @ buf : buffer to load symlink location into
* @ buflen : length of buffer
*
*/
static int v9fs_vfs_readlink ( struct dentry * dentry , char __user * buffer ,
int buflen )
{
int retval ;
int ret ;
char * link = __getname ( ) ;
2005-09-23 08:43:52 +04:00
if ( buflen > PATH_MAX )
buflen = PATH_MAX ;
2005-09-10 00:04:19 +04:00
dprintk ( DEBUG_VFS , " dentry: %s (%p) \n " , dentry - > d_iname , dentry ) ;
retval = v9fs_readlink ( dentry , link , buflen ) ;
if ( retval > 0 ) {
if ( ( ret = copy_to_user ( buffer , link , retval ) ) ! = 0 ) {
dprintk ( DEBUG_ERROR , " problem copying to user: %d \n " ,
ret ) ;
retval = ret ;
}
}
2005-11-07 11:59:36 +03:00
__putname ( link ) ;
2005-09-10 00:04:19 +04:00
return retval ;
}
/**
* v9fs_vfs_follow_link - follow a symlink path
* @ dentry : dentry for symlink
* @ nd : nameidata
*
*/
static void * v9fs_vfs_follow_link ( struct dentry * dentry , struct nameidata * nd )
{
int len = 0 ;
char * link = __getname ( ) ;
dprintk ( DEBUG_VFS , " %s n " , dentry - > d_name . name ) ;
if ( ! link )
link = ERR_PTR ( - ENOMEM ) ;
else {
2006-02-03 14:04:17 +03:00
len = v9fs_readlink ( dentry , link , PATH_MAX ) ;
2005-09-10 00:04:19 +04:00
if ( len < 0 ) {
2005-11-07 11:59:36 +03:00
__putname ( link ) ;
2005-09-10 00:04:19 +04:00
link = ERR_PTR ( len ) ;
} else
link [ len ] = 0 ;
}
nd_set_link ( nd , link ) ;
return NULL ;
}
/**
* v9fs_vfs_put_link - release a symlink path
* @ dentry : dentry for symlink
* @ nd : nameidata
*
*/
static void v9fs_vfs_put_link ( struct dentry * dentry , struct nameidata * nd , void * p )
{
char * s = nd_get_link ( nd ) ;
dprintk ( DEBUG_VFS , " %s %s \n " , dentry - > d_name . name , s ) ;
if ( ! IS_ERR ( s ) )
2005-11-07 11:59:36 +03:00
__putname ( s ) ;
2005-09-10 00:04:19 +04:00
}
2006-01-08 12:05:00 +03:00
static int v9fs_vfs_mkspecial ( struct inode * dir , struct dentry * dentry ,
int mode , const char * extension )
2005-09-10 00:04:19 +04:00
{
2006-01-08 12:05:00 +03:00
int err , retval ;
struct v9fs_session_info * v9ses ;
struct v9fs_fcall * fcall ;
struct v9fs_fid * fid ;
struct v9fs_wstat wstat ;
2005-09-10 00:04:19 +04:00
2006-01-08 12:05:00 +03:00
v9ses = v9fs_inode2v9ses ( dir ) ;
retval = - EPERM ;
fcall = NULL ;
2005-09-10 00:04:19 +04:00
if ( ! v9ses - > extended ) {
dprintk ( DEBUG_ERROR , " not extended \n " ) ;
2006-01-08 12:05:00 +03:00
goto free_mem ;
2005-09-10 00:04:19 +04:00
}
/* issue a create */
2006-01-08 12:05:00 +03:00
retval = v9fs_create ( dir , dentry , mode , 0 ) ;
2005-09-10 00:04:19 +04:00
if ( retval ! = 0 )
2006-01-08 12:05:00 +03:00
goto free_mem ;
2005-09-10 00:04:19 +04:00
2006-01-08 12:05:00 +03:00
fid = v9fs_fid_get_created ( dentry ) ;
if ( ! fid ) {
2005-09-10 00:04:19 +04:00
dprintk ( DEBUG_ERROR , " couldn't resolve fid from dentry \n " ) ;
2006-01-08 12:05:00 +03:00
goto free_mem ;
2005-09-10 00:04:19 +04:00
}
2006-01-08 12:05:00 +03:00
/* issue a Twstat */
v9fs_blank_wstat ( & wstat ) ;
wstat . muid = v9ses - > name ;
wstat . extension = ( char * ) extension ;
retval = v9fs_t_wstat ( v9ses , fid - > fid , & wstat , & fcall ) ;
2005-09-10 00:04:19 +04:00
if ( retval < 0 ) {
2006-01-08 12:05:00 +03:00
PRINT_FCALL_ERROR ( " wstat error " , fcall ) ;
goto free_mem ;
2005-09-10 00:04:19 +04:00
}
2006-01-08 12:05:00 +03:00
err = v9fs_t_clunk ( v9ses , fid - > fid ) ;
2006-01-08 12:04:58 +03:00
if ( err < 0 ) {
2006-01-08 12:05:00 +03:00
dprintk ( DEBUG_ERROR , " clunk failed: %d \n " , err ) ;
goto free_mem ;
2005-09-10 00:04:19 +04:00
}
d_drop ( dentry ) ; /* FID - will this also clunk? */
2006-01-08 12:05:00 +03:00
free_mem :
2005-09-10 00:04:19 +04:00
kfree ( fcall ) ;
2006-01-08 12:05:00 +03:00
return retval ;
}
/**
* v9fs_vfs_symlink - helper function to create symlinks
* @ dir : directory inode containing symlink
* @ dentry : dentry for symlink
* @ symname : symlink data
*
* See 9 P2000 . u RFC for more information
*
*/
static int
v9fs_vfs_symlink ( struct inode * dir , struct dentry * dentry , const char * symname )
{
dprintk ( DEBUG_VFS , " %lu,%s,%s \n " , dir - > i_ino , dentry - > d_name . name ,
symname ) ;
return v9fs_vfs_mkspecial ( dir , dentry , S_IFLNK , symname ) ;
}
/**
* v9fs_vfs_link - create a hardlink
* @ old_dentry : dentry for file to link to
* @ dir : inode destination for new link
* @ dentry : dentry for link
*
*/
/* XXX - lots of code dup'd from symlink and creates,
* figure out a better reuse strategy
*/
static int
v9fs_vfs_link ( struct dentry * old_dentry , struct inode * dir ,
struct dentry * dentry )
{
int retval ;
struct v9fs_fid * oldfid ;
char * name ;
dprintk ( DEBUG_VFS , " %lu,%s,%s \n " , dir - > i_ino , dentry - > d_name . name ,
old_dentry - > d_name . name ) ;
oldfid = v9fs_fid_lookup ( old_dentry ) ;
if ( ! oldfid ) {
dprintk ( DEBUG_ERROR , " can't find oldfid \n " ) ;
return - EPERM ;
}
name = __getname ( ) ;
sprintf ( name , " hardlink(%d) \n " , oldfid - > fid ) ;
retval = v9fs_vfs_mkspecial ( dir , dentry , V9FS_DMLINK , name ) ;
__putname ( name ) ;
2005-09-10 00:04:19 +04:00
return retval ;
}
/**
* v9fs_vfs_mknod - create a special file
* @ dir : inode destination for new link
* @ dentry : dentry for file
* @ mode : mode for creation
* @ dev_t : device associated with special file
*
*/
static int
v9fs_vfs_mknod ( struct inode * dir , struct dentry * dentry , int mode , dev_t rdev )
{
2006-01-08 12:05:00 +03:00
int retval ;
char * name ;
2005-09-10 00:04:19 +04:00
dprintk ( DEBUG_VFS , " %lu,%s mode: %x MAJOR: %u MINOR: %u \n " , dir - > i_ino ,
dentry - > d_name . name , mode , MAJOR ( rdev ) , MINOR ( rdev ) ) ;
2006-01-08 12:05:00 +03:00
if ( ! new_valid_dev ( rdev ) )
return - EINVAL ;
2005-09-10 00:04:19 +04:00
2006-01-08 12:05:00 +03:00
name = __getname ( ) ;
2005-09-10 00:04:19 +04:00
/* build extension */
if ( S_ISBLK ( mode ) )
2006-01-08 12:05:00 +03:00
sprintf ( name , " b %u %u " , MAJOR ( rdev ) , MINOR ( rdev ) ) ;
2005-09-10 00:04:19 +04:00
else if ( S_ISCHR ( mode ) )
2006-01-08 12:05:00 +03:00
sprintf ( name , " c %u %u " , MAJOR ( rdev ) , MINOR ( rdev ) ) ;
2005-09-10 00:04:26 +04:00
else if ( S_ISFIFO ( mode ) )
2006-01-08 12:05:00 +03:00
* name = 0 ;
2005-09-10 00:04:19 +04:00
else {
2006-01-08 12:05:00 +03:00
__putname ( name ) ;
return - EINVAL ;
2005-09-10 00:04:19 +04:00
}
2006-01-08 12:05:00 +03:00
retval = v9fs_vfs_mkspecial ( dir , dentry , mode , name ) ;
__putname ( name ) ;
2005-09-10 00:04:19 +04:00
return retval ;
}
2005-09-10 00:04:27 +04:00
static struct inode_operations v9fs_dir_inode_operations_ext = {
2005-09-10 00:04:19 +04:00
. create = v9fs_vfs_create ,
. lookup = v9fs_vfs_lookup ,
. symlink = v9fs_vfs_symlink ,
. link = v9fs_vfs_link ,
. unlink = v9fs_vfs_unlink ,
. mkdir = v9fs_vfs_mkdir ,
. rmdir = v9fs_vfs_rmdir ,
. mknod = v9fs_vfs_mknod ,
. rename = v9fs_vfs_rename ,
. readlink = v9fs_vfs_readlink ,
. getattr = v9fs_vfs_getattr ,
. setattr = v9fs_vfs_setattr ,
} ;
2005-09-10 00:04:27 +04:00
static struct inode_operations v9fs_dir_inode_operations = {
. create = v9fs_vfs_create ,
. lookup = v9fs_vfs_lookup ,
. unlink = v9fs_vfs_unlink ,
. mkdir = v9fs_vfs_mkdir ,
. rmdir = v9fs_vfs_rmdir ,
. mknod = v9fs_vfs_mknod ,
. rename = v9fs_vfs_rename ,
. getattr = v9fs_vfs_getattr ,
. setattr = v9fs_vfs_setattr ,
} ;
2005-09-10 00:04:19 +04:00
static struct inode_operations v9fs_file_inode_operations = {
. getattr = v9fs_vfs_getattr ,
. setattr = v9fs_vfs_setattr ,
} ;
static struct inode_operations v9fs_symlink_inode_operations = {
. readlink = v9fs_vfs_readlink ,
. follow_link = v9fs_vfs_follow_link ,
. put_link = v9fs_vfs_put_link ,
. getattr = v9fs_vfs_getattr ,
. setattr = v9fs_vfs_setattr ,
} ;