Home | History | Annotate | Download | only in libsparse
      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 		/* Already make sure b->type is BACKED_BLOCK_FILE */
    225 		if (strcmp(a->file.filename, b->file.filename) ||
    226 				a->file.offset + a->len != b->file.offset) {
    227 			return -EINVAL;
    228 		}
    229 		break;
    230 	case BACKED_BLOCK_FD:
    231 		if (a->fd.fd != b->fd.fd ||
    232 				a->fd.offset + a->len != b->fd.offset) {
    233 			return -EINVAL;
    234 		}
    235 		break;
    236 	}
    237 
    238 	/* Blocks are compatible and adjacent, with a before b.  Merge b into a,
    239 	 * and free b */
    240 	a->len += b->len;
    241 	a->next = b->next;
    242 
    243 	backed_block_destroy(b);
    244 
    245 	return 0;
    246 }
    247 
    248 static int queue_bb(struct backed_block_list *bbl, struct backed_block *new_bb)
    249 {
    250 	struct backed_block *bb;
    251 
    252 	if (bbl->data_blocks == NULL) {
    253 		bbl->data_blocks = new_bb;
    254 		return 0;
    255 	}
    256 
    257 	if (bbl->data_blocks->block > new_bb->block) {
    258 		new_bb->next = bbl->data_blocks;
    259 		bbl->data_blocks = new_bb;
    260 		return 0;
    261 	}
    262 
    263 	/* Optimization: blocks are mostly queued in sequence, so save the
    264 	   pointer to the last bb that was added, and start searching from
    265 	   there if the next block number is higher */
    266 	if (bbl->last_used && new_bb->block > bbl->last_used->block)
    267 		bb = bbl->last_used;
    268 	else
    269 		bb = bbl->data_blocks;
    270 	bbl->last_used = new_bb;
    271 
    272 	for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
    273 		;
    274 
    275 	if (bb->next == NULL) {
    276 		bb->next = new_bb;
    277 	} else {
    278 		new_bb->next = bb->next;
    279 		bb->next = new_bb;
    280 	}
    281 
    282 	merge_bb(bbl, new_bb, new_bb->next);
    283 	if (!merge_bb(bbl, bb, new_bb)) {
    284 		/* new_bb destroyed, point to retained as last_used */
    285 		bbl->last_used = bb;
    286 	}
    287 
    288 	return 0;
    289 }
    290 
    291 /* Queues a fill block of memory to be written to the specified data blocks */
    292 int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
    293 		unsigned int len, unsigned int block)
    294 {
    295 	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
    296 	if (bb == NULL) {
    297 		return -ENOMEM;
    298 	}
    299 
    300 	bb->block = block;
    301 	bb->len = len;
    302 	bb->type = BACKED_BLOCK_FILL;
    303 	bb->fill.val = fill_val;
    304 	bb->next = NULL;
    305 
    306 	return queue_bb(bbl, bb);
    307 }
    308 
    309 /* Queues a block of memory to be written to the specified data blocks */
    310 int backed_block_add_data(struct backed_block_list *bbl, void *data,
    311 		unsigned int len, unsigned int block)
    312 {
    313 	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
    314 	if (bb == NULL) {
    315 		return -ENOMEM;
    316 	}
    317 
    318 	bb->block = block;
    319 	bb->len = len;
    320 	bb->type = BACKED_BLOCK_DATA;
    321 	bb->data.data = data;
    322 	bb->next = NULL;
    323 
    324 	return queue_bb(bbl, bb);
    325 }
    326 
    327 /* Queues a chunk of a file on disk to be written to the specified data blocks */
    328 int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
    329 		int64_t offset, unsigned int len, unsigned int block)
    330 {
    331 	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
    332 	if (bb == NULL) {
    333 		return -ENOMEM;
    334 	}
    335 
    336 	bb->block = block;
    337 	bb->len = len;
    338 	bb->type = BACKED_BLOCK_FILE;
    339 	bb->file.filename = strdup(filename);
    340 	bb->file.offset = offset;
    341 	bb->next = NULL;
    342 
    343 	return queue_bb(bbl, bb);
    344 }
    345 
    346 /* Queues a chunk of a fd to be written to the specified data blocks */
    347 int backed_block_add_fd(struct backed_block_list *bbl, int fd, int64_t offset,
    348 		unsigned int len, unsigned int block)
    349 {
    350 	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
    351 	if (bb == NULL) {
    352 		return -ENOMEM;
    353 	}
    354 
    355 	bb->block = block;
    356 	bb->len = len;
    357 	bb->type = BACKED_BLOCK_FD;
    358 	bb->fd.fd = fd;
    359 	bb->fd.offset = offset;
    360 	bb->next = NULL;
    361 
    362 	return queue_bb(bbl, bb);
    363 }
    364 
    365 int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb,
    366 		unsigned int max_len)
    367 {
    368 	struct backed_block *new_bb;
    369 
    370 	max_len = ALIGN_DOWN(max_len, bbl->block_size);
    371 
    372 	if (bb->len <= max_len) {
    373 		return 0;
    374 	}
    375 
    376 	new_bb = malloc(sizeof(struct backed_block));
    377 	if (new_bb == NULL) {
    378 		return -ENOMEM;
    379 	}
    380 
    381 	*new_bb = *bb;
    382 
    383 	new_bb->len = bb->len - max_len;
    384 	new_bb->block = bb->block + max_len / bbl->block_size;
    385 	new_bb->next = bb->next;
    386 	bb->next = new_bb;
    387 	bb->len = max_len;
    388 
    389 	switch (bb->type) {
    390 	case BACKED_BLOCK_DATA:
    391 		new_bb->data.data = (char *)bb->data.data + max_len;
    392 		break;
    393 	case BACKED_BLOCK_FILE:
    394 		new_bb->file.offset += max_len;
    395 		break;
    396 	case BACKED_BLOCK_FD:
    397 		new_bb->fd.offset += max_len;
    398 		break;
    399 	case BACKED_BLOCK_FILL:
    400 		break;
    401 	}
    402 
    403 	return 0;
    404 }
    405