/* * Copyright (c) 2013 * Phillip Lougher * * This work is licensed under the terms of the GNU GPL, version 2. See * the COPYING file in the top-level directory. */ #include #include #include #include #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; }