123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- /*
- * 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 i, avail, pos = 0;
- 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 i, avail, pos = 0;
- 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, i, avail;
- 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;
- }
|