2005-04-17 02:20:36 +04:00
/*
* file . c
*
* Copyright ( C ) 1995 , 1996 , 1997 by Paal - Kr . Engstad and Volker Lendecke
* Copyright ( C ) 1997 by Volker Lendecke
*
* Please add a note about your changes to smbfs in the ChangeLog file .
*/
# include <linux/time.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/fcntl.h>
# include <linux/stat.h>
# include <linux/mm.h>
# include <linux/slab.h>
# include <linux/pagemap.h>
# include <linux/smp_lock.h>
# include <linux/net.h>
Detach sched.h from mm.h
First thing mm.h does is including sched.h solely for can_do_mlock() inline
function which has "current" dereference inside. By dealing with can_do_mlock()
mm.h can be detached from sched.h which is good. See below, why.
This patch
a) removes unconditional inclusion of sched.h from mm.h
b) makes can_do_mlock() normal function in mm/mlock.c
c) exports can_do_mlock() to not break compilation
d) adds sched.h inclusions back to files that were getting it indirectly.
e) adds less bloated headers to some files (asm/signal.h, jiffies.h) that were
getting them indirectly
Net result is:
a) mm.h users would get less code to open, read, preprocess, parse, ... if
they don't need sched.h
b) sched.h stops being dependency for significant number of files:
on x86_64 allmodconfig touching sched.h results in recompile of 4083 files,
after patch it's only 3744 (-8.3%).
Cross-compile tested on
all arm defconfigs, all mips defconfigs, all powerpc defconfigs,
alpha alpha-up
arm
i386 i386-up i386-defconfig i386-allnoconfig
ia64 ia64-up
m68k
mips
parisc parisc-up
powerpc powerpc-up
s390 s390-up
sparc sparc-up
sparc64 sparc64-up
um-x86_64
x86_64 x86_64-up x86_64-defconfig x86_64-allnoconfig
as well as my two usual configs.
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-05-21 01:22:52 +04:00
# include <linux/aio.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
# include <asm/system.h>
# include <linux/smbno.h>
# include <linux/smb_fs.h>
# include "smb_debug.h"
# include "proto.h"
static int
smb_fsync ( struct file * file , struct dentry * dentry , int datasync )
{
struct smb_sb_info * server = server_from_dentry ( dentry ) ;
int result ;
VERBOSE ( " sync file %s/%s \n " , DENTRY_PATH ( dentry ) ) ;
/*
* The VFS will writepage ( ) all dirty pages for us , but we
* should send a SMBflush to the server , letting it know that
* we want things synchronized with actual storage .
*
* Note : this function requires all pages to have been written already
* ( should be ok with writepage_sync )
*/
result = smb_proc_flush ( server , SMB_I ( dentry - > d_inode ) - > fileid ) ;
return result ;
}
/*
* Read a page synchronously .
*/
static int
smb_readpage_sync ( struct dentry * dentry , struct page * page )
{
char * buffer = kmap ( page ) ;
loff_t offset = ( loff_t ) page - > index < < PAGE_CACHE_SHIFT ;
struct smb_sb_info * server = server_from_dentry ( dentry ) ;
unsigned int rsize = smb_get_rsize ( server ) ;
int count = PAGE_SIZE ;
int result ;
VERBOSE ( " file %s/%s, count=%d@%Ld, rsize=%d \n " ,
DENTRY_PATH ( dentry ) , count , offset , rsize ) ;
result = smb_open ( dentry , SMB_O_RDONLY ) ;
if ( result < 0 )
goto io_error ;
do {
if ( count < rsize )
rsize = count ;
result = server - > ops - > read ( dentry - > d_inode , offset , rsize , buffer ) ;
if ( result < 0 )
goto io_error ;
count - = result ;
offset + = result ;
buffer + = result ;
dentry - > d_inode - > i_atime =
current_fs_time ( dentry - > d_inode - > i_sb ) ;
if ( result < rsize )
break ;
} while ( count ) ;
memset ( buffer , 0 , count ) ;
flush_dcache_page ( page ) ;
SetPageUptodate ( page ) ;
result = 0 ;
io_error :
kunmap ( page ) ;
unlock_page ( page ) ;
return result ;
}
/*
* We are called with the page locked and we unlock it when done .
*/
static int
smb_readpage ( struct file * file , struct page * page )
{
int error ;
2006-12-08 13:37:39 +03:00
struct dentry * dentry = file - > f_path . dentry ;
2005-04-17 02:20:36 +04:00
page_cache_get ( page ) ;
error = smb_readpage_sync ( dentry , page ) ;
page_cache_release ( page ) ;
return error ;
}
/*
* Write a page synchronously .
* Offset is the data offset within the page .
*/
static int
smb_writepage_sync ( struct inode * inode , struct page * page ,
unsigned long pageoffset , unsigned int count )
{
loff_t offset ;
char * buffer = kmap ( page ) + pageoffset ;
struct smb_sb_info * server = server_from_inode ( inode ) ;
unsigned int wsize = smb_get_wsize ( server ) ;
int ret = 0 ;
offset = ( ( loff_t ) page - > index < < PAGE_CACHE_SHIFT ) + pageoffset ;
VERBOSE ( " file ino=%ld, fileid=%d, count=%d@%Ld, wsize=%d \n " ,
inode - > i_ino , SMB_I ( inode ) - > fileid , count , offset , wsize ) ;
do {
int write_ret ;
if ( count < wsize )
wsize = count ;
write_ret = server - > ops - > write ( inode , offset , wsize , buffer ) ;
if ( write_ret < 0 ) {
PARANOIA ( " failed write, wsize=%d, write_ret=%d \n " ,
wsize , write_ret ) ;
ret = write_ret ;
break ;
}
/* N.B. what if result < wsize?? */
# ifdef SMBFS_PARANOIA
if ( write_ret < wsize )
PARANOIA ( " short write, wsize=%d, write_ret=%d \n " ,
wsize , write_ret ) ;
# endif
buffer + = wsize ;
offset + = wsize ;
count - = wsize ;
/*
* Update the inode now rather than waiting for a refresh .
*/
inode - > i_mtime = inode - > i_atime = current_fs_time ( inode - > i_sb ) ;
SMB_I ( inode ) - > flags | = SMB_F_LOCALWRITE ;
if ( offset > inode - > i_size )
inode - > i_size = offset ;
} while ( count ) ;
kunmap ( page ) ;
return ret ;
}
/*
* Write a page to the server . This will be used for NFS swapping only
* ( for now ) , and we currently do this synchronously only .
*
* We are called with the page locked and we unlock it when done .
*/
static int
smb_writepage ( struct page * page , struct writeback_control * wbc )
{
struct address_space * mapping = page - > mapping ;
struct inode * inode ;
unsigned long end_index ;
unsigned offset = PAGE_CACHE_SIZE ;
int err ;
2006-04-01 03:16:26 +04:00
BUG_ON ( ! mapping ) ;
2005-04-17 02:20:36 +04:00
inode = mapping - > host ;
2006-04-01 03:16:26 +04:00
BUG_ON ( ! inode ) ;
2005-04-17 02:20:36 +04:00
end_index = inode - > i_size > > PAGE_CACHE_SHIFT ;
/* easy case */
if ( page - > index < end_index )
goto do_it ;
/* things got complicated... */
offset = inode - > i_size & ( PAGE_CACHE_SIZE - 1 ) ;
/* OK, are we completely out? */
if ( page - > index > = end_index + 1 | | ! offset )
return 0 ; /* truncated - don't care */
do_it :
page_cache_get ( page ) ;
err = smb_writepage_sync ( inode , page , 0 , offset ) ;
SetPageUptodate ( page ) ;
unlock_page ( page ) ;
page_cache_release ( page ) ;
return err ;
}
static int
smb_updatepage ( struct file * file , struct page * page , unsigned long offset ,
unsigned int count )
{
2006-12-08 13:37:39 +03:00
struct dentry * dentry = file - > f_path . dentry ;
2005-04-17 02:20:36 +04:00
2006-01-08 12:03:05 +03:00
DEBUG1 ( " (%s/%s %d@%lld) \n " , DENTRY_PATH ( dentry ) , count ,
( ( unsigned long long ) page - > index < < PAGE_CACHE_SHIFT ) + offset ) ;
2005-04-17 02:20:36 +04:00
return smb_writepage_sync ( dentry - > d_inode , page , offset , count ) ;
}
static ssize_t
2006-10-01 10:28:48 +04:00
smb_file_aio_read ( struct kiocb * iocb , const struct iovec * iov ,
unsigned long nr_segs , loff_t pos )
2005-04-17 02:20:36 +04:00
{
2006-10-01 10:28:48 +04:00
struct file * file = iocb - > ki_filp ;
2006-12-08 13:37:39 +03:00
struct dentry * dentry = file - > f_path . dentry ;
2005-04-17 02:20:36 +04:00
ssize_t status ;
VERBOSE ( " file %s/%s, count=%lu@%lu \n " , DENTRY_PATH ( dentry ) ,
2006-10-01 10:28:48 +04:00
( unsigned long ) iocb - > ki_left , ( unsigned long ) pos ) ;
2005-04-17 02:20:36 +04:00
status = smb_revalidate_inode ( dentry ) ;
if ( status ) {
PARANOIA ( " %s/%s validation failed, error=%Zd \n " ,
DENTRY_PATH ( dentry ) , status ) ;
goto out ;
}
VERBOSE ( " before read, size=%ld, flags=%x, atime=%ld \n " ,
( long ) dentry - > d_inode - > i_size ,
2007-11-15 04:00:18 +03:00
dentry - > d_inode - > i_flags , dentry - > d_inode - > i_atime . tv_sec ) ;
2005-04-17 02:20:36 +04:00
2006-10-01 10:28:48 +04:00
status = generic_file_aio_read ( iocb , iov , nr_segs , pos ) ;
2005-04-17 02:20:36 +04:00
out :
return status ;
}
static int
smb_file_mmap ( struct file * file , struct vm_area_struct * vma )
{
2006-12-08 13:37:39 +03:00
struct dentry * dentry = file - > f_path . dentry ;
2005-04-17 02:20:36 +04:00
int status ;
VERBOSE ( " file %s/%s, address %lu - %lu \n " ,
DENTRY_PATH ( dentry ) , vma - > vm_start , vma - > vm_end ) ;
status = smb_revalidate_inode ( dentry ) ;
if ( status ) {
PARANOIA ( " %s/%s validation failed, error=%d \n " ,
DENTRY_PATH ( dentry ) , status ) ;
goto out ;
}
status = generic_file_mmap ( file , vma ) ;
out :
return status ;
}
static ssize_t
2007-06-01 13:49:19 +04:00
smb_file_splice_read ( struct file * file , loff_t * ppos ,
struct pipe_inode_info * pipe , size_t count ,
unsigned int flags )
2005-04-17 02:20:36 +04:00
{
2006-12-08 13:37:39 +03:00
struct dentry * dentry = file - > f_path . dentry ;
2005-04-17 02:20:36 +04:00
ssize_t status ;
2007-11-15 04:00:18 +03:00
VERBOSE ( " file %s/%s, pos=%Ld, count=%lu \n " ,
2005-04-17 02:20:36 +04:00
DENTRY_PATH ( dentry ) , * ppos , count ) ;
status = smb_revalidate_inode ( dentry ) ;
if ( status ) {
PARANOIA ( " %s/%s validation failed, error=%Zd \n " ,
DENTRY_PATH ( dentry ) , status ) ;
goto out ;
}
2007-06-01 13:49:19 +04:00
status = generic_file_splice_read ( file , ppos , pipe , count , flags ) ;
2005-04-17 02:20:36 +04:00
out :
return status ;
}
/*
* This does the " real " work of the write . The generic routine has
* allocated the page , locked it , done all the page alignment stuff
* calculations etc . Now we should just copy the data from user
* space and write it back to the real medium . .
*
* If the writer ends up delaying the write , the writer needs to
* increment the page use counts until he is done with the page .
*/
2007-10-16 12:25:16 +04:00
static int smb_write_begin ( struct file * file , struct address_space * mapping ,
loff_t pos , unsigned len , unsigned flags ,
struct page * * pagep , void * * fsdata )
2005-04-17 02:20:36 +04:00
{
2007-10-16 12:25:16 +04:00
pgoff_t index = pos > > PAGE_CACHE_SHIFT ;
* pagep = __grab_cache_page ( mapping , index ) ;
if ( ! * pagep )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-10-16 12:25:16 +04:00
static int smb_write_end ( struct file * file , struct address_space * mapping ,
loff_t pos , unsigned len , unsigned copied ,
struct page * page , void * fsdata )
2005-04-17 02:20:36 +04:00
{
int status ;
2007-10-16 12:25:16 +04:00
unsigned offset = pos & ( PAGE_CACHE_SIZE - 1 ) ;
2005-04-17 02:20:36 +04:00
lock_kernel ( ) ;
2007-10-16 12:25:16 +04:00
status = smb_updatepage ( file , page , offset , copied ) ;
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
2007-10-16 12:25:16 +04:00
if ( ! status ) {
if ( ! PageUptodate ( page ) & & copied = = PAGE_CACHE_SIZE )
SetPageUptodate ( page ) ;
status = copied ;
}
unlock_page ( page ) ;
page_cache_release ( page ) ;
2005-04-17 02:20:36 +04:00
return status ;
}
2006-06-28 15:26:44 +04:00
const struct address_space_operations smb_file_aops = {
2005-04-17 02:20:36 +04:00
. readpage = smb_readpage ,
. writepage = smb_writepage ,
2007-10-16 12:25:16 +04:00
. write_begin = smb_write_begin ,
. write_end = smb_write_end ,
2005-04-17 02:20:36 +04:00
} ;
/*
* Write to a file ( through the page cache ) .
*/
static ssize_t
2006-10-01 10:28:48 +04:00
smb_file_aio_write ( struct kiocb * iocb , const struct iovec * iov ,
unsigned long nr_segs , loff_t pos )
2005-04-17 02:20:36 +04:00
{
2006-10-01 10:28:48 +04:00
struct file * file = iocb - > ki_filp ;
2006-12-08 13:37:39 +03:00
struct dentry * dentry = file - > f_path . dentry ;
2005-04-17 02:20:36 +04:00
ssize_t result ;
VERBOSE ( " file %s/%s, count=%lu@%lu \n " ,
DENTRY_PATH ( dentry ) ,
2006-10-01 10:28:48 +04:00
( unsigned long ) iocb - > ki_left , ( unsigned long ) pos ) ;
2005-04-17 02:20:36 +04:00
result = smb_revalidate_inode ( dentry ) ;
if ( result ) {
PARANOIA ( " %s/%s validation failed, error=%Zd \n " ,
DENTRY_PATH ( dentry ) , result ) ;
goto out ;
}
result = smb_open ( dentry , SMB_O_WRONLY ) ;
if ( result )
goto out ;
2006-10-01 10:28:48 +04:00
if ( iocb - > ki_left > 0 ) {
result = generic_file_aio_write ( iocb , iov , nr_segs , pos ) ;
2005-04-17 02:20:36 +04:00
VERBOSE ( " pos=%ld, size=%ld, mtime=%ld, atime=%ld \n " ,
( long ) file - > f_pos , ( long ) dentry - > d_inode - > i_size ,
2007-11-15 04:00:18 +03:00
dentry - > d_inode - > i_mtime . tv_sec ,
dentry - > d_inode - > i_atime . tv_sec ) ;
2005-04-17 02:20:36 +04:00
}
out :
return result ;
}
static int
smb_file_open ( struct inode * inode , struct file * file )
{
int result ;
2006-12-08 13:37:39 +03:00
struct dentry * dentry = file - > f_path . dentry ;
2005-04-17 02:20:36 +04:00
int smb_mode = ( file - > f_mode & O_ACCMODE ) - 1 ;
lock_kernel ( ) ;
result = smb_open ( dentry , smb_mode ) ;
if ( result )
goto out ;
SMB_I ( inode ) - > openers + + ;
out :
unlock_kernel ( ) ;
return result ;
}
static int
smb_file_release ( struct inode * inode , struct file * file )
{
lock_kernel ( ) ;
if ( ! - - SMB_I ( inode ) - > openers ) {
/* We must flush any dirty pages now as we won't be able to
write anything after close . mmap can trigger this .
" openers " should perhaps include mmap ' ers . . . */
[PATCH] Fix and add EXPORT_SYMBOL(filemap_write_and_wait)
This patch add EXPORT_SYMBOL(filemap_write_and_wait) and use it.
See mm/filemap.c:
And changes the filemap_write_and_wait() and filemap_write_and_wait_range().
Current filemap_write_and_wait() doesn't wait if filemap_fdatawrite()
returns error. However, even if filemap_fdatawrite() returned an
error, it may have submitted the partially data pages to the device.
(e.g. in the case of -ENOSPC)
<quotation>
Andrew Morton writes,
If filemap_fdatawrite() returns an error, this might be due to some
I/O problem: dead disk, unplugged cable, etc. Given the generally
crappy quality of the kernel's handling of such exceptions, there's a
good chance that the filemap_fdatawait() will get stuck in D state
forever.
</quotation>
So, this patch doesn't wait if filemap_fdatawrite() returns the -EIO.
Trond, could you please review the nfs part? Especially I'm not sure,
nfs must use the "filemap_fdatawrite(inode->i_mapping) == 0", or not.
Acked-by: Trond Myklebust <trond.myklebust@fys.uio.no>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-08 12:02:14 +03:00
filemap_write_and_wait ( inode - > i_mapping ) ;
2005-04-17 02:20:36 +04:00
smb_close ( inode ) ;
}
unlock_kernel ( ) ;
return 0 ;
}
/*
* Check whether the required access is compatible with
* an inode ' s permission . SMB doesn ' t recognize superuser
* privileges , so we need our own check for this .
*/
static int
2008-07-16 05:03:57 +04:00
smb_file_permission ( struct inode * inode , int mask )
2005-04-17 02:20:36 +04:00
{
int mode = inode - > i_mode ;
int error = 0 ;
VERBOSE ( " mode=%x, mask=%x \n " , mode , mask ) ;
/* Look at user permissions */
mode > > = 6 ;
2008-07-16 05:03:57 +04:00
if ( mask & ~ mode & ( MAY_READ | MAY_WRITE | MAY_EXEC ) )
2005-04-17 02:20:36 +04:00
error = - EACCES ;
return error ;
}
2008-06-27 13:05:24 +04:00
static loff_t smb_remote_llseek ( struct file * file , loff_t offset , int origin )
{
loff_t ret ;
lock_kernel ( ) ;
ret = generic_file_llseek_unlocked ( file , offset , origin ) ;
unlock_kernel ( ) ;
return ret ;
}
2006-03-28 13:56:42 +04:00
const struct file_operations smb_file_operations =
2005-04-17 02:20:36 +04:00
{
2008-06-27 13:05:24 +04:00
. llseek = smb_remote_llseek ,
2006-10-01 10:28:48 +04:00
. read = do_sync_read ,
. aio_read = smb_file_aio_read ,
. write = do_sync_write ,
. aio_write = smb_file_aio_write ,
2005-04-17 02:20:36 +04:00
. ioctl = smb_ioctl ,
. mmap = smb_file_mmap ,
. open = smb_file_open ,
. release = smb_file_release ,
. fsync = smb_fsync ,
2007-06-01 13:49:19 +04:00
. splice_read = smb_file_splice_read ,
2005-04-17 02:20:36 +04:00
} ;
2007-02-12 11:55:40 +03:00
const struct inode_operations smb_file_inode_operations =
2005-04-17 02:20:36 +04:00
{
. permission = smb_file_permission ,
. getattr = smb_getattr ,
. setattr = smb_notify_change ,
} ;