aboutsummaryrefslogtreecommitdiff
path: root/fs/squashfs/page_actor.c
blob: 53863508e4000bfaf52abe0c34a44da6e8bae001 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
 * 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_CACHE_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_CACHE_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_CACHE_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_CACHE_SIZE && bytes < length) {
				avail = min_t(int, blksz - offset,
						PAGE_CACHE_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_CACHE_SIZE / blksz; ++i) {
				if (bh[b])
					put_bh(bh[b]);
				++b;
			}
			bytes += PAGE_CACHE_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;
}