2005-04-17 02:20:36 +04:00
/*
* Copyright ( C ) 2000 , 2001 , 2002 Jeff Dike ( jdike @ karaya . com )
* Licensed under the GPL
*
* Ported the filesystem routines to 2.5 .
* 2003 - 02 - 10 Petr Baudis < pasky @ ucw . cz >
*/
# include <linux/stddef.h>
# include <linux/fs.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/pagemap.h>
# include <linux/blkdev.h>
# include <linux/list.h>
# include <linux/statfs.h>
# include <linux/kdev_t.h>
# include <asm/uaccess.h>
# include "hostfs.h"
# include "kern_util.h"
# include "kern.h"
# include "user_util.h"
# include "init.h"
struct hostfs_inode_info {
char * host_filename ;
int fd ;
int mode ;
struct inode vfs_inode ;
} ;
static inline struct hostfs_inode_info * HOSTFS_I ( struct inode * inode )
{
return ( list_entry ( inode , struct hostfs_inode_info , vfs_inode ) ) ;
}
# define FILE_HOSTFS_I(file) HOSTFS_I((file)->f_dentry->d_inode)
int hostfs_d_delete ( struct dentry * dentry )
{
return ( 1 ) ;
}
struct dentry_operations hostfs_dentry_ops = {
. d_delete = hostfs_d_delete ,
} ;
/* Changed in hostfs_args before the kernel starts running */
static char * root_ino = " / " ;
static int append = 0 ;
# define HOSTFS_SUPER_MAGIC 0x00c0ffee
static struct inode_operations hostfs_iops ;
static struct inode_operations hostfs_dir_iops ;
static struct address_space_operations hostfs_link_aops ;
# ifndef MODULE
static int __init hostfs_args ( char * options , int * add )
{
char * ptr ;
ptr = strchr ( options , ' , ' ) ;
if ( ptr ! = NULL )
* ptr + + = ' \0 ' ;
if ( * options ! = ' \0 ' )
root_ino = options ;
options = ptr ;
while ( options ) {
ptr = strchr ( options , ' , ' ) ;
if ( ptr ! = NULL )
* ptr + + = ' \0 ' ;
if ( * options ! = ' \0 ' ) {
if ( ! strcmp ( options , " append " ) )
append = 1 ;
else printf ( " hostfs_args - unsupported option - %s \n " ,
options ) ;
}
options = ptr ;
}
return ( 0 ) ;
}
__uml_setup ( " hostfs= " , hostfs_args ,
" hostfs=<root dir>,<flags>,... \n "
" This is used to set hostfs parameters. The root directory argument \n "
" is used to confine all hostfs mounts to within the specified directory \n "
" tree on the host. If this isn't specified, then a user inside UML can \n "
" mount anything on the host that's accessible to the user that's running \n "
" it. \n "
" The only flag currently supported is 'append', which specifies that all \n "
" files opened by hostfs will be opened in append mode. \n \n "
) ;
# endif
static char * dentry_name ( struct dentry * dentry , int extra )
{
struct dentry * parent ;
char * root , * name ;
int len ;
len = 0 ;
parent = dentry ;
while ( parent - > d_parent ! = parent ) {
len + = parent - > d_name . len + 1 ;
parent = parent - > d_parent ;
}
root = HOSTFS_I ( parent - > d_inode ) - > host_filename ;
len + = strlen ( root ) ;
name = kmalloc ( len + extra + 1 , GFP_KERNEL ) ;
if ( name = = NULL ) return ( NULL ) ;
name [ len ] = ' \0 ' ;
parent = dentry ;
while ( parent - > d_parent ! = parent ) {
len - = parent - > d_name . len + 1 ;
name [ len ] = ' / ' ;
strncpy ( & name [ len + 1 ] , parent - > d_name . name ,
parent - > d_name . len ) ;
parent = parent - > d_parent ;
}
strncpy ( name , root , strlen ( root ) ) ;
return ( name ) ;
}
static char * inode_name ( struct inode * ino , int extra )
{
struct dentry * dentry ;
dentry = list_entry ( ino - > i_dentry . next , struct dentry , d_alias ) ;
return ( dentry_name ( dentry , extra ) ) ;
}
static int read_name ( struct inode * ino , char * name )
{
/* The non-int inode fields are copied into ints by stat_file and
* then copied into the inode because passing the actual pointers
* in and having them treated as int * breaks on big - endian machines
*/
int err ;
int i_mode , i_nlink , i_blksize ;
unsigned long long i_size ;
unsigned long long i_ino ;
unsigned long long i_blocks ;
err = stat_file ( name , & i_ino , & i_mode , & i_nlink , & ino - > i_uid ,
& ino - > i_gid , & i_size , & ino - > i_atime , & ino - > i_mtime ,
& ino - > i_ctime , & i_blksize , & i_blocks ) ;
if ( err )
return ( err ) ;
ino - > i_ino = i_ino ;
ino - > i_mode = i_mode ;
ino - > i_nlink = i_nlink ;
ino - > i_size = i_size ;
ino - > i_blksize = i_blksize ;
ino - > i_blocks = i_blocks ;
return ( 0 ) ;
}
static char * follow_link ( char * link )
{
int len , n ;
char * name , * resolved , * end ;
len = 64 ;
while ( 1 ) {
n = - ENOMEM ;
name = kmalloc ( len , GFP_KERNEL ) ;
if ( name = = NULL )
goto out ;
n = do_readlink ( link , name , len ) ;
if ( n < len )
break ;
len * = 2 ;
kfree ( name ) ;
}
if ( n < 0 )
goto out_free ;
if ( * name = = ' / ' )
return ( name ) ;
end = strrchr ( link , ' / ' ) ;
if ( end = = NULL )
return ( name ) ;
* ( end + 1 ) = ' \0 ' ;
len = strlen ( link ) + strlen ( name ) + 1 ;
resolved = kmalloc ( len , GFP_KERNEL ) ;
if ( resolved = = NULL ) {
n = - ENOMEM ;
goto out_free ;
}
sprintf ( resolved , " %s%s " , link , name ) ;
kfree ( name ) ;
kfree ( link ) ;
return ( resolved ) ;
out_free :
kfree ( name ) ;
out :
return ( ERR_PTR ( n ) ) ;
}
static int read_inode ( struct inode * ino )
{
char * name ;
int err = 0 ;
/* Unfortunately, we are called from iget() when we don't have a dentry
* allocated yet .
*/
if ( list_empty ( & ino - > i_dentry ) )
goto out ;
err = - ENOMEM ;
name = inode_name ( ino , 0 ) ;
if ( name = = NULL )
goto out ;
if ( file_type ( name , NULL , NULL ) = = OS_TYPE_SYMLINK ) {
name = follow_link ( name ) ;
if ( IS_ERR ( name ) ) {
err = PTR_ERR ( name ) ;
goto out ;
}
}
err = read_name ( ino , name ) ;
kfree ( name ) ;
out :
return ( err ) ;
}
int hostfs_statfs ( struct super_block * sb , struct kstatfs * sf )
{
/* do_statfs uses struct statfs64 internally, but the linux kernel
* struct statfs still has 32 - bit versions for most of these fields ,
* so we convert them here
*/
int err ;
long long f_blocks ;
long long f_bfree ;
long long f_bavail ;
long long f_files ;
long long f_ffree ;
err = do_statfs ( HOSTFS_I ( sb - > s_root - > d_inode ) - > host_filename ,
& sf - > f_bsize , & f_blocks , & f_bfree , & f_bavail , & f_files ,
& f_ffree , & sf - > f_fsid , sizeof ( sf - > f_fsid ) ,
& sf - > f_namelen , sf - > f_spare ) ;
if ( err ) return ( err ) ;
sf - > f_blocks = f_blocks ;
sf - > f_bfree = f_bfree ;
sf - > f_bavail = f_bavail ;
sf - > f_files = f_files ;
sf - > f_ffree = f_ffree ;
sf - > f_type = HOSTFS_SUPER_MAGIC ;
return ( 0 ) ;
}
static struct inode * hostfs_alloc_inode ( struct super_block * sb )
{
struct hostfs_inode_info * hi ;
hi = kmalloc ( sizeof ( * hi ) , GFP_KERNEL ) ;
if ( hi = = NULL )
return ( NULL ) ;
* hi = ( ( struct hostfs_inode_info ) { . host_filename = NULL ,
. fd = - 1 ,
. mode = 0 } ) ;
inode_init_once ( & hi - > vfs_inode ) ;
return ( & hi - > vfs_inode ) ;
}
static void hostfs_delete_inode ( struct inode * inode )
{
2005-09-10 00:01:31 +04:00
truncate_inode_pages ( & inode - > i_data , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( HOSTFS_I ( inode ) - > fd ! = - 1 ) {
close_file ( & HOSTFS_I ( inode ) - > fd ) ;
HOSTFS_I ( inode ) - > fd = - 1 ;
}
clear_inode ( inode ) ;
}
static void hostfs_destroy_inode ( struct inode * inode )
{
2005-11-07 12:01:34 +03:00
kfree ( HOSTFS_I ( inode ) - > host_filename ) ;
2005-04-17 02:20:36 +04:00
/*XXX: This should not happen, probably. The check is here for
* additional safety . */
if ( HOSTFS_I ( inode ) - > fd ! = - 1 ) {
close_file ( & HOSTFS_I ( inode ) - > fd ) ;
printk ( KERN_DEBUG " Closing host fd in .destroy_inode \n " ) ;
}
kfree ( HOSTFS_I ( inode ) ) ;
}
static void hostfs_read_inode ( struct inode * inode )
{
read_inode ( inode ) ;
}
static struct super_operations hostfs_sbops = {
. alloc_inode = hostfs_alloc_inode ,
. drop_inode = generic_delete_inode ,
. delete_inode = hostfs_delete_inode ,
. destroy_inode = hostfs_destroy_inode ,
. read_inode = hostfs_read_inode ,
. statfs = hostfs_statfs ,
} ;
int hostfs_readdir ( struct file * file , void * ent , filldir_t filldir )
{
void * dir ;
char * name ;
unsigned long long next , ino ;
int error , len ;
name = dentry_name ( file - > f_dentry , 0 ) ;
if ( name = = NULL ) return ( - ENOMEM ) ;
dir = open_dir ( name , & error ) ;
kfree ( name ) ;
if ( dir = = NULL ) return ( - error ) ;
next = file - > f_pos ;
while ( ( name = read_dir ( dir , & next , & ino , & len ) ) ! = NULL ) {
error = ( * filldir ) ( ent , name , len , file - > f_pos ,
ino , DT_UNKNOWN ) ;
if ( error ) break ;
file - > f_pos = next ;
}
close_dir ( dir ) ;
return ( 0 ) ;
}
int hostfs_file_open ( struct inode * ino , struct file * file )
{
char * name ;
int mode = 0 , r = 0 , w = 0 , fd ;
mode = file - > f_mode & ( FMODE_READ | FMODE_WRITE ) ;
if ( ( mode & HOSTFS_I ( ino ) - > mode ) = = mode )
return ( 0 ) ;
/* The file may already have been opened, but with the wrong access,
* so this resets things and reopens the file with the new access .
*/
if ( HOSTFS_I ( ino ) - > fd ! = - 1 ) {
close_file ( & HOSTFS_I ( ino ) - > fd ) ;
HOSTFS_I ( ino ) - > fd = - 1 ;
}
HOSTFS_I ( ino ) - > mode | = mode ;
if ( HOSTFS_I ( ino ) - > mode & FMODE_READ )
r = 1 ;
if ( HOSTFS_I ( ino ) - > mode & FMODE_WRITE )
w = 1 ;
if ( w )
r = 1 ;
name = dentry_name ( file - > f_dentry , 0 ) ;
if ( name = = NULL )
return ( - ENOMEM ) ;
fd = open_file ( name , r , w , append ) ;
kfree ( name ) ;
if ( fd < 0 ) return ( fd ) ;
FILE_HOSTFS_I ( file ) - > fd = fd ;
return ( 0 ) ;
}
int hostfs_fsync ( struct file * file , struct dentry * dentry , int datasync )
{
2005-07-29 08:16:15 +04:00
return fsync_file ( HOSTFS_I ( dentry - > d_inode ) - > fd , datasync ) ;
2005-04-17 02:20:36 +04:00
}
2006-03-28 13:56:42 +04:00
static const struct file_operations hostfs_file_fops = {
2005-04-17 02:20:36 +04:00
. llseek = generic_file_llseek ,
. read = generic_file_read ,
. sendfile = generic_file_sendfile ,
. aio_read = generic_file_aio_read ,
. aio_write = generic_file_aio_write ,
. readv = generic_file_readv ,
. writev = generic_file_writev ,
. write = generic_file_write ,
. mmap = generic_file_mmap ,
. open = hostfs_file_open ,
. release = NULL ,
. fsync = hostfs_fsync ,
} ;
2006-03-28 13:56:42 +04:00
static const struct file_operations hostfs_dir_fops = {
2005-04-17 02:20:36 +04:00
. llseek = generic_file_llseek ,
. readdir = hostfs_readdir ,
. read = generic_read_dir ,
} ;
int hostfs_writepage ( struct page * page , struct writeback_control * wbc )
{
struct address_space * mapping = page - > mapping ;
struct inode * inode = mapping - > host ;
char * buffer ;
unsigned long long base ;
int count = PAGE_CACHE_SIZE ;
int end_index = inode - > i_size > > PAGE_CACHE_SHIFT ;
int err ;
if ( page - > index > = end_index )
count = inode - > i_size & ( PAGE_CACHE_SIZE - 1 ) ;
buffer = kmap ( page ) ;
base = ( ( unsigned long long ) page - > index ) < < PAGE_CACHE_SHIFT ;
err = write_file ( HOSTFS_I ( inode ) - > fd , & base , buffer , count ) ;
if ( err ! = count ) {
ClearPageUptodate ( page ) ;
goto out ;
}
if ( base > inode - > i_size )
inode - > i_size = base ;
if ( PageError ( page ) )
ClearPageError ( page ) ;
err = 0 ;
out :
kunmap ( page ) ;
unlock_page ( page ) ;
return err ;
}
int hostfs_readpage ( struct file * file , struct page * page )
{
char * buffer ;
long long start ;
int err = 0 ;
start = ( long long ) page - > index < < PAGE_CACHE_SHIFT ;
buffer = kmap ( page ) ;
err = read_file ( FILE_HOSTFS_I ( file ) - > fd , & start , buffer ,
PAGE_CACHE_SIZE ) ;
if ( err < 0 ) goto out ;
memset ( & buffer [ err ] , 0 , PAGE_CACHE_SIZE - err ) ;
flush_dcache_page ( page ) ;
SetPageUptodate ( page ) ;
if ( PageError ( page ) ) ClearPageError ( page ) ;
err = 0 ;
out :
kunmap ( page ) ;
unlock_page ( page ) ;
return ( err ) ;
}
int hostfs_prepare_write ( struct file * file , struct page * page ,
unsigned int from , unsigned int to )
{
char * buffer ;
long long start , tmp ;
int err ;
start = ( long long ) page - > index < < PAGE_CACHE_SHIFT ;
buffer = kmap ( page ) ;
if ( from ! = 0 ) {
tmp = start ;
err = read_file ( FILE_HOSTFS_I ( file ) - > fd , & tmp , buffer ,
from ) ;
if ( err < 0 ) goto out ;
}
if ( to ! = PAGE_CACHE_SIZE ) {
start + = to ;
err = read_file ( FILE_HOSTFS_I ( file ) - > fd , & start , buffer + to ,
PAGE_CACHE_SIZE - to ) ;
if ( err < 0 ) goto out ;
}
err = 0 ;
out :
kunmap ( page ) ;
return ( err ) ;
}
int hostfs_commit_write ( struct file * file , struct page * page , unsigned from ,
unsigned to )
{
struct address_space * mapping = page - > mapping ;
struct inode * inode = mapping - > host ;
char * buffer ;
long long start ;
int err = 0 ;
2005-12-29 19:39:57 +03:00
start = ( ( ( long long ) page - > index ) < < PAGE_CACHE_SHIFT ) + from ;
2005-04-17 02:20:36 +04:00
buffer = kmap ( page ) ;
err = write_file ( FILE_HOSTFS_I ( file ) - > fd , & start , buffer + from ,
to - from ) ;
if ( err > 0 ) err = 0 ;
2005-12-29 19:39:57 +03:00
/* Actually, if !err, write_file has added to-from to start, so, despite
* the appearance , we are comparing i_size against the _last_ written
* location , as we should . */
2005-04-17 02:20:36 +04:00
if ( ! err & & ( start > inode - > i_size ) )
inode - > i_size = start ;
kunmap ( page ) ;
return ( err ) ;
}
static struct address_space_operations hostfs_aops = {
. writepage = hostfs_writepage ,
. readpage = hostfs_readpage ,
2005-05-01 19:58:56 +04:00
. set_page_dirty = __set_page_dirty_nobuffers ,
2005-04-17 02:20:36 +04:00
. prepare_write = hostfs_prepare_write ,
. commit_write = hostfs_commit_write
} ;
static int init_inode ( struct inode * inode , struct dentry * dentry )
{
char * name ;
int type , err = - ENOMEM ;
int maj , min ;
dev_t rdev = 0 ;
if ( dentry ) {
name = dentry_name ( dentry , 0 ) ;
if ( name = = NULL )
goto out ;
type = file_type ( name , & maj , & min ) ;
/*Reencode maj and min with the kernel encoding.*/
rdev = MKDEV ( maj , min ) ;
kfree ( name ) ;
}
else type = OS_TYPE_DIR ;
err = 0 ;
if ( type = = OS_TYPE_SYMLINK )
inode - > i_op = & page_symlink_inode_operations ;
else if ( type = = OS_TYPE_DIR )
inode - > i_op = & hostfs_dir_iops ;
else inode - > i_op = & hostfs_iops ;
if ( type = = OS_TYPE_DIR ) inode - > i_fop = & hostfs_dir_fops ;
else inode - > i_fop = & hostfs_file_fops ;
if ( type = = OS_TYPE_SYMLINK )
inode - > i_mapping - > a_ops = & hostfs_link_aops ;
else inode - > i_mapping - > a_ops = & hostfs_aops ;
switch ( type ) {
case OS_TYPE_CHARDEV :
init_special_inode ( inode , S_IFCHR , rdev ) ;
break ;
case OS_TYPE_BLOCKDEV :
init_special_inode ( inode , S_IFBLK , rdev ) ;
break ;
case OS_TYPE_FIFO :
init_special_inode ( inode , S_IFIFO , 0 ) ;
break ;
case OS_TYPE_SOCK :
init_special_inode ( inode , S_IFSOCK , 0 ) ;
break ;
}
out :
return ( err ) ;
}
int hostfs_create ( struct inode * dir , struct dentry * dentry , int mode ,
struct nameidata * nd )
{
struct inode * inode ;
char * name ;
int error , fd ;
error = - ENOMEM ;
inode = iget ( dir - > i_sb , 0 ) ;
if ( inode = = NULL ) goto out ;
error = init_inode ( inode , dentry ) ;
if ( error )
goto out_put ;
error = - ENOMEM ;
name = dentry_name ( dentry , 0 ) ;
if ( name = = NULL )
goto out_put ;
fd = file_create ( name ,
mode & S_IRUSR , mode & S_IWUSR , mode & S_IXUSR ,
mode & S_IRGRP , mode & S_IWGRP , mode & S_IXGRP ,
mode & S_IROTH , mode & S_IWOTH , mode & S_IXOTH ) ;
if ( fd < 0 )
error = fd ;
else error = read_name ( inode , name ) ;
kfree ( name ) ;
if ( error )
goto out_put ;
HOSTFS_I ( inode ) - > fd = fd ;
HOSTFS_I ( inode ) - > mode = FMODE_READ | FMODE_WRITE ;
d_instantiate ( dentry , inode ) ;
return ( 0 ) ;
out_put :
iput ( inode ) ;
out :
return ( error ) ;
}
struct dentry * hostfs_lookup ( struct inode * ino , struct dentry * dentry ,
struct nameidata * nd )
{
struct inode * inode ;
char * name ;
int err ;
err = - ENOMEM ;
inode = iget ( ino - > i_sb , 0 ) ;
if ( inode = = NULL )
goto out ;
err = init_inode ( inode , dentry ) ;
if ( err )
goto out_put ;
err = - ENOMEM ;
name = dentry_name ( dentry , 0 ) ;
if ( name = = NULL )
goto out_put ;
err = read_name ( inode , name ) ;
kfree ( name ) ;
if ( err = = - ENOENT ) {
iput ( inode ) ;
inode = NULL ;
}
else if ( err )
goto out_put ;
d_add ( dentry , inode ) ;
dentry - > d_op = & hostfs_dentry_ops ;
return ( NULL ) ;
out_put :
iput ( inode ) ;
out :
return ( ERR_PTR ( err ) ) ;
}
static char * inode_dentry_name ( struct inode * ino , struct dentry * dentry )
{
char * file ;
int len ;
file = inode_name ( ino , dentry - > d_name . len + 1 ) ;
if ( file = = NULL ) return ( NULL ) ;
strcat ( file , " / " ) ;
len = strlen ( file ) ;
strncat ( file , dentry - > d_name . name , dentry - > d_name . len ) ;
file [ len + dentry - > d_name . len ] = ' \0 ' ;
return ( file ) ;
}
int hostfs_link ( struct dentry * to , struct inode * ino , struct dentry * from )
{
char * from_name , * to_name ;
int err ;
if ( ( from_name = inode_dentry_name ( ino , from ) ) = = NULL )
return ( - ENOMEM ) ;
to_name = dentry_name ( to , 0 ) ;
if ( to_name = = NULL ) {
kfree ( from_name ) ;
return ( - ENOMEM ) ;
}
err = link_file ( to_name , from_name ) ;
kfree ( from_name ) ;
kfree ( to_name ) ;
return ( err ) ;
}
int hostfs_unlink ( struct inode * ino , struct dentry * dentry )
{
char * file ;
int err ;
if ( ( file = inode_dentry_name ( ino , dentry ) ) = = NULL ) return ( - ENOMEM ) ;
if ( append )
return ( - EPERM ) ;
err = unlink_file ( file ) ;
kfree ( file ) ;
return ( err ) ;
}
int hostfs_symlink ( struct inode * ino , struct dentry * dentry , const char * to )
{
char * file ;
int err ;
if ( ( file = inode_dentry_name ( ino , dentry ) ) = = NULL ) return ( - ENOMEM ) ;
err = make_symlink ( file , to ) ;
kfree ( file ) ;
return ( err ) ;
}
int hostfs_mkdir ( struct inode * ino , struct dentry * dentry , int mode )
{
char * file ;
int err ;
if ( ( file = inode_dentry_name ( ino , dentry ) ) = = NULL ) return ( - ENOMEM ) ;
err = do_mkdir ( file , mode ) ;
kfree ( file ) ;
return ( err ) ;
}
int hostfs_rmdir ( struct inode * ino , struct dentry * dentry )
{
char * file ;
int err ;
if ( ( file = inode_dentry_name ( ino , dentry ) ) = = NULL ) return ( - ENOMEM ) ;
err = do_rmdir ( file ) ;
kfree ( file ) ;
return ( err ) ;
}
int hostfs_mknod ( struct inode * dir , struct dentry * dentry , int mode , dev_t dev )
{
struct inode * inode ;
char * name ;
int err = - ENOMEM ;
inode = iget ( dir - > i_sb , 0 ) ;
if ( inode = = NULL )
goto out ;
err = init_inode ( inode , dentry ) ;
if ( err )
goto out_put ;
err = - ENOMEM ;
name = dentry_name ( dentry , 0 ) ;
if ( name = = NULL )
goto out_put ;
init_special_inode ( inode , mode , dev ) ;
err = do_mknod ( name , mode , dev ) ;
if ( err )
goto out_free ;
err = read_name ( inode , name ) ;
kfree ( name ) ;
if ( err )
goto out_put ;
d_instantiate ( dentry , inode ) ;
return ( 0 ) ;
out_free :
kfree ( name ) ;
out_put :
iput ( inode ) ;
out :
return ( err ) ;
}
int hostfs_rename ( struct inode * from_ino , struct dentry * from ,
struct inode * to_ino , struct dentry * to )
{
char * from_name , * to_name ;
int err ;
if ( ( from_name = inode_dentry_name ( from_ino , from ) ) = = NULL )
return ( - ENOMEM ) ;
if ( ( to_name = inode_dentry_name ( to_ino , to ) ) = = NULL ) {
kfree ( from_name ) ;
return ( - ENOMEM ) ;
}
err = rename_file ( from_name , to_name ) ;
kfree ( from_name ) ;
kfree ( to_name ) ;
return ( err ) ;
}
int hostfs_permission ( struct inode * ino , int desired , struct nameidata * nd )
{
char * name ;
int r = 0 , w = 0 , x = 0 , err ;
if ( desired & MAY_READ ) r = 1 ;
if ( desired & MAY_WRITE ) w = 1 ;
if ( desired & MAY_EXEC ) x = 1 ;
name = inode_name ( ino , 0 ) ;
if ( name = = NULL ) return ( - ENOMEM ) ;
if ( S_ISCHR ( ino - > i_mode ) | | S_ISBLK ( ino - > i_mode ) | |
S_ISFIFO ( ino - > i_mode ) | | S_ISSOCK ( ino - > i_mode ) )
err = 0 ;
else
err = access_file ( name , r , w , x ) ;
kfree ( name ) ;
if ( ! err )
err = generic_permission ( ino , desired , NULL ) ;
return err ;
}
int hostfs_setattr ( struct dentry * dentry , struct iattr * attr )
{
struct hostfs_iattr attrs ;
char * name ;
int err ;
err = inode_change_ok ( dentry - > d_inode , attr ) ;
if ( err )
return err ;
if ( append )
attr - > ia_valid & = ~ ATTR_SIZE ;
attrs . ia_valid = 0 ;
if ( attr - > ia_valid & ATTR_MODE ) {
attrs . ia_valid | = HOSTFS_ATTR_MODE ;
attrs . ia_mode = attr - > ia_mode ;
}
if ( attr - > ia_valid & ATTR_UID ) {
attrs . ia_valid | = HOSTFS_ATTR_UID ;
attrs . ia_uid = attr - > ia_uid ;
}
if ( attr - > ia_valid & ATTR_GID ) {
attrs . ia_valid | = HOSTFS_ATTR_GID ;
attrs . ia_gid = attr - > ia_gid ;
}
if ( attr - > ia_valid & ATTR_SIZE ) {
attrs . ia_valid | = HOSTFS_ATTR_SIZE ;
attrs . ia_size = attr - > ia_size ;
}
if ( attr - > ia_valid & ATTR_ATIME ) {
attrs . ia_valid | = HOSTFS_ATTR_ATIME ;
attrs . ia_atime = attr - > ia_atime ;
}
if ( attr - > ia_valid & ATTR_MTIME ) {
attrs . ia_valid | = HOSTFS_ATTR_MTIME ;
attrs . ia_mtime = attr - > ia_mtime ;
}
if ( attr - > ia_valid & ATTR_CTIME ) {
attrs . ia_valid | = HOSTFS_ATTR_CTIME ;
attrs . ia_ctime = attr - > ia_ctime ;
}
if ( attr - > ia_valid & ATTR_ATIME_SET ) {
attrs . ia_valid | = HOSTFS_ATTR_ATIME_SET ;
}
if ( attr - > ia_valid & ATTR_MTIME_SET ) {
attrs . ia_valid | = HOSTFS_ATTR_MTIME_SET ;
}
name = dentry_name ( dentry , 0 ) ;
if ( name = = NULL ) return ( - ENOMEM ) ;
err = set_attr ( name , & attrs ) ;
kfree ( name ) ;
if ( err )
return ( err ) ;
return ( inode_setattr ( dentry - > d_inode , attr ) ) ;
}
int hostfs_getattr ( struct vfsmount * mnt , struct dentry * dentry ,
struct kstat * stat )
{
generic_fillattr ( dentry - > d_inode , stat ) ;
return ( 0 ) ;
}
static struct inode_operations hostfs_iops = {
. create = hostfs_create ,
. link = hostfs_link ,
. unlink = hostfs_unlink ,
. symlink = hostfs_symlink ,
. mkdir = hostfs_mkdir ,
. rmdir = hostfs_rmdir ,
. mknod = hostfs_mknod ,
. rename = hostfs_rename ,
. permission = hostfs_permission ,
. setattr = hostfs_setattr ,
. getattr = hostfs_getattr ,
} ;
static struct inode_operations hostfs_dir_iops = {
. create = hostfs_create ,
. lookup = hostfs_lookup ,
. link = hostfs_link ,
. unlink = hostfs_unlink ,
. symlink = hostfs_symlink ,
. mkdir = hostfs_mkdir ,
. rmdir = hostfs_rmdir ,
. mknod = hostfs_mknod ,
. rename = hostfs_rename ,
. permission = hostfs_permission ,
. setattr = hostfs_setattr ,
. getattr = hostfs_getattr ,
} ;
int hostfs_link_readpage ( struct file * file , struct page * page )
{
char * buffer , * name ;
int err ;
buffer = kmap ( page ) ;
name = inode_name ( page - > mapping - > host , 0 ) ;
if ( name = = NULL ) return ( - ENOMEM ) ;
err = do_readlink ( name , buffer , PAGE_CACHE_SIZE ) ;
kfree ( name ) ;
if ( err = = PAGE_CACHE_SIZE )
err = - E2BIG ;
else if ( err > 0 ) {
flush_dcache_page ( page ) ;
SetPageUptodate ( page ) ;
if ( PageError ( page ) ) ClearPageError ( page ) ;
err = 0 ;
}
kunmap ( page ) ;
unlock_page ( page ) ;
return ( err ) ;
}
static struct address_space_operations hostfs_link_aops = {
. readpage = hostfs_link_readpage ,
} ;
static int hostfs_fill_sb_common ( struct super_block * sb , void * d , int silent )
{
struct inode * root_inode ;
char * name , * data = d ;
int err ;
sb - > s_blocksize = 1024 ;
sb - > s_blocksize_bits = 10 ;
sb - > s_magic = HOSTFS_SUPER_MAGIC ;
sb - > s_op = & hostfs_sbops ;
if ( ( data = = NULL ) | | ( * data = = ' \0 ' ) )
data = root_ino ;
err = - ENOMEM ;
name = kmalloc ( strlen ( data ) + 1 , GFP_KERNEL ) ;
if ( name = = NULL )
goto out ;
strcpy ( name , data ) ;
root_inode = iget ( sb , 0 ) ;
if ( root_inode = = NULL )
goto out_free ;
err = init_inode ( root_inode , NULL ) ;
if ( err )
goto out_put ;
HOSTFS_I ( root_inode ) - > host_filename = name ;
err = - ENOMEM ;
sb - > s_root = d_alloc_root ( root_inode ) ;
if ( sb - > s_root = = NULL )
goto out_put ;
err = read_inode ( root_inode ) ;
2005-05-06 03:15:34 +04:00
if ( err ) {
/* No iput in this case because the dput does that for us */
dput ( sb - > s_root ) ;
sb - > s_root = NULL ;
goto out_free ;
}
2005-04-17 02:20:36 +04:00
return ( 0 ) ;
out_put :
2005-05-06 03:15:34 +04:00
iput ( root_inode ) ;
2005-04-17 02:20:36 +04:00
out_free :
kfree ( name ) ;
out :
return ( err ) ;
}
static struct super_block * hostfs_read_sb ( struct file_system_type * type ,
int flags , const char * dev_name ,
void * data )
{
return ( get_sb_nodev ( type , flags , data , hostfs_fill_sb_common ) ) ;
}
static struct file_system_type hostfs_type = {
. owner = THIS_MODULE ,
. name = " hostfs " ,
. get_sb = hostfs_read_sb ,
. kill_sb = kill_anon_super ,
. fs_flags = 0 ,
} ;
static int __init init_hostfs ( void )
{
return ( register_filesystem ( & hostfs_type ) ) ;
}
static void __exit exit_hostfs ( void )
{
unregister_filesystem ( & hostfs_type ) ;
}
module_init ( init_hostfs )
module_exit ( exit_hostfs )
MODULE_LICENSE ( " GPL " ) ;
/*
* Overrides for Emacs so that we follow Linus ' s tabbing style .
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only . This must remain at the end
* of the file .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Local variables :
* c - file - style : " linux "
* End :
*/