diff options
Diffstat (limited to 'fs/squashfs/file.c')
| -rw-r--r-- | fs/squashfs/file.c | 152 | 
1 files changed, 77 insertions, 75 deletions
diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c index a25c5060bdc..e5c9689062b 100644 --- a/fs/squashfs/file.c +++ b/fs/squashfs/file.c @@ -2,7 +2,7 @@   * Squashfs - a compressed read only filesystem for Linux   *   * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 - * Phillip Lougher <phillip@lougher.demon.co.uk> + * Phillip Lougher <phillip@squashfs.org.uk>   *   * This program is free software; you can redistribute it and/or   * modify it under the terms of the GNU General Public License @@ -370,77 +370,15 @@ static int read_blocklist(struct inode *inode, int index, u64 *block)  	return le32_to_cpu(size);  } - -static int squashfs_readpage(struct file *file, struct page *page) +/* Copy data into page cache  */ +void squashfs_copy_cache(struct page *page, struct squashfs_cache_entry *buffer, +	int bytes, int offset)  {  	struct inode *inode = page->mapping->host;  	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; -	int bytes, i, offset = 0, sparse = 0; -	struct squashfs_cache_entry *buffer = NULL;  	void *pageaddr; - -	int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1; -	int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT); -	int start_index = page->index & ~mask; -	int end_index = start_index | mask; -	int file_end = i_size_read(inode) >> msblk->block_log; - -	TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", -				page->index, squashfs_i(inode)->start); - -	if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> -					PAGE_CACHE_SHIFT)) -		goto out; - -	if (index < file_end || squashfs_i(inode)->fragment_block == -					SQUASHFS_INVALID_BLK) { -		/* -		 * Reading a datablock from disk.  Need to read block list -		 * to get location and block size. -		 */ -		u64 block = 0; -		int bsize = read_blocklist(inode, index, &block); -		if (bsize < 0) -			goto error_out; - -		if (bsize == 0) { /* hole */ -			bytes = index == file_end ? -				(i_size_read(inode) & (msblk->block_size - 1)) : -				 msblk->block_size; -			sparse = 1; -		} else { -			/* -			 * Read and decompress datablock. -			 */ -			buffer = squashfs_get_datablock(inode->i_sb, -								block, bsize); -			if (buffer->error) { -				ERROR("Unable to read page, block %llx, size %x" -					"\n", block, bsize); -				squashfs_cache_put(buffer); -				goto error_out; -			} -			bytes = buffer->length; -		} -	} else { -		/* -		 * Datablock is stored inside a fragment (tail-end packed -		 * block). -		 */ -		buffer = squashfs_get_fragment(inode->i_sb, -				squashfs_i(inode)->fragment_block, -				squashfs_i(inode)->fragment_size); - -		if (buffer->error) { -			ERROR("Unable to read page, block %llx, size %x\n", -				squashfs_i(inode)->fragment_block, -				squashfs_i(inode)->fragment_size); -			squashfs_cache_put(buffer); -			goto error_out; -		} -		bytes = i_size_read(inode) & (msblk->block_size - 1); -		offset = squashfs_i(inode)->fragment_offset; -	} +	int i, mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1; +	int start_index = page->index & ~mask, end_index = start_index | mask;  	/*  	 * Loop copying datablock into pages.  As the datablock likely covers @@ -451,7 +389,7 @@ static int squashfs_readpage(struct file *file, struct page *page)  	for (i = start_index; i <= end_index && bytes > 0; i++,  			bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) {  		struct page *push_page; -		int avail = sparse ? 0 : min_t(int, bytes, PAGE_CACHE_SIZE); +		int avail = buffer ? min_t(int, bytes, PAGE_CACHE_SIZE) : 0;  		TRACE("bytes %d, i %d, available_bytes %d\n", bytes, i, avail); @@ -464,10 +402,10 @@ static int squashfs_readpage(struct file *file, struct page *page)  		if (PageUptodate(push_page))  			goto skip_page; -		pageaddr = kmap_atomic(push_page, KM_USER0); +		pageaddr = kmap_atomic(push_page);  		squashfs_copy_data(pageaddr, buffer, offset, avail);  		memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail); -		kunmap_atomic(pageaddr, KM_USER0); +		kunmap_atomic(pageaddr);  		flush_dcache_page(push_page);  		SetPageUptodate(push_page);  skip_page: @@ -475,18 +413,82 @@ skip_page:  		if (i != page->index)  			page_cache_release(push_page);  	} +} + +/* Read datablock stored packed inside a fragment (tail-end packed block) */ +static int squashfs_readpage_fragment(struct page *page) +{ +	struct inode *inode = page->mapping->host; +	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; +	struct squashfs_cache_entry *buffer = squashfs_get_fragment(inode->i_sb, +		squashfs_i(inode)->fragment_block, +		squashfs_i(inode)->fragment_size); +	int res = buffer->error; + +	if (res) +		ERROR("Unable to read page, block %llx, size %x\n", +			squashfs_i(inode)->fragment_block, +			squashfs_i(inode)->fragment_size); +	else +		squashfs_copy_cache(page, buffer, i_size_read(inode) & +			(msblk->block_size - 1), +			squashfs_i(inode)->fragment_offset); + +	squashfs_cache_put(buffer); +	return res; +} -	if (!sparse) -		squashfs_cache_put(buffer); +static int squashfs_readpage_sparse(struct page *page, int index, int file_end) +{ +	struct inode *inode = page->mapping->host; +	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; +	int bytes = index == file_end ? +			(i_size_read(inode) & (msblk->block_size - 1)) : +			 msblk->block_size; +	squashfs_copy_cache(page, NULL, bytes, 0);  	return 0; +} + +static int squashfs_readpage(struct file *file, struct page *page) +{ +	struct inode *inode = page->mapping->host; +	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; +	int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT); +	int file_end = i_size_read(inode) >> msblk->block_log; +	int res; +	void *pageaddr; + +	TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", +				page->index, squashfs_i(inode)->start); + +	if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> +					PAGE_CACHE_SHIFT)) +		goto out; + +	if (index < file_end || squashfs_i(inode)->fragment_block == +					SQUASHFS_INVALID_BLK) { +		u64 block = 0; +		int bsize = read_blocklist(inode, index, &block); +		if (bsize < 0) +			goto error_out; + +		if (bsize == 0) +			res = squashfs_readpage_sparse(page, index, file_end); +		else +			res = squashfs_readpage_block(page, block, bsize); +	} else +		res = squashfs_readpage_fragment(page); + +	if (!res) +		return 0;  error_out:  	SetPageError(page);  out: -	pageaddr = kmap_atomic(page, KM_USER0); +	pageaddr = kmap_atomic(page);  	memset(pageaddr, 0, PAGE_CACHE_SIZE); -	kunmap_atomic(pageaddr, KM_USER0); +	kunmap_atomic(pageaddr);  	flush_dcache_page(page);  	if (!PageError(page))  		SetPageUptodate(page);  | 
