blob: 6e8ef44247653dcc3f36f69ed8d71d34f4139d3b [file] [log] [blame]
Colin Cross28fa5bc2012-05-20 13:28:05 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdlib.h>
18#include <string.h>
19
20#include "backed_block.h"
21#include "sparse_defs.h"
22
23struct data_block {
24 u32 block;
25 u32 len;
26 void *data;
27 const char *filename;
28 int64_t offset;
29 struct data_block *next;
30 u32 fill_val;
31 u8 fill;
32 u8 pad1;
33 u16 pad2;
34};
35
Colin Cross411619e2012-04-24 18:51:42 -070036struct backed_block_list {
37 struct data_block *data_blocks;
38 struct data_block *last_used;
39};
Colin Cross28fa5bc2012-05-20 13:28:05 -070040
Colin Cross411619e2012-04-24 18:51:42 -070041struct backed_block_list *backed_block_list_new(void)
42{
43 struct backed_block_list *b = calloc(sizeof(struct backed_block_list), 1);
44
45 return b;
46}
47
48void backed_block_list_destroy(struct backed_block_list *b)
49{
50 if (b->data_blocks) {
51 struct data_block *db = b->data_blocks;
52 while (db) {
53 struct data_block *next = db->next;
54 free((void*)db->filename);
55
56 free(db);
57 db = next;
58 }
59 }
60
61 free(b);
62}
63
64static void queue_db(struct backed_block_list *b, struct data_block *new_db)
Colin Cross28fa5bc2012-05-20 13:28:05 -070065{
66 struct data_block *db;
67
Colin Cross411619e2012-04-24 18:51:42 -070068 if (b->data_blocks == NULL) {
69 b->data_blocks = new_db;
Colin Cross28fa5bc2012-05-20 13:28:05 -070070 return;
71 }
72
Colin Cross411619e2012-04-24 18:51:42 -070073 if (b->data_blocks->block > new_db->block) {
74 new_db->next = b->data_blocks;
75 b->data_blocks = new_db;
Colin Cross28fa5bc2012-05-20 13:28:05 -070076 return;
77 }
78
79 /* Optimization: blocks are mostly queued in sequence, so save the
80 pointer to the last db that was added, and start searching from
81 there if the next block number is higher */
Colin Cross411619e2012-04-24 18:51:42 -070082 if (b->last_used && new_db->block > b->last_used->block)
83 db = b->last_used;
Colin Cross28fa5bc2012-05-20 13:28:05 -070084 else
Colin Cross411619e2012-04-24 18:51:42 -070085 db = b->data_blocks;
86 b->last_used = new_db;
Colin Cross28fa5bc2012-05-20 13:28:05 -070087
88 for (; db->next && db->next->block < new_db->block; db = db->next)
89 ;
90
91 if (db->next == NULL) {
92 db->next = new_db;
93 } else {
94 new_db->next = db->next;
95 db->next = new_db;
96 }
97}
98
99/* Queues a fill block of memory to be written to the specified data blocks */
Colin Cross411619e2012-04-24 18:51:42 -0700100void queue_fill_block(struct backed_block_list *b, unsigned int fill_val,
101 unsigned int len, unsigned int block)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700102{
Colin Cross411619e2012-04-24 18:51:42 -0700103 struct data_block *db = calloc(1, sizeof(struct data_block));
Colin Cross28fa5bc2012-05-20 13:28:05 -0700104 if (db == NULL) {
105 error_errno("malloc");
106 return;
107 }
108
109 db->block = block;
110 db->len = len;
111 db->fill = 1;
112 db->fill_val = fill_val;
113 db->data = NULL;
114 db->filename = NULL;
115 db->next = NULL;
116
Colin Cross411619e2012-04-24 18:51:42 -0700117 queue_db(b, db);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700118}
119
120/* Queues a block of memory to be written to the specified data blocks */
Colin Cross411619e2012-04-24 18:51:42 -0700121void queue_data_block(struct backed_block_list *b, void *data, unsigned int len,
122 unsigned int block)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700123{
124 struct data_block *db = malloc(sizeof(struct data_block));
125 if (db == NULL) {
126 error_errno("malloc");
127 return;
128 }
129
130 db->block = block;
131 db->len = len;
132 db->data = data;
133 db->filename = NULL;
134 db->fill = 0;
135 db->next = NULL;
136
Colin Cross411619e2012-04-24 18:51:42 -0700137 queue_db(b, db);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700138}
139
140/* Queues a chunk of a file on disk to be written to the specified data blocks */
Colin Cross411619e2012-04-24 18:51:42 -0700141void queue_data_file(struct backed_block_list *b, const char *filename,
142 int64_t offset, unsigned int len, unsigned int block)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700143{
144 struct data_block *db = malloc(sizeof(struct data_block));
145 if (db == NULL) {
146 error_errno("malloc");
147 return;
148 }
149
150 db->block = block;
151 db->len = len;
152 db->filename = strdup(filename);
153 db->offset = offset;
154 db->data = NULL;
155 db->fill = 0;
156 db->next = NULL;
157
Colin Cross411619e2012-04-24 18:51:42 -0700158 queue_db(b, db);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700159}
160
161/* Iterates over the queued data blocks, calling data_func for each contiguous
162 data block, and file_func for each contiguous file block */
Colin Cross411619e2012-04-24 18:51:42 -0700163void for_each_data_block(struct backed_block_list *b,
164 data_block_callback_t data_func,
Colin Cross28fa5bc2012-05-20 13:28:05 -0700165 data_block_file_callback_t file_func,
Colin Cross411619e2012-04-24 18:51:42 -0700166 data_block_fill_callback_t fill_func,
167 void *priv, unsigned int block_size)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700168{
169 struct data_block *db;
170 u32 last_block = 0;
171
Colin Cross411619e2012-04-24 18:51:42 -0700172 for (db = b->data_blocks; db; db = db->next) {
Colin Cross28fa5bc2012-05-20 13:28:05 -0700173 if (db->block < last_block)
174 error("data blocks out of order: %u < %u", db->block, last_block);
175 last_block = db->block + DIV_ROUND_UP(db->len, block_size) - 1;
176
177 if (db->filename)
178 file_func(priv, (u64)db->block * block_size, db->filename, db->offset, db->len);
179 else if (db->fill)
180 fill_func(priv, (u64)db->block * block_size, db->fill_val, db->len);
181 else
182 data_func(priv, (u64)db->block * block_size, db->data, db->len);
183 }
184}