2005-09-10 00:10:30 +04:00
/*
FUSE : Filesystem in Userspace
2006-04-11 23:16:51 +04:00
Copyright ( C ) 2001 - 2006 Miklos Szeredi < miklos @ szeredi . hu >
2005-09-10 00:10:30 +04:00
This program can be distributed under the terms of the GNU GPL .
See the file COPYING .
*/
# include "fuse_i.h"
# include <linux/pagemap.h>
# include <linux/slab.h>
# include <linux/kernel.h>
2006-03-28 13:56:42 +04:00
static const struct file_operations fuse_direct_io_file_operations ;
2005-09-10 00:10:37 +04:00
2005-11-07 11:59:51 +03:00
static int fuse_send_open ( struct inode * inode , struct file * file , int isdir ,
struct fuse_open_out * outargp )
2005-09-10 00:10:30 +04:00
{
struct fuse_conn * fc = get_fuse_conn ( inode ) ;
struct fuse_open_in inarg ;
2005-11-07 11:59:51 +03:00
struct fuse_req * req ;
int err ;
2006-04-11 09:54:58 +04:00
req = fuse_get_req ( fc ) ;
if ( IS_ERR ( req ) )
return PTR_ERR ( req ) ;
2005-11-07 11:59:51 +03:00
memset ( & inarg , 0 , sizeof ( inarg ) ) ;
inarg . flags = file - > f_flags & ~ ( O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC ) ;
req - > in . h . opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN ;
req - > in . h . nodeid = get_node_id ( inode ) ;
req - > in . numargs = 1 ;
req - > in . args [ 0 ] . size = sizeof ( inarg ) ;
req - > in . args [ 0 ] . value = & inarg ;
req - > out . numargs = 1 ;
req - > out . args [ 0 ] . size = sizeof ( * outargp ) ;
req - > out . args [ 0 ] . value = outargp ;
request_send ( fc , req ) ;
err = req - > out . h . error ;
fuse_put_request ( fc , req ) ;
return err ;
}
struct fuse_file * fuse_file_alloc ( void )
{
struct fuse_file * ff ;
ff = kmalloc ( sizeof ( struct fuse_file ) , GFP_KERNEL ) ;
if ( ff ) {
2006-06-25 16:48:52 +04:00
ff - > reserved_req = fuse_request_alloc ( ) ;
if ( ! ff - > reserved_req ) {
2005-11-07 11:59:51 +03:00
kfree ( ff ) ;
ff = NULL ;
}
}
return ff ;
}
void fuse_file_free ( struct fuse_file * ff )
{
2006-06-25 16:48:52 +04:00
fuse_request_free ( ff - > reserved_req ) ;
2005-11-07 11:59:51 +03:00
kfree ( ff ) ;
}
void fuse_finish_open ( struct inode * inode , struct file * file ,
struct fuse_file * ff , struct fuse_open_out * outarg )
{
if ( outarg - > open_flags & FOPEN_DIRECT_IO )
file - > f_op = & fuse_direct_io_file_operations ;
if ( ! ( outarg - > open_flags & FOPEN_KEEP_CACHE ) )
invalidate_inode_pages ( inode - > i_mapping ) ;
ff - > fh = outarg - > fh ;
file - > private_data = ff ;
}
int fuse_open_common ( struct inode * inode , struct file * file , int isdir )
{
2005-09-10 00:10:30 +04:00
struct fuse_open_out outarg ;
struct fuse_file * ff ;
int err ;
2005-09-30 22:59:02 +04:00
/* VFS checks this, but only _after_ ->open() */
if ( file - > f_flags & O_DIRECT )
return - EINVAL ;
2005-09-10 00:10:30 +04:00
err = generic_file_open ( inode , file ) ;
if ( err )
return err ;
/* If opening the root node, no lookup has been performed on
it , so the attributes must be refreshed */
if ( get_node_id ( inode ) = = FUSE_ROOT_ID ) {
2005-11-07 11:59:51 +03:00
err = fuse_do_getattr ( inode ) ;
2005-09-10 00:10:30 +04:00
if ( err )
return err ;
}
2005-11-07 11:59:51 +03:00
ff = fuse_file_alloc ( ) ;
2005-09-10 00:10:30 +04:00
if ( ! ff )
2005-11-07 11:59:51 +03:00
return - ENOMEM ;
2005-09-10 00:10:30 +04:00
2005-11-07 11:59:51 +03:00
err = fuse_send_open ( inode , file , isdir , & outarg ) ;
if ( err )
fuse_file_free ( ff ) ;
else {
if ( isdir )
outarg . open_flags & = ~ FOPEN_DIRECT_IO ;
fuse_finish_open ( inode , file , ff , & outarg ) ;
2005-09-10 00:10:30 +04:00
}
return err ;
}
2006-06-25 16:48:50 +04:00
struct fuse_req * fuse_release_fill ( struct fuse_file * ff , u64 nodeid , int flags ,
int opcode )
2006-01-17 09:14:42 +03:00
{
2006-06-25 16:48:52 +04:00
struct fuse_req * req = ff - > reserved_req ;
2005-09-10 00:10:30 +04:00
struct fuse_release_in * inarg = & req - > misc . release_in ;
inarg - > fh = ff - > fh ;
2005-11-07 11:59:51 +03:00
inarg - > flags = flags ;
2006-06-25 16:48:50 +04:00
req - > in . h . opcode = opcode ;
2005-11-07 11:59:51 +03:00
req - > in . h . nodeid = nodeid ;
2005-09-10 00:10:30 +04:00
req - > in . numargs = 1 ;
req - > in . args [ 0 ] . size = sizeof ( struct fuse_release_in ) ;
req - > in . args [ 0 ] . value = inarg ;
kfree ( ff ) ;
2006-06-25 16:48:50 +04:00
return req ;
2005-11-07 11:59:51 +03:00
}
int fuse_release_common ( struct inode * inode , struct file * file , int isdir )
{
struct fuse_file * ff = file - > private_data ;
if ( ff ) {
struct fuse_conn * fc = get_fuse_conn ( inode ) ;
2006-06-25 16:48:50 +04:00
struct fuse_req * req ;
req = fuse_release_fill ( ff , get_node_id ( inode ) , file - > f_flags ,
isdir ? FUSE_RELEASEDIR : FUSE_RELEASE ) ;
/* Hold vfsmount and dentry until release is finished */
req - > vfsmount = mntget ( file - > f_vfsmnt ) ;
req - > dentry = dget ( file - > f_dentry ) ;
request_send_background ( fc , req ) ;
2005-11-07 11:59:51 +03:00
}
2005-09-10 00:10:30 +04:00
/* Return value is ignored by VFS */
return 0 ;
}
2005-09-10 00:10:36 +04:00
static int fuse_open ( struct inode * inode , struct file * file )
{
return fuse_open_common ( inode , file , 0 ) ;
}
static int fuse_release ( struct inode * inode , struct file * file )
{
return fuse_release_common ( inode , file , 0 ) ;
}
2006-06-25 16:48:52 +04:00
/*
2006-06-25 16:48:55 +04:00
* Scramble the ID space with XTEA , so that the value of the files_struct
* pointer is not exposed to userspace .
2006-06-25 16:48:52 +04:00
*/
2006-06-25 16:48:55 +04:00
static u64 fuse_lock_owner_id ( struct fuse_conn * fc , fl_owner_t id )
2006-06-25 16:48:52 +04:00
{
2006-06-25 16:48:55 +04:00
u32 * k = fc - > scramble_key ;
u64 v = ( unsigned long ) id ;
u32 v0 = v ;
u32 v1 = v > > 32 ;
u32 sum = 0 ;
int i ;
for ( i = 0 ; i < 32 ; i + + ) {
v0 + = ( ( v1 < < 4 ^ v1 > > 5 ) + v1 ) ^ ( sum + k [ sum & 3 ] ) ;
sum + = 0x9E3779B9 ;
v1 + = ( ( v0 < < 4 ^ v0 > > 5 ) + v0 ) ^ ( sum + k [ sum > > 11 & 3 ] ) ;
}
return ( u64 ) v0 + ( ( u64 ) v1 < < 32 ) ;
2006-06-25 16:48:52 +04:00
}
2006-06-23 13:05:12 +04:00
static int fuse_flush ( struct file * file , fl_owner_t id )
2005-09-10 00:10:30 +04:00
{
struct inode * inode = file - > f_dentry - > d_inode ;
struct fuse_conn * fc = get_fuse_conn ( inode ) ;
struct fuse_file * ff = file - > private_data ;
struct fuse_req * req ;
struct fuse_flush_in inarg ;
int err ;
2006-01-06 11:19:39 +03:00
if ( is_bad_inode ( inode ) )
return - EIO ;
2005-09-10 00:10:30 +04:00
if ( fc - > no_flush )
return 0 ;
2006-06-25 16:48:52 +04:00
req = fuse_get_req_nofail ( fc , file ) ;
2005-09-10 00:10:30 +04:00
memset ( & inarg , 0 , sizeof ( inarg ) ) ;
inarg . fh = ff - > fh ;
2006-06-25 16:48:55 +04:00
inarg . lock_owner = fuse_lock_owner_id ( fc , id ) ;
2005-09-10 00:10:30 +04:00
req - > in . h . opcode = FUSE_FLUSH ;
req - > in . h . nodeid = get_node_id ( inode ) ;
req - > in . numargs = 1 ;
req - > in . args [ 0 ] . size = sizeof ( inarg ) ;
req - > in . args [ 0 ] . value = & inarg ;
2006-06-25 16:48:52 +04:00
req - > force = 1 ;
2005-09-10 00:10:39 +04:00
request_send ( fc , req ) ;
2005-09-10 00:10:30 +04:00
err = req - > out . h . error ;
fuse_put_request ( fc , req ) ;
if ( err = = - ENOSYS ) {
fc - > no_flush = 1 ;
err = 0 ;
}
return err ;
}
2005-09-10 00:10:38 +04:00
int fuse_fsync_common ( struct file * file , struct dentry * de , int datasync ,
int isdir )
2005-09-10 00:10:30 +04:00
{
struct inode * inode = de - > d_inode ;
struct fuse_conn * fc = get_fuse_conn ( inode ) ;
struct fuse_file * ff = file - > private_data ;
struct fuse_req * req ;
struct fuse_fsync_in inarg ;
int err ;
2006-01-06 11:19:39 +03:00
if ( is_bad_inode ( inode ) )
return - EIO ;
2005-09-10 00:10:38 +04:00
if ( ( ! isdir & & fc - > no_fsync ) | | ( isdir & & fc - > no_fsyncdir ) )
2005-09-10 00:10:30 +04:00
return 0 ;
2006-04-11 09:54:58 +04:00
req = fuse_get_req ( fc ) ;
if ( IS_ERR ( req ) )
return PTR_ERR ( req ) ;
2005-09-10 00:10:30 +04:00
memset ( & inarg , 0 , sizeof ( inarg ) ) ;
inarg . fh = ff - > fh ;
inarg . fsync_flags = datasync ? 1 : 0 ;
2005-09-10 00:10:38 +04:00
req - > in . h . opcode = isdir ? FUSE_FSYNCDIR : FUSE_FSYNC ;
2005-09-10 00:10:30 +04:00
req - > in . h . nodeid = get_node_id ( inode ) ;
req - > in . numargs = 1 ;
req - > in . args [ 0 ] . size = sizeof ( inarg ) ;
req - > in . args [ 0 ] . value = & inarg ;
request_send ( fc , req ) ;
err = req - > out . h . error ;
fuse_put_request ( fc , req ) ;
if ( err = = - ENOSYS ) {
2005-09-10 00:10:38 +04:00
if ( isdir )
fc - > no_fsyncdir = 1 ;
else
fc - > no_fsync = 1 ;
2005-09-10 00:10:30 +04:00
err = 0 ;
}
return err ;
}
2005-09-10 00:10:38 +04:00
static int fuse_fsync ( struct file * file , struct dentry * de , int datasync )
{
return fuse_fsync_common ( file , de , datasync , 0 ) ;
}
2006-01-17 09:14:45 +03:00
void fuse_read_fill ( struct fuse_req * req , struct file * file ,
struct inode * inode , loff_t pos , size_t count , int opcode )
2005-09-10 00:10:30 +04:00
{
struct fuse_file * ff = file - > private_data ;
2006-01-17 09:14:45 +03:00
struct fuse_read_in * inarg = & req - > misc . read_in ;
2005-09-10 00:10:30 +04:00
2006-01-17 09:14:45 +03:00
inarg - > fh = ff - > fh ;
inarg - > offset = pos ;
inarg - > size = count ;
req - > in . h . opcode = opcode ;
2005-09-10 00:10:30 +04:00
req - > in . h . nodeid = get_node_id ( inode ) ;
req - > in . numargs = 1 ;
req - > in . args [ 0 ] . size = sizeof ( struct fuse_read_in ) ;
2006-01-17 09:14:46 +03:00
req - > in . args [ 0 ] . value = inarg ;
2005-09-10 00:10:30 +04:00
req - > out . argpages = 1 ;
req - > out . argvar = 1 ;
req - > out . numargs = 1 ;
req - > out . args [ 0 ] . size = count ;
}
2006-01-17 09:14:28 +03:00
static size_t fuse_send_read ( struct fuse_req * req , struct file * file ,
struct inode * inode , loff_t pos , size_t count )
2005-09-10 00:10:36 +04:00
{
2006-01-17 09:14:45 +03:00
struct fuse_conn * fc = get_fuse_conn ( inode ) ;
fuse_read_fill ( req , file , inode , pos , count , FUSE_READ ) ;
request_send ( fc , req ) ;
return req - > out . args [ 0 ] . size ;
2005-09-10 00:10:36 +04:00
}
2005-09-10 00:10:30 +04:00
static int fuse_readpage ( struct file * file , struct page * page )
{
struct inode * inode = page - > mapping - > host ;
struct fuse_conn * fc = get_fuse_conn ( inode ) ;
2006-01-06 11:19:39 +03:00
struct fuse_req * req ;
int err ;
err = - EIO ;
if ( is_bad_inode ( inode ) )
goto out ;
2006-04-11 09:54:58 +04:00
req = fuse_get_req ( fc ) ;
err = PTR_ERR ( req ) ;
if ( IS_ERR ( req ) )
2005-09-10 00:10:30 +04:00
goto out ;
req - > out . page_zeroing = 1 ;
req - > num_pages = 1 ;
req - > pages [ 0 ] = page ;
2006-01-06 11:19:36 +03:00
fuse_send_read ( req , file , inode , page_offset ( page ) , PAGE_CACHE_SIZE ) ;
2005-09-10 00:10:30 +04:00
err = req - > out . h . error ;
fuse_put_request ( fc , req ) ;
if ( ! err )
SetPageUptodate ( page ) ;
2005-09-10 00:10:38 +04:00
fuse_invalidate_attr ( inode ) ; /* atime changed */
2005-09-10 00:10:30 +04:00
out :
unlock_page ( page ) ;
return err ;
}
2006-01-17 09:14:46 +03:00
static void fuse_readpages_end ( struct fuse_conn * fc , struct fuse_req * req )
2005-09-10 00:10:33 +04:00
{
2006-01-17 09:14:46 +03:00
int i ;
fuse_invalidate_attr ( req - > pages [ 0 ] - > mapping - > host ) ; /* atime changed */
2005-09-10 00:10:33 +04:00
for ( i = 0 ; i < req - > num_pages ; i + + ) {
struct page * page = req - > pages [ i ] ;
if ( ! req - > out . h . error )
SetPageUptodate ( page ) ;
2006-01-17 09:14:46 +03:00
else
SetPageError ( page ) ;
2005-09-10 00:10:33 +04:00
unlock_page ( page ) ;
}
2006-01-17 09:14:46 +03:00
fuse_put_request ( fc , req ) ;
}
static void fuse_send_readpages ( struct fuse_req * req , struct file * file ,
struct inode * inode )
{
struct fuse_conn * fc = get_fuse_conn ( inode ) ;
loff_t pos = page_offset ( req - > pages [ 0 ] ) ;
size_t count = req - > num_pages < < PAGE_CACHE_SHIFT ;
req - > out . page_zeroing = 1 ;
fuse_read_fill ( req , file , inode , pos , count , FUSE_READ ) ;
2006-02-01 14:04:40 +03:00
if ( fc - > async_read ) {
2006-06-25 16:48:50 +04:00
get_file ( file ) ;
req - > file = file ;
2006-02-01 14:04:40 +03:00
req - > end = fuse_readpages_end ;
request_send_background ( fc , req ) ;
} else {
request_send ( fc , req ) ;
fuse_readpages_end ( fc , req ) ;
}
2005-09-10 00:10:33 +04:00
}
struct fuse_readpages_data {
struct fuse_req * req ;
struct file * file ;
struct inode * inode ;
} ;
static int fuse_readpages_fill ( void * _data , struct page * page )
{
struct fuse_readpages_data * data = _data ;
struct fuse_req * req = data - > req ;
struct inode * inode = data - > inode ;
struct fuse_conn * fc = get_fuse_conn ( inode ) ;
if ( req - > num_pages & &
( req - > num_pages = = FUSE_MAX_PAGES_PER_REQ | |
( req - > num_pages + 1 ) * PAGE_CACHE_SIZE > fc - > max_read | |
req - > pages [ req - > num_pages - 1 ] - > index + 1 ! = page - > index ) ) {
2006-01-17 09:14:46 +03:00
fuse_send_readpages ( req , data - > file , inode ) ;
2006-04-11 09:54:58 +04:00
data - > req = req = fuse_get_req ( fc ) ;
if ( IS_ERR ( req ) ) {
2005-09-10 00:10:33 +04:00
unlock_page ( page ) ;
2006-04-11 09:54:58 +04:00
return PTR_ERR ( req ) ;
2005-09-10 00:10:33 +04:00
}
}
req - > pages [ req - > num_pages ] = page ;
req - > num_pages + + ;
return 0 ;
}
static int fuse_readpages ( struct file * file , struct address_space * mapping ,
struct list_head * pages , unsigned nr_pages )
{
struct inode * inode = mapping - > host ;
struct fuse_conn * fc = get_fuse_conn ( inode ) ;
struct fuse_readpages_data data ;
int err ;
2006-01-06 11:19:39 +03:00
2006-08-14 10:24:27 +04:00
err = - EIO ;
2006-01-06 11:19:39 +03:00
if ( is_bad_inode ( inode ) )
2006-11-03 09:07:09 +03:00
goto out ;
2006-01-06 11:19:39 +03:00
2005-09-10 00:10:33 +04:00
data . file = file ;
data . inode = inode ;
2006-04-11 09:54:58 +04:00
data . req = fuse_get_req ( fc ) ;
2006-08-14 10:24:27 +04:00
err = PTR_ERR ( data . req ) ;
2006-04-11 09:54:58 +04:00
if ( IS_ERR ( data . req ) )
2006-11-03 09:07:09 +03:00
goto out ;
2005-09-10 00:10:33 +04:00
err = read_cache_pages ( mapping , pages , fuse_readpages_fill , & data ) ;
2006-04-11 09:54:49 +04:00
if ( ! err ) {
if ( data . req - > num_pages )
fuse_send_readpages ( data . req , file , inode ) ;
else
fuse_put_request ( fc , data . req ) ;
}
2006-11-03 09:07:09 +03:00
out :
2006-08-14 10:24:27 +04:00
return err ;
2005-09-10 00:10:33 +04:00
}
2005-09-10 00:10:36 +04:00
static size_t fuse_send_write ( struct fuse_req * req , struct file * file ,
struct inode * inode , loff_t pos , size_t count )
2005-09-10 00:10:30 +04:00
{
struct fuse_conn * fc = get_fuse_conn ( inode ) ;
struct fuse_file * ff = file - > private_data ;
struct fuse_write_in inarg ;
struct fuse_write_out outarg ;
memset ( & inarg , 0 , sizeof ( struct fuse_write_in ) ) ;
inarg . fh = ff - > fh ;
inarg . offset = pos ;
inarg . size = count ;
req - > in . h . opcode = FUSE_WRITE ;
req - > in . h . nodeid = get_node_id ( inode ) ;
req - > in . argpages = 1 ;
req - > in . numargs = 2 ;
req - > in . args [ 0 ] . size = sizeof ( struct fuse_write_in ) ;
req - > in . args [ 0 ] . value = & inarg ;
req - > in . args [ 1 ] . size = count ;
req - > out . numargs = 1 ;
req - > out . args [ 0 ] . size = sizeof ( struct fuse_write_out ) ;
req - > out . args [ 0 ] . value = & outarg ;
2005-09-10 00:10:39 +04:00
request_send ( fc , req ) ;
2005-09-10 00:10:30 +04:00
return outarg . size ;
}
static int fuse_prepare_write ( struct file * file , struct page * page ,
unsigned offset , unsigned to )
{
/* No op */
return 0 ;
}
static int fuse_commit_write ( struct file * file , struct page * page ,
unsigned offset , unsigned to )
{
int err ;
2005-09-10 00:10:36 +04:00
size_t nres ;
2005-09-10 00:10:30 +04:00
unsigned count = to - offset ;
struct inode * inode = page - > mapping - > host ;
struct fuse_conn * fc = get_fuse_conn ( inode ) ;
2006-01-06 11:19:36 +03:00
loff_t pos = page_offset ( page ) + offset ;
2006-01-06 11:19:39 +03:00
struct fuse_req * req ;
if ( is_bad_inode ( inode ) )
return - EIO ;
2006-04-11 09:54:58 +04:00
req = fuse_get_req ( fc ) ;
if ( IS_ERR ( req ) )
return PTR_ERR ( req ) ;
2005-09-10 00:10:30 +04:00
req - > num_pages = 1 ;
req - > pages [ 0 ] = page ;
req - > page_offset = offset ;
nres = fuse_send_write ( req , file , inode , pos , count ) ;
err = req - > out . h . error ;
fuse_put_request ( fc , req ) ;
if ( ! err & & nres ! = count )
err = - EIO ;
if ( ! err ) {
pos + = count ;
2006-10-17 11:10:06 +04:00
spin_lock ( & fc - > lock ) ;
if ( pos > inode - > i_size )
2005-09-10 00:10:30 +04:00
i_size_write ( inode , pos ) ;
2006-10-17 11:10:06 +04:00
spin_unlock ( & fc - > lock ) ;
2005-09-10 00:10:30 +04:00
if ( offset = = 0 & & to = = PAGE_CACHE_SIZE ) {
clear_page_dirty ( page ) ;
SetPageUptodate ( page ) ;
}
2005-09-10 00:10:38 +04:00
}
fuse_invalidate_attr ( inode ) ;
2005-09-10 00:10:30 +04:00
return err ;
}
2005-09-10 00:10:35 +04:00
static void fuse_release_user_pages ( struct fuse_req * req , int write )
{
unsigned i ;
for ( i = 0 ; i < req - > num_pages ; i + + ) {
struct page * page = req - > pages [ i ] ;
if ( write )
set_page_dirty_lock ( page ) ;
put_page ( page ) ;
}
}
static int fuse_get_user_pages ( struct fuse_req * req , const char __user * buf ,
unsigned nbytes , int write )
{
unsigned long user_addr = ( unsigned long ) buf ;
unsigned offset = user_addr & ~ PAGE_MASK ;
int npages ;
/* This doesn't work with nfsd */
if ( ! current - > mm )
return - EPERM ;
nbytes = min ( nbytes , ( unsigned ) FUSE_MAX_PAGES_PER_REQ < < PAGE_SHIFT ) ;
npages = ( nbytes + offset + PAGE_SIZE - 1 ) > > PAGE_SHIFT ;
2006-01-06 11:19:42 +03:00
npages = min ( max ( npages , 1 ) , FUSE_MAX_PAGES_PER_REQ ) ;
2005-09-10 00:10:35 +04:00
down_read ( & current - > mm - > mmap_sem ) ;
npages = get_user_pages ( current , current - > mm , user_addr , npages , write ,
0 , req - > pages , NULL ) ;
up_read ( & current - > mm - > mmap_sem ) ;
if ( npages < 0 )
return npages ;
req - > num_pages = npages ;
req - > page_offset = offset ;
return 0 ;
}
static ssize_t fuse_direct_io ( struct file * file , const char __user * buf ,
size_t count , loff_t * ppos , int write )
{
struct inode * inode = file - > f_dentry - > d_inode ;
struct fuse_conn * fc = get_fuse_conn ( inode ) ;
size_t nmax = write ? fc - > max_write : fc - > max_read ;
loff_t pos = * ppos ;
ssize_t res = 0 ;
2006-01-06 11:19:39 +03:00
struct fuse_req * req ;
if ( is_bad_inode ( inode ) )
return - EIO ;
2006-04-11 09:54:58 +04:00
req = fuse_get_req ( fc ) ;
if ( IS_ERR ( req ) )
return PTR_ERR ( req ) ;
2005-09-10 00:10:35 +04:00
while ( count ) {
size_t nres ;
size_t nbytes = min ( count , nmax ) ;
int err = fuse_get_user_pages ( req , buf , nbytes , ! write ) ;
if ( err ) {
res = err ;
break ;
}
2006-01-06 11:19:42 +03:00
nbytes = ( req - > num_pages < < PAGE_SHIFT ) - req - > page_offset ;
nbytes = min ( count , nbytes ) ;
2005-09-10 00:10:35 +04:00
if ( write )
nres = fuse_send_write ( req , file , inode , pos , nbytes ) ;
else
nres = fuse_send_read ( req , file , inode , pos , nbytes ) ;
fuse_release_user_pages ( req , ! write ) ;
if ( req - > out . h . error ) {
if ( ! res )
res = req - > out . h . error ;
break ;
} else if ( nres > nbytes ) {
res = - EIO ;
break ;
}
count - = nres ;
res + = nres ;
pos + = nres ;
buf + = nres ;
if ( nres ! = nbytes )
break ;
2006-04-11 23:16:51 +04:00
if ( count ) {
fuse_put_request ( fc , req ) ;
req = fuse_get_req ( fc ) ;
if ( IS_ERR ( req ) )
break ;
}
2005-09-10 00:10:35 +04:00
}
fuse_put_request ( fc , req ) ;
if ( res > 0 ) {
2006-10-17 11:10:06 +04:00
if ( write ) {
spin_lock ( & fc - > lock ) ;
if ( pos > inode - > i_size )
i_size_write ( inode , pos ) ;
spin_unlock ( & fc - > lock ) ;
}
2005-09-10 00:10:35 +04:00
* ppos = pos ;
2005-09-10 00:10:38 +04:00
}
fuse_invalidate_attr ( inode ) ;
2005-09-10 00:10:35 +04:00
return res ;
}
static ssize_t fuse_direct_read ( struct file * file , char __user * buf ,
size_t count , loff_t * ppos )
{
return fuse_direct_io ( file , buf , count , ppos , 0 ) ;
}
static ssize_t fuse_direct_write ( struct file * file , const char __user * buf ,
size_t count , loff_t * ppos )
{
struct inode * inode = file - > f_dentry - > d_inode ;
ssize_t res ;
/* Don't allow parallel writes to the same file */
2006-01-10 02:59:24 +03:00
mutex_lock ( & inode - > i_mutex ) ;
2005-09-10 00:10:35 +04:00
res = fuse_direct_io ( file , buf , count , ppos , 1 ) ;
2006-01-10 02:59:24 +03:00
mutex_unlock ( & inode - > i_mutex ) ;
2005-09-10 00:10:35 +04:00
return res ;
}
2005-09-10 00:10:30 +04:00
static int fuse_file_mmap ( struct file * file , struct vm_area_struct * vma )
{
if ( ( vma - > vm_flags & VM_SHARED ) ) {
if ( ( vma - > vm_flags & VM_WRITE ) )
return - ENODEV ;
else
vma - > vm_flags & = ~ VM_MAYWRITE ;
}
return generic_file_mmap ( file , vma ) ;
}
static int fuse_set_page_dirty ( struct page * page )
{
printk ( " fuse_set_page_dirty: should not happen \n " ) ;
dump_stack ( ) ;
return 0 ;
}
2006-06-25 16:48:52 +04:00
static int convert_fuse_file_lock ( const struct fuse_file_lock * ffl ,
struct file_lock * fl )
{
switch ( ffl - > type ) {
case F_UNLCK :
break ;
case F_RDLCK :
case F_WRLCK :
if ( ffl - > start > OFFSET_MAX | | ffl - > end > OFFSET_MAX | |
ffl - > end < ffl - > start )
return - EIO ;
fl - > fl_start = ffl - > start ;
fl - > fl_end = ffl - > end ;
fl - > fl_pid = ffl - > pid ;
break ;
default :
return - EIO ;
}
fl - > fl_type = ffl - > type ;
return 0 ;
}
static void fuse_lk_fill ( struct fuse_req * req , struct file * file ,
const struct file_lock * fl , int opcode , pid_t pid )
{
struct inode * inode = file - > f_dentry - > d_inode ;
2006-06-25 16:48:55 +04:00
struct fuse_conn * fc = get_fuse_conn ( inode ) ;
2006-06-25 16:48:52 +04:00
struct fuse_file * ff = file - > private_data ;
struct fuse_lk_in * arg = & req - > misc . lk_in ;
arg - > fh = ff - > fh ;
2006-06-25 16:48:55 +04:00
arg - > owner = fuse_lock_owner_id ( fc , fl - > fl_owner ) ;
2006-06-25 16:48:52 +04:00
arg - > lk . start = fl - > fl_start ;
arg - > lk . end = fl - > fl_end ;
arg - > lk . type = fl - > fl_type ;
arg - > lk . pid = pid ;
req - > in . h . opcode = opcode ;
req - > in . h . nodeid = get_node_id ( inode ) ;
req - > in . numargs = 1 ;
req - > in . args [ 0 ] . size = sizeof ( * arg ) ;
req - > in . args [ 0 ] . value = arg ;
}
static int fuse_getlk ( struct file * file , struct file_lock * fl )
{
struct inode * inode = file - > f_dentry - > d_inode ;
struct fuse_conn * fc = get_fuse_conn ( inode ) ;
struct fuse_req * req ;
struct fuse_lk_out outarg ;
int err ;
req = fuse_get_req ( fc ) ;
if ( IS_ERR ( req ) )
return PTR_ERR ( req ) ;
fuse_lk_fill ( req , file , fl , FUSE_GETLK , 0 ) ;
req - > out . numargs = 1 ;
req - > out . args [ 0 ] . size = sizeof ( outarg ) ;
req - > out . args [ 0 ] . value = & outarg ;
request_send ( fc , req ) ;
err = req - > out . h . error ;
fuse_put_request ( fc , req ) ;
if ( ! err )
err = convert_fuse_file_lock ( & outarg . lk , fl ) ;
return err ;
}
static int fuse_setlk ( struct file * file , struct file_lock * fl )
{
struct inode * inode = file - > f_dentry - > d_inode ;
struct fuse_conn * fc = get_fuse_conn ( inode ) ;
struct fuse_req * req ;
int opcode = ( fl - > fl_flags & FL_SLEEP ) ? FUSE_SETLKW : FUSE_SETLK ;
pid_t pid = fl - > fl_type ! = F_UNLCK ? current - > tgid : 0 ;
int err ;
/* Unlock on close is handled by the flush method */
if ( fl - > fl_flags & FL_CLOSE )
return 0 ;
req = fuse_get_req ( fc ) ;
if ( IS_ERR ( req ) )
return PTR_ERR ( req ) ;
fuse_lk_fill ( req , file , fl , opcode , pid ) ;
request_send ( fc , req ) ;
err = req - > out . h . error ;
2006-06-25 16:48:54 +04:00
/* locking is restartable */
if ( err = = - EINTR )
err = - ERESTARTSYS ;
2006-06-25 16:48:52 +04:00
fuse_put_request ( fc , req ) ;
return err ;
}
static int fuse_file_lock ( struct file * file , int cmd , struct file_lock * fl )
{
struct inode * inode = file - > f_dentry - > d_inode ;
struct fuse_conn * fc = get_fuse_conn ( inode ) ;
int err ;
if ( cmd = = F_GETLK ) {
if ( fc - > no_lock ) {
if ( ! posix_test_lock ( file , fl , fl ) )
fl - > fl_type = F_UNLCK ;
err = 0 ;
} else
err = fuse_getlk ( file , fl ) ;
} else {
if ( fc - > no_lock )
err = posix_lock_file_wait ( file , fl ) ;
else
err = fuse_setlk ( file , fl ) ;
}
return err ;
}
2006-03-28 13:56:42 +04:00
static const struct file_operations fuse_file_operations = {
2005-09-10 00:10:30 +04:00
. llseek = generic_file_llseek ,
2006-10-01 10:28:48 +04:00
. read = do_sync_read ,
. aio_read = generic_file_aio_read ,
. write = do_sync_write ,
. aio_write = generic_file_aio_write ,
2005-09-10 00:10:30 +04:00
. mmap = fuse_file_mmap ,
. open = fuse_open ,
. flush = fuse_flush ,
. release = fuse_release ,
. fsync = fuse_fsync ,
2006-06-25 16:48:52 +04:00
. lock = fuse_file_lock ,
2005-09-10 00:10:30 +04:00
. sendfile = generic_file_sendfile ,
} ;
2006-03-28 13:56:42 +04:00
static const struct file_operations fuse_direct_io_file_operations = {
2005-09-10 00:10:35 +04:00
. llseek = generic_file_llseek ,
. read = fuse_direct_read ,
. write = fuse_direct_write ,
. open = fuse_open ,
. flush = fuse_flush ,
. release = fuse_release ,
. fsync = fuse_fsync ,
2006-06-25 16:48:52 +04:00
. lock = fuse_file_lock ,
2005-09-10 00:10:35 +04:00
/* no mmap and sendfile */
} ;
2006-06-28 15:26:44 +04:00
static const struct address_space_operations fuse_file_aops = {
2005-09-10 00:10:30 +04:00
. readpage = fuse_readpage ,
. prepare_write = fuse_prepare_write ,
. commit_write = fuse_commit_write ,
2005-09-10 00:10:33 +04:00
. readpages = fuse_readpages ,
2005-09-10 00:10:30 +04:00
. set_page_dirty = fuse_set_page_dirty ,
} ;
void fuse_init_file_inode ( struct inode * inode )
{
2005-09-10 00:10:37 +04:00
inode - > i_fop = & fuse_file_operations ;
inode - > i_data . a_ops = & fuse_file_aops ;
2005-09-10 00:10:30 +04:00
}