153 lines
3.4 KiB
C
153 lines
3.4 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 <linux/buffer_head.h>
|
|
#include "page_actor.h"
|
|
|
|
struct squashfs_page_actor *squashfs_page_actor_init(struct page **page,
|
|
int pages, int length, void (*release_pages)(struct page **, int, int))
|
|
{
|
|
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->release_pages = release_pages;
|
|
return actor;
|
|
}
|
|
|
|
void squashfs_page_actor_free(struct squashfs_page_actor *actor, int error)
|
|
{
|
|
if (!actor)
|
|
return;
|
|
|
|
if (actor->release_pages)
|
|
actor->release_pages(actor->page, actor->pages, error);
|
|
kfree(actor);
|
|
}
|
|
|
|
void squashfs_actor_to_buf(struct squashfs_page_actor *actor, void *buf,
|
|
int length)
|
|
{
|
|
void *pageaddr;
|
|
int pos = 0, avail, i;
|
|
|
|
for (i = 0; i < actor->pages && pos < length; ++i) {
|
|
avail = min_t(int, length - pos, PAGE_SIZE);
|
|
if (actor->page[i]) {
|
|
pageaddr = kmap_atomic(actor->page[i]);
|
|
memcpy(buf + pos, pageaddr, avail);
|
|
kunmap_atomic(pageaddr);
|
|
}
|
|
pos += avail;
|
|
}
|
|
}
|
|
|
|
void squashfs_buf_to_actor(void *buf, struct squashfs_page_actor *actor,
|
|
int length)
|
|
{
|
|
void *pageaddr;
|
|
int pos = 0, avail, i;
|
|
|
|
for (i = 0; i < actor->pages && pos < length; ++i) {
|
|
avail = min_t(int, length - pos, PAGE_SIZE);
|
|
if (actor->page[i]) {
|
|
pageaddr = kmap_atomic(actor->page[i]);
|
|
memcpy(pageaddr, buf + pos, avail);
|
|
kunmap_atomic(pageaddr);
|
|
}
|
|
pos += avail;
|
|
}
|
|
}
|
|
|
|
void squashfs_bh_to_actor(struct buffer_head **bh, int nr_buffers,
|
|
struct squashfs_page_actor *actor, int offset, int length, int blksz)
|
|
{
|
|
void *kaddr = NULL;
|
|
int bytes = 0, pgoff = 0, b = 0, p = 0, avail, i;
|
|
|
|
while (bytes < length) {
|
|
if (actor->page[p]) {
|
|
kaddr = kmap_atomic(actor->page[p]);
|
|
while (pgoff < PAGE_SIZE && bytes < length) {
|
|
avail = min_t(int, blksz - offset,
|
|
PAGE_SIZE - pgoff);
|
|
memcpy(kaddr + pgoff, bh[b]->b_data + offset,
|
|
avail);
|
|
pgoff += avail;
|
|
bytes += avail;
|
|
offset = (offset + avail) % blksz;
|
|
if (!offset) {
|
|
put_bh(bh[b]);
|
|
++b;
|
|
}
|
|
}
|
|
kunmap_atomic(kaddr);
|
|
pgoff = 0;
|
|
} else {
|
|
for (i = 0; i < PAGE_SIZE / blksz; ++i) {
|
|
if (bh[b])
|
|
put_bh(bh[b]);
|
|
++b;
|
|
}
|
|
bytes += PAGE_SIZE;
|
|
}
|
|
++p;
|
|
}
|
|
}
|
|
|
|
void squashfs_bh_to_buf(struct buffer_head **bh, int nr_buffers, void *buf,
|
|
int offset, int length, int blksz)
|
|
{
|
|
int i, avail, bytes = 0;
|
|
|
|
for (i = 0; i < nr_buffers && bytes < length; ++i) {
|
|
avail = min_t(int, length - bytes, blksz - offset);
|
|
if (bh[i]) {
|
|
memcpy(buf + bytes, bh[i]->b_data + offset, avail);
|
|
put_bh(bh[i]);
|
|
}
|
|
bytes += avail;
|
|
offset = 0;
|
|
}
|
|
}
|
|
|
|
void free_page_array(struct page **page, int nr_pages)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < nr_pages; ++i)
|
|
__free_page(page[i]);
|
|
kfree(page);
|
|
}
|
|
|
|
struct page **alloc_page_array(int nr_pages, int gfp_mask)
|
|
{
|
|
int i;
|
|
struct page **page;
|
|
|
|
page = kcalloc(nr_pages, sizeof(struct page *), gfp_mask);
|
|
if (!page)
|
|
return NULL;
|
|
for (i = 0; i < nr_pages; ++i) {
|
|
page[i] = alloc_page(gfp_mask);
|
|
if (!page[i]) {
|
|
free_page_array(page, i);
|
|
return NULL;
|
|
}
|
|
}
|
|
return page;
|
|
}
|