2005-04-16 15:20:36 -07:00
/*
* JFFS2 - - Journalling Flash File System , Version 2.
*
2007-04-25 14:16:47 +01:00
* Copyright © 2001 - 2007 Red Hat , Inc .
2005-04-16 15:20:36 -07:00
*
* Created by David Woodhouse < dwmw2 @ infradead . org >
*
* For licensing information , see the file ' LICENCE ' in this directory .
*
*/
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/fs.h>
# include <linux/time.h>
# include <linux/pagemap.h>
# include <linux/highmem.h>
# include <linux/crc32.h>
# include <linux/jffs2.h>
# include "nodelist.h"
static int jffs2_commit_write ( struct file * filp , struct page * pg ,
unsigned start , unsigned end ) ;
static int jffs2_prepare_write ( struct file * filp , struct page * pg ,
unsigned start , unsigned end ) ;
static int jffs2_readpage ( struct file * filp , struct page * pg ) ;
int jffs2_fsync ( struct file * filp , struct dentry * dentry , int datasync )
{
struct inode * inode = dentry - > d_inode ;
struct jffs2_sb_info * c = JFFS2_SB_INFO ( inode - > i_sb ) ;
/* Trigger GC to flush any pending writes for this inode */
jffs2_flush_wbuf_gc ( c , inode - > i_ino ) ;
2005-11-07 11:16:07 +00:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2006-03-28 01:56:42 -08:00
const struct file_operations jffs2_file_operations =
2005-04-16 15:20:36 -07:00
{
. llseek = generic_file_llseek ,
. open = generic_file_open ,
2006-09-30 23:28:48 -07:00
. read = do_sync_read ,
. aio_read = generic_file_aio_read ,
. write = do_sync_write ,
. aio_write = generic_file_aio_write ,
2005-04-16 15:20:36 -07:00
. ioctl = jffs2_ioctl ,
. mmap = generic_file_readonly_mmap ,
. fsync = jffs2_fsync ,
. sendfile = generic_file_sendfile
} ;
/* jffs2_file_inode_operations */
2007-02-12 00:55:39 -08:00
const struct inode_operations jffs2_file_inode_operations =
2005-04-16 15:20:36 -07:00
{
2006-05-13 15:09:47 +09:00
. permission = jffs2_permission ,
. setattr = jffs2_setattr ,
. setxattr = jffs2_setxattr ,
. getxattr = jffs2_getxattr ,
. listxattr = jffs2_listxattr ,
. removexattr = jffs2_removexattr
2005-04-16 15:20:36 -07:00
} ;
2006-06-28 04:26:44 -07:00
const struct address_space_operations jffs2_file_address_operations =
2005-04-16 15:20:36 -07:00
{
. readpage = jffs2_readpage ,
. prepare_write = jffs2_prepare_write ,
. commit_write = jffs2_commit_write
} ;
static int jffs2_do_readpage_nolock ( struct inode * inode , struct page * pg )
{
struct jffs2_inode_info * f = JFFS2_INODE_INFO ( inode ) ;
struct jffs2_sb_info * c = JFFS2_SB_INFO ( inode - > i_sb ) ;
unsigned char * pg_buf ;
int ret ;
D2 ( printk ( KERN_DEBUG " jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%lx \n " , inode - > i_ino , pg - > index < < PAGE_CACHE_SHIFT ) ) ;
2005-05-01 08:59:01 -07:00
BUG_ON ( ! PageLocked ( pg ) ) ;
2005-04-16 15:20:36 -07:00
pg_buf = kmap ( pg ) ;
/* FIXME: Can kmap fail? */
ret = jffs2_read_inode_range ( c , f , pg_buf , pg - > index < < PAGE_CACHE_SHIFT , PAGE_CACHE_SIZE ) ;
if ( ret ) {
ClearPageUptodate ( pg ) ;
SetPageError ( pg ) ;
} else {
SetPageUptodate ( pg ) ;
ClearPageError ( pg ) ;
}
flush_dcache_page ( pg ) ;
kunmap ( pg ) ;
D2 ( printk ( KERN_DEBUG " readpage finished \n " ) ) ;
return 0 ;
}
int jffs2_do_readpage_unlock ( struct inode * inode , struct page * pg )
{
int ret = jffs2_do_readpage_nolock ( inode , pg ) ;
unlock_page ( pg ) ;
return ret ;
}
static int jffs2_readpage ( struct file * filp , struct page * pg )
{
struct jffs2_inode_info * f = JFFS2_INODE_INFO ( pg - > mapping - > host ) ;
int ret ;
2005-11-07 11:16:07 +00:00
2005-04-16 15:20:36 -07:00
down ( & f - > sem ) ;
ret = jffs2_do_readpage_unlock ( pg - > mapping - > host , pg ) ;
up ( & f - > sem ) ;
return ret ;
}
static int jffs2_prepare_write ( struct file * filp , struct page * pg ,
unsigned start , unsigned end )
{
struct inode * inode = pg - > mapping - > host ;
struct jffs2_inode_info * f = JFFS2_INODE_INFO ( inode ) ;
uint32_t pageofs = pg - > index < < PAGE_CACHE_SHIFT ;
int ret = 0 ;
D1 ( printk ( KERN_DEBUG " jffs2_prepare_write() \n " ) ) ;
if ( pageofs > inode - > i_size ) {
/* Make new hole frag from old EOF to new page */
struct jffs2_sb_info * c = JFFS2_SB_INFO ( inode - > i_sb ) ;
struct jffs2_raw_inode ri ;
struct jffs2_full_dnode * fn ;
2006-05-23 00:38:06 +01:00
uint32_t alloc_len ;
2005-11-07 11:16:07 +00:00
2005-04-16 15:20:36 -07:00
D1 ( printk ( KERN_DEBUG " Writing new hole frag 0x%x-0x%x between current EOF and new page \n " ,
( unsigned int ) inode - > i_size , pageofs ) ) ;
2006-05-23 00:38:06 +01:00
ret = jffs2_reserve_space ( c , sizeof ( ri ) , & alloc_len ,
ALLOC_NORMAL , JFFS2_SUMMARY_INODE_SIZE ) ;
2005-04-16 15:20:36 -07:00
if ( ret )
return ret ;
down ( & f - > sem ) ;
memset ( & ri , 0 , sizeof ( ri ) ) ;
ri . magic = cpu_to_je16 ( JFFS2_MAGIC_BITMASK ) ;
ri . nodetype = cpu_to_je16 ( JFFS2_NODETYPE_INODE ) ;
ri . totlen = cpu_to_je32 ( sizeof ( ri ) ) ;
ri . hdr_crc = cpu_to_je32 ( crc32 ( 0 , & ri , sizeof ( struct jffs2_unknown_node ) - 4 ) ) ;
ri . ino = cpu_to_je32 ( f - > inocache - > ino ) ;
ri . version = cpu_to_je32 ( + + f - > highest_version ) ;
ri . mode = cpu_to_jemode ( inode - > i_mode ) ;
ri . uid = cpu_to_je16 ( inode - > i_uid ) ;
ri . gid = cpu_to_je16 ( inode - > i_gid ) ;
ri . isize = cpu_to_je32 ( max ( ( uint32_t ) inode - > i_size , pageofs ) ) ;
ri . atime = ri . ctime = ri . mtime = cpu_to_je32 ( get_seconds ( ) ) ;
ri . offset = cpu_to_je32 ( inode - > i_size ) ;
ri . dsize = cpu_to_je32 ( pageofs - inode - > i_size ) ;
ri . csize = cpu_to_je32 ( 0 ) ;
ri . compr = JFFS2_COMPR_ZERO ;
ri . node_crc = cpu_to_je32 ( crc32 ( 0 , & ri , sizeof ( ri ) - 8 ) ) ;
ri . data_crc = cpu_to_je32 ( 0 ) ;
2005-11-07 11:16:07 +00:00
2006-05-23 00:38:06 +01:00
fn = jffs2_write_dnode ( c , f , & ri , NULL , 0 , ALLOC_NORMAL ) ;
2005-04-16 15:20:36 -07:00
if ( IS_ERR ( fn ) ) {
ret = PTR_ERR ( fn ) ;
jffs2_complete_reservation ( c ) ;
up ( & f - > sem ) ;
return ret ;
}
ret = jffs2_add_full_dnode_to_inode ( c , f , fn ) ;
if ( f - > metadata ) {
jffs2_mark_node_obsolete ( c , f - > metadata - > raw ) ;
jffs2_free_full_dnode ( f - > metadata ) ;
f - > metadata = NULL ;
}
if ( ret ) {
D1 ( printk ( KERN_DEBUG " Eep. add_full_dnode_to_inode() failed in prepare_write, returned %d \n " , ret ) ) ;
jffs2_mark_node_obsolete ( c , fn - > raw ) ;
jffs2_free_full_dnode ( fn ) ;
jffs2_complete_reservation ( c ) ;
up ( & f - > sem ) ;
return ret ;
}
jffs2_complete_reservation ( c ) ;
inode - > i_size = pageofs ;
up ( & f - > sem ) ;
}
2005-11-07 11:16:07 +00:00
2005-04-16 15:20:36 -07:00
/* Read in the page if it wasn't already present, unless it's a whole page */
if ( ! PageUptodate ( pg ) & & ( start | | end < PAGE_CACHE_SIZE ) ) {
down ( & f - > sem ) ;
ret = jffs2_do_readpage_nolock ( inode , pg ) ;
up ( & f - > sem ) ;
}
D1 ( printk ( KERN_DEBUG " end prepare_write(). pg->flags %lx \n " , pg - > flags ) ) ;
return ret ;
}
static int jffs2_commit_write ( struct file * filp , struct page * pg ,
unsigned start , unsigned end )
{
/* Actually commit the write from the page cache page we're looking at.
* For now , we write the full page out each time . It sucks , but it ' s simple
*/
struct inode * inode = pg - > mapping - > host ;
struct jffs2_inode_info * f = JFFS2_INODE_INFO ( inode ) ;
struct jffs2_sb_info * c = JFFS2_SB_INFO ( inode - > i_sb ) ;
struct jffs2_raw_inode * ri ;
unsigned aligned_start = start & ~ 3 ;
int ret = 0 ;
uint32_t writtenlen = 0 ;
D1 ( printk ( KERN_DEBUG " jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx \n " ,
inode - > i_ino , pg - > index < < PAGE_CACHE_SHIFT , start , end , pg - > flags ) ) ;
2006-05-14 04:06:24 +01:00
if ( end = = PAGE_CACHE_SIZE ) {
if ( ! start ) {
/* We need to avoid deadlock with page_cache_read() in
jffs2_garbage_collect_pass ( ) . So we have to mark the
page up to date , to prevent page_cache_read ( ) from
trying to re - lock it . */
SetPageUptodate ( pg ) ;
} else {
/* When writing out the end of a page, write out the
_whole_ page . This helps to reduce the number of
nodes in files which have many short writes , like
syslog files . */
start = aligned_start = 0 ;
}
2005-04-16 15:20:36 -07:00
}
ri = jffs2_alloc_raw_inode ( ) ;
if ( ! ri ) {
D1 ( printk ( KERN_DEBUG " jffs2_commit_write(): Allocation of raw inode failed \n " ) ) ;
return - ENOMEM ;
}
/* Set the fields that the generic jffs2_write_inode_range() code can't find */
ri - > ino = cpu_to_je32 ( inode - > i_ino ) ;
ri - > mode = cpu_to_jemode ( inode - > i_mode ) ;
ri - > uid = cpu_to_je16 ( inode - > i_uid ) ;
ri - > gid = cpu_to_je16 ( inode - > i_gid ) ;
ri - > isize = cpu_to_je32 ( ( uint32_t ) inode - > i_size ) ;
ri - > atime = ri - > ctime = ri - > mtime = cpu_to_je32 ( get_seconds ( ) ) ;
/* In 2.4, it was already kmapped by generic_file_write(). Doesn't
hurt to do it again . The alternative is ifdefs , which are ugly . */
kmap ( pg ) ;
ret = jffs2_write_inode_range ( c , f , ri , page_address ( pg ) + aligned_start ,
( pg - > index < < PAGE_CACHE_SHIFT ) + aligned_start ,
end - aligned_start , & writtenlen ) ;
kunmap ( pg ) ;
if ( ret ) {
/* There was an error writing. */
SetPageError ( pg ) ;
}
2005-11-07 11:16:07 +00:00
2005-04-16 15:20:36 -07:00
/* Adjust writtenlen for the padding we did, so we don't confuse our caller */
if ( writtenlen < ( start & 3 ) )
writtenlen = 0 ;
else
writtenlen - = ( start & 3 ) ;
if ( writtenlen ) {
if ( inode - > i_size < ( pg - > index < < PAGE_CACHE_SHIFT ) + start + writtenlen ) {
inode - > i_size = ( pg - > index < < PAGE_CACHE_SHIFT ) + start + writtenlen ;
inode - > i_blocks = ( inode - > i_size + 511 ) > > 9 ;
2005-11-07 11:16:07 +00:00
2005-04-16 15:20:36 -07:00
inode - > i_ctime = inode - > i_mtime = ITIME ( je32_to_cpu ( ri - > ctime ) ) ;
}
}
jffs2_free_raw_inode ( ri ) ;
if ( start + writtenlen < end ) {
/* generic_file_write has written more to the page cache than we've
2005-11-07 11:16:07 +00:00
actually written to the medium . Mark the page ! Uptodate so that
2005-04-16 15:20:36 -07:00
it gets reread */
D1 ( printk ( KERN_DEBUG " jffs2_commit_write(): Not all bytes written. Marking page !uptodate \n " ) ) ;
SetPageError ( pg ) ;
ClearPageUptodate ( pg ) ;
}
2005-10-19 00:29:38 +01:00
D1 ( printk ( KERN_DEBUG " jffs2_commit_write() returning %d \n " , start + writtenlen = = end ? 0 : ret ) ) ;
return start + writtenlen = = end ? 0 : ret ;
2005-04-16 15:20:36 -07:00
}