2005-04-17 02:20:36 +04:00
/*
* File operations for Coda .
* Original version : ( C ) 1996 Peter Braam
* Rewritten for Linux 2.1 : ( C ) 1997 Carnegie Mellon University
*
* Carnegie Mellon encourages users of this code to contribute improvements
* to the Coda project . Contact Peter Braam < coda @ cs . cmu . edu > .
*/
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/time.h>
# include <linux/file.h>
# include <linux/fs.h>
# include <linux/stat.h>
2008-11-19 22:30:27 +03:00
# include <linux/cred.h>
2005-04-17 02:20:36 +04:00
# include <linux/errno.h>
# include <linux/smp_lock.h>
# include <linux/string.h>
# include <asm/uaccess.h>
# include <linux/coda.h>
# include <linux/coda_linux.h>
# include <linux/coda_fs_i.h>
# include <linux/coda_psdev.h>
2006-03-24 14:15:53 +03:00
# include "coda_int.h"
2005-04-17 02:20:36 +04:00
static ssize_t
coda_file_read ( struct file * coda_file , char __user * buf , size_t count , loff_t * ppos )
{
struct coda_file_info * cfi ;
struct file * host_file ;
cfi = CODA_FTOC ( coda_file ) ;
BUG_ON ( ! cfi | | cfi - > cfi_magic ! = CODA_MAGIC ) ;
host_file = cfi - > cfi_container ;
if ( ! host_file - > f_op | | ! host_file - > f_op - > read )
return - EINVAL ;
return host_file - > f_op - > read ( host_file , buf , count , ppos ) ;
}
static ssize_t
2007-06-01 13:49:19 +04:00
coda_file_splice_read ( struct file * coda_file , loff_t * ppos ,
struct pipe_inode_info * pipe , size_t count ,
unsigned int flags )
2005-04-17 02:20:36 +04:00
{
2009-05-07 17:37:36 +04:00
ssize_t ( * splice_read ) ( struct file * , loff_t * ,
struct pipe_inode_info * , size_t , unsigned int ) ;
2005-04-17 02:20:36 +04:00
struct coda_file_info * cfi ;
struct file * host_file ;
cfi = CODA_FTOC ( coda_file ) ;
BUG_ON ( ! cfi | | cfi - > cfi_magic ! = CODA_MAGIC ) ;
host_file = cfi - > cfi_container ;
2009-05-07 17:37:36 +04:00
splice_read = host_file - > f_op - > splice_read ;
if ( ! splice_read )
splice_read = default_file_splice_read ;
2005-04-17 02:20:36 +04:00
2009-05-07 17:37:36 +04:00
return splice_read ( host_file , ppos , pipe , count , flags ) ;
2005-04-17 02:20:36 +04:00
}
static ssize_t
coda_file_write ( struct file * coda_file , const char __user * buf , size_t count , loff_t * ppos )
{
2006-12-08 13:36:56 +03:00
struct inode * host_inode , * coda_inode = coda_file - > f_path . dentry - > d_inode ;
2005-04-17 02:20:36 +04:00
struct coda_file_info * cfi ;
struct file * host_file ;
ssize_t ret ;
cfi = CODA_FTOC ( coda_file ) ;
BUG_ON ( ! cfi | | cfi - > cfi_magic ! = CODA_MAGIC ) ;
host_file = cfi - > cfi_container ;
if ( ! host_file - > f_op | | ! host_file - > f_op - > write )
return - EINVAL ;
2006-12-08 13:36:56 +03:00
host_inode = host_file - > f_path . dentry - > d_inode ;
2006-01-10 02:59:24 +03:00
mutex_lock ( & coda_inode - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
ret = host_file - > f_op - > write ( host_file , buf , count , ppos ) ;
coda_inode - > i_size = host_inode - > i_size ;
coda_inode - > i_blocks = ( coda_inode - > i_size + 511 ) > > 9 ;
coda_inode - > i_mtime = coda_inode - > i_ctime = CURRENT_TIME_SEC ;
2006-01-10 02:59:24 +03:00
mutex_unlock ( & coda_inode - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
static int
coda_file_mmap ( struct file * coda_file , struct vm_area_struct * vma )
{
struct coda_file_info * cfi ;
struct coda_inode_info * cii ;
struct file * host_file ;
struct inode * coda_inode , * host_inode ;
cfi = CODA_FTOC ( coda_file ) ;
BUG_ON ( ! cfi | | cfi - > cfi_magic ! = CODA_MAGIC ) ;
host_file = cfi - > cfi_container ;
if ( ! host_file - > f_op | | ! host_file - > f_op - > mmap )
return - ENODEV ;
2006-12-08 13:36:56 +03:00
coda_inode = coda_file - > f_path . dentry - > d_inode ;
host_inode = host_file - > f_path . dentry - > d_inode ;
2005-04-17 02:20:36 +04:00
coda_file - > f_mapping = host_file - > f_mapping ;
if ( coda_inode - > i_mapping = = & coda_inode - > i_data )
coda_inode - > i_mapping = host_inode - > i_mapping ;
/* only allow additional mmaps as long as userspace isn't changing
* the container file on us ! */
else if ( coda_inode - > i_mapping ! = host_inode - > i_mapping )
return - EBUSY ;
/* keep track of how often the coda_inode/host_file has been mmapped */
cii = ITOC ( coda_inode ) ;
cii - > c_mapcount + + ;
cfi - > cfi_mapcount + + ;
return host_file - > f_op - > mmap ( host_file , vma ) ;
}
int coda_open ( struct inode * coda_inode , struct file * coda_file )
{
struct file * host_file = NULL ;
int error ;
unsigned short flags = coda_file - > f_flags & ( ~ O_EXCL ) ;
unsigned short coda_flags = coda_flags_to_cflags ( flags ) ;
struct coda_file_info * cfi ;
cfi = kmalloc ( sizeof ( struct coda_file_info ) , GFP_KERNEL ) ;
2006-07-30 14:03:56 +04:00
if ( ! cfi )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
lock_kernel ( ) ;
error = venus_open ( coda_inode - > i_sb , coda_i2f ( coda_inode ) , coda_flags ,
2007-07-19 12:48:41 +04:00
& host_file ) ;
if ( ! host_file )
error = - EIO ;
if ( error ) {
2005-04-17 02:20:36 +04:00
kfree ( cfi ) ;
unlock_kernel ( ) ;
return error ;
}
host_file - > f_flags | = coda_file - > f_flags & ( O_APPEND | O_SYNC ) ;
cfi - > cfi_magic = CODA_MAGIC ;
cfi - > cfi_mapcount = 0 ;
cfi - > cfi_container = host_file ;
BUG_ON ( coda_file - > private_data ! = NULL ) ;
coda_file - > private_data = cfi ;
unlock_kernel ( ) ;
return 0 ;
}
int coda_release ( struct inode * coda_inode , struct file * coda_file )
{
unsigned short flags = ( coda_file - > f_flags ) & ( ~ O_EXCL ) ;
unsigned short coda_flags = coda_flags_to_cflags ( flags ) ;
struct coda_file_info * cfi ;
struct coda_inode_info * cii ;
struct inode * host_inode ;
int err = 0 ;
lock_kernel ( ) ;
2007-07-19 12:48:51 +04:00
2005-04-17 02:20:36 +04:00
cfi = CODA_FTOC ( coda_file ) ;
BUG_ON ( ! cfi | | cfi - > cfi_magic ! = CODA_MAGIC ) ;
2007-07-21 15:37:26 +04:00
err = venus_close ( coda_inode - > i_sb , coda_i2f ( coda_inode ) ,
2008-11-14 02:39:25 +03:00
coda_flags , coda_file - > f_cred - > fsuid ) ;
2005-04-17 02:20:36 +04:00
2006-12-08 13:36:56 +03:00
host_inode = cfi - > cfi_container - > f_path . dentry - > d_inode ;
2005-04-17 02:20:36 +04:00
cii = ITOC ( coda_inode ) ;
/* did we mmap this file? */
if ( coda_inode - > i_mapping = = & host_inode - > i_data ) {
cii - > c_mapcount - = cfi - > cfi_mapcount ;
if ( ! cii - > c_mapcount )
coda_inode - > i_mapping = & coda_inode - > i_data ;
}
fput ( cfi - > cfi_container ) ;
kfree ( coda_file - > private_data ) ;
coda_file - > private_data = NULL ;
unlock_kernel ( ) ;
2007-07-21 15:37:26 +04:00
/* VFS fput ignores the return value from file_operations->release, so
* there is no use returning an error here */
return 0 ;
2005-04-17 02:20:36 +04:00
}
int coda_fsync ( struct file * coda_file , struct dentry * coda_dentry , int datasync )
{
struct file * host_file ;
2008-12-22 23:11:15 +03:00
struct inode * coda_inode = coda_dentry - > d_inode ;
2005-04-17 02:20:36 +04:00
struct coda_file_info * cfi ;
int err = 0 ;
if ( ! ( S_ISREG ( coda_inode - > i_mode ) | | S_ISDIR ( coda_inode - > i_mode ) | |
S_ISLNK ( coda_inode - > i_mode ) ) )
return - EINVAL ;
cfi = CODA_FTOC ( coda_file ) ;
BUG_ON ( ! cfi | | cfi - > cfi_magic ! = CODA_MAGIC ) ;
host_file = cfi - > cfi_container ;
2008-12-22 23:11:15 +03:00
err = vfs_fsync ( host_file , host_file - > f_path . dentry , datasync ) ;
2005-04-17 02:20:36 +04:00
if ( ! err & & ! datasync ) {
lock_kernel ( ) ;
err = venus_fsync ( coda_inode - > i_sb , coda_i2f ( coda_inode ) ) ;
unlock_kernel ( ) ;
}
return err ;
}
2006-03-28 13:56:42 +04:00
const struct file_operations coda_file_operations = {
2005-04-17 02:20:36 +04:00
. llseek = generic_file_llseek ,
. read = coda_file_read ,
. write = coda_file_write ,
. mmap = coda_file_mmap ,
. open = coda_open ,
. release = coda_release ,
. fsync = coda_fsync ,
2007-06-01 13:49:19 +04:00
. splice_read = coda_file_splice_read ,
2005-04-17 02:20:36 +04:00
} ;