2 * Copyright (C) 2012 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 #include <sparse/sparse.h>
23 #include "sparse_file.h"
25 #include "output_file.h"
26 #include "backed_block.h"
27 #include "sparse_defs.h"
28 #include "sparse_format.h"
30 struct sparse_file
*sparse_file_new(unsigned int block_size
, int64_t len
)
32 struct sparse_file
*s
= calloc(sizeof(struct sparse_file
), 1);
37 s
->backed_block_list
= backed_block_list_new(block_size
);
38 if (!s
->backed_block_list
) {
43 s
->block_size
= block_size
;
49 void sparse_file_destroy(struct sparse_file
*s
)
51 backed_block_list_destroy(s
->backed_block_list
);
55 int sparse_file_add_data(struct sparse_file
*s
,
56 void *data
, unsigned int len
, unsigned int block
)
58 return backed_block_add_data(s
->backed_block_list
, data
, len
, block
);
61 int sparse_file_add_fill(struct sparse_file
*s
,
62 uint32_t fill_val
, unsigned int len
, unsigned int block
)
64 return backed_block_add_fill(s
->backed_block_list
, fill_val
, len
, block
);
67 int sparse_file_add_file(struct sparse_file
*s
,
68 const char *filename
, int64_t file_offset
, unsigned int len
,
71 return backed_block_add_file(s
->backed_block_list
, filename
, file_offset
,
75 int sparse_file_add_fd(struct sparse_file
*s
,
76 int fd
, int64_t file_offset
, unsigned int len
, unsigned int block
)
78 return backed_block_add_fd(s
->backed_block_list
, fd
, file_offset
,
81 unsigned int sparse_count_chunks(struct sparse_file
*s
)
83 struct backed_block
*bb
;
84 unsigned int last_block
= 0;
85 unsigned int chunks
= 0;
87 for (bb
= backed_block_iter_new(s
->backed_block_list
); bb
;
88 bb
= backed_block_iter_next(bb
)) {
89 if (backed_block_block(bb
) > last_block
) {
90 /* If there is a gap between chunks, add a skip chunk */
94 last_block
= backed_block_block(bb
) +
95 DIV_ROUND_UP(backed_block_len(bb
), s
->block_size
);
97 if (last_block
< DIV_ROUND_UP(s
->len
, s
->block_size
)) {
104 static int sparse_file_write_block(struct output_file
*out
,
105 struct backed_block
*bb
)
109 switch (backed_block_type(bb
)) {
110 case BACKED_BLOCK_DATA
:
111 ret
= write_data_chunk(out
, backed_block_len(bb
), backed_block_data(bb
));
113 case BACKED_BLOCK_FILE
:
114 ret
= write_file_chunk(out
, backed_block_len(bb
),
115 backed_block_filename(bb
),
116 backed_block_file_offset(bb
));
118 case BACKED_BLOCK_FD
:
119 ret
= write_fd_chunk(out
, backed_block_len(bb
),
121 backed_block_file_offset(bb
));
123 case BACKED_BLOCK_FILL
:
124 ret
= write_fill_chunk(out
, backed_block_len(bb
),
125 backed_block_fill_val(bb
));
132 static int write_all_blocks(struct sparse_file
*s
, struct output_file
*out
)
134 struct backed_block
*bb
;
135 unsigned int last_block
= 0;
139 for (bb
= backed_block_iter_new(s
->backed_block_list
); bb
;
140 bb
= backed_block_iter_next(bb
)) {
141 if (backed_block_block(bb
) > last_block
) {
142 unsigned int blocks
= backed_block_block(bb
) - last_block
;
143 write_skip_chunk(out
, (int64_t)blocks
* s
->block_size
);
145 ret
= sparse_file_write_block(out
, bb
);
148 last_block
= backed_block_block(bb
) +
149 DIV_ROUND_UP(backed_block_len(bb
), s
->block_size
);
152 pad
= s
->len
- (int64_t)last_block
* s
->block_size
;
155 write_skip_chunk(out
, pad
);
161 int sparse_file_write(struct sparse_file
*s
, int fd
, bool gz
, bool sparse
,
166 struct output_file
*out
;
168 chunks
= sparse_count_chunks(s
);
169 out
= output_file_open_fd(fd
, s
->block_size
, s
->len
, gz
, sparse
, chunks
, crc
);
174 ret
= write_all_blocks(s
, out
);
176 output_file_close(out
);
181 int sparse_file_callback(struct sparse_file
*s
, bool sparse
, bool crc
,
182 int (*write
)(void *priv
, const void *data
, int len
), void *priv
)
186 struct output_file
*out
;
188 chunks
= sparse_count_chunks(s
);
189 out
= output_file_open_callback(write
, priv
, s
->block_size
, s
->len
, false,
190 sparse
, chunks
, crc
);
195 ret
= write_all_blocks(s
, out
);
197 output_file_close(out
);
202 static int out_counter_write(void *priv
, const void *data __unused
, int len
)
204 int64_t *count
= priv
;
209 int64_t sparse_file_len(struct sparse_file
*s
, bool sparse
, bool crc
)
212 int chunks
= sparse_count_chunks(s
);
214 struct output_file
*out
;
216 out
= output_file_open_callback(out_counter_write
, &count
,
217 s
->block_size
, s
->len
, false, sparse
, chunks
, crc
);
222 ret
= write_all_blocks(s
, out
);
224 output_file_close(out
);
233 static struct backed_block
*move_chunks_up_to_len(struct sparse_file
*from
,
234 struct sparse_file
*to
, unsigned int len
)
237 struct output_file
*out_counter
;
238 struct backed_block
*last_bb
= NULL
;
239 struct backed_block
*bb
;
240 struct backed_block
*start
;
241 int64_t file_len
= 0;
245 * overhead is sparse file header, initial skip chunk, split chunk, end
246 * skip chunk, and crc chunk.
248 int overhead
= sizeof(sparse_header_t
) + 4 * sizeof(chunk_header_t
) +
252 start
= backed_block_iter_new(from
->backed_block_list
);
253 out_counter
= output_file_open_callback(out_counter_write
, &count
,
254 to
->block_size
, to
->len
, false, true, 0, false);
259 for (bb
= start
; bb
; bb
= backed_block_iter_next(bb
)) {
261 /* will call out_counter_write to update count */
262 ret
= sparse_file_write_block(out_counter
, bb
);
267 if (file_len
+ count
> len
) {
269 * If the remaining available size is more than 1/8th of the
270 * requested size, split the chunk. Results in sparse files that
271 * are at least 7/8ths of the requested size
273 if (!last_bb
|| (len
- file_len
> (len
/ 8))) {
274 backed_block_split(from
->backed_block_list
, bb
, len
- file_len
);
284 backed_block_list_move(from
->backed_block_list
,
285 to
->backed_block_list
, start
, last_bb
);
288 output_file_close(out_counter
);
293 int sparse_file_resparse(struct sparse_file
*in_s
, unsigned int max_len
,
294 struct sparse_file
**out_s
, int out_s_count
)
296 struct backed_block
*bb
;
297 struct sparse_file
*s
;
298 struct sparse_file
*tmp
;
301 tmp
= sparse_file_new(in_s
->block_size
, in_s
->len
);
307 s
= sparse_file_new(in_s
->block_size
, in_s
->len
);
309 bb
= move_chunks_up_to_len(in_s
, s
, max_len
);
311 if (c
< out_s_count
) {
314 backed_block_list_move(s
->backed_block_list
, tmp
->backed_block_list
,
316 sparse_file_destroy(s
);
321 backed_block_list_move(tmp
->backed_block_list
, in_s
->backed_block_list
,
324 sparse_file_destroy(tmp
);
329 void sparse_file_verbose(struct sparse_file
*s
)