summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorColin Cross2012-04-25 01:07:49 -0500
committerColin Cross2012-07-10 00:09:37 -0500
commitb55dceea986ab24f8b836b5116b389ed619c816e (patch)
treeaa4f4ed6384931ec362c70eca7b25a7cb8dab450 /libsparse
parent411619e921904b896eddae81c086c1f687c8304d (diff)
downloadplatform-system-core-b55dceea986ab24f8b836b5116b389ed619c816e.tar.gz
platform-system-core-b55dceea986ab24f8b836b5116b389ed619c816e.tar.xz
platform-system-core-b55dceea986ab24f8b836b5116b389ed619c816e.zip
libsparse: cleanups
Move block loops into sparse.c with iterator helpers in backed_block.c. Simplify chunk writing by moving skip chunk calls from output_file.c to sparse.c. Rename variables to be consistent with new naming. Remove use of u8, u32, u64. Change-Id: Ic138ad58bef9f96239266ccee12ee83ea285e7eb
Diffstat (limited to 'libsparse')
-rw-r--r--libsparse/backed_block.c245
-rw-r--r--libsparse/backed_block.h45
-rw-r--r--libsparse/output_file.c478
-rw-r--r--libsparse/output_file.h11
-rw-r--r--libsparse/sparse.c137
-rw-r--r--libsparse/sparse_crc32.h4
6 files changed, 447 insertions, 473 deletions
diff --git a/libsparse/backed_block.c b/libsparse/backed_block.c
index 6e8ef4424..b25919008 100644
--- a/libsparse/backed_block.c
+++ b/libsparse/backed_block.c
@@ -14,30 +14,87 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17#include <assert.h>
18#include <errno.h>
19#include <stdint.h>
17#include <stdlib.h> 20#include <stdlib.h>
18#include <string.h> 21#include <string.h>
19 22
20#include "backed_block.h" 23#include "backed_block.h"
21#include "sparse_defs.h" 24
22 25struct backed_block {
23struct data_block { 26 unsigned int block;
24 u32 block; 27 unsigned int len;
25 u32 len; 28 enum backed_block_type type;
26 void *data; 29 union {
27 const char *filename; 30 struct {
28 int64_t offset; 31 void *data;
29 struct data_block *next; 32 } data;
30 u32 fill_val; 33 struct {
31 u8 fill; 34 char *filename;
32 u8 pad1; 35 int64_t offset;
33 u16 pad2; 36 } file;
37 struct {
38 uint32_t val;
39 } fill;
40 };
41 struct backed_block *next;
34}; 42};
35 43
36struct backed_block_list { 44struct backed_block_list {
37 struct data_block *data_blocks; 45 struct backed_block *data_blocks;
38 struct data_block *last_used; 46 struct backed_block *last_used;
39}; 47};
40 48
49struct backed_block *backed_block_iter_new(struct backed_block_list *bbl)
50{
51 return bbl->data_blocks;
52}
53
54struct backed_block *backed_block_iter_next(struct backed_block *bb)
55{
56 return bb->next;
57}
58
59unsigned int backed_block_len(struct backed_block *bb)
60{
61 return bb->len;
62}
63
64unsigned int backed_block_block(struct backed_block *bb)
65{
66 return bb->block;
67}
68
69void *backed_block_data(struct backed_block *bb)
70{
71 assert(bb->type == BACKED_BLOCK_DATA);
72 return bb->data.data;
73}
74
75const char *backed_block_filename(struct backed_block *bb)
76{
77 assert(bb->type == BACKED_BLOCK_FILE);
78 return bb->file.filename;
79}
80
81int64_t backed_block_file_offset(struct backed_block *bb)
82{
83 assert(bb->type == BACKED_BLOCK_FILE);
84 return bb->file.offset;
85}
86
87uint32_t backed_block_fill_val(struct backed_block *bb)
88{
89 assert(bb->type == BACKED_BLOCK_FILL);
90 return bb->fill.val;
91}
92
93enum backed_block_type backed_block_type(struct backed_block *bb)
94{
95 return bb->type;
96}
97
41struct backed_block_list *backed_block_list_new(void) 98struct backed_block_list *backed_block_list_new(void)
42{ 99{
43 struct backed_block_list *b = calloc(sizeof(struct backed_block_list), 1); 100 struct backed_block_list *b = calloc(sizeof(struct backed_block_list), 1);
@@ -45,140 +102,112 @@ struct backed_block_list *backed_block_list_new(void)
45 return b; 102 return b;
46} 103}
47 104
48void backed_block_list_destroy(struct backed_block_list *b) 105void backed_block_list_destroy(struct backed_block_list *bbl)
49{ 106{
50 if (b->data_blocks) { 107 if (bbl->data_blocks) {
51 struct data_block *db = b->data_blocks; 108 struct backed_block *bb = bbl->data_blocks;
52 while (db) { 109 while (bb) {
53 struct data_block *next = db->next; 110 struct backed_block *next = bb->next;
54 free((void*)db->filename); 111 if (bb->type == BACKED_BLOCK_FILE) {
55 112 free(bb->file.filename);
56 free(db); 113 }
57 db = next; 114
115 free(bb);
116 bb = next;
58 } 117 }
59 } 118 }
60 119
61 free(b); 120 free(bbl);
62} 121}
63 122
64static void queue_db(struct backed_block_list *b, struct data_block *new_db) 123static int queue_bb(struct backed_block_list *bbl, struct backed_block *new_bb)
65{ 124{
66 struct data_block *db; 125 struct backed_block *bb;
67 126
68 if (b->data_blocks == NULL) { 127 if (bbl->data_blocks == NULL) {
69 b->data_blocks = new_db; 128 bbl->data_blocks = new_bb;
70 return; 129 return 0;
71 } 130 }
72 131
73 if (b->data_blocks->block > new_db->block) { 132 if (bbl->data_blocks->block > new_bb->block) {
74 new_db->next = b->data_blocks; 133 new_bb->next = bbl->data_blocks;
75 b->data_blocks = new_db; 134 bbl->data_blocks = new_bb;
76 return; 135 return 0;
77 } 136 }
78 137
79 /* Optimization: blocks are mostly queued in sequence, so save the 138 /* Optimization: blocks are mostly queued in sequence, so save the
80 pointer to the last db that was added, and start searching from 139 pointer to the last bb that was added, and start searching from
81 there if the next block number is higher */ 140 there if the next block number is higher */
82 if (b->last_used && new_db->block > b->last_used->block) 141 if (bbl->last_used && new_bb->block > bbl->last_used->block)
83 db = b->last_used; 142 bb = bbl->last_used;
84 else 143 else
85 db = b->data_blocks; 144 bb = bbl->data_blocks;
86 b->last_used = new_db; 145 bbl->last_used = new_bb;
87 146
88 for (; db->next && db->next->block < new_db->block; db = db->next) 147 for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
89 ; 148 ;
90 149
91 if (db->next == NULL) { 150 if (bb->next == NULL) {
92 db->next = new_db; 151 bb->next = new_bb;
93 } else { 152 } else {
94 new_db->next = db->next; 153 new_bb->next = bb->next;
95 db->next = new_db; 154 bb->next = new_bb;
96 } 155 }
156
157 return 0;
97} 158}
98 159
99/* Queues a fill block of memory to be written to the specified data blocks */ 160/* Queues a fill block of memory to be written to the specified data blocks */
100void queue_fill_block(struct backed_block_list *b, unsigned int fill_val, 161int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
101 unsigned int len, unsigned int block) 162 unsigned int len, unsigned int block)
102{ 163{
103 struct data_block *db = calloc(1, sizeof(struct data_block)); 164 struct backed_block *bb = calloc(1, sizeof(struct backed_block));
104 if (db == NULL) { 165 if (bb == NULL) {
105 error_errno("malloc"); 166 return -ENOMEM;
106 return;
107 } 167 }
108 168
109 db->block = block; 169 bb->block = block;
110 db->len = len; 170 bb->len = len;
111 db->fill = 1; 171 bb->type = BACKED_BLOCK_FILL;
112 db->fill_val = fill_val; 172 bb->fill.val = fill_val;
113 db->data = NULL; 173 bb->next = NULL;
114 db->filename = NULL;
115 db->next = NULL;
116 174
117 queue_db(b, db); 175 return queue_bb(bbl, bb);
118} 176}
119 177
120/* Queues a block of memory to be written to the specified data blocks */ 178/* Queues a block of memory to be written to the specified data blocks */
121void queue_data_block(struct backed_block_list *b, void *data, unsigned int len, 179int backed_block_add_data(struct backed_block_list *bbl, void *data,
122 unsigned int block) 180 unsigned int len, unsigned int block)
123{ 181{
124 struct data_block *db = malloc(sizeof(struct data_block)); 182 struct backed_block *bb = calloc(1, sizeof(struct backed_block));
125 if (db == NULL) { 183 if (bb == NULL) {
126 error_errno("malloc"); 184 return -ENOMEM;
127 return;
128 } 185 }
129 186
130 db->block = block; 187 bb->block = block;
131 db->len = len; 188 bb->len = len;
132 db->data = data; 189 bb->type = BACKED_BLOCK_DATA;
133 db->filename = NULL; 190 bb->data.data = data;
134 db->fill = 0; 191 bb->next = NULL;
135 db->next = NULL;
136 192
137 queue_db(b, db); 193 return queue_bb(bbl, bb);
138} 194}
139 195
140/* Queues a chunk of a file on disk to be written to the specified data blocks */ 196/* Queues a chunk of a file on disk to be written to the specified data blocks */
141void queue_data_file(struct backed_block_list *b, const char *filename, 197int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
142 int64_t offset, unsigned int len, unsigned int block) 198 int64_t offset, unsigned int len, unsigned int block)
143{ 199{
144 struct data_block *db = malloc(sizeof(struct data_block)); 200 struct backed_block *bb = calloc(1, sizeof(struct backed_block));
145 if (db == NULL) { 201 if (bb == NULL) {
146 error_errno("malloc"); 202 return -ENOMEM;
147 return;
148 } 203 }
149 204
150 db->block = block; 205 bb->block = block;
151 db->len = len; 206 bb->len = len;
152 db->filename = strdup(filename); 207 bb->type = BACKED_BLOCK_FILE;
153 db->offset = offset; 208 bb->file.filename = strdup(filename);
154 db->data = NULL; 209 bb->file.offset = offset;
155 db->fill = 0; 210 bb->next = NULL;
156 db->next = NULL;
157 211
158 queue_db(b, db); 212 return queue_bb(bbl, bb);
159}
160
161/* Iterates over the queued data blocks, calling data_func for each contiguous
162 data block, and file_func for each contiguous file block */
163void for_each_data_block(struct backed_block_list *b,
164 data_block_callback_t data_func,
165 data_block_file_callback_t file_func,
166 data_block_fill_callback_t fill_func,
167 void *priv, unsigned int block_size)
168{
169 struct data_block *db;
170 u32 last_block = 0;
171
172 for (db = b->data_blocks; db; db = db->next) {
173 if (db->block < last_block)
174 error("data blocks out of order: %u < %u", db->block, last_block);
175 last_block = db->block + DIV_ROUND_UP(db->len, block_size) - 1;
176
177 if (db->filename)
178 file_func(priv, (u64)db->block * block_size, db->filename, db->offset, db->len);
179 else if (db->fill)
180 fill_func(priv, (u64)db->block * block_size, db->fill_val, db->len);
181 else
182 data_func(priv, (u64)db->block * block_size, db->data, db->len);
183 }
184} 213}
diff --git a/libsparse/backed_block.h b/libsparse/backed_block.h
index d1bfa1ec3..316650505 100644
--- a/libsparse/backed_block.h
+++ b/libsparse/backed_block.h
@@ -17,29 +17,38 @@
17#ifndef _BACKED_BLOCK_H_ 17#ifndef _BACKED_BLOCK_H_
18#define _BACKED_BLOCK_H_ 18#define _BACKED_BLOCK_H_
19 19
20#include <stdint.h>
21
20struct backed_block_list; 22struct backed_block_list;
23struct backed_block;
24
25enum backed_block_type {
26 BACKED_BLOCK_DATA,
27 BACKED_BLOCK_FILE,
28 BACKED_BLOCK_FILL,
29};
21 30
22typedef void (*data_block_callback_t)(void *priv, int64_t off, void *data, 31int backed_block_add_data(struct backed_block_list *bbl, void *data,
23 int len); 32 unsigned int len, unsigned int block);
24typedef void (*data_block_fill_callback_t)(void *priv, int64_t off, 33int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
25 unsigned int fill_val, int len);
26typedef void (*data_block_file_callback_t)(void *priv, int64_t off,
27 const char *file, int64_t offset, int len);
28
29void for_each_data_block(struct backed_block_list *b,
30 data_block_callback_t data_func,
31 data_block_file_callback_t file_func,
32 data_block_fill_callback_t fill_func,
33 void *priv, unsigned int);
34
35void queue_data_block(struct backed_block_list *b,void *data, unsigned int len,
36 unsigned int block);
37void queue_fill_block(struct backed_block_list *b,unsigned int fill_val,
38 unsigned int len, unsigned int block); 34 unsigned int len, unsigned int block);
39void queue_data_file(struct backed_block_list *b,const char *filename, 35int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
40 int64_t offset, unsigned int len, unsigned int block); 36 int64_t offset, unsigned int len, unsigned int block);
41 37
38struct backed_block *backed_block_iter_new(struct backed_block_list *bbl);
39struct backed_block *backed_block_iter_next(struct backed_block *bb);
40unsigned int backed_block_len(struct backed_block *bb);
41unsigned int backed_block_block(struct backed_block *bb);
42void *backed_block_data(struct backed_block *bb);
43const char *backed_block_filename(struct backed_block *bb);
44int64_t backed_block_file_offset(struct backed_block *bb);
45uint32_t backed_block_fill_val(struct backed_block *bb);
46enum backed_block_type backed_block_type(struct backed_block *bb);
47
48struct backed_block *backed_block_iter_new(struct backed_block_list *bbl);
49struct backed_block *backed_block_iter_next(struct backed_block *bb);
50
42struct backed_block_list *backed_block_list_new(void); 51struct backed_block_list *backed_block_list_new(void);
43void backed_block_list_destroy(struct backed_block_list *b); 52void backed_block_list_destroy(struct backed_block_list *bbl);
44 53
45#endif 54#endif
diff --git a/libsparse/output_file.c b/libsparse/output_file.c
index 2c4b5573e..f911f8cc5 100644
--- a/libsparse/output_file.c
+++ b/libsparse/output_file.c
@@ -51,36 +51,50 @@ static inline void *mmap64(void *addr, size_t length, int prot, int flags,
51} 51}
52#endif 52#endif
53 53
54#define min(a, b) \
55 ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; })
56
54#define SPARSE_HEADER_MAJOR_VER 1 57#define SPARSE_HEADER_MAJOR_VER 1
55#define SPARSE_HEADER_MINOR_VER 0 58#define SPARSE_HEADER_MINOR_VER 0
56#define SPARSE_HEADER_LEN (sizeof(sparse_header_t)) 59#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
57#define CHUNK_HEADER_LEN (sizeof(chunk_header_t)) 60#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
58 61
59struct output_file_ops { 62struct output_file_ops {
60 int (*seek)(struct output_file *, int64_t); 63 int (*skip)(struct output_file *, int64_t);
61 int (*write)(struct output_file *, u8 *, int); 64 int (*write)(struct output_file *, void *, int);
62 void (*close)(struct output_file *); 65 void (*close)(struct output_file *);
63}; 66};
64 67
68struct sparse_file_ops {
69 int (*write_data_chunk)(struct output_file *out, unsigned int len,
70 void *data);
71 int (*write_fill_chunk)(struct output_file *out, unsigned int len,
72 uint32_t fill_val);
73 int (*write_skip_chunk)(struct output_file *out, int64_t len);
74 int (*write_end_chunk)(struct output_file *out);
75};
76
65struct output_file { 77struct output_file {
66 int fd; 78 int fd;
67 gzFile gz_fd; 79 gzFile gz_fd;
68 bool close_fd; 80 bool close_fd;
69 int sparse;
70 int64_t cur_out_ptr; 81 int64_t cur_out_ptr;
71 u32 chunk_cnt; 82 unsigned int chunk_cnt;
72 u32 crc32; 83 uint32_t crc32;
73 struct output_file_ops *ops; 84 struct output_file_ops *ops;
85 struct sparse_file_ops *sparse_ops;
74 int use_crc; 86 int use_crc;
75 unsigned int block_size; 87 unsigned int block_size;
76 int64_t len; 88 int64_t len;
89 char *zero_buf;
90 uint32_t *fill_buf;
77}; 91};
78 92
79static int file_seek(struct output_file *out, int64_t off) 93static int file_skip(struct output_file *out, int64_t cnt)
80{ 94{
81 off64_t ret; 95 off64_t ret;
82 96
83 ret = lseek64(out->fd, off, SEEK_SET); 97 ret = lseek64(out->fd, cnt, SEEK_CUR);
84 if (ret < 0) { 98 if (ret < 0) {
85 error_errno("lseek64"); 99 error_errno("lseek64");
86 return -1; 100 return -1;
@@ -88,7 +102,7 @@ static int file_seek(struct output_file *out, int64_t off)
88 return 0; 102 return 0;
89} 103}
90 104
91static int file_write(struct output_file *out, u8 *data, int len) 105static int file_write(struct output_file *out, void *data, int len)
92{ 106{
93 int ret; 107 int ret;
94 ret = write(out->fd, data, len); 108 ret = write(out->fd, data, len);
@@ -110,18 +124,17 @@ static void file_close(struct output_file *out)
110 } 124 }
111} 125}
112 126
113
114static struct output_file_ops file_ops = { 127static struct output_file_ops file_ops = {
115 .seek = file_seek, 128 .skip = file_skip,
116 .write = file_write, 129 .write = file_write,
117 .close = file_close, 130 .close = file_close,
118}; 131};
119 132
120static int gz_file_seek(struct output_file *out, int64_t off) 133static int gz_file_skip(struct output_file *out, int64_t cnt)
121{ 134{
122 off64_t ret; 135 off64_t ret;
123 136
124 ret = gzseek(out->gz_fd, off, SEEK_SET); 137 ret = gzseek(out->gz_fd, cnt, SEEK_CUR);
125 if (ret < 0) { 138 if (ret < 0) {
126 error_errno("gzseek"); 139 error_errno("gzseek");
127 return -1; 140 return -1;
@@ -129,7 +142,7 @@ static int gz_file_seek(struct output_file *out, int64_t off)
129 return 0; 142 return 0;
130} 143}
131 144
132static int gz_file_write(struct output_file *out, u8 *data, int len) 145static int gz_file_write(struct output_file *out, void *data, int len)
133{ 146{
134 int ret; 147 int ret;
135 ret = gzwrite(out->gz_fd, data, len); 148 ret = gzwrite(out->gz_fd, data, len);
@@ -150,32 +163,16 @@ static void gz_file_close(struct output_file *out)
150} 163}
151 164
152static struct output_file_ops gz_file_ops = { 165static struct output_file_ops gz_file_ops = {
153 .seek = gz_file_seek, 166 .skip = gz_file_skip,
154 .write = gz_file_write, 167 .write = gz_file_write,
155 .close = gz_file_close, 168 .close = gz_file_close,
156}; 169};
157 170
158static sparse_header_t sparse_header = { 171static int write_sparse_skip_chunk(struct output_file *out, int64_t skip_len)
159 .magic = SPARSE_HEADER_MAGIC,
160 .major_version = SPARSE_HEADER_MAJOR_VER,
161 .minor_version = SPARSE_HEADER_MINOR_VER,
162 .file_hdr_sz = SPARSE_HEADER_LEN,
163 .chunk_hdr_sz = CHUNK_HEADER_LEN,
164 .blk_sz = 0,
165 .total_blks = 0,
166 .total_chunks = 0,
167 .image_checksum = 0
168};
169
170static u8 *zero_buf;
171
172static int emit_skip_chunk(struct output_file *out, u64 skip_len)
173{ 172{
174 chunk_header_t chunk_header; 173 chunk_header_t chunk_header;
175 int ret, chunk; 174 int ret, chunk;
176 175
177 //DBG printf("skip chunk: 0x%llx bytes\n", skip_len);
178
179 if (skip_len % out->block_size) { 176 if (skip_len % out->block_size) {
180 error("don't care size %llu is not a multiple of the block size %u", 177 error("don't care size %llu is not a multiple of the block size %u",
181 skip_len, out->block_size); 178 skip_len, out->block_size);
@@ -187,7 +184,7 @@ static int emit_skip_chunk(struct output_file *out, u64 skip_len)
187 chunk_header.reserved1 = 0; 184 chunk_header.reserved1 = 0;
188 chunk_header.chunk_sz = skip_len / out->block_size; 185 chunk_header.chunk_sz = skip_len / out->block_size;
189 chunk_header.total_sz = CHUNK_HEADER_LEN; 186 chunk_header.total_sz = CHUNK_HEADER_LEN;
190 ret = out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header)); 187 ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
191 if (ret < 0) 188 if (ret < 0)
192 return -1; 189 return -1;
193 190
@@ -197,71 +194,34 @@ static int emit_skip_chunk(struct output_file *out, u64 skip_len)
197 return 0; 194 return 0;
198} 195}
199 196
200static int write_chunk_fill(struct output_file *out, int64_t off, u32 fill_val, int len) 197static int write_sparse_fill_chunk(struct output_file *out, unsigned int len,
198 uint32_t fill_val)
201{ 199{
202 chunk_header_t chunk_header; 200 chunk_header_t chunk_header;
203 int rnd_up_len, zero_len, count; 201 int rnd_up_len, zero_len, count;
204 int ret; 202 int ret;
205 unsigned int i; 203 unsigned int i;
206 u32 fill_buf[4096/sizeof(u32)]; /* Maximum size of a block */
207
208 /* We can assume that all the chunks to be written are in
209 * ascending order, block-size aligned, and non-overlapping.
210 * So, if the offset is less than the current output pointer,
211 * throw an error, and if there is a gap, emit a "don't care"
212 * chunk. The first write (of the super block) may not be
213 * blocksize aligned, so we need to deal with that too.
214 */
215 //DBG printf("write chunk: offset 0x%llx, length 0x%x bytes\n", off, len);
216
217 if (off < out->cur_out_ptr) {
218 error("offset %llu is less than the current output offset %llu",
219 off, out->cur_out_ptr);
220 return -1;
221 }
222 204
223 if (off > out->cur_out_ptr) { 205 /* Round up the fill length to a multiple of the block size */
224 emit_skip_chunk(out, off - out->cur_out_ptr); 206 rnd_up_len = ALIGN(len, out->block_size);
225 }
226
227 if (off % out->block_size) {
228 error("write chunk offset %llu is not a multiple of the block size %u",
229 off, out->block_size);
230 return -1;
231 }
232
233 if (off != out->cur_out_ptr) {
234 error("internal error, offset accounting screwy in write_chunk_raw()");
235 return -1;
236 }
237
238 /* Round up the file length to a multiple of the block size */
239 rnd_up_len = (len + (out->block_size - 1)) & (~(out->block_size -1));
240 207
241 /* Finally we can safely emit a chunk of data */ 208 /* Finally we can safely emit a chunk of data */
242 chunk_header.chunk_type = CHUNK_TYPE_FILL; 209 chunk_header.chunk_type = CHUNK_TYPE_FILL;
243 chunk_header.reserved1 = 0; 210 chunk_header.reserved1 = 0;
244 chunk_header.chunk_sz = rnd_up_len / out->block_size; 211 chunk_header.chunk_sz = rnd_up_len / out->block_size;
245 chunk_header.total_sz = CHUNK_HEADER_LEN + sizeof(fill_val); 212 chunk_header.total_sz = CHUNK_HEADER_LEN + sizeof(fill_val);
246 ret = out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header)); 213 ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
247 214
248 if (ret < 0) 215 if (ret < 0)
249 return -1; 216 return -1;
250 ret = out->ops->write(out, (u8 *)&fill_val, sizeof(fill_val)); 217 ret = out->ops->write(out, &fill_val, sizeof(fill_val));
251 if (ret < 0) 218 if (ret < 0)
252 return -1; 219 return -1;
253 220
254 if (out->use_crc) { 221 if (out->use_crc) {
255 /* Initialize fill_buf with the fill_val */ 222 count = out->block_size / sizeof(uint32_t);
256 for (i = 0; i < (out->block_size / sizeof(u32)); i++) { 223 while (count--)
257 fill_buf[i] = fill_val; 224 out->crc32 = sparse_crc32(out->crc32, &fill_val, sizeof(uint32_t));
258 }
259
260 count = chunk_header.chunk_sz;
261 while (count) {
262 out->crc32 = sparse_crc32(out->crc32, fill_buf, out->block_size);
263 count--;
264 }
265 } 225 }
266 226
267 out->cur_out_ptr += rnd_up_len; 227 out->cur_out_ptr += rnd_up_len;
@@ -270,44 +230,15 @@ static int write_chunk_fill(struct output_file *out, int64_t off, u32 fill_val,
270 return 0; 230 return 0;
271} 231}
272 232
273static int write_chunk_raw(struct output_file *out, int64_t off, u8 *data, int len) 233static int write_sparse_data_chunk(struct output_file *out, unsigned int len,
234 void *data)
274{ 235{
275 chunk_header_t chunk_header; 236 chunk_header_t chunk_header;
276 int rnd_up_len, zero_len; 237 int rnd_up_len, zero_len;
277 int ret; 238 int ret;
278 239
279 /* We can assume that all the chunks to be written are in 240 /* Round up the data length to a multiple of the block size */
280 * ascending order, block-size aligned, and non-overlapping. 241 rnd_up_len = ALIGN(len, out->block_size);
281 * So, if the offset is less than the current output pointer,
282 * throw an error, and if there is a gap, emit a "don't care"
283 * chunk. The first write (of the super block) may not be
284 * blocksize aligned, so we need to deal with that too.
285 */
286 //DBG printf("write chunk: offset 0x%llx, length 0x%x bytes\n", off, len);
287
288 if (off < out->cur_out_ptr) {
289 error("offset %llu is less than the current output offset %llu",
290 off, out->cur_out_ptr);
291 return -1;
292 }
293
294 if (off > out->cur_out_ptr) {
295 emit_skip_chunk(out, off - out->cur_out_ptr);
296 }
297
298 if (off % out->block_size) {
299 error("write chunk offset %llu is not a multiple of the block size %u",
300 off, out->block_size);
301 return -1;
302 }
303
304 if (off != out->cur_out_ptr) {
305 error("internal error, offset accounting screwy in write_chunk_raw()");
306 return -1;
307 }
308
309 /* Round up the file length to a multiple of the block size */
310 rnd_up_len = (len + (out->block_size - 1)) & (~(out->block_size -1));
311 zero_len = rnd_up_len - len; 242 zero_len = rnd_up_len - len;
312 243
313 /* Finally we can safely emit a chunk of data */ 244 /* Finally we can safely emit a chunk of data */
@@ -315,7 +246,7 @@ static int write_chunk_raw(struct output_file *out, int64_t off, u8 *data, int l
315 chunk_header.reserved1 = 0; 246 chunk_header.reserved1 = 0;
316 chunk_header.chunk_sz = rnd_up_len / out->block_size; 247 chunk_header.chunk_sz = rnd_up_len / out->block_size;
317 chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len; 248 chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
318 ret = out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header)); 249 ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
319 250
320 if (ret < 0) 251 if (ret < 0)
321 return -1; 252 return -1;
@@ -323,7 +254,7 @@ static int write_chunk_raw(struct output_file *out, int64_t off, u8 *data, int l
323 if (ret < 0) 254 if (ret < 0)
324 return -1; 255 return -1;
325 if (zero_len) { 256 if (zero_len) {
326 ret = out->ops->write(out, zero_buf, zero_len); 257 ret = out->ops->write(out, out->zero_buf, zero_len);
327 if (ret < 0) 258 if (ret < 0)
328 return -1; 259 return -1;
329 } 260 }
@@ -331,7 +262,7 @@ static int write_chunk_raw(struct output_file *out, int64_t off, u8 *data, int l
331 if (out->use_crc) { 262 if (out->use_crc) {
332 out->crc32 = sparse_crc32(out->crc32, data, len); 263 out->crc32 = sparse_crc32(out->crc32, data, len);
333 if (zero_len) 264 if (zero_len)
334 out->crc32 = sparse_crc32(out->crc32, zero_buf, zero_len); 265 out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len);
335 } 266 }
336 267
337 out->cur_out_ptr += rnd_up_len; 268 out->cur_out_ptr += rnd_up_len;
@@ -340,29 +271,115 @@ static int write_chunk_raw(struct output_file *out, int64_t off, u8 *data, int l
340 return 0; 271 return 0;
341} 272}
342 273
343void close_output_file(struct output_file *out) 274int write_sparse_end_chunk(struct output_file *out)
344{ 275{
345 int ret;
346 chunk_header_t chunk_header; 276 chunk_header_t chunk_header;
277 int ret;
347 278
348 if (out->sparse) { 279 if (out->use_crc) {
349 if (out->use_crc) { 280 chunk_header.chunk_type = CHUNK_TYPE_CRC32;
350 chunk_header.chunk_type = CHUNK_TYPE_CRC32; 281 chunk_header.reserved1 = 0;
351 chunk_header.reserved1 = 0; 282 chunk_header.chunk_sz = 0;
352 chunk_header.chunk_sz = 0; 283 chunk_header.total_sz = CHUNK_HEADER_LEN + 4;
353 chunk_header.total_sz = CHUNK_HEADER_LEN + 4; 284
285 ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
286 if (ret < 0) {
287 return ret;
288 }
289 out->ops->write(out, &out->crc32, 4);
290 if (ret < 0) {
291 return ret;
292 }
293
294 out->chunk_cnt++;
295 }
296
297 return 0;
298}
299
300static struct sparse_file_ops sparse_file_ops = {
301 .write_data_chunk = write_sparse_data_chunk,
302 .write_fill_chunk = write_sparse_fill_chunk,
303 .write_skip_chunk = write_sparse_skip_chunk,
304 .write_end_chunk = write_sparse_end_chunk,
305};
306
307static int write_normal_data_chunk(struct output_file *out, unsigned int len,
308 void *data)
309{
310 int ret;
311 unsigned int rnd_up_len = ALIGN(len, out->block_size);
312
313 ret = out->ops->write(out, data, len);
314 if (ret < 0) {
315 return ret;
316 }
317
318 if (rnd_up_len > len) {
319 ret = out->ops->skip(out, rnd_up_len - len);
320 }
354 321
355 out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header)); 322 return ret;
356 out->ops->write(out, (u8 *)&out->crc32, 4); 323}
324
325static int write_normal_fill_chunk(struct output_file *out, unsigned int len,
326 uint32_t fill_val)
327{
328 int ret;
329 unsigned int i;
330 unsigned int write_len;
357 331
358 out->chunk_cnt++; 332 /* Initialize fill_buf with the fill_val */
333 for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
334 out->fill_buf[i] = fill_val;
335 }
336
337 while (len) {
338 write_len = min(len, out->block_size);
339 ret = out->ops->write(out, out->fill_buf, write_len);
340 if (ret < 0) {
341 return ret;
359 } 342 }
360 343
361 if (out->chunk_cnt != sparse_header.total_chunks) 344 len -= write_len;
362 error("sparse chunk count did not match: %d %d", out->chunk_cnt, 345 }
363 sparse_header.total_chunks); 346
347 return 0;
348}
349
350static int write_normal_skip_chunk(struct output_file *out, int64_t len)
351{
352 int ret;
353
354 return out->ops->skip(out, len);
355}
356
357int write_normal_end_chunk(struct output_file *out)
358{
359 int ret;
360
361 ret = ftruncate64(out->fd, out->len);
362 if (ret < 0) {
363 return -errno;
364 } 364 }
365
366 return 0;
367}
368
369static struct sparse_file_ops normal_file_ops = {
370 .write_data_chunk = write_normal_data_chunk,
371 .write_fill_chunk = write_normal_fill_chunk,
372 .write_skip_chunk = write_normal_skip_chunk,
373 .write_end_chunk = write_normal_end_chunk,
374};
375
376void close_output_file(struct output_file *out)
377{
378 int ret;
379
380 out->sparse_ops->write_end_chunk(out);
365 out->ops->close(out); 381 out->ops->close(out);
382 free(out);
366} 383}
367 384
368struct output_file *open_output_fd(int fd, unsigned int block_size, int64_t len, 385struct output_file *open_output_fd(int fd, unsigned int block_size, int64_t len,
@@ -374,28 +391,37 @@ struct output_file *open_output_fd(int fd, unsigned int block_size, int64_t len,
374 error_errno("malloc struct out"); 391 error_errno("malloc struct out");
375 return NULL; 392 return NULL;
376 } 393 }
377 zero_buf = malloc(out->block_size); 394 out->zero_buf = calloc(block_size, 1);
378 if (!zero_buf) { 395 if (!out->zero_buf) {
379 error_errno("malloc zero_buf"); 396 error_errno("malloc zero_buf");
380 free(out); 397 goto err_zero_buf;
381 return NULL; 398 }
399
400 out->fill_buf = calloc(block_size, 1);
401 if (!out->fill_buf) {
402 error_errno("malloc fill_buf");
403 goto err_fill_buf;
382 } 404 }
383 memset(zero_buf, '\0', out->block_size);
384 405
385 if (gz) { 406 if (gz) {
386 out->ops = &gz_file_ops; 407 out->ops = &gz_file_ops;
387 out->gz_fd = gzdopen(fd, "wb9"); 408 out->gz_fd = gzdopen(fd, "wb9");
388 if (!out->gz_fd) { 409 if (!out->gz_fd) {
389 error_errno("gzopen"); 410 error_errno("gzopen");
390 free(out); 411 goto err_gzopen;
391 return NULL;
392 } 412 }
393 } else { 413 } else {
394 out->fd = fd; 414 out->fd = fd;
395 out->ops = &file_ops; 415 out->ops = &file_ops;
396 } 416 }
417
418 if (sparse) {
419 out->sparse_ops = &sparse_file_ops;
420 } else {
421 out->sparse_ops = &normal_file_ops;
422 }
423
397 out->close_fd = false; 424 out->close_fd = false;
398 out->sparse = sparse;
399 out->cur_out_ptr = 0ll; 425 out->cur_out_ptr = 0ll;
400 out->chunk_cnt = 0; 426 out->chunk_cnt = 0;
401 427
@@ -406,19 +432,42 @@ struct output_file *open_output_fd(int fd, unsigned int block_size, int64_t len,
406 out->len = len; 432 out->len = len;
407 out->block_size = block_size; 433 out->block_size = block_size;
408 434
409 if (out->sparse) { 435 if (sparse) {
410 sparse_header.blk_sz = out->block_size, 436 sparse_header_t sparse_header = {
411 sparse_header.total_blks = out->len / out->block_size, 437 .magic = SPARSE_HEADER_MAGIC,
412 sparse_header.total_chunks = chunks; 438 .major_version = SPARSE_HEADER_MAJOR_VER,
413 if (out->use_crc) 439 .minor_version = SPARSE_HEADER_MINOR_VER,
440 .file_hdr_sz = SPARSE_HEADER_LEN,
441 .chunk_hdr_sz = CHUNK_HEADER_LEN,
442 .blk_sz = out->block_size,
443 .total_blks = out->len / out->block_size,
444 .total_chunks = chunks,
445 .image_checksum = 0
446 };
447
448 if (out->use_crc) {
414 sparse_header.total_chunks++; 449 sparse_header.total_chunks++;
450 }
415 451
416 ret = out->ops->write(out, (u8 *)&sparse_header, sizeof(sparse_header)); 452 ret = out->ops->write(out, &sparse_header, sizeof(sparse_header));
417 if (ret < 0) 453 if (ret < 0) {
418 return NULL; 454 goto err_write;
455 }
419 } 456 }
420 457
421 return out; 458 return out;
459
460err_write:
461 if (gz) {
462 gzclose(out->gz_fd);
463 }
464err_gzopen:
465 free(out->fill_buf);
466err_fill_buf:
467 free(out->zero_buf);
468err_zero_buf:
469 free(out);
470 return NULL;
422} 471}
423 472
424struct output_file *open_output_file(const char *filename, 473struct output_file *open_output_file(const char *filename,
@@ -449,123 +498,31 @@ struct output_file *open_output_file(const char *filename,
449 return file; 498 return file;
450} 499}
451 500
452void pad_output_file(struct output_file *out, int64_t len)
453{
454 int ret;
455
456 if (len > out->len) {
457 error("attempted to pad file %llu bytes past end of filesystem",
458 len - out->len);
459 return;
460 }
461 if (out->sparse) {
462 /* We need to emit a DONT_CARE chunk to pad out the file if the
463 * cur_out_ptr is not already at the end of the filesystem.
464 */
465 if (len < out->cur_out_ptr) {
466 error("attempted to pad file %llu bytes less than the current output pointer",
467 out->cur_out_ptr - len);
468 return;
469 }
470 if (len > out->cur_out_ptr) {
471 emit_skip_chunk(out, len - out->cur_out_ptr);
472 }
473 } else {
474 //KEN TODO: Fixme. If the filesystem image needs no padding,
475 // this will overwrite the last byte in the file with 0
476 // The answer is to do accounting like the sparse image
477 // code does and know if there is already data there.
478 ret = out->ops->seek(out, len - 1);
479 if (ret < 0)
480 return;
481
482 ret = out->ops->write(out, (u8*)"", 1);
483 if (ret < 0)
484 return;
485 }
486}
487
488/* Write a contiguous region of data blocks from a memory buffer */ 501/* Write a contiguous region of data blocks from a memory buffer */
489void write_data_block(struct output_file *out, int64_t off, void *data, int len) 502int write_data_chunk(struct output_file *out, unsigned int len, void *data)
490{ 503{
491 int ret; 504 return out->sparse_ops->write_data_chunk(out, len, data);
492
493 if (off + len > out->len) {
494 error("attempted to write block %llu past end of filesystem",
495 off + len - out->len);
496 return;
497 }
498
499 if (out->sparse) {
500 write_chunk_raw(out, off, data, len);
501 } else {
502 ret = out->ops->seek(out, off);
503 if (ret < 0)
504 return;
505
506 ret = out->ops->write(out, data, len);
507 if (ret < 0)
508 return;
509 }
510} 505}
511 506
512/* Write a contiguous region of data blocks with a fill value */ 507/* Write a contiguous region of data blocks with a fill value */
513void write_fill_block(struct output_file *out, int64_t off, unsigned int fill_val, int len) 508int write_fill_chunk(struct output_file *out, unsigned int len,
509 uint32_t fill_val)
514{ 510{
515 int ret; 511 return out->sparse_ops->write_fill_chunk(out, len, fill_val);
516 unsigned int i;
517 int write_len;
518 u32 fill_buf[4096/sizeof(u32)]; /* Maximum size of a block */
519
520 if (off + len > out->len) {
521 error("attempted to write block %llu past end of filesystem",
522 off + len - out->len);
523 return;
524 }
525
526 if (out->sparse) {
527 write_chunk_fill(out, off, fill_val, len);
528 } else {
529 /* Initialize fill_buf with the fill_val */
530 for (i = 0; i < sizeof(fill_buf)/sizeof(u32); i++) {
531 fill_buf[i] = fill_val;
532 }
533
534 ret = out->ops->seek(out, off);
535 if (ret < 0)
536 return;
537
538 while (len) {
539 write_len = (len > (int)sizeof(fill_buf) ? (int)sizeof(fill_buf) : len);
540 ret = out->ops->write(out, (u8 *)fill_buf, write_len);
541 if (ret < 0) {
542 return;
543 } else {
544 len -= write_len;
545 }
546 }
547 }
548} 512}
549 513
550/* Write a contiguous region of data blocks from a file */ 514/* Write a contiguous region of data blocks from a file */
551void write_data_file(struct output_file *out, int64_t off, const char *file, 515int write_file_chunk(struct output_file *out, unsigned int len,
552 int64_t offset, int len) 516 const char *file, int64_t offset)
553{ 517{
554 int ret; 518 int ret;
555 int64_t aligned_offset; 519 int64_t aligned_offset;
556 int aligned_diff; 520 int aligned_diff;
557 int buffer_size; 521 int buffer_size;
558 522
559 if (off + len >= out->len) {
560 error("attempted to write block %llu past end of filesystem",
561 off + len - out->len);
562 return;
563 }
564
565 int file_fd = open(file, O_RDONLY | O_BINARY); 523 int file_fd = open(file, O_RDONLY | O_BINARY);
566 if (file_fd < 0) { 524 if (file_fd < 0) {
567 error_errno("open"); 525 return -errno;
568 return;
569 } 526 }
570 527
571 aligned_offset = offset & ~(4096 - 1); 528 aligned_offset = offset & ~(4096 - 1);
@@ -573,41 +530,36 @@ void write_data_file(struct output_file *out, int64_t off, const char *file,
573 buffer_size = len + aligned_diff; 530 buffer_size = len + aligned_diff;
574 531
575#ifndef USE_MINGW 532#ifndef USE_MINGW
576 u8 *data = mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, file_fd, 533 char *data = mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, file_fd,
577 aligned_offset); 534 aligned_offset);
578 if (data == MAP_FAILED) { 535 if (data == MAP_FAILED) {
579 error_errno("mmap64"); 536 ret = -errno;
580 close(file_fd); 537 close(file_fd);
581 return; 538 return ret;
582 } 539 }
583#else 540#else
584 u8 *data = malloc(buffer_size); 541 char *data = malloc(buffer_size);
585 if (!data) { 542 if (!data) {
586 error_errno("malloc"); 543 ret = -errno;
587 close(file_fd); 544 close(file_fd);
588 return; 545 return ret;
589 } 546 }
590 memset(data, 0, buffer_size); 547 memset(data, 0, buffer_size);
591#endif 548#endif
592 549
593 if (out->sparse) { 550 ret = out->sparse_ops->write_data_chunk(out, len, data + aligned_diff);
594 write_chunk_raw(out, off, data + aligned_diff, len);
595 } else {
596 ret = out->ops->seek(out, off);
597 if (ret < 0)
598 goto err;
599
600 ret = out->ops->write(out, data + aligned_diff, len);
601 if (ret < 0)
602 goto err;
603 }
604 551
605err:
606#ifndef USE_MINGW 552#ifndef USE_MINGW
607 munmap(data, buffer_size); 553 munmap(data, buffer_size);
608#else 554#else
609 write(file_fd, data, buffer_size);
610 free(data); 555 free(data);
611#endif 556#endif
612 close(file_fd); 557 close(file_fd);
558
559 return ret;
560}
561
562int write_skip_chunk(struct output_file *out, int64_t len)
563{
564 return out->sparse_ops->write_skip_chunk(out, len);
613} 565}
diff --git a/libsparse/output_file.h b/libsparse/output_file.h
index b12194fc7..cb2feb7fb 100644
--- a/libsparse/output_file.h
+++ b/libsparse/output_file.h
@@ -26,11 +26,12 @@ struct output_file *open_output_file(const char *filename,
26 int gz, int sparse, int chunks, int crc); 26 int gz, int sparse, int chunks, int crc);
27struct output_file *open_output_fd(int fd, unsigned int block_size, int64_t len, 27struct output_file *open_output_fd(int fd, unsigned int block_size, int64_t len,
28 int gz, int sparse, int chunks, int crc); 28 int gz, int sparse, int chunks, int crc);
29void write_data_block(struct output_file *out, int64_t off, void *data, int len); 29int write_data_chunk(struct output_file *out, unsigned int len, void *data);
30void write_fill_block(struct output_file *out, int64_t off, unsigned int fill_val, int len); 30int write_fill_chunk(struct output_file *out, unsigned int len,
31void write_data_file(struct output_file *out, int64_t off, const char *file, 31 uint32_t fill_val);
32 int64_t offset, int len); 32int write_file_chunk(struct output_file *out, unsigned int len,
33void pad_output_file(struct output_file *out, int64_t len); 33 const char *file, int64_t offset);
34int write_skip_chunk(struct output_file *out, int64_t len);
34void close_output_file(struct output_file *out); 35void close_output_file(struct output_file *out);
35 36
36#endif 37#endif
diff --git a/libsparse/sparse.c b/libsparse/sparse.c
index a6134c9d2..fce9dbbb6 100644
--- a/libsparse/sparse.c
+++ b/libsparse/sparse.c
@@ -14,6 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17#include <assert.h>
17#include <stdlib.h> 18#include <stdlib.h>
18 19
19#include <sparse/sparse.h> 20#include <sparse/sparse.h>
@@ -24,7 +25,6 @@
24#include "backed_block.h" 25#include "backed_block.h"
25#include "sparse_defs.h" 26#include "sparse_defs.h"
26 27
27
28struct sparse_file *sparse_file_new(unsigned int block_size, int64_t len) 28struct sparse_file *sparse_file_new(unsigned int block_size, int64_t len)
29{ 29{
30 struct sparse_file *s = calloc(sizeof(struct sparse_file), 1); 30 struct sparse_file *s = calloc(sizeof(struct sparse_file), 1);
@@ -53,108 +53,89 @@ void sparse_file_destroy(struct sparse_file *s)
53int sparse_file_add_data(struct sparse_file *s, 53int sparse_file_add_data(struct sparse_file *s,
54 void *data, unsigned int len, unsigned int block) 54 void *data, unsigned int len, unsigned int block)
55{ 55{
56 queue_data_block(s->backed_block_list, data, len, block); 56 return backed_block_add_data(s->backed_block_list, data, len, block);
57
58 return 0;
59} 57}
60 58
61int sparse_file_add_fill(struct sparse_file *s, 59int sparse_file_add_fill(struct sparse_file *s,
62 uint32_t fill_val, unsigned int len, unsigned int block) 60 uint32_t fill_val, unsigned int len, unsigned int block)
63{ 61{
64 queue_fill_block(s->backed_block_list, fill_val, len, block); 62 return backed_block_add_fill(s->backed_block_list, fill_val, len, block);
65
66 return 0;
67} 63}
68 64
69int sparse_file_add_file(struct sparse_file *s, 65int sparse_file_add_file(struct sparse_file *s,
70 const char *filename, int64_t file_offset, unsigned int len, 66 const char *filename, int64_t file_offset, unsigned int len,
71 unsigned int block) 67 unsigned int block)
72{ 68{
73 queue_data_file(s->backed_block_list, filename, file_offset, len, block); 69 return backed_block_add_file(s->backed_block_list, filename, file_offset,
74 70 len, block);
75 return 0;
76}
77
78struct count_chunks {
79 unsigned int chunks;
80 int64_t cur_ptr;
81 unsigned int block_size;
82};
83
84static void count_data_block(void *priv, int64_t off, void *data, int len)
85{
86 struct count_chunks *count_chunks = priv;
87 if (off > count_chunks->cur_ptr)
88 count_chunks->chunks++;
89 count_chunks->cur_ptr = off + ALIGN(len, count_chunks->block_size);
90 count_chunks->chunks++;
91}
92
93static void count_fill_block(void *priv, int64_t off, unsigned int fill_val, int len)
94{
95 struct count_chunks *count_chunks = priv;
96 if (off > count_chunks->cur_ptr)
97 count_chunks->chunks++;
98 count_chunks->cur_ptr = off + ALIGN(len, count_chunks->block_size);
99 count_chunks->chunks++;
100}
101
102static void count_file_block(void *priv, int64_t off, const char *file,
103 int64_t offset, int len)
104{
105 struct count_chunks *count_chunks = priv;
106 if (off > count_chunks->cur_ptr)
107 count_chunks->chunks++;
108 count_chunks->cur_ptr = off + ALIGN(len, count_chunks->block_size);
109 count_chunks->chunks++;
110} 71}
111 72
112static int count_sparse_chunks(struct backed_block_list *b, 73unsigned int sparse_count_chunks(struct sparse_file *s)
113 unsigned int block_size, int64_t len)
114{ 74{
115 struct count_chunks count_chunks = {0, 0, block_size}; 75 struct backed_block *bb;
116 76 unsigned int last_block = 0;
117 for_each_data_block(b, count_data_block, count_file_block, 77 unsigned int chunks = 0;
118 count_fill_block, &count_chunks, block_size); 78
119 79 for (bb = backed_block_iter_new(s->backed_block_list); bb;
120 if (count_chunks.cur_ptr != len) 80 bb = backed_block_iter_next(bb)) {
121 count_chunks.chunks++; 81 if (backed_block_block(bb) > last_block) {
122 82 /* If there is a gap between chunks, add a skip chunk */
123 return count_chunks.chunks; 83 chunks++;
124} 84 }
125 85 chunks++;
126static void ext4_write_data_block(void *priv, int64_t off, void *data, int len) 86 last_block = backed_block_block(bb) +
127{ 87 DIV_ROUND_UP(backed_block_len(bb), s->block_size);
128 write_data_block(priv, off, data, len); 88 }
129} 89 if (last_block < DIV_ROUND_UP(s->len, s->block_size)) {
130 90 chunks++;
131static void ext4_write_fill_block(void *priv, int64_t off, unsigned int fill_val, int len) 91 }
132{
133 write_fill_block(priv, off, fill_val, len);
134}
135 92
136static void ext4_write_data_file(void *priv, int64_t off, const char *file, 93 return chunks;
137 int64_t offset, int len)
138{
139 write_data_file(priv, off, file, offset, len);
140} 94}
141 95
142int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse, 96int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse,
143 bool crc) 97 bool crc)
144{ 98{
145 int chunks = count_sparse_chunks(s->backed_block_list, s->block_size, 99 struct backed_block *bb;
146 s->len); 100 unsigned int last_block = 0;
147 struct output_file *out = open_output_fd(fd, s->block_size, s->len, 101 int64_t pad;
148 gz, sparse, chunks, crc); 102 int chunks;
103 struct output_file *out;
104
105 chunks = sparse_count_chunks(s);
106 out = open_output_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc);
149 107
150 if (!out) 108 if (!out)
151 return -ENOMEM; 109 return -ENOMEM;
152 110
153 for_each_data_block(s->backed_block_list, ext4_write_data_block, 111 for (bb = backed_block_iter_new(s->backed_block_list); bb;
154 ext4_write_data_file, ext4_write_fill_block, out, s->block_size); 112 bb = backed_block_iter_next(bb)) {
113 if (backed_block_block(bb) > last_block) {
114 unsigned int blocks = backed_block_block(bb) - last_block;
115 write_skip_chunk(out, (int64_t)blocks * s->block_size);
116 }
117 switch (backed_block_type(bb)) {
118 case BACKED_BLOCK_DATA:
119 write_data_chunk(out, backed_block_len(bb), backed_block_data(bb));
120 break;
121 case BACKED_BLOCK_FILE:
122 write_file_chunk(out, backed_block_len(bb),
123 backed_block_filename(bb), backed_block_file_offset(bb));
124 break;
125 case BACKED_BLOCK_FILL:
126 write_fill_chunk(out, backed_block_len(bb),
127 backed_block_fill_val(bb));
128 break;
129 }
130 last_block = backed_block_block(bb) +
131 DIV_ROUND_UP(backed_block_len(bb), s->block_size);
132 }
155 133
156 if (s->len) 134 pad = s->len - last_block * s->block_size;
157 pad_output_file(out, s->len); 135 assert(pad >= 0);
136 if (pad > 0) {
137 write_skip_chunk(out, pad);
138 }
158 139
159 close_output_file(out); 140 close_output_file(out);
160 141
diff --git a/libsparse/sparse_crc32.h b/libsparse/sparse_crc32.h
index 21625bac9..cad8a8630 100644
--- a/libsparse/sparse_crc32.h
+++ b/libsparse/sparse_crc32.h
@@ -14,5 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17u32 sparse_crc32(u32 crc, const void *buf, size_t size); 17#include <stdint.h>
18
19uint32_t sparse_crc32(uint32_t crc, const void *buf, size_t size);
18 20