blob: 254813809af52a99273a141905a10aa909d97c73 [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
36static struct data_block *data_blocks = NULL;
37static struct data_block *last_used = NULL;
38
39static void queue_db(struct data_block *new_db)
40{
41 struct data_block *db;
42
43 if (data_blocks == NULL) {
44 data_blocks = new_db;
45 return;
46 }
47
48 if (data_blocks->block > new_db->block) {
49 new_db->next = data_blocks;
50 data_blocks = new_db;
51 return;
52 }
53
54 /* Optimization: blocks are mostly queued in sequence, so save the
55 pointer to the last db that was added, and start searching from
56 there if the next block number is higher */
57 if (last_used && new_db->block > last_used->block)
58 db = last_used;
59 else
60 db = data_blocks;
61 last_used = new_db;
62
63 for (; db->next && db->next->block < new_db->block; db = db->next)
64 ;
65
66 if (db->next == NULL) {
67 db->next = new_db;
68 } else {
69 new_db->next = db->next;
70 db->next = new_db;
71 }
72}
73
74/* Queues a fill block of memory to be written to the specified data blocks */
75void queue_fill_block(unsigned int fill_val, unsigned int len, unsigned int block)
76{
77 struct data_block *db = malloc(sizeof(struct data_block));
78 if (db == NULL) {
79 error_errno("malloc");
80 return;
81 }
82
83 db->block = block;
84 db->len = len;
85 db->fill = 1;
86 db->fill_val = fill_val;
87 db->data = NULL;
88 db->filename = NULL;
89 db->next = NULL;
90
91 queue_db(db);
92}
93
94/* Queues a block of memory to be written to the specified data blocks */
95void queue_data_block(void *data, unsigned int len, unsigned int block)
96{
97 struct data_block *db = malloc(sizeof(struct data_block));
98 if (db == NULL) {
99 error_errno("malloc");
100 return;
101 }
102
103 db->block = block;
104 db->len = len;
105 db->data = data;
106 db->filename = NULL;
107 db->fill = 0;
108 db->next = NULL;
109
110 queue_db(db);
111}
112
113/* Queues a chunk of a file on disk to be written to the specified data blocks */
114void queue_data_file(const char *filename, int64_t offset, unsigned int len,
115 unsigned int block)
116{
117 struct data_block *db = malloc(sizeof(struct data_block));
118 if (db == NULL) {
119 error_errno("malloc");
120 return;
121 }
122
123 db->block = block;
124 db->len = len;
125 db->filename = strdup(filename);
126 db->offset = offset;
127 db->data = NULL;
128 db->fill = 0;
129 db->next = NULL;
130
131 queue_db(db);
132}
133
134/* Iterates over the queued data blocks, calling data_func for each contiguous
135 data block, and file_func for each contiguous file block */
136void for_each_data_block(data_block_callback_t data_func,
137 data_block_file_callback_t file_func,
138 data_block_fill_callback_t fill_func, void *priv, unsigned int block_size)
139{
140 struct data_block *db;
141 u32 last_block = 0;
142
143 for (db = data_blocks; db; db = db->next) {
144 if (db->block < last_block)
145 error("data blocks out of order: %u < %u", db->block, last_block);
146 last_block = db->block + DIV_ROUND_UP(db->len, block_size) - 1;
147
148 if (db->filename)
149 file_func(priv, (u64)db->block * block_size, db->filename, db->offset, db->len);
150 else if (db->fill)
151 fill_func(priv, (u64)db->block * block_size, db->fill_val, db->len);
152 else
153 data_func(priv, (u64)db->block * block_size, db->data, db->len);
154 }
155}
156
157/* Frees the memory used by the linked list of data blocks */
158void free_data_blocks()
159{
160 if (!data_blocks) return;
161 struct data_block *db = data_blocks;
162 while (db) {
163 struct data_block *next = db->next;
164 free((void*)db->filename);
165
166 // There used to be a free() of db->data here, but it
167 // made the function crash since queue_data_block() is
168 // sometimes passed pointers it can't take ownership of
169 // (like a pointer into the middle of an allocated
170 // block). It's not clear what the queue_data_block
171 // contract is supposed to be, but we'd rather leak
172 // memory than crash.
173
174 free(db);
175 db = next;
176 }
177 data_blocks = NULL;
178 last_used = NULL;
179}