• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2013
3  * Phillip Lougher <phillip@squashfs.org.uk>
4  *
5  * This work is licensed under the terms of the GNU GPL, version 2. See
6  * the COPYING file in the top-level directory.
7  */
8 
9 #include <linux/kernel.h>
10 #include <linux/slab.h>
11 #include <linux/pagemap.h>
12 #include <linux/buffer_head.h>
13 #include "page_actor.h"
14 
squashfs_page_actor_init(struct page ** page,int pages,int length,void (* release_pages)(struct page **,int,int))15 struct squashfs_page_actor *squashfs_page_actor_init(struct page **page,
16 	int pages, int length, void (*release_pages)(struct page **, int, int))
17 {
18 	struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
19 
20 	if (actor == NULL)
21 		return NULL;
22 
23 	actor->length = length ? : pages * PAGE_CACHE_SIZE;
24 	actor->page = page;
25 	actor->pages = pages;
26 	actor->next_page = 0;
27 	actor->pageaddr = NULL;
28 	actor->release_pages = release_pages;
29 	return actor;
30 }
31 
squashfs_page_actor_free(struct squashfs_page_actor * actor,int error)32 void squashfs_page_actor_free(struct squashfs_page_actor *actor, int error)
33 {
34 	if (!actor)
35 		return;
36 
37 	if (actor->release_pages)
38 		actor->release_pages(actor->page, actor->pages, error);
39 	kfree(actor);
40 }
41 
squashfs_actor_to_buf(struct squashfs_page_actor * actor,void * buf,int length)42 void squashfs_actor_to_buf(struct squashfs_page_actor *actor, void *buf,
43 	int length)
44 {
45 	void *pageaddr;
46 	int pos = 0, avail, i;
47 
48 	for (i = 0; i < actor->pages && pos < length; ++i) {
49 		avail = min_t(int, length - pos, PAGE_CACHE_SIZE);
50 		if (actor->page[i]) {
51 			pageaddr = kmap_atomic(actor->page[i]);
52 			memcpy(buf + pos, pageaddr, avail);
53 			kunmap_atomic(pageaddr);
54 		}
55 		pos += avail;
56 	}
57 }
58 
squashfs_buf_to_actor(void * buf,struct squashfs_page_actor * actor,int length)59 void squashfs_buf_to_actor(void *buf, struct squashfs_page_actor *actor,
60 	int length)
61 {
62 	void *pageaddr;
63 	int pos = 0, avail, i;
64 
65 	for (i = 0; i < actor->pages && pos < length; ++i) {
66 		avail = min_t(int, length - pos, PAGE_CACHE_SIZE);
67 		if (actor->page[i]) {
68 			pageaddr = kmap_atomic(actor->page[i]);
69 			memcpy(pageaddr, buf + pos, avail);
70 			kunmap_atomic(pageaddr);
71 		}
72 		pos += avail;
73 	}
74 }
75 
squashfs_bh_to_actor(struct buffer_head ** bh,int nr_buffers,struct squashfs_page_actor * actor,int offset,int length,int blksz)76 void squashfs_bh_to_actor(struct buffer_head **bh, int nr_buffers,
77 	struct squashfs_page_actor *actor, int offset, int length, int blksz)
78 {
79 	void *kaddr = NULL;
80 	int bytes = 0, pgoff = 0, b = 0, p = 0, avail, i;
81 
82 	while (bytes < length) {
83 		if (actor->page[p]) {
84 			kaddr = kmap_atomic(actor->page[p]);
85 			while (pgoff < PAGE_CACHE_SIZE && bytes < length) {
86 				avail = min_t(int, blksz - offset,
87 						PAGE_CACHE_SIZE - pgoff);
88 				memcpy(kaddr + pgoff, bh[b]->b_data + offset,
89 				       avail);
90 				pgoff += avail;
91 				bytes += avail;
92 				offset = (offset + avail) % blksz;
93 				if (!offset) {
94 					put_bh(bh[b]);
95 					++b;
96 				}
97 			}
98 			kunmap_atomic(kaddr);
99 			pgoff = 0;
100 		} else {
101 			for (i = 0; i < PAGE_CACHE_SIZE / blksz; ++i) {
102 				if (bh[b])
103 					put_bh(bh[b]);
104 				++b;
105 			}
106 			bytes += PAGE_CACHE_SIZE;
107 		}
108 		++p;
109 	}
110 }
111 
squashfs_bh_to_buf(struct buffer_head ** bh,int nr_buffers,void * buf,int offset,int length,int blksz)112 void squashfs_bh_to_buf(struct buffer_head **bh, int nr_buffers, void *buf,
113 	int offset, int length, int blksz)
114 {
115 	int i, avail, bytes = 0;
116 
117 	for (i = 0; i < nr_buffers && bytes < length; ++i) {
118 		avail = min_t(int, length - bytes, blksz - offset);
119 		if (bh[i]) {
120 			memcpy(buf + bytes, bh[i]->b_data + offset, avail);
121 			put_bh(bh[i]);
122 		}
123 		bytes += avail;
124 		offset = 0;
125 	}
126 }
127 
free_page_array(struct page ** page,int nr_pages)128 void free_page_array(struct page **page, int nr_pages)
129 {
130 	int i;
131 
132 	for (i = 0; i < nr_pages; ++i)
133 		__free_page(page[i]);
134 	kfree(page);
135 }
136 
alloc_page_array(int nr_pages,int gfp_mask)137 struct page **alloc_page_array(int nr_pages, int gfp_mask)
138 {
139 	int i;
140 	struct page **page;
141 
142 	page = kcalloc(nr_pages, sizeof(struct page *), gfp_mask);
143 	if (!page)
144 		return NULL;
145 	for (i = 0; i < nr_pages; ++i) {
146 		page[i] = alloc_page(gfp_mask);
147 		if (!page[i]) {
148 			free_page_array(page, i);
149 			return NULL;
150 		}
151 	}
152 	return page;
153 }
154