Add LDFLAGS when building libsparse.a
[project/make_ext4fs.git] / libsparse / backed_block.c
1 /*
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 <assert.h>
18 #include <errno.h>
19 #include <stdint.h>
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include "backed_block.h"
24 #include "sparse_defs.h"
25
26 struct backed_block {
27 unsigned int block;
28 unsigned int len;
29 enum backed_block_type type;
30 union {
31 struct {
32 void *data;
33 } data;
34 struct {
35 char *filename;
36 int64_t offset;
37 } file;
38 struct {
39 int fd;
40 int64_t offset;
41 } fd;
42 struct {
43 uint32_t val;
44 } fill;
45 };
46 struct backed_block *next;
47 };
48
49 struct backed_block_list {
50 struct backed_block *data_blocks;
51 struct backed_block *last_used;
52 unsigned int block_size;
53 };
54
55 struct backed_block *backed_block_iter_new(struct backed_block_list *bbl)
56 {
57 return bbl->data_blocks;
58 }
59
60 struct backed_block *backed_block_iter_next(struct backed_block *bb)
61 {
62 return bb->next;
63 }
64
65 unsigned int backed_block_len(struct backed_block *bb)
66 {
67 return bb->len;
68 }
69
70 unsigned int backed_block_block(struct backed_block *bb)
71 {
72 return bb->block;
73 }
74
75 void *backed_block_data(struct backed_block *bb)
76 {
77 assert(bb->type == BACKED_BLOCK_DATA);
78 return bb->data.data;
79 }
80
81 const char *backed_block_filename(struct backed_block *bb)
82 {
83 assert(bb->type == BACKED_BLOCK_FILE);
84 return bb->file.filename;
85 }
86
87 int backed_block_fd(struct backed_block *bb)
88 {
89 assert(bb->type == BACKED_BLOCK_FD);
90 return bb->fd.fd;
91 }
92
93 int64_t backed_block_file_offset(struct backed_block *bb)
94 {
95 assert(bb->type == BACKED_BLOCK_FILE || bb->type == BACKED_BLOCK_FD);
96 if (bb->type == BACKED_BLOCK_FILE) {
97 return bb->file.offset;
98 } else { /* bb->type == BACKED_BLOCK_FD */
99 return bb->fd.offset;
100 }
101 }
102
103 uint32_t backed_block_fill_val(struct backed_block *bb)
104 {
105 assert(bb->type == BACKED_BLOCK_FILL);
106 return bb->fill.val;
107 }
108
109 enum backed_block_type backed_block_type(struct backed_block *bb)
110 {
111 return bb->type;
112 }
113
114 void backed_block_destroy(struct backed_block *bb)
115 {
116 if (bb->type == BACKED_BLOCK_FILE) {
117 free(bb->file.filename);
118 }
119
120 free(bb);
121 }
122
123 struct backed_block_list *backed_block_list_new(unsigned int block_size)
124 {
125 struct backed_block_list *b = calloc(sizeof(struct backed_block_list), 1);
126 b->block_size = block_size;
127 return b;
128 }
129
130 void backed_block_list_destroy(struct backed_block_list *bbl)
131 {
132 if (bbl->data_blocks) {
133 struct backed_block *bb = bbl->data_blocks;
134 while (bb) {
135 struct backed_block *next = bb->next;
136 backed_block_destroy(bb);
137 bb = next;
138 }
139 }
140
141 free(bbl);
142 }
143
144 void backed_block_list_move(struct backed_block_list *from,
145 struct backed_block_list *to, struct backed_block *start,
146 struct backed_block *end)
147 {
148 struct backed_block *bb;
149
150 if (start == NULL) {
151 start = from->data_blocks;
152 }
153
154 if (!end) {
155 for (end = start; end && end->next; end = end->next)
156 ;
157 }
158
159 if (start == NULL || end == NULL) {
160 return;
161 }
162
163 from->last_used = NULL;
164 to->last_used = NULL;
165 if (from->data_blocks == start) {
166 from->data_blocks = end->next;
167 } else {
168 for (bb = from->data_blocks; bb; bb = bb->next) {
169 if (bb->next == start) {
170 bb->next = end->next;
171 break;
172 }
173 }
174 }
175
176 if (!to->data_blocks) {
177 to->data_blocks = start;
178 end->next = NULL;
179 } else {
180 for (bb = to->data_blocks; bb; bb = bb->next) {
181 if (!bb->next || bb->next->block > start->block) {
182 end->next = bb->next;
183 bb->next = start;
184 break;
185 }
186 }
187 }
188 }
189
190 /* may free b */
191 static int merge_bb(struct backed_block_list *bbl,
192 struct backed_block *a, struct backed_block *b)
193 {
194 unsigned int block_len;
195
196 /* Block doesn't exist (possible if one block is the last block) */
197 if (!a || !b) {
198 return -EINVAL;
199 }
200
201 assert(a->block < b->block);
202
203 /* Blocks are of different types */
204 if (a->type != b->type) {
205 return -EINVAL;
206 }
207
208 /* Blocks are not adjacent */
209 block_len = a->len / bbl->block_size; /* rounds down */
210 if (a->block + block_len != b->block) {
211 return -EINVAL;
212 }
213
214 switch (a->type) {
215 case BACKED_BLOCK_DATA:
216 /* Don't support merging data for now */
217 return -EINVAL;
218 case BACKED_BLOCK_FILL:
219 if (a->fill.val != b->fill.val) {
220 return -EINVAL;
221 }
222 break;
223 case BACKED_BLOCK_FILE:
224 if (a->file.filename != b->file.filename ||
225 a->file.offset + a->len != b->file.offset) {
226 return -EINVAL;
227 }
228 break;
229 case BACKED_BLOCK_FD:
230 if (a->fd.fd != b->fd.fd ||
231 a->fd.offset + a->len != b->fd.offset) {
232 return -EINVAL;
233 }
234 break;
235 }
236
237 /* Blocks are compatible and adjacent, with a before b. Merge b into a,
238 * and free b */
239 a->len += b->len;
240 a->next = b->next;
241
242 backed_block_destroy(b);
243
244 return 0;
245 }
246
247 static int queue_bb(struct backed_block_list *bbl, struct backed_block *new_bb)
248 {
249 struct backed_block *bb;
250
251 if (bbl->data_blocks == NULL) {
252 bbl->data_blocks = new_bb;
253 return 0;
254 }
255
256 if (bbl->data_blocks->block > new_bb->block) {
257 new_bb->next = bbl->data_blocks;
258 bbl->data_blocks = new_bb;
259 return 0;
260 }
261
262 /* Optimization: blocks are mostly queued in sequence, so save the
263 pointer to the last bb that was added, and start searching from
264 there if the next block number is higher */
265 if (bbl->last_used && new_bb->block > bbl->last_used->block)
266 bb = bbl->last_used;
267 else
268 bb = bbl->data_blocks;
269 bbl->last_used = new_bb;
270
271 for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
272 ;
273
274 if (bb->next == NULL) {
275 bb->next = new_bb;
276 } else {
277 new_bb->next = bb->next;
278 bb->next = new_bb;
279 }
280
281 merge_bb(bbl, new_bb, new_bb->next);
282 merge_bb(bbl, bb, new_bb);
283
284 return 0;
285 }
286
287 /* Queues a fill block of memory to be written to the specified data blocks */
288 int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
289 unsigned int len, unsigned int block)
290 {
291 struct backed_block *bb = calloc(1, sizeof(struct backed_block));
292 if (bb == NULL) {
293 return -ENOMEM;
294 }
295
296 bb->block = block;
297 bb->len = len;
298 bb->type = BACKED_BLOCK_FILL;
299 bb->fill.val = fill_val;
300 bb->next = NULL;
301
302 return queue_bb(bbl, bb);
303 }
304
305 /* Queues a block of memory to be written to the specified data blocks */
306 int backed_block_add_data(struct backed_block_list *bbl, void *data,
307 unsigned int len, unsigned int block)
308 {
309 struct backed_block *bb = calloc(1, sizeof(struct backed_block));
310 if (bb == NULL) {
311 return -ENOMEM;
312 }
313
314 bb->block = block;
315 bb->len = len;
316 bb->type = BACKED_BLOCK_DATA;
317 bb->data.data = data;
318 bb->next = NULL;
319
320 return queue_bb(bbl, bb);
321 }
322
323 /* Queues a chunk of a file on disk to be written to the specified data blocks */
324 int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
325 int64_t offset, unsigned int len, unsigned int block)
326 {
327 struct backed_block *bb = calloc(1, sizeof(struct backed_block));
328 if (bb == NULL) {
329 return -ENOMEM;
330 }
331
332 bb->block = block;
333 bb->len = len;
334 bb->type = BACKED_BLOCK_FILE;
335 bb->file.filename = strdup(filename);
336 bb->file.offset = offset;
337 bb->next = NULL;
338
339 return queue_bb(bbl, bb);
340 }
341
342 /* Queues a chunk of a fd to be written to the specified data blocks */
343 int backed_block_add_fd(struct backed_block_list *bbl, int fd, int64_t offset,
344 unsigned int len, unsigned int block)
345 {
346 struct backed_block *bb = calloc(1, sizeof(struct backed_block));
347 if (bb == NULL) {
348 return -ENOMEM;
349 }
350
351 bb->block = block;
352 bb->len = len;
353 bb->type = BACKED_BLOCK_FD;
354 bb->fd.fd = fd;
355 bb->fd.offset = offset;
356 bb->next = NULL;
357
358 return queue_bb(bbl, bb);
359 }
360
361 int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb,
362 unsigned int max_len)
363 {
364 struct backed_block *new_bb;
365
366 max_len = ALIGN_DOWN(max_len, bbl->block_size);
367
368 if (bb->len <= max_len) {
369 return 0;
370 }
371
372 new_bb = malloc(sizeof(struct backed_block));
373 if (new_bb == NULL) {
374 return -ENOMEM;
375 }
376
377 *new_bb = *bb;
378
379 new_bb->len = bb->len - max_len;
380 new_bb->block = bb->block + max_len / bbl->block_size;
381 new_bb->next = bb->next;
382 bb->next = new_bb;
383 bb->len = max_len;
384
385 switch (bb->type) {
386 case BACKED_BLOCK_DATA:
387 new_bb->data.data = (char *)bb->data.data + max_len;
388 break;
389 case BACKED_BLOCK_FILE:
390 new_bb->file.offset += max_len;
391 break;
392 case BACKED_BLOCK_FD:
393 new_bb->fd.offset += max_len;
394 break;
395 case BACKED_BLOCK_FILL:
396 break;
397 }
398
399 return 0;
400 }