summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libsparse/backed_block.c81
-rw-r--r--libsparse/backed_block.h2
-rw-r--r--libsparse/sparse.c2
3 files changed, 76 insertions, 9 deletions
diff --git a/libsparse/backed_block.c b/libsparse/backed_block.c
index 8c3fab04a..629fc284f 100644
--- a/libsparse/backed_block.c
+++ b/libsparse/backed_block.c
@@ -21,6 +21,7 @@
21#include <string.h> 21#include <string.h>
22 22
23#include "backed_block.h" 23#include "backed_block.h"
24#include "sparse_defs.h"
24 25
25struct backed_block { 26struct backed_block {
26 unsigned int block; 27 unsigned int block;
@@ -48,6 +49,7 @@ struct backed_block {
48struct backed_block_list { 49struct backed_block_list {
49 struct backed_block *data_blocks; 50 struct backed_block *data_blocks;
50 struct backed_block *last_used; 51 struct backed_block *last_used;
52 unsigned int block_size;
51}; 53};
52 54
53struct backed_block *backed_block_iter_new(struct backed_block_list *bbl) 55struct backed_block *backed_block_iter_new(struct backed_block_list *bbl)
@@ -109,10 +111,19 @@ enum backed_block_type backed_block_type(struct backed_block *bb)
109 return bb->type; 111 return bb->type;
110} 112}
111 113
112struct backed_block_list *backed_block_list_new(void) 114void backed_block_destroy(struct backed_block *bb)
113{ 115{
114 struct backed_block_list *b = calloc(sizeof(struct backed_block_list), 1); 116 if (bb->type == BACKED_BLOCK_FILE) {
117 free(bb->file.filename);
118 }
115 119
120 free(bb);
121}
122
123struct 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;
116 return b; 127 return b;
117} 128}
118 129
@@ -122,11 +133,7 @@ void backed_block_list_destroy(struct backed_block_list *bbl)
122 struct backed_block *bb = bbl->data_blocks; 133 struct backed_block *bb = bbl->data_blocks;
123 while (bb) { 134 while (bb) {
124 struct backed_block *next = bb->next; 135 struct backed_block *next = bb->next;
125 if (bb->type == BACKED_BLOCK_FILE) { 136 backed_block_destroy(bb);
126 free(bb->file.filename);
127 }
128
129 free(bb);
130 bb = next; 137 bb = next;
131 } 138 }
132 } 139 }
@@ -134,6 +141,63 @@ void backed_block_list_destroy(struct backed_block_list *bbl)
134 free(bbl); 141 free(bbl);
135} 142}
136 143
144/* may free b */
145static int merge_bb(struct backed_block_list *bbl,
146 struct backed_block *a, struct backed_block *b)
147{
148 unsigned int block_len;
149
150 /* Block doesn't exist (possible if one block is the last block) */
151 if (!a || !b) {
152 return -EINVAL;
153 }
154
155 assert(a->block < b->block);
156
157 /* Blocks are of different types */
158 if (a->type != b->type) {
159 return -EINVAL;
160 }
161
162 /* Blocks are not adjacent */
163 block_len = a->len / bbl->block_size; /* rounds down */
164 if (a->block + block_len != b->block) {
165 return -EINVAL;
166 }
167
168 switch (a->type) {
169 case BACKED_BLOCK_DATA:
170 /* Don't support merging data for now */
171 return -EINVAL;
172 case BACKED_BLOCK_FILL:
173 if (a->fill.val != b->fill.val) {
174 return -EINVAL;
175 }
176 break;
177 case BACKED_BLOCK_FILE:
178 if (a->file.filename != b->file.filename ||
179 a->file.offset + a->len != b->file.offset) {
180 return -EINVAL;
181 }
182 break;
183 case BACKED_BLOCK_FD:
184 if (a->fd.fd != b->fd.fd ||
185 a->fd.offset + a->len != b->fd.offset) {
186 return -EINVAL;
187 }
188 break;
189 }
190
191 /* Blocks are compatible and adjacent, with a before b. Merge b into a,
192 * and free b */
193 a->len += b->len;
194 a->next = b->next;
195
196 backed_block_destroy(b);
197
198 return 0;
199}
200
137static int queue_bb(struct backed_block_list *bbl, struct backed_block *new_bb) 201static int queue_bb(struct backed_block_list *bbl, struct backed_block *new_bb)
138{ 202{
139 struct backed_block *bb; 203 struct backed_block *bb;
@@ -168,6 +232,9 @@ static int queue_bb(struct backed_block_list *bbl, struct backed_block *new_bb)
168 bb->next = new_bb; 232 bb->next = new_bb;
169 } 233 }
170 234
235 merge_bb(bbl, new_bb, new_bb->next);
236 merge_bb(bbl, bb, new_bb);
237
171 return 0; 238 return 0;
172} 239}
173 240
diff --git a/libsparse/backed_block.h b/libsparse/backed_block.h
index ca2ad1d9b..692691712 100644
--- a/libsparse/backed_block.h
+++ b/libsparse/backed_block.h
@@ -52,7 +52,7 @@ enum backed_block_type backed_block_type(struct backed_block *bb);
52struct backed_block *backed_block_iter_new(struct backed_block_list *bbl); 52struct backed_block *backed_block_iter_new(struct backed_block_list *bbl);
53struct backed_block *backed_block_iter_next(struct backed_block *bb); 53struct backed_block *backed_block_iter_next(struct backed_block *bb);
54 54
55struct backed_block_list *backed_block_list_new(void); 55struct backed_block_list *backed_block_list_new(unsigned int block_size);
56void backed_block_list_destroy(struct backed_block_list *bbl); 56void backed_block_list_destroy(struct backed_block_list *bbl);
57 57
58#endif 58#endif
diff --git a/libsparse/sparse.c b/libsparse/sparse.c
index 3403604d4..d778e1dc6 100644
--- a/libsparse/sparse.c
+++ b/libsparse/sparse.c
@@ -32,7 +32,7 @@ struct sparse_file *sparse_file_new(unsigned int block_size, int64_t len)
32 return NULL; 32 return NULL;
33 } 33 }
34 34
35 s->backed_block_list = backed_block_list_new(); 35 s->backed_block_list = backed_block_list_new(block_size);
36 if (!s->backed_block_list) { 36 if (!s->backed_block_list) {
37 free(s); 37 free(s);
38 return NULL; 38 return NULL;