/* may free b */ static int merge_bb(struct backed_block_list *bbl, struct backed_block *a, struct backed_block *b) { unsigned int block_len; /* Block doesn't exist (possible if one block is the last block) */ if (!a || !b) { return -EINVAL; } assert(a->block < b->block); /* Blocks are of different types */ if (a->type != b->type) { return -EINVAL; } /* Blocks are not adjacent */ block_len = a->len / bbl->block_size; /* rounds down */ if (a->block + block_len != b->block) { return -EINVAL; } switch (a->type) { case BACKED_BLOCK_DATA: /* Don't support merging data for now */ return -EINVAL; case BACKED_BLOCK_FILL: if (a->fill.val != b->fill.val) { return -EINVAL; } break; case BACKED_BLOCK_FILE: /* Already make sure b->type is BACKED_BLOCK_FILE */ if (strcmp(a->file.filename, b->file.filename) || a->file.offset + a->len != b->file.offset) { return -EINVAL; } break; case BACKED_BLOCK_FD: if (a->fd.fd != b->fd.fd || a->fd.offset + a->len != b->fd.offset) { return -EINVAL; } break; } /* Blocks are compatible and adjacent, with a before b. Merge b into a, * and free b */ a->len += b->len; a->next = b->next; backed_block_destroy(b); return 0; }
void backed_block_list_destroy(struct backed_block_list *bbl) { if (bbl->data_blocks) { struct backed_block *bb = bbl->data_blocks; while (bb) { struct backed_block *next = bb->next; backed_block_destroy(bb); bb = next; } } free(bbl); }