summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libsparse/backed_block.c99
-rw-r--r--libsparse/backed_block.h28
-rw-r--r--libsparse/sparse.c26
-rw-r--r--libsparse/sparse_file.h1
4 files changed, 87 insertions, 67 deletions
diff --git a/libsparse/backed_block.c b/libsparse/backed_block.c
index 254813809..6e8ef4424 100644
--- a/libsparse/backed_block.c
+++ b/libsparse/backed_block.c
@@ -33,32 +33,57 @@ struct data_block {
33 u16 pad2; 33 u16 pad2;
34}; 34};
35 35
36static struct data_block *data_blocks = NULL; 36struct backed_block_list {
37static struct data_block *last_used = NULL; 37 struct data_block *data_blocks;
38 struct data_block *last_used;
39};
40
41struct backed_block_list *backed_block_list_new(void)
42{
43 struct backed_block_list *b = calloc(sizeof(struct backed_block_list), 1);
44
45 return b;
46}
38 47
39static void queue_db(struct data_block *new_db) 48void backed_block_list_destroy(struct backed_block_list *b)
49{
50 if (b->data_blocks) {
51 struct data_block *db = b->data_blocks;
52 while (db) {
53 struct data_block *next = db->next;
54 free((void*)db->filename);
55
56 free(db);
57 db = next;
58 }
59 }
60
61 free(b);
62}
63
64static void queue_db(struct backed_block_list *b, struct data_block *new_db)
40{ 65{
41 struct data_block *db; 66 struct data_block *db;
42 67
43 if (data_blocks == NULL) { 68 if (b->data_blocks == NULL) {
44 data_blocks = new_db; 69 b->data_blocks = new_db;
45 return; 70 return;
46 } 71 }
47 72
48 if (data_blocks->block > new_db->block) { 73 if (b->data_blocks->block > new_db->block) {
49 new_db->next = data_blocks; 74 new_db->next = b->data_blocks;
50 data_blocks = new_db; 75 b->data_blocks = new_db;
51 return; 76 return;
52 } 77 }
53 78
54 /* Optimization: blocks are mostly queued in sequence, so save the 79 /* Optimization: blocks are mostly queued in sequence, so save the
55 pointer to the last db that was added, and start searching from 80 pointer to the last db that was added, and start searching from
56 there if the next block number is higher */ 81 there if the next block number is higher */
57 if (last_used && new_db->block > last_used->block) 82 if (b->last_used && new_db->block > b->last_used->block)
58 db = last_used; 83 db = b->last_used;
59 else 84 else
60 db = data_blocks; 85 db = b->data_blocks;
61 last_used = new_db; 86 b->last_used = new_db;
62 87
63 for (; db->next && db->next->block < new_db->block; db = db->next) 88 for (; db->next && db->next->block < new_db->block; db = db->next)
64 ; 89 ;
@@ -72,9 +97,10 @@ static void queue_db(struct data_block *new_db)
72} 97}
73 98
74/* Queues a fill block of memory to be written to the specified data blocks */ 99/* Queues a fill block of memory to be written to the specified data blocks */
75void queue_fill_block(unsigned int fill_val, unsigned int len, unsigned int block) 100void queue_fill_block(struct backed_block_list *b, unsigned int fill_val,
101 unsigned int len, unsigned int block)
76{ 102{
77 struct data_block *db = malloc(sizeof(struct data_block)); 103 struct data_block *db = calloc(1, sizeof(struct data_block));
78 if (db == NULL) { 104 if (db == NULL) {
79 error_errno("malloc"); 105 error_errno("malloc");
80 return; 106 return;
@@ -88,11 +114,12 @@ void queue_fill_block(unsigned int fill_val, unsigned int len, unsigned int bloc
88 db->filename = NULL; 114 db->filename = NULL;
89 db->next = NULL; 115 db->next = NULL;
90 116
91 queue_db(db); 117 queue_db(b, db);
92} 118}
93 119
94/* Queues a block of memory to be written to the specified data blocks */ 120/* Queues a block of memory to be written to the specified data blocks */
95void queue_data_block(void *data, unsigned int len, unsigned int block) 121void queue_data_block(struct backed_block_list *b, void *data, unsigned int len,
122 unsigned int block)
96{ 123{
97 struct data_block *db = malloc(sizeof(struct data_block)); 124 struct data_block *db = malloc(sizeof(struct data_block));
98 if (db == NULL) { 125 if (db == NULL) {
@@ -107,12 +134,12 @@ void queue_data_block(void *data, unsigned int len, unsigned int block)
107 db->fill = 0; 134 db->fill = 0;
108 db->next = NULL; 135 db->next = NULL;
109 136
110 queue_db(db); 137 queue_db(b, db);
111} 138}
112 139
113/* Queues a chunk of a file on disk to be written to the specified data blocks */ 140/* Queues a chunk of a file on disk to be written to the specified data blocks */
114void queue_data_file(const char *filename, int64_t offset, unsigned int len, 141void queue_data_file(struct backed_block_list *b, const char *filename,
115 unsigned int block) 142 int64_t offset, unsigned int len, unsigned int block)
116{ 143{
117 struct data_block *db = malloc(sizeof(struct data_block)); 144 struct data_block *db = malloc(sizeof(struct data_block));
118 if (db == NULL) { 145 if (db == NULL) {
@@ -128,19 +155,21 @@ void queue_data_file(const char *filename, int64_t offset, unsigned int len,
128 db->fill = 0; 155 db->fill = 0;
129 db->next = NULL; 156 db->next = NULL;
130 157
131 queue_db(db); 158 queue_db(b, db);
132} 159}
133 160
134/* Iterates over the queued data blocks, calling data_func for each contiguous 161/* Iterates over the queued data blocks, calling data_func for each contiguous
135 data block, and file_func for each contiguous file block */ 162 data block, and file_func for each contiguous file block */
136void for_each_data_block(data_block_callback_t data_func, 163void for_each_data_block(struct backed_block_list *b,
164 data_block_callback_t data_func,
137 data_block_file_callback_t file_func, 165 data_block_file_callback_t file_func,
138 data_block_fill_callback_t fill_func, void *priv, unsigned int block_size) 166 data_block_fill_callback_t fill_func,
167 void *priv, unsigned int block_size)
139{ 168{
140 struct data_block *db; 169 struct data_block *db;
141 u32 last_block = 0; 170 u32 last_block = 0;
142 171
143 for (db = data_blocks; db; db = db->next) { 172 for (db = b->data_blocks; db; db = db->next) {
144 if (db->block < last_block) 173 if (db->block < last_block)
145 error("data blocks out of order: %u < %u", db->block, last_block); 174 error("data blocks out of order: %u < %u", db->block, last_block);
146 last_block = db->block + DIV_ROUND_UP(db->len, block_size) - 1; 175 last_block = db->block + DIV_ROUND_UP(db->len, block_size) - 1;
@@ -153,27 +182,3 @@ void for_each_data_block(data_block_callback_t data_func,
153 data_func(priv, (u64)db->block * block_size, db->data, db->len); 182 data_func(priv, (u64)db->block * block_size, db->data, db->len);
154 } 183 }
155} 184}
156
157/* Frees the memory used by the linked list of data blocks */
158void free_data_blocks()
159{
160 if (!data_blocks) return;
161 struct data_block *db = data_blocks;
162 while (db) {
163 struct data_block *next = db->next;
164 free((void*)db->filename);
165
166 // There used to be a free() of db->data here, but it
167 // made the function crash since queue_data_block() is
168 // sometimes passed pointers it can't take ownership of
169 // (like a pointer into the middle of an allocated
170 // block). It's not clear what the queue_data_block
171 // contract is supposed to be, but we'd rather leak
172 // memory than crash.
173
174 free(db);
175 db = next;
176 }
177 data_blocks = NULL;
178 last_used = NULL;
179}
diff --git a/libsparse/backed_block.h b/libsparse/backed_block.h
index 7b7c90aad..d1bfa1ec3 100644
--- a/libsparse/backed_block.h
+++ b/libsparse/backed_block.h
@@ -17,23 +17,29 @@
17#ifndef _BACKED_BLOCK_H_ 17#ifndef _BACKED_BLOCK_H_
18#define _BACKED_BLOCK_H_ 18#define _BACKED_BLOCK_H_
19 19
20#include <sparse/sparse.h> 20struct backed_block_list;
21 21
22typedef void (*data_block_callback_t)(void *priv, int64_t off, void *data, int len); 22typedef void (*data_block_callback_t)(void *priv, int64_t off, void *data,
23typedef void (*data_block_fill_callback_t)(void *priv, int64_t off, unsigned int fill_val, int len); 23 int len);
24typedef void (*data_block_fill_callback_t)(void *priv, int64_t off,
25 unsigned int fill_val, int len);
24typedef void (*data_block_file_callback_t)(void *priv, int64_t off, 26typedef void (*data_block_file_callback_t)(void *priv, int64_t off,
25 const char *file, int64_t offset, 27 const char *file, int64_t offset, int len);
26 int len);
27 28
28void for_each_data_block(data_block_callback_t data_func, 29void for_each_data_block(struct backed_block_list *b,
30 data_block_callback_t data_func,
29 data_block_file_callback_t file_func, 31 data_block_file_callback_t file_func,
30 data_block_fill_callback_t fill_func, void *priv, unsigned int); 32 data_block_fill_callback_t fill_func,
33 void *priv, unsigned int);
31 34
32void queue_data_block(void *data, unsigned int len, unsigned int block); 35void queue_data_block(struct backed_block_list *b,void *data, unsigned int len,
33void queue_fill_block(unsigned int fill_val, unsigned int len, unsigned int block);
34void queue_data_file(const char *filename, int64_t offset, unsigned int len,
35 unsigned int block); 36 unsigned int block);
37void queue_fill_block(struct backed_block_list *b,unsigned int fill_val,
38 unsigned int len, unsigned int block);
39void queue_data_file(struct backed_block_list *b,const char *filename,
40 int64_t offset, unsigned int len, unsigned int block);
36 41
37void free_data_blocks(); 42struct backed_block_list *backed_block_list_new(void);
43void backed_block_list_destroy(struct backed_block_list *b);
38 44
39#endif 45#endif
diff --git a/libsparse/sparse.c b/libsparse/sparse.c
index d6f556114..a6134c9d2 100644
--- a/libsparse/sparse.c
+++ b/libsparse/sparse.c
@@ -32,7 +32,11 @@ struct sparse_file *sparse_file_new(unsigned int block_size, int64_t len)
32 return NULL; 32 return NULL;
33 } 33 }
34 34
35 /* TODO: allocate backed block list */ 35 s->backed_block_list = backed_block_list_new();
36 if (!s->backed_block_list) {
37 free(s);
38 return NULL;
39 }
36 40
37 s->block_size = block_size; 41 s->block_size = block_size;
38 s->len = len; 42 s->len = len;
@@ -42,14 +46,14 @@ struct sparse_file *sparse_file_new(unsigned int block_size, int64_t len)
42 46
43void sparse_file_destroy(struct sparse_file *s) 47void sparse_file_destroy(struct sparse_file *s)
44{ 48{
45 free_data_blocks(); 49 backed_block_list_destroy(s->backed_block_list);
46 free(s); 50 free(s);
47} 51}
48 52
49int sparse_file_add_data(struct sparse_file *s, 53int sparse_file_add_data(struct sparse_file *s,
50 void *data, unsigned int len, unsigned int block) 54 void *data, unsigned int len, unsigned int block)
51{ 55{
52 queue_data_block(data, len, block); 56 queue_data_block(s->backed_block_list, data, len, block);
53 57
54 return 0; 58 return 0;
55} 59}
@@ -57,7 +61,7 @@ int sparse_file_add_data(struct sparse_file *s,
57int sparse_file_add_fill(struct sparse_file *s, 61int sparse_file_add_fill(struct sparse_file *s,
58 uint32_t fill_val, unsigned int len, unsigned int block) 62 uint32_t fill_val, unsigned int len, unsigned int block)
59{ 63{
60 queue_fill_block(fill_val, len, block); 64 queue_fill_block(s->backed_block_list, fill_val, len, block);
61 65
62 return 0; 66 return 0;
63} 67}
@@ -66,7 +70,7 @@ int sparse_file_add_file(struct sparse_file *s,
66 const char *filename, int64_t file_offset, unsigned int len, 70 const char *filename, int64_t file_offset, unsigned int len,
67 unsigned int block) 71 unsigned int block)
68{ 72{
69 queue_data_file(filename, file_offset, len, block); 73 queue_data_file(s->backed_block_list, filename, file_offset, len, block);
70 74
71 return 0; 75 return 0;
72} 76}
@@ -105,11 +109,13 @@ static void count_file_block(void *priv, int64_t off, const char *file,
105 count_chunks->chunks++; 109 count_chunks->chunks++;
106} 110}
107 111
108static int count_sparse_chunks(unsigned int block_size, int64_t len) 112static int count_sparse_chunks(struct backed_block_list *b,
113 unsigned int block_size, int64_t len)
109{ 114{
110 struct count_chunks count_chunks = {0, 0, block_size}; 115 struct count_chunks count_chunks = {0, 0, block_size};
111 116
112 for_each_data_block(count_data_block, count_file_block, count_fill_block, &count_chunks, block_size); 117 for_each_data_block(b, count_data_block, count_file_block,
118 count_fill_block, &count_chunks, block_size);
113 119
114 if (count_chunks.cur_ptr != len) 120 if (count_chunks.cur_ptr != len)
115 count_chunks.chunks++; 121 count_chunks.chunks++;
@@ -136,14 +142,16 @@ static void ext4_write_data_file(void *priv, int64_t off, const char *file,
136int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse, 142int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse,
137 bool crc) 143 bool crc)
138{ 144{
139 int chunks = count_sparse_chunks(s->block_size, s->len); 145 int chunks = count_sparse_chunks(s->backed_block_list, s->block_size,
146 s->len);
140 struct output_file *out = open_output_fd(fd, s->block_size, s->len, 147 struct output_file *out = open_output_fd(fd, s->block_size, s->len,
141 gz, sparse, chunks, crc); 148 gz, sparse, chunks, crc);
142 149
143 if (!out) 150 if (!out)
144 return -ENOMEM; 151 return -ENOMEM;
145 152
146 for_each_data_block(ext4_write_data_block, ext4_write_data_file, ext4_write_fill_block, out, s->block_size); 153 for_each_data_block(s->backed_block_list, ext4_write_data_block,
154 ext4_write_data_file, ext4_write_fill_block, out, s->block_size);
147 155
148 if (s->len) 156 if (s->len)
149 pad_output_file(out, s->len); 157 pad_output_file(out, s->len);
diff --git a/libsparse/sparse_file.h b/libsparse/sparse_file.h
index 05a78d96e..fae1c168c 100644
--- a/libsparse/sparse_file.h
+++ b/libsparse/sparse_file.h
@@ -23,6 +23,7 @@ struct sparse_file {
23 unsigned int block_size; 23 unsigned int block_size;
24 int64_t len; 24 int64_t len;
25 25
26 struct backed_block_list *backed_block_list;
26 struct output_file *out; 27 struct output_file *out;
27}; 28};
28 29