1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 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 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 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. 15 */ 16 17#include <assert.h> 18#include <stdlib.h> 19 20#include <sparse/sparse.h> 21 22#include "sparse_file.h" 23 24#include "output_file.h" 25#include "backed_block.h" 26#include "sparse_defs.h" 27#include "sparse_format.h" 28 29struct sparse_file *sparse_file_new(unsigned int block_size, int64_t len) 30{ 31 struct sparse_file *s = calloc(sizeof(struct sparse_file), 1); 32 if (!s) { 33 return NULL; 34 } 35 36 s->backed_block_list = backed_block_list_new(block_size); 37 if (!s->backed_block_list) { 38 free(s); 39 return NULL; 40 } 41 42 s->block_size = block_size; 43 s->len = len; 44 45 return s; 46} 47 48void sparse_file_destroy(struct sparse_file *s) 49{ 50 backed_block_list_destroy(s->backed_block_list); 51 free(s); 52} 53 54int sparse_file_add_data(struct sparse_file *s, 55 void *data, unsigned int len, unsigned int block) 56{ 57 return backed_block_add_data(s->backed_block_list, data, len, block); 58} 59 60int sparse_file_add_fill(struct sparse_file *s, 61 uint32_t fill_val, unsigned int len, unsigned int block) 62{ 63 return backed_block_add_fill(s->backed_block_list, fill_val, len, block); 64} 65 66int sparse_file_add_file(struct sparse_file *s, 67 const char *filename, int64_t file_offset, unsigned int len, 68 unsigned int block) 69{ 70 return backed_block_add_file(s->backed_block_list, filename, file_offset, 71 len, block); 72} 73 74int sparse_file_add_fd(struct sparse_file *s, 75 int fd, int64_t file_offset, unsigned int len, unsigned int block) 76{ 77 return backed_block_add_fd(s->backed_block_list, fd, file_offset, 78 len, block); 79} 80unsigned int sparse_count_chunks(struct sparse_file *s) 81{ 82 struct backed_block *bb; 83 unsigned int last_block = 0; 84 unsigned int chunks = 0; 85 86 for (bb = backed_block_iter_new(s->backed_block_list); bb; 87 bb = backed_block_iter_next(bb)) { 88 if (backed_block_block(bb) > last_block) { 89 /* If there is a gap between chunks, add a skip chunk */ 90 chunks++; 91 } 92 chunks++; 93 last_block = backed_block_block(bb) + 94 DIV_ROUND_UP(backed_block_len(bb), s->block_size); 95 } 96 if (last_block < DIV_ROUND_UP(s->len, s->block_size)) { 97 chunks++; 98 } 99 100 return chunks; 101} 102 103static void sparse_file_write_block(struct output_file *out, 104 struct backed_block *bb) 105{ 106 switch (backed_block_type(bb)) { 107 case BACKED_BLOCK_DATA: 108 write_data_chunk(out, backed_block_len(bb), backed_block_data(bb)); 109 break; 110 case BACKED_BLOCK_FILE: 111 write_file_chunk(out, backed_block_len(bb), 112 backed_block_filename(bb), backed_block_file_offset(bb)); 113 break; 114 case BACKED_BLOCK_FD: 115 write_fd_chunk(out, backed_block_len(bb), 116 backed_block_fd(bb), backed_block_file_offset(bb)); 117 break; 118 case BACKED_BLOCK_FILL: 119 write_fill_chunk(out, backed_block_len(bb), 120 backed_block_fill_val(bb)); 121 break; 122 } 123} 124 125static int write_all_blocks(struct sparse_file *s, struct output_file *out) 126{ 127 struct backed_block *bb; 128 unsigned int last_block = 0; 129 int64_t pad; 130 131 for (bb = backed_block_iter_new(s->backed_block_list); bb; 132 bb = backed_block_iter_next(bb)) { 133 if (backed_block_block(bb) > last_block) { 134 unsigned int blocks = backed_block_block(bb) - last_block; 135 write_skip_chunk(out, (int64_t)blocks * s->block_size); 136 } 137 sparse_file_write_block(out, bb); 138 last_block = backed_block_block(bb) + 139 DIV_ROUND_UP(backed_block_len(bb), s->block_size); 140 } 141 142 pad = s->len - (int64_t)last_block * s->block_size; 143 assert(pad >= 0); 144 if (pad > 0) { 145 write_skip_chunk(out, pad); 146 } 147 148 return 0; 149} 150 151int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse, 152 bool crc) 153{ 154 int ret; 155 int chunks; 156 struct output_file *out; 157 158 chunks = sparse_count_chunks(s); 159 out = output_file_open_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc); 160 161 if (!out) 162 return -ENOMEM; 163 164 ret = write_all_blocks(s, out); 165 166 output_file_close(out); 167 168 return ret; 169} 170 171int sparse_file_callback(struct sparse_file *s, bool sparse, bool crc, 172 int (*write)(void *priv, const void *data, int len), void *priv) 173{ 174 int ret; 175 int chunks; 176 struct output_file *out; 177 178 chunks = sparse_count_chunks(s); 179 out = output_file_open_callback(write, priv, s->block_size, s->len, false, 180 sparse, chunks, crc); 181 182 if (!out) 183 return -ENOMEM; 184 185 ret = write_all_blocks(s, out); 186 187 output_file_close(out); 188 189 return ret; 190} 191 192static int out_counter_write(void *priv, const void *data, int len) 193{ 194 int64_t *count = priv; 195 *count += len; 196 return 0; 197} 198 199int64_t sparse_file_len(struct sparse_file *s, bool sparse, bool crc) 200{ 201 int ret; 202 int chunks = sparse_count_chunks(s); 203 int64_t count = 0; 204 struct output_file *out; 205 206 out = output_file_open_callback(out_counter_write, &count, 207 s->block_size, s->len, false, sparse, chunks, crc); 208 if (!out) { 209 return -1; 210 } 211 212 ret = write_all_blocks(s, out); 213 214 output_file_close(out); 215 216 if (ret < 0) { 217 return -1; 218 } 219 220 return count; 221} 222 223static struct backed_block *move_chunks_up_to_len(struct sparse_file *from, 224 struct sparse_file *to, unsigned int len) 225{ 226 int64_t count = 0; 227 struct output_file *out_counter; 228 struct backed_block *last_bb = NULL; 229 struct backed_block *bb; 230 struct backed_block *start; 231 int64_t file_len = 0; 232 233 /* 234 * overhead is sparse file header, initial skip chunk, split chunk, end 235 * skip chunk, and crc chunk. 236 */ 237 int overhead = sizeof(sparse_header_t) + 4 * sizeof(chunk_header_t) + 238 sizeof(uint32_t); 239 len -= overhead; 240 241 start = backed_block_iter_new(from->backed_block_list); 242 out_counter = output_file_open_callback(out_counter_write, &count, 243 to->block_size, to->len, false, true, 0, false); 244 if (!out_counter) { 245 return NULL; 246 } 247 248 for (bb = start; bb; bb = backed_block_iter_next(bb)) { 249 count = 0; 250 /* will call out_counter_write to update count */ 251 sparse_file_write_block(out_counter, bb); 252 if (file_len + count > len) { 253 /* 254 * If the remaining available size is more than 1/8th of the 255 * requested size, split the chunk. Results in sparse files that 256 * are at least 7/8ths of the requested size 257 */ 258 if (!last_bb || (len - file_len > (len / 8))) { 259 backed_block_split(from->backed_block_list, bb, len - file_len); 260 last_bb = bb; 261 } 262 goto out; 263 } 264 file_len += count; 265 last_bb = bb; 266 } 267 268out: 269 backed_block_list_move(from->backed_block_list, 270 to->backed_block_list, start, last_bb); 271 272 output_file_close(out_counter); 273 274 return bb; 275} 276 277int sparse_file_resparse(struct sparse_file *in_s, unsigned int max_len, 278 struct sparse_file **out_s, int out_s_count) 279{ 280 struct backed_block *bb; 281 unsigned int overhead; 282 struct sparse_file *s; 283 struct sparse_file *tmp; 284 int c = 0; 285 286 tmp = sparse_file_new(in_s->block_size, in_s->len); 287 if (!tmp) { 288 return -ENOMEM; 289 } 290 291 do { 292 s = sparse_file_new(in_s->block_size, in_s->len); 293 294 bb = move_chunks_up_to_len(in_s, s, max_len); 295 296 if (c < out_s_count) { 297 out_s[c] = s; 298 } else { 299 backed_block_list_move(s->backed_block_list, tmp->backed_block_list, 300 NULL, NULL); 301 sparse_file_destroy(s); 302 } 303 c++; 304 } while (bb); 305 306 backed_block_list_move(tmp->backed_block_list, in_s->backed_block_list, 307 NULL, NULL); 308 309 sparse_file_destroy(tmp); 310 311 return c; 312} 313 314void sparse_file_verbose(struct sparse_file *s) 315{ 316 s->verbose = true; 317} 318