101 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			101 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2013
 | 
						|
 * Phillip Lougher <phillip@squashfs.org.uk>
 | 
						|
 *
 | 
						|
 * This work is licensed under the terms of the GNU GPL, version 2. See
 | 
						|
 * the COPYING file in the top-level directory.
 | 
						|
 */
 | 
						|
 | 
						|
#include <linux/kernel.h>
 | 
						|
#include <linux/slab.h>
 | 
						|
#include <linux/pagemap.h>
 | 
						|
#include "page_actor.h"
 | 
						|
 | 
						|
/*
 | 
						|
 * This file contains implementations of page_actor for decompressing into
 | 
						|
 * an intermediate buffer, and for decompressing directly into the
 | 
						|
 * page cache.
 | 
						|
 *
 | 
						|
 * Calling code should avoid sleeping between calls to squashfs_first_page()
 | 
						|
 * and squashfs_finish_page().
 | 
						|
 */
 | 
						|
 | 
						|
/* Implementation of page_actor for decompressing into intermediate buffer */
 | 
						|
static void *cache_first_page(struct squashfs_page_actor *actor)
 | 
						|
{
 | 
						|
	actor->next_page = 1;
 | 
						|
	return actor->buffer[0];
 | 
						|
}
 | 
						|
 | 
						|
static void *cache_next_page(struct squashfs_page_actor *actor)
 | 
						|
{
 | 
						|
	if (actor->next_page == actor->pages)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	return actor->buffer[actor->next_page++];
 | 
						|
}
 | 
						|
 | 
						|
static void cache_finish_page(struct squashfs_page_actor *actor)
 | 
						|
{
 | 
						|
	/* empty */
 | 
						|
}
 | 
						|
 | 
						|
struct squashfs_page_actor *squashfs_page_actor_init(void **buffer,
 | 
						|
	int pages, int length)
 | 
						|
{
 | 
						|
	struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
 | 
						|
 | 
						|
	if (actor == NULL)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	actor->length = length ? : pages * PAGE_SIZE;
 | 
						|
	actor->buffer = buffer;
 | 
						|
	actor->pages = pages;
 | 
						|
	actor->next_page = 0;
 | 
						|
	actor->squashfs_first_page = cache_first_page;
 | 
						|
	actor->squashfs_next_page = cache_next_page;
 | 
						|
	actor->squashfs_finish_page = cache_finish_page;
 | 
						|
	return actor;
 | 
						|
}
 | 
						|
 | 
						|
/* Implementation of page_actor for decompressing directly into page cache. */
 | 
						|
static void *direct_first_page(struct squashfs_page_actor *actor)
 | 
						|
{
 | 
						|
	actor->next_page = 1;
 | 
						|
	return actor->pageaddr = kmap_atomic(actor->page[0]);
 | 
						|
}
 | 
						|
 | 
						|
static void *direct_next_page(struct squashfs_page_actor *actor)
 | 
						|
{
 | 
						|
	if (actor->pageaddr)
 | 
						|
		kunmap_atomic(actor->pageaddr);
 | 
						|
 | 
						|
	return actor->pageaddr = actor->next_page == actor->pages ? NULL :
 | 
						|
		kmap_atomic(actor->page[actor->next_page++]);
 | 
						|
}
 | 
						|
 | 
						|
static void direct_finish_page(struct squashfs_page_actor *actor)
 | 
						|
{
 | 
						|
	if (actor->pageaddr)
 | 
						|
		kunmap_atomic(actor->pageaddr);
 | 
						|
}
 | 
						|
 | 
						|
struct squashfs_page_actor *squashfs_page_actor_init_special(struct page **page,
 | 
						|
	int pages, int length)
 | 
						|
{
 | 
						|
	struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
 | 
						|
 | 
						|
	if (actor == NULL)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	actor->length = length ? : pages * PAGE_SIZE;
 | 
						|
	actor->page = page;
 | 
						|
	actor->pages = pages;
 | 
						|
	actor->next_page = 0;
 | 
						|
	actor->pageaddr = NULL;
 | 
						|
	actor->squashfs_first_page = direct_first_page;
 | 
						|
	actor->squashfs_next_page = direct_next_page;
 | 
						|
	actor->squashfs_finish_page = direct_finish_page;
 | 
						|
	return actor;
 | 
						|
}
 |